我正在尝试创建一个Electron JS应用程序,其目的是打印字母大小的PDF。
这是我的打印代码片段:
print({silent:false})
我的问题是:如果我有build-wrapper-macosx-x86 --out-dir ./bw_output xcrun xcodebuild
,我的打印机会打印一个空白页面。如果我有dyld: warning: could not load inserted library 'somepath/libinterceptor.dylib' into library validated process because no suitable image found. Did find:
somepath/libinterceptor.dylib: code signing blocked mmap() of 'somepath/build-wrapper-macosx-x86/libinterceptor.dylib'
somepath/libinterceptor.dylib: stat() failed with errno=1
,则打印机的打印方式与屏幕截图相同,包括标题,控件等。
我需要对PDF内容进行无声打印,而且我无法在数天内完成这项工作。有没有人和Electron一样经历过同样的事情?
答案 0 :(得分:5)
最简单的方法是使用PDF.js将PDF页面呈现为页面上各个画布元素,然后调用print。
我修复了this gist以使用它设计的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>
答案 1 :(得分:4)
我正面临着同样的问题。尽管自2017年以来一直要求在打印机中将PDF打印到打印机上,但实际上并未在Electron中实现。这是SO上的另一个相关问题以及GitHub上的功能请求:
一种可能的解决方案可能是使用Google PDFium和包装NodeJS library ,这似乎允许从PDF转换为一组EMF,因此可以将EMF打印到本地/网络打印机,至少在Windows上。
作为另一个可行的选择,this answer为使用PdfiumViewer的PDF打印提供了一个简单的C#解决方案,这是.NET的PDFium包装库。
我还在寻找其他选择。对于我们来说,使用本地安装的Acrobat Reader实例进行打印不是可接受的解决方案。
因此,现在我认为我们可以继续使用PDF.js(用于Electron的Renderer流程中的UI)和PDFium(用于从Main流程进行实际打印)的组合。
基于Tim's answer,以下是使用ES8 async/await
的PDF.js渲染器版本(从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);
}
}
答案 2 :(得分:4)
如果您已经有了pdf文件,或者在打印“我认为是”之前保存了pdf,则可以获取文件位置,然后可以使用外部过程使用child_process
进行打印。 / p>
您可以在Windows上使用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用于Windows https://github.com/sumatrapdfreader/sumatrapdf
答案 3 :(得分:3)
由于您使用的是contents.print([options], [callback])
,因此我假设您要在纸上而不是磁盘上打印。
您的问题的答案很简单。是您正在监听的事件导致了错误。因此,如果您只是这样做:
winObject.webContents.on('did-frame-finish-load', () => {
setTimeout(() => {winObject.webContents.print({silent: true, printBackground:true})}, 3000);
});
如果默认打印机是正确的打印机,那么一切都可以正常工作。我确实对此进行了测试,它将或多或少地完成其工作。您可以将我的事件更改为任何您喜欢的事件,重要的是等待setTimeout。使用silent:true
时,您尝试打印的PDF在框架中根本不可用。
不过,让我在这里详细说明一下:
Electron将文件或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.
}
});
提示:如果没有webPreferences: { plugins: true }
,将下载源PDF,而不是将其加载到窗口中。
那是说您将PDF加载到窗口的webContents
中。因此,我们必须侦听与BrowserWindow
兼容的事件。您做对了所有事情,唯一错过的部分就是打印是另一个界面。
当您按“打印”时,打印将捕获您的webContents
。在使用打印机时,这非常重要。因为如果某些东西在其他系统上的加载时间会稍长一些,例如PDF查看器将仍然是深灰色而没有字母,那么您的打印将打印深灰色的背景甚至按钮。
setTimeout()
可以轻松解决这个小问题。
有关电子打印的有用问答:
但是,由于大多数代码都是闭门造车的,没有世界范围的API可以使用,因此打印还有很多可能的问题。请记住,每台打印机的行为都可能有所不同,因此在更多计算机上进行测试将有所帮助。
答案 4 :(得分:2)
因此,似乎您正在尝试下载pdf文件,而不是打印print
试图做的当前屏幕的pdf。因此,您有两种选择。
1)禁用电子版的本机pdf查看器:
如果您不关心显示pdf的电子窗口,则在电子中禁用本机pdf查看器应改为使其将文件视为下载文件并尝试下载。
new BrowserWindow({
webPreferences: {
plugins: false
}
})
您可能还想检出electron's DownloadItem api来对文件的保存位置进行一些操作。
2)通过其他一些api下载pdf
我不会为此提供任何细节,因为您应该可以自己找到一些信息,但是基本上,如果您想从某个地方下载文件,则可以使用其他一些下载API,例如AJAX库以下载文件并将其保存在某处。这也可能使您也可以在电子窗口中呈现文档,因为一旦启动下载,您就可以将窗口重定向到pdf url并由本机查看器处理。
长话短说,对我来说听起来像您真的不想用电子打印,您只想保存要显示的pdf文件。用电子打印将呈现您在屏幕上看到的内容,而不是pdf文档本身,因此我认为您只是误解了打印的目的是什么。希望这对您有帮助,祝您好运!
===编辑===
不幸的是,我不认为有一种直接从电子打印文件的方法,因为电子打印用于打印电子显示器的内容。但是您应该可以通过简单的文件下载请求下载文件(见上文)。
我对您的建议是创建一个页面来预览文件。这将是一个独立的页面,而不是内置的pdf查看器。然后,您可以在页面上的某个位置插入一个按钮,以通过某种方式下载pdf,并跳过所有保存位置提示(这应该很容易找到文档)。
然后,为了获得预览,可以在同一页面上插入webview
tag,以显示本机pdf查看器。为了使本机pdf查看器在webview标记中起作用,您必须 在标记中包含plugins
属性。这是一个布尔标记,因此仅需要存在就可以了,例如<webview ... plugins>
,这将打开对pdf查看器所需的webview渲染器的插件支持。
您可以根据需要在页面上修改此标签的大小样式。摆脱下载和打印选项以使用户无法按下的一种技巧是在PDF url的末尾附加#toolbar=0
,以防止本机pdf查看器使用这些按钮显示顶部工具栏。>
因此,通过这种方式,您可以进行预览,确保用户无法使用带有额外ui的pdf下载器中的内置下载或打印功能,并且可以添加另一个按钮进行下载,以便可以进行打印以后。
答案 5 :(得分:0)
这是 2021 年,这是有史以来最简单的方法。
开始吧
首先,安装pdftoprinter
npm install --save pdf-to-printer
将库导入您的文件
const ptp require('pdf-to-printer') // something like this
然后将该方法调用到您的函数中
ptp.print('specify your route/url');
它应该可以工作!