
关键词:Vue打印、PDF预览、web-print-pdf、在线打印、Vue3打印、PDF打印、Web打印解决方案、Vue打印组件、PDF预览打印、Vue打印预览
技术标签:Vue3、Vue.js、PDF、打印、web-print-pdf、Electron、PDF-lib、前端打印、Web打印、Vue组件、Composition API
在现代Web应用开发中,在线预览打印PDF是一个重要的技术需求。传统的Web打印方案往往需要用户下载文件后再进行打印,用户体验不佳。本文将深入探讨如何通过Vue技术栈结合web-print-pdf npm包,实现真正的在线预览打印功能,让用户能够实时预览PDF效果并直接打印。
适用场景:企业报表系统、电商订单打印、教育文档打印、财务报表、发票打印、合同打印、技术文档打印等。

图:Vue实现在线预览打印PDF系统界面 - 左侧为PDF预览区域,右侧为打印配置面板
我们的项目采用了Vue 3 + Electron的架构,通过web-print-pdf npm包实现PDF预览和打印功能。整个系统分为三个层次:
Vue前端界面 → web-print-pdf npm包 → Electron打印专家我们的预览组件采用了左右分栏的设计模式:
<template>
<div class="preview">
<div class="content">
<!-- 左侧:PDF预览区域 -->
<div class="left">
<PDFPreview
:pdfUrl="pdfComputedUrl"
:loading="renderComputedProgress.renderPdfLoading"
:progress="renderComputedProgress"
/>
</div>
<!-- 右侧:打印配置面板 -->
<div class="right">
<PrintConfigPanel
:form="state.form"
@config-change="handleConfigChange"
/>
</div>
</div>
</div>
</template>PDF预览的核心是动态渲染,根据用户配置实时生成预览PDF:
const _renderPdfDoc = async () => {
// 1. 重置状态
state.pdfUrl = null;
state.renderProgress = { startTime: Date.now() };
// 2. 获取纸张配置
const paper = state.papers?.find(one => one.value === state.form.paperFormat);
if (paper && paper.width && paper.height) {
try {
// 3. 加载源PDF
if (!sourcePdf.value) {
sourcePdf.value = await fetch(paramsPdfUrl.value.pdfUrl)
.then(res => res?.arrayBuffer());
}
// 4. 创建新PDF文档
const newPdfDoc = await PDFDocument.create();
const pdfDoc = await PDFDocument.load(sourcePdf.value, {
parseSpeed: Infinity,
});
// 5. 逐页处理
const pageCount = pdfDoc.getPageCount();
for (let j = 0; j < pageCount; j++) {
// 更新进度
state.renderProgress = {
...state.renderProgress,
page: j + 1,
totalPages: pageCount,
updateTime: Date.now(),
};
// 添加页面并应用配置
const page = newPdfDoc.addPage([newPaperWidth, newPaperHeight]);
const [embeddedPage] = await newPdfDoc.embedPdf(sourcePdf.value, [j]);
// 应用缩放和旋转配置
applyPageTransform(page, embeddedPage, paper, state.form);
}
// 6. 生成预览URL
const pdfBytes = await newPdfDoc.save();
const blob = new Blob([pdfBytes], { type: "application/pdf" });
state.pdfUrl = URL.createObjectURL(blob);
} catch (err) {
console.error(err);
state.pdfUrl = paramsPdfUrl.value.pdfUrl; // 降级到原始PDF
}
}
};页面变换算法是整个PDF预览系统的核心,也是Chrome浏览器打印预览页面的实现原理! 这个算法决定了PDF页面如何根据纸张大小、缩放模式、方向等配置进行精确的变换和定位。
页面变换算法的核心思想是:将源PDF页面按照用户配置精确地映射到目标纸张上,包括缩放、旋转、居中定位等操作。这与Chrome浏览器的打印预览功能完全一致。
const applyPageTransform = (page, embeddedPage, paper, config) => {
const { width, height } = embeddedPage.size();
const [oldPageWidth, oldPageHeight] = [width, height];
// 计算缩放比例
const scale_x = paper.width / oldPageWidth;
const scale_y = paper.height / oldPageHeight;
let zoom = Math.min(scale_x, scale_y);
// 横向模式处理
if (config.landscape) {
const scale_x = paper.width / oldPageHeight;
const scale_y = paper.height / oldPageWidth;
zoom = Math.min(scale_x, scale_y);
}
// 🎯 关键:根据缩放模式调整(与Chrome打印预览逻辑一致)
if (config.scaleMode === "shrink" && zoom > 1) {
zoom = 1; // shrink模式不放大,保持原始大小
}
// 计算居中位置
const x = paper.width / 2 - (oldPageWidth * zoom) / 2;
const y = paper.height / 2 - (oldPageHeight * zoom) / 2;
// 应用变换
page.drawPage(embeddedPage, {
x,
y,
xScale: zoom,
yScale: zoom,
rotate: config.landscape ? degrees(-90) : undefined,
});
};缩放模式 | 行为描述 | Chrome打印预览对应 | 适用场景 |
|---|---|---|---|
shrink | 只缩小,不放大 |
| 保持内容完整,避免裁剪 |
noscale | 保持原始大小 |
| 精确尺寸,适合标准纸张 |
fit | 填满纸张 |
| 最大化利用纸张空间 |
我们的页面变换算法完全遵循了Chrome浏览器的打印预览标准:
@page规则完全对应// 财务报表:使用shrink模式,确保内容完整
const financialReport = {
scaleMode: 'shrink',
paperFormat: 'A4',
landscape: false
};
// 照片打印:使用fit模式,最大化利用纸张
const photoPrint = {
scaleMode: 'fit',
paperFormat: '6x4',
landscape: true
};
// 技术图纸:使用noscale模式,保持精确尺寸
const technicalDrawing = {
scaleMode: 'noscale',
paperFormat: 'A3',
landscape: true
};这个页面变换算法不仅是我们系统的核心技术,更是Web打印领域的标准实现方案。通过深入理解这个算法,开发者可以:
通过计算属性实现智能的进度显示逻辑:
const renderComputedProgress = computed(() => {
const { page = 1, totalPages = 1 } = state.renderProgress;
const isFinish = page === totalPages;
const wasteTime = (state.renderProgress.updateTime - state.renderProgress.startTime) / 1000;
// 智能显示进度条:超过2秒才显示
const showTopProgress = !isFinish && wasteTime > 2;
return {
progressText: isEnglish.value
? `Loading progress ${page}/${totalPages}`
: `加载进度 ${page}/${totalPages}`,
percent: ((page / totalPages) * 100).toFixed(0),
showTopProgress,
renderPdfLoading: state.renderPdfLoading && !showTopProgress,
};
});支持多种纸张格式的动态配置:
const getPapers = async (printerName) => {
try {
state.paperLoading = true;
const papers = await getPrinterPaper({ printerName });
state.papers = papers.map(paper => ({
label: paper.name,
value: paper.name,
width: paper.width,
height: paper.height,
}));
} finally {
state.paperLoading = false;
}
};提供三种缩放模式满足不同需求:
const scaleModeOptions = computed(() => [
{
label: "将页面缩小至可打印区域 (如果需要) (默认)",
value: "shrink",
},
{
label: "使用原始页面大小",
value: "noscale",
},
{
label: "调整页面至可打印区域",
value: "fit",
},
]);支持灵活的页面范围配置:
const pageRangesNormalized = computed(() => {
let pageRanges = null;
if (+state.pageRangeType === 1) {
// 全部页面
} else if (+state.pageRangeType === 2) {
// 自定义页面范围
pageRanges = state.pageRangesInputs?.filter(item => item.from && item.to);
}
return { pageRanges, pageRangesLength: pageRanges?.length };
});通过web-print-pdf执行最终的打印任务:
const handlePrint = async () => {
const printOptions = {
...(props.pdfParams?.printOptions || {}),
...JSON.parse(JSON.stringify(state.form)),
};
state.printLoading = true;
try {
await printPreviewFile({
pdfFilePath: props.pdfParams.pdfFilePath,
printOptions,
});
message.success("打印成功!");
} catch (err) {
message.error(err?.message || "打印失败");
} finally {
state.printLoading = false;
}
};实时同步用户配置到PDF预览:
// 监听配置变化,自动重新渲染PDF
watch(
() => JSON.stringify(pageRangesNormalized.value.pageRanges),
(v1, v2) => {
if (v1 !== v2) {
state.form.pageRanges = pageRangesNormalized.value.pageRanges;
renderPdfDoc(); // 重新渲染PDF
}
}
);
// 监听其他配置变化
watch(
() => [
state.form.paperFormat,
state.form.scaleMode,
state.form.landscape,
state.form.colorful,
],
() => renderPdfDoc(),
{ deep: true }
);对窗口大小变化等高频事件进行防抖处理:
const getPageHeight = debounce(() => {
const height = +getComputedStyle(
document.getElementById("_printPreview")
)?.height?.replace("px", "");
state.clientHeight = height - 20 + "px";
}, 500);
onMounted(() => {
getPageHeight();
window.addEventListener("resize", getPageHeight);
});支持用户快速切换配置时的渲染中断:
const _renderPdfDoc = async () => {
const currentRenderPdfDocTimestamp = state.renderPdfDocTimestamp;
// 检查是否被中断
const checkIsBreak = () => {
return currentRenderPdfDocTimestamp !== state.renderPdfDocTimestamp;
};
// 在关键节点检查中断
if (checkIsBreak()) return;
// ... 渲染逻辑
};及时释放PDF资源,避免内存泄漏:
onUnmounted(() => {
window.removeEventListener("resize", getPageHeight);
// 释放PDF URL
if (state.pdfUrl && state.pdfUrl.startsWith('blob:')) {
URL.revokeObjectURL(state.pdfUrl);
}
});// 财务报表预览打印
const financialReportPreview = {
paperFormat: 'A4',
scaleMode: 'fit',
landscape: false,
copies: 2,
duplexMode: 'duplex',
pageRanges: [{ from: 1, to: 10 }]
};// 订单详情预览打印
const orderPrintPreview = {
paperFormat: 'A5',
scaleMode: 'shrink',
landscape: false,
copies: 1,
colorful: false, // 黑白打印节省成本
};// 教学资料预览打印
const educationPrintPreview = {
paperFormat: 'A4',
scaleMode: 'fit',
landscape: true, // 横向打印
copies: 30,
duplexMode: 'duplexlong',
};我们的Vue预览组件与web-print-pdf npm包形成了完美的技术栈组合:
这种架构设计让开发者可以:
web-print-pdf带来的强大打印能力通过Vue技术栈结合web-print-pdf npm包,我们成功实现了一个功能完整、性能优异的PDF在线预览打印系统。这个系统不仅解决了传统Web打印的各种痛点,还为用户提供了直观、高效的打印体验。
web-print-pdf作为核心的打印解决方案,为我们的Vue应用提供了强大的后端支持,让前端开发者能够专注于用户体验的优化,而不用担心复杂的打印技术实现。这正是现代Web开发所追求的关注点分离和技术栈协作的完美体现。
A: 我们的方案基于Electron,支持所有主流浏览器,包括Chrome、Firefox、Safari、Edge等。
A: 我们实现了智能的进度显示和渲染中断机制,大文件会分批渲染,用户可以实时看到进度。
A: 完全支持!系统会动态获取打印机支持的纸张格式,用户可以选择任意纸张进行打印。
A: 通过web-print-pdf的批量打印功能,可以一次性处理多个PDF文件,提高工作效率。
A: 支持!系统可以连接到网络打印机,实现远程打印功能。
相关链接:
技术栈:
📚 相关阅读推荐:
🏷️ 技术标签: #Vue3 #PDF打印 #web-print-pdf #在线预览 #Vue打印组件 #Web打印解决方案 #前端打印 #PDF预览打印
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。