nextjs项目使用react-pdf生成PDF文件遇到的问题

Error: render is a Node specific API

我想要做一个功能,点击按钮时可以触发生成pdf文件并下载,所以觉得可以调用下面的js去执行

ReactPDF.render(<Pdf data={data}/>, `${__dirname}/example.pdf`)

结果就是报错了
在这里插入图片描述render这个方法是一个node api,看来只能在服务侧运行了,我不想这么干

Error: PDFDownloadLink is a web specific API

所以我又尝试了PDFDownloadLink,用如下的代码

<PDFDownloadLink
  document={<Pdf data={data}/>}
  fileName='somename.pdf'
>
  {({ loading }) => (loading ? 'Loading document...' : 'Download now!')}
</PDFDownloadLink>

结果又报错了
在这里插入图片描述
PDFDownloadLink是一个web api只能在浏览器端运行,原来nextjs的页面是在服务端渲染的,所以才报了这个错,而上面的render方法却是在页面上按钮触发时调用的,所以报了那个错。
所以,要让PDFDownloadLink在页面返回浏览器之后再渲染,于是有了下面的代码

const [isClient, setIsClient] = useState(false)
useEffect(() => {
 setIsClient(true)
}, [])
...
{
 isClient ? (
    <PDFDownloadLink
      document={<Pdf data={data}/>}
      fileName='somename.pdf'
    >
      {({ loading }) => (loading ? 'Loading document...' : 'Download now!')}
    </PDFDownloadLink>
  ) : null
}
...

isClient在页面渲染完之后置为TRUE,这时候才去渲染PDFDownloadLink
可能这个方案有时候觉得还不够完美,那么可以使用另一个方案,那就是动态加载PDFDownloadLink,设置ssr=false,这个方法相比判断isClient似乎更加优雅一点
将PDFDownloadLink放在pdf组件中,整个组件需要动态引入

import { PDFDownloadLink } from "@react-pdf/renderer"
import Pdf from "."

const DyPdf = ({ ...props }) => {
  return (
    <PDFDownloadLink
      document={<Pdf data={props.data} />}
      fileName='somename.pdf'
    >
      {({ loading }) => (loading ? 'Loading document...' : 'Download now!')}
    </PDFDownloadLink>
  )
}

export default DyPdf

在使用的地方动态引入,就可以当作普通标签正常使用了

const DyPdf = dynamic(() => import("components/DyPdf"), {
  loading: () => <p>Loading...</p>,
  ssr: false,
  });
  ...
<DyPdf data={data}></DyPdf>

中文乱码问题

这个需要register字体,将字体文件放在项目里,调用Font.register
字体文件在我们电脑上就可以找到,我的字体文件在C:\Windows\Fonts
最好是找*.ttf文件,因为我试了*.ttc文件不行,可能是兼容性问题
我这个是仿宋,我是放在pdf模板文件中调用的,下面就可以使用这个字体了

Font.register({
  family: 'fangsong',
  src: '/fonts/simfang.ttf'
});

自动换行问题

对于英文文档,使用css样式就可以解决自动换行的问题了,但是对于中文却不能,这里增加一个分词器,就可以完美解决这个问题,我也放在了pdf模板文件里

Font.registerHyphenationCallback((word: string) => {
  if (word.length === 1) {
    return [word]
  }
  return Array.from(word).flatMap(char => {
    const c = char.charCodeAt(0)
    // 判断如果是中文,就在前后增加空字符串(注意是空字符串,不是空格)
    if (c >= 0x4E00 && c <= 0x9FA5) {
      return ['', char, '']
    }
    return [char]
  })
})

总结

以上,这些问题的解决方法与思路,当然不是我自己摸黑摸索出来的,要感谢强大的网友,当时搜索过的链接没有仔细保存,只能是万分感谢了,不过我还是进行了整合还有改进的,当时搜索的时候网友没有描述清楚的,我也具体化了,哈哈,我还是有功劳的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

n_rts

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值