Печать PDF файла с помощью Electron JS

Я пытаюсь создать приложение Electron JS, которое предназначено для печати PDF файлов формата письма.

Это мой фрагмент кода для печати:

win = new BrowserWindow({
  width: 378, 
  height: 566, 
  show: true, 
  webPreferences: {
    webSecurity: false,
    plugins: true
  }
});

// load PDF
win.loadURL('file://' + __dirname + '/header1_X_BTR.pdf');

// if pdf is loaded start printing
win.webContents.on('did-finish-load', () => {
  win.webContents.print({silent: true, printBackground:true});
});

Мои проблемы: если у меня есть print({silent:true}) мой принтер печатает пустую страницу. Если у меня есть print({silent:false}), принтер печатает так же, как и снимок экрана, с заголовками, элементами управления и т.д.

enter image description here

Мне нужна тихая печать содержимого PDF, и я не могу сделать это в течение нескольких дней. Кто-нибудь испытывал то же самое с Electron?

Ответы

Ответ 1

Если у вас уже есть файл в формате pdf или вы сохраняете pdf перед печатью "Я предполагаю, что это", то вы можете захватить местоположение файла, затем вы можете использовать внешний процесс для печати с помощью child_process.

Вы можете использовать lp command или PDFtoPrinter для окон

const ch = require('os');

switch (process.platform) {
    case 'darwin':
    case 'linux':
        ch.exec(
            'lp ' + pdf.filename, (e) => {
                if (e) {
                    throw e;
                }
            });
        break;
    case 'win32':
        ch.exec(
            'ptp ' + pdf.filename, {
                windowsHide: true
            }, (e) => {
                if (e) {
                    throw e;
                }
            });
        break;
    default:
        throw new Error(
            'Platform not supported.'
        );
}

Я надеюсь, что это помогает.

Изменить: вы также можете использовать SumatraPDF для окон https://github.com/sumatrapdfreader/sumatrapdf

Ответ 2

Я столкнулся с той же проблемой. Похоже, что печать PDF на принтер просто не реализована в Electron, несмотря на то, что она запрашивалась с 2017 года. Вот еще один связанный с этим вопрос о SO и запрос функции на GitHub:

Одним из возможных решений может быть использование Google PDFium и упаковочной библиотеки NodeJS, которая, как представляется, позволяет преобразовывать PDF из набора в EMF, поэтому EMF можно печатать на локальном/сетевом принтере, по крайней мере, в Windows.

В качестве еще одного жизнеспособного варианта этот ответ представляет собой простое решение С# для печати PDF с использованием PdfiumViewer, который представляет собой библиотеку обложек PDFium для.NET.

Я смотрю на любые другие варианты. Использование локально установленного экземпляра Acrobat Reader для печати не является приемлемым решением для нас.


ОБНОВЛЕНО. На данный момент PDF.js решает проблему с рендерингом/предварительным просмотром отдельных страниц, но, что касается самой печати, появляется Electron (во время этой публикации) просто не хватает надлежащих API печати. Например, вы не можете установить режим формата бумаги/альбомной ориентации и т.д. Кроме того, при печати PDF.js создает растрированные отпечатки - благодаря тому, как работает холст HTML5 - в отличие от того, как это делает программа просмотра PDF в формате PDF. Вот обсуждение некоторых других недостатков PDF.js.

Поэтому на данный момент я думаю, что мы можем продолжить комбинацию PDF.js (для пользовательского интерфейса в процессе Electron Renderer) и PDFium (для фактической печати из основного процесса).

На основе ответа Тима здесь представлена версия средства просмотра PDF.js с использованием ES8 async/await (поддерживается на текущей версии Electron):

async function renderPDF(url, canvasContainer, options) {
    options = options || { scale: 1 };

    async function renderPage(page) {
        let viewport = page.getViewport(options.scale);
        let canvas = document.createElement('canvas');
        let ctx = canvas.getContext('2d');
        let renderContext = {
            canvasContext: ctx,
            viewport: viewport
        };

        canvas.height = viewport.height;
        canvas.width = viewport.width;
        canvasContainer.appendChild(canvas);

        await page.render(renderContext);
    }

    let pdfDoc = await pdfjsLib.getDocument(url);

    for (let num = 1; num <= pdfDoc.numPages; num++)
    {
        if (num > 1)
        {
            // page separator
            canvasContainer.appendChild(document.createElement('hr'));
        }
        let page = await pdfDoc.getPage(num);
        await renderPage(page);
    }
}

Ответ 3

Самый простой способ сделать это - сделать PDF-страницы отдельными элементами холста на странице с помощью PDF.js, а затем вызвать печать.

Я исправил этот смысл, чтобы использовать версию PDF.js(v1), для которой он был разработан, и, вероятно, это хорошая отправная точка.

Это, по сути, то, что делает электронный/хром-просмотрщик pdf, но теперь у вас есть полный контроль над компоновкой!

<html>
<body>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.10.90/pdf.js"></script>
<script type="text/javascript">
function renderPDF(url, canvasContainer, options) {
    var options = options || { scale: 1 };
        
    function renderPage(page) {
        var viewport = page.getViewport(options.scale);
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        var renderContext = {
          canvasContext: ctx,
          viewport: viewport
        };
        
        canvas.height = viewport.height;
        canvas.width = viewport.width;
        canvasContainer.appendChild(canvas);
        
        page.render(renderContext);
    }
    
    function renderPages(pdfDoc) {
        for(var num = 1; num <= pdfDoc.numPages; num++)
            pdfDoc.getPage(num).then(renderPage);
    }
    PDFJS.disableWorker = true;
    PDFJS.getDocument(url).then(renderPages);
}   
</script> 

<div id="holder"></div>

<script type="text/javascript">
renderPDF('//cdn.mozilla.net/pdfjs/helloworld.pdf', document.getElementById('holder'));
</script>  

</body>
</html>

Ответ 4

Поскольку вы используете contents.print([options], [callback]) я предполагаю, что вы хотите печатать на бумаге, а не на своем диске.


Ответ на ваш вопрос прост. Это событие, которое вы слушаете, вызывает ошибку. Поэтому, если вы просто это сделаете:

  winObject.webContents.on('did-frame-finish-load', () => {
    setTimeout(() => {winObject.webContents.print({silent: true, printBackground:true})}, 3000);
  });

все будет работать нормально, если принтер по умолчанию является правильным. Я проверил это, и он будет выполнять свою работу более или менее. Вы можете изменить свое событие на любое событие, которое вам нравится, важной частью является ожидание с помощью setTimeout. PDF, который вы пытаетесь распечатать, просто недоступен в кадре при использовании silent:true.

Однако позвольте мне подробно остановиться здесь немного, чтобы прояснить ситуацию:

Электрон будет загружать файлы или URL-адреса в созданное окно (BrowserWindow), связанное с событиями. Проблема в том, что каждое событие "может" вести себя по-разному в разных системах. Мы должны жить с этим и не можем легко это изменить. Но знание этого поможет улучшить разработку пользовательских приложений.

Если вы загружаете URL-адреса или htmls, все будет работать без установки каких-либо настраиваемых параметров. Используя PDF файлы в качестве источника, мы должны использовать это:

import electron, { BrowserWindow } from 'electron';
const win = new BrowserWindow({
  // @NOTE I did keep the standard options out of this.
  webPreferences: { // You need this options to load pdfs
    plugins: true // this will enable you to use pdfs as source and not just download it.
  }
});

hint: без webPreferences: { plugins: true } исходный PDF файл будет загружен вместо загрузки в окно.

При этом вы загрузите свой PDF файл в webContents вашего окна. Поэтому мы должны слушать события, совместимые с BrowserWindow. Вы все сделали правильно, единственная часть, которую вы пропустили, - это еще один интерфейс.

Печать будет захватывать ваши webContents как и при нажатии "print". Это очень важно знать при работе с принтерами. Поскольку, если что-то будет немного загружаться в другой системе, например, просмотрщик PDF файлов будет по-прежнему темно-серым без букв, тогда ваша печать будет печатать темно-серый фон или даже кнопки.

Эта небольшая проблема легко исправлена с помощью setTimeout().

Полезные вопросы и ответы для печати с электроном:

Однако есть много проблем с печатью, поскольку большая часть кода находится за закрытыми дверями без использования API-интерфейсов во всем мире. Просто имейте в виду, что каждый принтер может вести себя по-другому, так что тестирование на других машинах поможет.

Ответ 5

Похоже, вы пытаетесь загрузить pdf файл, а не печатать pdf файл текущего экрана, который пытается сделать print. Таким образом, у вас есть несколько вариантов.

1) Отключите встроенный просмотрщик pdf в электронном режиме:

Если вы не заботитесь о электронном окне, отображающем pdf, отключение встроенного средства просмотра pdf в электронном режиме должно заставить его обрабатывать файл как загрузку и пытаться загрузить его.

new BrowserWindow({
  webPreferences: {
    plugins: false
  }
})

Вы также можете проверить электронную загрузку DownloadItem api, чтобы сделать некоторые манипуляции о том, где файл будет сохранен.

2) Загрузите pdf через какой-либо другой api

Я не собираюсь давать какие-либо особенности для этого, потому что вы должны быть в состоянии найти некоторую информацию об этом самостоятельно, но в основном, если вы хотите загрузить файл откуда-то, тогда вы можете использовать какой-либо другой API загрузки, например, библиотеку AJAX для загрузки файл и сохранить его где-нибудь. Это потенциально позволит вам также визуализировать документ в электронном окне, так как как только вы начнете загрузку, вы, вероятно, можете перенаправить окно на URL-адрес pdf и обработать его с помощью собственного средства просмотра.

Короче говоря, это звучит так, как будто вы не хотите печатать с электрона, вы просто хотите сохранить файл pdf, который вы показываете. Печать с электрона будет отображать то, что вы видите на экране, а не сам документ pdf, поэтому я думаю, что вы просто неправильно поняли, какова цель печати. Надеюсь, это поможет вам, удачи!

=== EDIT ===

К сожалению, я не считаю, что есть способ распечатать файл непосредственно с электрона, так как электронная печать предназначена для печати содержимого дисплея электронов. Но вы должны иметь возможность загрузить файл через простой запрос на файл (см. Выше).

Моя рекомендация для вас - создать страницу для предварительного просмотра файла. Это будет независимая страница, а не встроенная программа просмотра PDF. Затем вы можете вставить кнопку где-нибудь на странице, чтобы загрузить pdf с помощью некоторых средств и пропустить любые приглашения на сохранение местоположения (это должно быть достаточно легко, чтобы найти документацию).

Затем, чтобы иметь свой предварительный просмотр, на той же странице вы можете иметь тег webview на своей странице, на котором будет отображаться встроенный просмотрщик PDF. Для того, чтобы встроенный просмотрщик PDF работал в теге webview, вы должны включить атрибут plugins в тег. Это логический тег, поэтому простое присутствие - это все, что необходимо, например, <webview... plugins> Это включает поддержку плагинов для этого средства просмотра веб-страниц, которое требуется для просмотра PDF файлов.

Вы можете изменить размер стиля этого тега на странице так, как хотите, чтобы удовлетворить ваши потребности. Трюк, чтобы избавиться от настроек загрузки и печати, чтобы пользователь не мог их нажимать, заключается в том, чтобы добавить #toolbar=0 в конец URL-адреса pdf, чтобы предотвратить просмотр встроенного средства просмотра PDF с верхней панели инструментов этими кнопками.

Таким образом, вы можете получить предварительный просмотр, убедитесь, что пользователь не может использовать встроенную загрузку или печать из программы просмотра PDF с помощью дополнительного ui, и вы можете добавить еще одну кнопку для ее загрузки, чтобы ее можно было напечатать позже.