You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下Puppeteer生成PDF打印时宽大于高出现尺寸方向错误

问题描述

使用Puppeteer加载HTML文件转换为PDF后,通过Node.js调用打印机打印。当PDF宽度小于高度时打印正常,但宽度大于高度时,打印机输出的尺寸和方向均出现错误。

Node.js生成并打印PDF的代码
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
const express = require('express');
const expressWs = require('express-ws');
const printer = require('pdf-to-printer');

let browser;

async function initializeBrowser() {
    if (!browser) {
        browser = await puppeteer.launch({
            headless: 'new',//'new'
        });
    }
}

async function printHtmlToCustomSize(page, htmlFilePath, outputPath, widthMM, heightMM, printerName) {
    await page.setViewport({
        width:  widthMM* 25.4,
        height: heightMM * 25.4,
        deviceScaleFactor: 2,
    });

    const fileUrl = `file://${path.resolve(htmlFilePath)}`;
    console.log(fileUrl);

    await page.goto(fileUrl);

    await page.pdf({
        path: outputPath,
        width: `${widthMM}mm`,
        height: `${heightMM}mm`,
    });

    const printerOptions = {
        printer: printerName,
    };

    await printer.print(outputPath, printerOptions);
    console.log(`PDF printed to ${printerName}`);
}

async function startServer() {
    const app = express();
    expressWs(app);

    app.ws('/print', (ws, req) => {
        ws.on('message', async (message) => {
            const { htmlFilePath, outputPath, widthMM, heightMM, printerName } = JSON.parse(message);

            await initializeBrowser();
            const page = await browser.newPage();

            try {
                await printHtmlToCustomSize(page, htmlFilePath, outputPath, widthMM, heightMM, printerName);
                ws.send(JSON.stringify({ status: 'success' }));
            } finally {
                await page.close();
            }
        });
    });

    app.listen(3000, () => {
        console.log('WebSocket server listening on port 3000');
    });
}

startServer();
使用设备

WP300B 4英寸热敏打印机

打印效果
  • 实际打印:内容尺寸缩放错误,方向偏移,无法匹配设定的宽高
  • 预期打印:内容完整贴合设定的宽高布局,位置准确
HTML标签代码
<meta charset="utf-8">
<style>
    @media print {
        body {
            -webkit-print-color-adjust:exact;
        }
        hr{
            -webkit-print-color-adjust:exact;
        }
        body{
            margin: 0 auto;
        }
        .preview-mode{
            border: 0 !important;
        }
    }
    body{
        line-height: 1.5;
    }
    .preview-body {
        margin: 0 auto;
        padding: 0;
        height: 100%;
/*                font-size: 14px;*/
        font-family: Arial;
    }
    .table{
        font-size:inherit;
    }
    @page{
        margin: 0;
    }
    hr{
        margin: 0;
        -webkit-print-color-adjust:exact;
    }
    .preview-border{
        border: 1px solid #000000;
        /*width: 100%;
        height: 100%;*/
    }

    .preview-mode .textarea,
    .preview-mode input {
        border: 0;
        resize: none;
        user-select: none;
        writing-mode: unset;
        cursor: default;
        outline: none;
        pointer-events: none;
        touch-action: none;
        font-size: 14px;
    }

    .preview-mode table {
        resize: none;
        user-select: none;
        writing-mode: unset;
        outline: none;
        border-collapse: collapse;
    }

    .preview-mode .draggable-table td .textarea {
        resize: none;
        user-select: none;
        writing-mode: unset;
        outline: none;
        width: 100%;
        height: 100%;
        overflow: hidden;
        box-sizing: border-box;
        border-collapse: collapse;
    }
    .preview-mode .draggable-table td{
        position: relative;
    }

    .preview-mode .draggable-table .element{
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
    }

    .preview-mode .element {
        position: absolute;
        cursor: default;
        resize: none;
    }

    .preview-mode .delete-element,
    .preview-mode .duplicate-element,
    .preview-mode .rotate-element {
        display: none;
    }
    .preview-mode .textarea span {
        display: block !important;
        font-size: inherit;
    }

    .table-inside-border td {
        border: 1px solid black;
    }

    .table-inside-border tr:first-child td {
        border-top: 0;
    }

    .table-inside-border tr:last-child td {
        border-bottom: 0;
    }

    .table-inside-border tr td:first-child,
    .table-inside-border tr th:first-child {
        border-left: 0;
    }

    .table-inside-border tr td:last-child,
    .table-inside-border tr th:last-child {
        border-right: 0;
    }

    .table-full-border,
    .table-full-border tr,
    .table-full-border td {
        border: 1px solid black;
    }

    .table-outside-border {
        border: 1px solid #000;
    }

    .table-outside-border tr:first-child {
        border-top: 1px solid #000;
    }

    .table-outside-border tr+tr {
        border-top: 0;
    }

    .table-outside-border td {
        border: 0;
        border-left: 1px solid #000;
    }

    .table-outside-border td+td {
        border-left: 0;
    }

    .table-top-border tr:first-child {
        border-top: 1px solid #000;
    }

    .table-top-border tr,
    .table-top-border td {
        border: 0;
    }

    .table-bottom-border tr:last-child {
        border-bottom: 1px solid #000;
    }

    .table-bottom-border tr,
    .table-bottom-border td {
        border: 0;
    }

    .table-left-border tr td:first-child {
        border-left: 1px solid #000;
    }

    .table-left-border tr,
    .table-left-border td {
        border: 0;
    }

    .table-right-border tr td:last-child {
        border-right: 1px solid #000;
    }

    .table-right-border tr,
    .table-right-border td {
        border: 0;
    }
</style>
<input name="label_identifier" type="hidden" value="LB-65c49a6277c7f8.20063631">
<input name="width" type="hidden" value="264.566929137">
<input name="height" type="hidden" value="207.8740157505">

<div class="print-wrapper">
    <table class="table table-bordered m-auto central-table preview-mode" style="position:relative;height:207.8740157505px; width: 264.566929137px">
        <tbody>
                            <tr class="central-table-row">
                                            <td class="droppable ui-droppable">
                            <div class="draggable element draggable-text ui-draggable" id="droppable-1707383401662" data-id="text" style="z-index:1; top:-0.5px;left:-0.5px;" data-format-selected="1"><div class="element-child"><div contenteditable="true" class="textarea ui-resizable"><span>Text 1</span><div class="ui-resizable-handle ui-resizable-e" style="z-index: 90;"></div><div class="ui-resizable-handle ui-resizable-s" style="z-index: 90;"></div></div></div></div></td>
                                            <td class="droppable ui-droppable">
                            </td>
                                            <td class="droppable ui-droppable">
                            <div class="draggable element draggable-text ui-draggable" id="droppable-1707383410831" data-id="text" style="z-index: 1; top: 2.5px; left: 222.281px;" data-format-selected="1"><div class="element-child"><div contenteditable="true" class="textarea ui-resizable"><span>Text 2</span><div class="ui-resizable-handle ui-resizable-e" style="z-index: 90;"></div><div class="ui-resizable-handle ui-resizable-s" style="z-index: 90;"></div></div></div></div></td>
                                            <td class="droppable ui-droppable">
                            </td>
                                        </tr>
                            <tr class="central-table-row">
                                            <td class="droppable ui-droppable">
                            </td>
                                            <td class="droppable ui-droppable">
                            </td>
                                            <td class="droppable ui-droppable">
                            </td>
                                            <td class="droppable ui-droppable">
                            <div class="draggable element draggable-text ui-draggable" id="droppable-1707383429730" data-id="text" style="z-index: 1; top: 182.453px; left: 226.172px;" data-format-selected="1"><div class="element-child"><div contenteditable="true" class="textarea ui-resizable"><span>Text4</span><div class="ui-resizable-handle ui-resizable-e" style="z-index: 90;"></div><div class="ui-resizable-handle ui-resizable-s" style="z-index: 90;"></div></div></div></div></td>
                                        </tr>
                            <tr class="central-table-row">
                                            <td class="droppable ui-droppable">
                            <div class="draggable element draggable-text ui-draggable" id="droppable-1707383427788" data-id="text" style="z-index: 1; top: 182.906px; left: 2px;" data-format-selected="1"><div class="element-child"><div contenteditable="true" class="textarea ui-resizable"><span>Text3</span><div class="ui-resizable-handle ui-resizable-s" style="z-index: 90;"></div></div></div></div></td>
                                            <td class="droppable ui-droppable">
                            </td>
                                            <td class="droppable ui-droppable">
                            </td>
                                            <td class="droppable ui-droppable">
                            </td>
                                        </tr>
                </tbody>
    </table>
</div>
<script type="text/javascript">
    if(document.getElementsByClassName('central-table')[0].classList.contains('preview-mode')){
        document.getElementsByClassName('central-table')[0].classList.add('preview-border');
        document.getElementsByTagName('body')[0].setAttribute( 'class', 'preview-body');
        document.getElementsByTagName('html')[0].setAttribute( 'class', 'preview-body');
        var elements=document.getElementsByClassName('ui-rotatable-handle');
        while(elements.length > 0){
            elements[0].parentNode.removeChild(elements[0]);
        }
    }
</script>
WebSocket请求体格式
{
"htmlFilePath":"page.html",
"outputPath":"./output.pdf",
"widthMM":70,
"heightMM":55,
"printerName":"printerName"
}

内容的提问来源于stack exchange,提问作者unkownDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:34:55