SpreadJS 导出PDF是一个强大的功能,解决了很多用户的实际问题,但是在导出PDF时有些问题始终困扰着很多开发者,例如排版、字体乱码等问题。那么本文就着重讲解一下字体乱码的解决办法。
问题分析
有些朋友会有疑问,同样是使用了printInfo实例来调整样式,为什么浏览器打印看着好好的字体,导出到PDF就乱码了呢?
这是由PDF文件的特殊性造成的。PDF是Portable Document Format的简称,意为“便携式文档格式”,它原生仅仅带有英文字体,不包含任何中文字体,因此当导出的内容中含有中文字体编码时,一定会显示乱码,所以通常情况下,我们都需要为PDF进行字体注册操作。
Spread JS 对于导出PDF时的字体乱码的处理方式,分为两个部分,一是表格内容,二是页眉页脚。下面我将利用一个代码示例来说明如何处理乱码。
示例下载与部署
由于字体文件比较大,因此示例项目采用网盘来分享,请访问:
链接:https://pan.baidu.com/s/1Cw33CHC-aE9YmGyhLBlYlw 密码:sq4v
下载完毕后,将得到的zip包解压到您系统的任意目录下,本例是直接解压到D盘根目录下。
这个示例是纯前端工程,可以直接运行,但是JS访问目录下的字体文件时,很大概率会被浏览器的安全策略所限制,这是前端语言无法避免的问题,所以我们先把项目部署到服务器。
本例直接采用了Win10系统的IIS服务器来部署,如果不了解如何部署IIS工程,请移步:
部署成功后,访问localhost:[port num]/index.html即可访问到示例页面,本例设置端口是8899,即:http://localhost:8899/index.html
示例分析
为了把问题考虑得更全面,本例采用了多种字体和多种注册方法来全面展示如何解决导出PDF的乱码问题,字体包括微软雅黑和宋体,注册方法分为前端注册和Ajax访问后端字体文件注册两种方式。
前端字体注册:
要实现前端注册字体,需要获取到对应字体文件的base64编码字符串,示例目录下的data/fonts.js中有两种字体的base64编码,分别是MTCORSVA和simkai,代码如下:
function addFontsToFontManager(fontsObj) { var fonts = { normal: fontsObj["simkai.ttf"] }; GC.Spread.Sheets.PDF.PDFFontsManager.fallbackFont = function (font) { var fontInfoArray = font.split(' '); var fontName = fontInfoArray[fontInfoArray.length - 1]; if (fontName === 'mtcorsva') { return fonts.normal; } } }
其中参数fontsObj就是fonts.js中定义的字体编码实例。此处实现了将simkai注册到PDF中,除此之外还将默认字体设置为mtcorsva。
访问后台字体文件执行注册:
由于字体文件通常比较大,而前端注册字体的方法要求在页面加载时就要加载进来,这会导致前端加载时间长,影响性能。而且字体文件的base64编码通常也需要事先生成,这给开发同学们带来了不小的麻烦。
后台字体文件注册可以解决这些问题,字体文件在服务器上存储,只有当需要导出PDF时才执行加载操作,简直完美解决问题!
先看代码:
function registerServerFont(name, type, serverPath) { var xhr = new XMLHttpRequest(); xhr.open('GET', serverPath, true); xhr.responseType = 'blob'; xhr.onload = function (e) { if (this.status == 200) { // get binary data as a response var blob = this.response; //将Blob 对象转换成 ArrayBuffer var reader = new FileReader(); reader.onload = function (e) { var fontrrayBuffer = reader.result; var fonts = GC.Spread.Sheets.PDF.PDFFontsManager.getFont(name) || {}; fonts[type] = fontrrayBuffer; GC.Spread.Sheets.PDF.PDFFontsManager.registerFont(name, fonts); } reader.readAsArrayBuffer(blob); } }; xhr.send(); }
OK,熟悉Ajax的同学就明白了这里实际上采用了XMLHttpRequest对象的数据流读取了blob格式的字体文件,然后把这个流直接注册到PDFFontsManager中,实现了字体注册操作。
调用方法很简单,如下:
registerServerFont('微软雅黑', 'normal', 'font/msyh.ttf');
第一个参数对应了PDF中的字体名称,最后一个参数指定了字体文件的相对路径(这就是为什么要先部署后才能访问)。
如果设置的字体在后台字体库中没有怎么办?其实解决方法也很简单,我们的客户需要的首先应该是一个能够阅读的PDF文件,当服务器端没有对应的字体文件时,可以指定别的字体库来代替,例如:
registerServerFont('Times New Roman', 'normal', 'font/simsun.ttf');
这是我们小伙伴遇到的一个实际的问题,客户的Excel中显示的中文字体是Times New Roman,但是Times New Roman本身是英文字体库,并不包含中文,在这种情况下浏览器会自动指定一个字体,比如简宋,那么我们在注册PDF字体时也可以灵活应用。
页眉页脚乱码怎么办:
有同学发现,导出PDF时如果设置了页眉页脚,这里出现乱码怎么解决呢?其实学习指南里有代码示例,只是不在PDF导出里,而是在自定制打印的“描述”标签页的最下边,如图:
其中的printInfo.headerCenter("&\"Comic Sans MS\"System Information");的参数中是以“&”开头,后面的Comic Sans MS被双引号引起来了,这里就制定了字体名称。 在本例中,我们稍稍修改一下代码,验证一下这个方法: 在432行处添加一行:
<input type="text" id="headerCenter" value="表头内容" class="input" style="font-size: 16px">
在168行处添加一行:
printInfo.headerCenter('&"simsun"' + $("#headerCenter").val());
再执行导出,OK,问题解决!
注意:导出的PDF中仍会有一处乱码,这是有意留下的,这里的字体示例中没有注册,留给同学们练手。