对字体的要求:常用的科研使用字体,可商业
问题提出的原因是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,
})
这个方案得到的字体虽然是正确的,但是已经是转换成路径,不再可编辑