vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue在线预览打印PDF

Vue web-print-pdf实现在线预览打印PDF的技术深度解析

作者:web打印社区

在现代Web应用开发中,在线预览打印PDF是一个重要的技术需求,本文将深入探讨如何通过Vue技术栈结合web-print-pdf npm包,实现真正的在线预览打印功能,需要的可以了解下

引言

在现代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打印专家

核心优势

Vue组件架构设计

预览组件结构

我们的预览组件采用了左右分栏的设计模式:

<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预览核心实现

1. PDF渲染流程

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
    }
  }
};

2. 页面变换算法

页面变换算法是整个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只缩小,不放大fit-to-page保持内容完整,避免裁剪
noscale保持原始大小actual-size精确尺寸,适合标准纸张
fit填满纸张fit-to-paper最大化利用纸张空间

与Chrome打印预览的一致性

我们的页面变换算法完全遵循了Chrome浏览器的打印预览标准:

技术优势

实际应用示例

// 财务报表:使用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打印领域的标准实现方案。通过深入理解这个算法,开发者可以:

3. 实时进度显示

通过计算属性实现智能的进度显示逻辑:

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,
  };
});

打印配置面板

1. 纸张格式选择

支持多种纸张格式的动态配置:

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;
  }
};

2. 缩放模式配置

提供三种缩放模式满足不同需求:

const scaleModeOptions = computed(() => [
  {
    label: "将页面缩小至可打印区域 (如果需要) (默认)",
    value: "shrink",
  },
  {
    label: "使用原始页面大小",
    value: "noscale",
  },
  {
    label: "调整页面至可打印区域",
    value: "fit",
  },
]);

3. 页面范围选择

支持灵活的页面范围配置:

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集成

1. 打印执行

通过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;
  }
};

2. 配置同步

实时同步用户配置到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 }
);

性能优化策略

1. 防抖处理

对窗口大小变化等高频事件进行防抖处理:

const getPageHeight = debounce(() => {
  const height = +getComputedStyle(
    document.getElementById("_printPreview")
  )?.height?.replace("px", "");
  state.clientHeight = height - 20 + "px";
}, 500);

onMounted(() => {
  getPageHeight();
  window.addEventListener("resize", getPageHeight);
});

2. 渲染中断机制

支持用户快速切换配置时的渲染中断:

const _renderPdfDoc = async () => {
  const currentRenderPdfDocTimestamp = state.renderPdfDocTimestamp;
  
  // 检查是否被中断
  const checkIsBreak = () => {
    return currentRenderPdfDocTimestamp !== state.renderPdfDocTimestamp;
  };
  
  // 在关键节点检查中断
  if (checkIsBreak()) return;
  
  // ... 渲染逻辑
};

3. 内存管理

及时释放PDF资源,避免内存泄漏:

onUnmounted(() => {
  window.removeEventListener("resize", getPageHeight);
  
  // 释放PDF URL
  if (state.pdfUrl && state.pdfUrl.startsWith('blob:')) {
    URL.revokeObjectURL(state.pdfUrl);
  }
});

实际应用场景

1. 企业报表系统

// 财务报表预览打印
const financialReportPreview = {
  paperFormat: 'A4',
  scaleMode: 'fit',
  landscape: false,
  copies: 2,
  duplexMode: 'duplex',
  pageRanges: [{ from: 1, to: 10 }]
};

2. 电商订单打印

// 订单详情预览打印
const orderPrintPreview = {
  paperFormat: 'A5',
  scaleMode: 'shrink',
  landscape: false,
  copies: 1,
  colorful: false, // 黑白打印节省成本
};

3. 教育文档打印

// 教学资料预览打印
const educationPrintPreview = {
  paperFormat: 'A4',
  scaleMode: 'fit',
  landscape: true, // 横向打印
  copies: 30,
  duplexMode: 'duplexlong',
};

技术亮点总结

核心技术创新

用户体验优化

与web-print-pdf的完美结合

我们的Vue预览组件与web-print-pdf npm包形成了完美的技术栈组合:

这种架构设计让开发者可以:

结语

通过Vue技术栈结合web-print-pdf npm包,我们成功实现了一个功能完整、性能优异的PDF在线预览打印系统。这个系统不仅解决了传统Web打印的各种痛点,还为用户提供了直观、高效的打印体验。

web-print-pdf作为核心的打印解决方案,为我们的Vue应用提供了强大的后端支持,让前端开发者能够专注于用户体验的优化,而不用担心复杂的打印技术实现。这正是现代Web开发所追求的关注点分离技术栈协作的完美体现。

常见问题解答 (FAQ)

Q1: 这个方案支持哪些浏览器?

A: 我们的方案基于Electron,支持所有主流浏览器,包括Chrome、Firefox、Safari、Edge等。

Q2: 如何处理大文件PDF的预览?

A: 我们实现了智能的进度显示和渲染中断机制,大文件会分批渲染,用户可以实时看到进度。

Q3: 是否支持自定义纸张格式?

A: 完全支持!系统会动态获取打印机支持的纸张格式,用户可以选择任意纸张进行打印。

Q4: 如何实现批量打印?

A: 通过web-print-pdf的批量打印功能,可以一次性处理多个PDF文件,提高工作效率。

Q5: 是否支持网络打印?

A: 支持!系统可以连接到网络打印机,实现远程打印功能。

技术栈:

以上就是Vue web-print-pdf实现在线预览打印PDF的技术深度解析的详细内容,更多关于Vue在线预览打印PDF的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文