You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:15:56