最近在研究普通文本,图片如何转存为pdf文件,搜了一圈发现都是推荐用第三库,扒拉了一下比较出名的jsPDF的核心代码,算是小小的研究明白了,下面是图片转pdf的小demo代码以及效果图:
demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generate PDF with Image</title>
</head>
<body>
<button id="exportPdf">Export to PDF</button>
<script>
document.getElementById('exportPdf').addEventListener('click', () => {
try {
const imageData = createImage();
const pdfData = createPDF(imageData);
// 创建 Blob 并下载 PDF
const blob = new Blob([pdfData], { type: 'application/pdf' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'image.pdf';
link.click();
} catch (error) {
console.error('Error generating PDF:', error);
}
});
function createImage() {
// 创建一个 Canvas 元素并绘制图像
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 200;
canvas.height = 200;
context.fillStyle = 'green';
context.fillRect(10, 10, 180, 180);
// 将 canvas 内容转换为 Data URL
return canvas.toDataURL('image/jpeg', 1.0);
}
function createPDF(imageData) {
const width = 210; // A4 宽度 (mm)
const height = 297; // A4 高度 (mm)
const pdfWidth = width * 72 / 25.4; // 转换为点 (pt)
const pdfHeight = height * 72 / 25.4; // 转换为点 (pt)
const imgObj = createImageObject(imageData);
// 计算图片在页面中的位置
const imgX = (pdfWidth - imgObj.width) / 2;
const imgY = (pdfHeight - imgObj.height) / 2;
// PDF 文档结构
const pdf = [
'%PDF-1.4',
'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj',
'2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj',
'3 0 obj << /Type /Page /Parent 2 0 R /Resources << /XObject << /I1 4 0 R >> >> /MediaBox [0 0 ' + pdfWidth + ' ' + pdfHeight + '] /Contents 5 0 R >> endobj',
'4 0 obj << /Type /XObject /Subtype /Image /Width ' + imgObj.width + ' /Height ' + imgObj.height + ' /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ' + imgObj.data.byteLength + ' >> stream',
binaryString(imgObj.data),
'endstream endobj',
'5 0 obj << /Length 44 >> stream',
'q ' + imgObj.width + ' 0 0 ' + imgObj.height + ' ' + imgX + ' ' + imgY + ' cm /I1 Do Q',
'endstream endobj',
'xref',
'0 6',
'0000000000 65535 f ',
'0000000009 00000 n ',
'0000000056 00000 n ',
'0000000115 00000 n ',
'0000000202 00000 n ',
'0000000281 00000 n ',
'trailer << /Size 6 /Root 1 0 R >>',
'startxref',
];
pdf.push(
pdf.join('\n').length,
'%%EOF')
return new Uint8Array(pdf.join('\n').split('').map(c => c.charCodeAt(0)));
}
function createImageObject(imageData) {
const parts = imageData.split(',')[1];
const binary = atob(parts);
const length = binary.length;
const buffer = new ArrayBuffer(length);
const view = new Uint8Array(buffer);
for (let i = 0; i < length; i++) {
view[i] = binary.charCodeAt(i);
}
return {
data: view,
width: 200, // 图片宽度 (px)
height: 200 // 图片高度 (px)
};
}
function binaryString(buffer) {
let result = '';
const len = buffer.byteLength;
for (let i = 0; i < len; i++) {
result += String.fromCharCode(buffer[i]);
}
return result;
}
</script>
</body>
</html>
效果图
文末附上上面用到的pdf各个字段的解释:
在 PDF 文件中,嵌入图像对象的过程涉及定义图像对象(/XObject)、颜色空间(/ColorSpace)、图像数据流等。
PDF 图像对象结构
- 图像对象定义: 使用 /Type /XObject 和 /Subtype /Image 指定图像对象的类型。
- 颜色空间: 使用 /ColorSpace /DeviceRGB 指定颜色空间。
- 位深度: 使用 /BitsPerComponent 8 指定每个颜色分量的位数。
- 图像尺寸: 使用 /Width 和 /Height 指定图像的宽度和高度。
- 图像数据流: 使用 /Filter 指定压缩类型(如 /DCTDecode 用于 JPEG 压缩)。
pdf文档解析
关于更多关于pdf文档对象及其结构的定义和使用可以查看:pdf文档结构解析
第三方库
当然了,现实中如果不是专门做pdf相关产品, 可以直接借用一些第三方库,比如jsPDF(只能导出pdf), PDF.js(只解析和导入pdf), pdf-lib(导入导出都有,但功能局限)来实现pdf的导入导出甚至更复杂的交互。