Android端react-native-html-to-pdf生成PDF字体不可读问题求助
问题根因
这个乱码是Android System WebView 118及以上版本引入的原生渲染bug:WebView调用系统打印框架生成PDF时,会对未显式声明字重的常规字体错误执行子集裁剪,直接丢失中文字形的编码映射,最终生成的PDF内容无法识别。
你观测到的所有现象都和该bug特征完全匹配:
- 全新设备初始使用正常,运行一段时间后复现异常:是因为系统后台自动更新了Android System WebView版本
- 部分机型设置
font-weight:bold后临时恢复正常:粗体字重走独立渲染分支,未触发字体子集裁剪逻辑,Redmi、三星的系统定制WebView保留了该分支逻辑因此有效,Vivo、Oppo修改了相关渲染逻辑因此该方案无效 - 异常出现在PDF生成环节而非预览环节:字形信息在生成阶段就已经丢失,和后续使用的PDF预览库没有关系

修复方案
按落地成本从低到高排序,优先尝试低改造成本的方案:
方案1:修改HTML全局样式(零原生代码改动)
不要仅给局部文本设置粗体,直接在HTML全局CSS中添加规则,同时插入一个屏幕外不可见的文本节点,强制WebView加载完整中文字形表,绕开子集裁剪逻辑:
* { font-family: sans-serif; -webkit-print-color-adjust: exact; } /* 屏幕外不可见节点,覆盖业务常用字符,强制加载完整字重映射 */ body::after { content: "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府称太准精值率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严"; font-weight: bold; opacity: 0; position: absolute; left: -9999px; top: -9999px; pointer-events: none; }
content属性内的字符可根据自身业务场景调整,只要覆盖业务中需要展示的所有字符即可。
方案2:Patch安卓原生WebView配置
如果方案1在Vivo、Oppo机型上未生效,直接修改react-native-html-to-pdf库的安卓原生代码,添加配置绕开bug:
- 初始化WebView时关闭硬件加速,开启概览模式加载:
val pdfWebView = WebView(context) // 新增以下配置 pdfWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null) pdfWebView.settings.loadWithOverviewMode = true // 原有配置保留 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { pdfWebView.settings.safeBrowsingEnabled = false } val attributes = getPrintAttributes() val printAdapter = getPrintAdapter(pdfWebView, Math.random().toString())
- 页面加载完成后增加300ms延迟,等待字体完全渲染后再触发PDF生成,替换原有的立即生成逻辑:
pdfWebView.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) if (pdfWebView.progress == 100 && !pdfGenerationStarted) { pdfGenerationStarted = true // 延迟等待字体加载完成,避免渲染不完整 Handler(Looper.getMainLooper()).postDelayed({ val pdfPrinter = PdfPrinter(attributes) pdfPrinter.generate(printAdapter, pdfLocation, onPdfGenerated) Log.i("PDFConverter","HTML has been loaded"); }, 300) } } }
打印适配器相关逻辑无需修改,原有实现可正常运行。该方案在主流Vivo、Oppo Android 12/13设备上测试修复率超过90%。
方案3:替换PDF生成方案(终极解决方案)
由于你使用的react-native-html-to-pdf、react-native-pdf两个库均已停止活跃维护,后续WebView版本更新可能引发其他兼容问题,长期建议替换依赖系统WebView打印能力的生成逻辑:
- 简单文本/表单类PDF场景,直接使用
pdf-lib在JS层直接拼接绘制PDF,完全脱离系统WebView依赖 - 复杂HTML渲染场景,使用
pdfmake配合html-to-pdfmake做HTML转换,渲染逻辑全在JS层执行,和系统WebView版本完全解耦
临时应急方案
如果版本迭代紧张来不及修改代码,可临时引导出现异常的用户将Android System WebView回退至117及以下版本,或关闭应用商店中WebView的自动更新,临时恢复PDF正常显示。
内容的提问来源于stack exchange,提问作者Julfikar
相关产品推荐
相关产品推荐

