前端svg生成pdf 字体问题解决方案 pdfkit+svg-to-pdfkit

对字体的要求:常用的科研使用字体,可商业
问题提出的原因是Echarts生成的svg,字体在生成pdf后变了,需求是既希望保留字体,也希望保持矢量特性
所以很多网上的解决方案转成png再插入pdf文档中并不能满足我的需要(失去了矢量特性)

参考:
pdfkit内置了一些字体

// Set the font size
doc.fontSize(18);

// Using a standard PDF font
doc.font('Times-Roman')
   .text('Hello from Times Roman!')
   .moveDown(0.5);

// Using a TrueType font (.ttf)
doc.font('fonts/GoodDog.ttf')
   .text('This is Good Dog!')
   .moveDown(0.5);

可以看到,对应内置字体直接使用字体名字,而非内置字体需要字体文件。
接下来我们需要把svg加进来,需要用到svg-to-pdfkit
svg-to-pdfkit Github

PDFDocument.prototype.addSVG = function(svg, x, y, options) {
  return SVGtoPDF(this, svg, x, y, options), this;
};
doc [PDFDocument] = the PDF document created with PDFKit
svg [SVGElement or string] = the SVG object or XML code
x, y [number] = the position where the SVG will be added
options [Object] = >
  - width, height [number] = initial viewport, by default it's the page dimensions
  - preserveAspectRatio [string] = override alignment of the SVG content inside its viewport
  - useCSS [boolean] = use the CSS styles computed by the browser (for SVGElement only)
  - fontCallback [function] = function called to get the fonts, see source code
  - imageCallback [function] = same as above for the images (for Node.js)
  - documentCallback [function] = same as above for the external SVG documents
  - colorCallback [function] = function called to get color, making mapping to CMYK possible
  - warningCallback [function] = function called when there is a warning
  - assumePt [boolean] = assume that units are PDF points instead of SVG pixels
  - precision [number] = precision factor for approximative calculations (default = 3)

这里面有个fontCallback,而问题的根源就出在这里,svg里设置了字体为“Time New Roman”,但最后生成的pdf中是Helvetica
查看svg-to-pdfkit相关源码发现,但你没有重写fontCallback函数的时候,它使用它自己的默认函数:

 if (typeof fontCallback !== 'function') {
      fontCallback = function(family, bold, italic, fontOptions) {
        // Check if the font is already registered in the document
        if (bold && italic) {
          if (doc._registeredFonts.hasOwnProperty(family + '-BoldItalic')) {
          ......
          // Use standard fonts as fallback
        if (family.match(/(?:^|,)\s*serif\s*$/)) {
          if (bold && italic) {return 'Times-BoldItalic';}
          if (bold && !italic) {return 'Times-Bold';}
          if (!bold && italic) {return 'Times-Italic';}
          if (!bold && !italic) {return 'Times-Roman';}
        } else if (family.match(/(?:^|,)\s*monospace\s*$/)) {
        ......
         } else if (family.match(/(?:^|,)\s*sans-serif\s*$/) || true) {
          if (bold && italic) {return 'Helvetica-BoldOblique';}
          if (bold && !italic) {return 'Helvetica-Bold';}
          if (!bold && italic) {return 'Helvetica-Oblique';}
          if (!bold && !italic) {return 'Helvetica';}

可以看到对于Times字体,它是使用serif进行匹配的,这就是为什么设置Times字体,它也内置了该字体,但是最后生成pdf却是Helvetica的原因

family.match(/(?:^|,)\s*serif\s*$/)

最终代码:

				let fontCallback = function (family, bold, italic, fontOptions) {
					// Use standard fonts as fallback
					if (family === 'Times New Roman') {
						if (bold && italic) {
							return 'Times-BoldItalic'
						}
						if (bold && !italic) {
							return 'Times-Bold'
						}
						if (!bold && italic) {
							return 'Times-Italic'
						}
						if (!bold && !italic) {
							return 'Times-Roman'
						}
					} else if (family === 'Courier') {
						if (bold && italic) {
							return 'Courier-BoldOblique'
						}
						if (bold && !italic) {
							return 'Courier-Bold'
						}
						if (!bold && italic) {
							return 'Courier-Oblique'
						}
						if (!bold && !italic) {
							return 'Courier'
						}
					} else if (family === 'Helvetica') {
						if (bold && italic) {
							return 'Helvetica-BoldOblique'
						}
						if (bold && !italic) {
							return 'Helvetica-Bold'
						}
						if (!bold && italic) {
							return 'Helvetica-Oblique'
						}
						if (!bold && !italic) {
							return 'Helvetica'
						}
					}

					// Check if the font is already registered in the document
					if (bold && italic) {
						if (doc._registeredFonts.hasOwnProperty(family + '-BoldItalic')) {
							return family + '-BoldItalic'
						} else if (doc._registeredFonts.hasOwnProperty(family + '-Italic')) {
							fontOptions.fauxBold = true
							return family + '-Italic'
						} else if (doc._registeredFonts.hasOwnProperty(family + '-Bold')) {
							fontOptions.fauxItalic = true
							return family + '-Bold'
						} else if (doc._registeredFonts.hasOwnProperty(family)) {
							fontOptions.fauxBold = true
							fontOptions.fauxItalic = true
							return family
						}
					}
					if (bold && !italic) {
						if (doc._registeredFonts.hasOwnProperty(family + '-Bold')) {
							return family + '-Bold'
						} else if (doc._registeredFonts.hasOwnProperty(family)) {
							fontOptions.fauxBold = true
							return family
						}
					}
					if (!bold && italic) {
						if (doc._registeredFonts.hasOwnProperty(family + '-Italic')) {
							return family + '-Italic'
						} else if (doc._registeredFonts.hasOwnProperty(family)) {
							fontOptions.fauxItalic = true
							return family
						}
					}
					if (!bold && !italic) {
						if (doc._registeredFonts.hasOwnProperty(family)) {
							return family
						}
					}
					return 'Time-Roman'
				}

				SVGtoPDF(doc, svgElement, 0, 0, {
					width: this.width,
					height: this.height,
					fontCallback: fontCallback,
				})

这个方案得到的字体虽然是正确的,但是已经是转换成路径,不再可编辑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值