在开始微信小程序的图片转PDF功能开发前,我们需要先搭建好开发环境。这里推荐使用微信开发者工具的最新稳定版本,它能提供完整的调试和预览功能。我实测过多个版本,发现2.25.1以上的版本对npm支持最稳定。
首先在项目根目录下执行这两个npm命令:
bash复制npm install pdf-lib
npm install @pdf-lib/fontkit
安装完成后,需要在微信开发者工具中点击"工具"→"构建npm",这一步经常被新手忽略。我遇到过好几次构建失败的情况,后来发现是因为node_modules目录权限问题。建议在安装前先删除旧的node_modules目录,确保干净的环境。
在utils目录下新建pdf.js文件,这个文件将包含核心的PDF生成逻辑。为什么选择utils目录?因为这是微信小程序项目的约定俗成位置,方便统一管理工具函数。实际开发中,你也可以根据项目结构灵活调整。
图片处理是整个功能的基础环节。我们首先需要处理用户上传的图片数组,这里支持JPG和PNG两种常见格式。在代码实现时,我发现微信小程序的临时文件路径需要特殊处理:
javascript复制let sBase64 = wx.getFileSystemManager().readFileSync(lian, 'base64')
这段代码将图片转换为Base64格式,方便后续嵌入PDF。实测中发现,大图片(超过5MB)直接转换可能会导致内存溢出。我的解决方案是先通过wx.compressImage压缩图片,再进行处理。
图片尺寸计算是个关键点。为了保证PDF中图片显示正常,需要保持原始宽高比。我采用的算法是固定宽度为700px,高度按比例缩放:
javascript复制let h = (img.height * 700) / img.width
PDF文档的页面设置需要特别注意。我采用的方法是先计算所有图片的总高度,然后创建对应高度的单页PDF:
javascript复制page.setWidth(800)
page.setHeight(zheight)
这里留了左右各50px的边距,让文档看起来更专业。在实际项目中,我发现有些开发者喜欢每张图片单独一页,这可以通过修改循环逻辑实现。两种方式各有优劣,单页适合连续阅读,多页适合单独查看。
图片定位采用了从下往上的绘制方式,这是PDF坐标系的特性决定的。通过guodu_height变量累计已绘制图片的高度,确保图片按顺序排列:
javascript复制let height = zheight - guodu_height
page.drawImage(content_arr[i].img, {
x: 50,
y: height,
width: content_arr[i].width,
height: content_arr[i].height
})
水印功能是很多业务场景的刚需。pdf-lib库原生支持文本水印,但有几个坑需要注意。首先是不支持中文水印,这是字体库的限制。我测试过多种解决方案,最终选择使用英文水印加版本号的组合:
javascript复制page.drawText(`Identification Assistant \n` + `\n` + `version: 1.0.12 \n`, {
size: 50,
color: rgb(1, 0, 0)
})
水印位置我选择放在文档中间,通过zheight/2计算垂直位置。实际开发中可以根据需求调整,比如斜角平铺、底部居中等不同样式。
对于更复杂的水印需求,我探索出两种进阶方案。第一种是使用Canvas预先合成水印图片,再将图片嵌入PDF。这种方法支持中文和图形水印,但会增加处理复杂度。
第二种方案是引入自定义字体。通过doc.registerFontkit注册中文字体后,就可以显示中文水印了。不过要注意字体文件大小,过大的字体会影响小程序性能。
水印透明度也是需要考虑的因素。pdf-lib的颜色rgb值支持透明度参数,可以创建半透明水印:
javascript复制color: rgb(1, 0, 0, 0.5) // 50%透明度的红色
微信小程序的媒体选择API提供了完善的图片获取功能。我推荐使用wx.chooseMedia而不是旧的wx.chooseImage,因为前者支持相机和相册的统一接口:
javascript复制wx.chooseMedia({
count: 9,
mediaType: ['image'],
sourceType: ['album', 'camera'],
success: (res) => {
// 处理返回的临时文件路径
}
})
在实际项目中,我增加了图片预览和排序功能,让用户可以在生成PDF前调整图片顺序。这个增强功能显著提升了用户体验。
PDF生成完成后,需要通过微信的开放接口进行保存和预览。这里有几个关键点需要注意:
javascript复制fs.writeFile({
filePath: wx.env.USER_DATA_PATH + "/output.pdf",
data: resl.docBase64,
encoding: "base64",
success: resx => {
wx.openDocument({
filePath: wx.env.USER_DATA_PATH + "/output.pdf"
})
}
})
wx.env.USER_DATA_PATH是小程序的用户文件目录,具有读写权限。我遇到过文件保存成功但无法打开的情况,后来发现是文件名中包含了中文或特殊字符。建议统一使用英文命名。
处理多张大图时,内存管理尤为重要。我总结出几个优化点:
可以通过wx.cleanStorage清理缓存文件,特别是在处理失败时:
javascript复制wx.cleanStorage({
success: () => console.log('缓存清理完成')
})
完善的错误处理能大幅提升用户体验。我建议至少处理以下几种常见错误:
在代码中加入try-catch块捕获异常,并给出友好提示:
javascript复制try {
// PDF生成代码
} catch (e) {
wx.showToast({
title: '生成失败:' + e.message,
icon: 'none'
})
}
在最近的一个电商项目中,我实现了带商品信息的水印功能。水印内容包含商品ID、价格和购买日期,有效防止了图片被盗用。这个案例证明,水印功能可以做得非常灵活实用。
另一个教育类小程序中,我们允许用户自定义水印文字和位置。这个需求通过增加配置参数轻松实现,证明了基础架构的重要性。好的代码应该易于扩展,而不是每次改动都要大动干戈。
处理过最棘手的情况是一位用户上传了50张高清图片,导致小程序闪退。最终解决方案是分批次处理,每处理10张就保存进度,并显示处理进度条。这个经验告诉我,性能优化需要结合实际使用场景。