AWS Lambda中React-pdf/renderer渲染Vega SVG文本异常求助
SVG文本在React-pdf生成的PDF中渲染异常问题解决
问题背景
在AWS Lambda上运行Node.js函数,用Vega生成图表后通过React-pdf/renderer生成可下载PDF。直接将Vega生成的SVG保存到S3显示完全正常,但PDF中的SVG文本存在排版位置偏移、字体样式丢失的异常。
问题分析
核心问题出在自定义SVG转React-pdf组件的逻辑上——React-pdf的SVG组件与原生SVG的属性规则存在明显差异:
- React-pdf的
<Text>组件不直接支持原生SVG的transform属性,需手动解析translate值转换为x/y坐标 - 原生SVG采用短横线分隔的属性名(如
font-family),React-pdf要求驼峰命名(如fontFamily) - Lambda运行环境默认缺少常用字体库,导致
font-family: sans-serif无法正确渲染 - 原生SVG的
text-anchor属性需映射为React-pdf的textAlign属性,而非直接传递
解决方案
1. 修复属性映射逻辑
修改转换器代码,针对<Text>组件做针对性处理,适配React-pdf的属性规则:
import React from "react"; import { Path, Svg, Text, G } from "@react-pdf/renderer"; import { parse } from "svg-parser"; // 解析transform中的translate坐标 const parseTranslate = (transformStr: string) => { const match = transformStr.match(/translate\(([^)]+)\)/); if (!match) return { x: 0, y: 0 }; const [x, y] = match[1].split(",").map(num => parseFloat(num.trim())); return { x, y: y || 0 }; }; // 短横线属性名转驼峰命名 const toCamelCase = (str: string) => { return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase()); }; const convertJsonToPdfComponent = (jsonElement: any) => { if (jsonElement.type === "element") { const { tagName, properties, children } = jsonElement; // 统一转换属性名为驼峰格式 const convertedProps = Object.entries(properties || {}).reduce((acc, [key, value]) => { const camelKey = toCamelCase(key); acc[camelKey] = value; return acc; }, {} as any); switch (tagName) { case "svg": return ( <Svg {...convertedProps}> {children?.map((child: any) => convertJsonToPdfComponent(child))} </Svg> ); case "g": return ( <G {...convertedProps}> {children?.map((child: any) => convertJsonToPdfComponent(child))} </G> ); case "path": return <Path {...convertedProps} />; case "text": let textProps = { ...convertedProps }; // 解析translate转换为x/y属性 if (textProps.transform) { const { x, y } = parseTranslate(textProps.transform); textProps.x = x; textProps.y = y; delete textProps.transform; } // 映射text-anchor到textAlign if (textProps.textAnchor) { textProps.textAlign = textProps.textAnchor; delete textProps.textAnchor; } // 转换px单位为pt(React-pdf默认单位) if (textProps.fontSize?.endsWith("px")) { textProps.fontSize = `${parseFloat(textProps.fontSize) * 0.75}pt`; } return ( <Text {...textProps}> {children?.map((child: any) => convertJsonToPdfComponent(child))} </Text> ); default: return null; } } else if (jsonElement.type === "text") { return jsonElement.value; } return null; }; export const recursivelyConvertJsonToPdfComponents = (svgString: string) => { const svgObject = parse(svgString); return svgObject.children.map((child: any) => convertJsonToPdfComponent(child)); };
2. 为Lambda添加字体支持
Lambda默认环境缺少完整字体库,需手动添加所需字体:
- 下载开源无衬线字体(如
DejaVuSans.ttf)到项目fonts目录 - 在代码中注册字体:
import { Font } from "@react-pdf/renderer"; Font.register({ family: "sans-serif", src: `${__dirname}/fonts/DejaVuSans.ttf`, });
- 确保部署包包含字体文件(在
serverless.yml或SAM模板中配置字体文件的打包规则)
3. 验证效果
修改完成后,文本将通过x/y属性正确定位,字体样式通过驼峰属性正常传递,结合Lambda环境中的字体支持,PDF中的SVG文本渲染效果将与原生SVG完全一致。
内容的提问来源于stack exchange,提问作者WebSight
相关产品推荐
相关产品推荐

