Opening a local pdf file using node.js and express - javascript

I am attempting to make a pdf viewer app using electron and electron-pdf-window
the code below works when i want to open from a URL file path, but when i tried to open a pdf from my local files using the file:/// the application download the pdf instead of viewing it on my window.
const { app } = require('electron')
const PDFWindow = require('electron-pdf-window')
app.on('ready', () => {
const win = new PDFWindow({
width: 800,
height: 600
})
win.loadURL('file://///C://username/desktop/myfile.pdf')
})
I tried also below code but below error displays.
TypeError: Cannot match against 'undefined' or 'null'.
const { BrowserWindow } = require('electron').remote
const PDFWindow = require('electron-pdf-window')
const win = new BrowserWindow({ width: 800, height: 600 })
PDFWindow.addSupport(win)
win.loadURL('file://///C://username/desktop/myfile.pdf')
Is there another way to open local pdf files from my PC directory?

Base on this readme, https://github.com/electron/electron/blob/master/docs/api/browser-window.md
you can do it something like
win.loadURL(`file://${__dirname}/app/index.html`)
but you have to put this inside app.on('ready', function() {} to avoid getting the Cannot create BrowserWindow before app is ready error.
Reason why that error appears
Because the app is not yet ready and is still loading

You need Modify electron-pdf-window package.
Open electron-pdf-window/index.js
Go to comment line 23 const->let fileUrl = url.replace(/^file:///i, '');
firstly change const via let,then add after this code
fileUrl = fileUrl.replace('/', '');
you can use normally ;
const w= new BrowserWindow({
width: 1200,
height: 920,
webPreferences: {
nodeIntegration: true,
contextIsolation: true
}
});
PDFWindow.addSupport(w)
w.loadURL(url.format ({
pathname: path.join(__dirname, '../pdf/123.pdf'),
protocol: 'file:',
slashes: true
}));

Related

Electron reactjs app show empty page after reloading if user navigate to other route

I create my project with create-react-app. I use react-router-dom for router and electron-builder to package my app.
When I first run my application, it works normally but once I reload the page all becomes white. If I don't navigate to other routes, the app still works after reloading.
Below is my electron/index.js code
function createWindow() {
mainWindow = new BrowserWindow({
width: 1280,
height: 600,
minWidth: 800,
minHeight: 200,
autoHideMenuBar: true,
useContentSize: true,
resizable: true,
webPreferences: {
nodeIntegration: true,
},
});
mainWindow.loadURL(isDev
? 'http://localhost:3000'
: `file://${path.join(__dirname, '../build/index.html')}`);
mainWindow.focus();
mainWindow.on('close', (e) => {
if (willQuitApp) {
/* the user tried to quit the app */
mainWindow = null;
} else {
/* the user only tried to close the window */
e.preventDefault();
mainWindow.hide();
}
});
}
app.on('ready', createWindow);
I found the answer. I only need to use <HashRouter> instead of <BrowserRouter>
You got empty page after reloading because when you navigate to another page, you change the url. So when you reload, electron will load that url instead of index.html file

Two browsers in the same Electron window

I'd like to have one single Electron window, split in two parts:
the left part is a BrowserWindow loading https://gmail.com
the right part is another BrowserWindow loading Gmail too, but I'd like these two browsers to be "independent", i.e. the cookies/LocalStorage/etc. should be independent (like if we have a normal Chrome window vs. a incognito window) ; thus allowing to have one Gmail account on left / another account connected on the right part
some other UI buttons on top of the single Electron window.
This code works, but it creates 2 windows instead of one:
const { app, BrowserWindow } = require('electron')
const path = require('path')
app.once('ready', () => {
let win = new BrowserWindow({show: false})
win.once('show', () => { win.webContents.executeJavaScript('validateFlights()') })
win.loadURL('https://www.gmail.com')
win.show()
let win2 = new BrowserWindow({show: false})
win2.once('show', () => { win.webContents.executeJavaScript('validateFlights()') })
win2.loadURL('https://www.gmail.com')
win2.show()
})
How to have them in one window?
A little late, but to add two browsers within one window you have to use BrowserWindow.addBrowserView instead of BrowserWindow.setBrowserView. You'll get the following:
const { BrowserView, BrowserWindow, app } = require('electron')
function twoViews () {
const win = new BrowserWindow({ width: 800, height: 600 })
const view = new BrowserView()
win.addBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 400, height: 300 })
view.webContents.loadURL('https://electronjs.org')
const secondView = new BrowserView()
win.addBrowserView(secondView)
secondView.setBounds({ x: 400, y: 0, width: 400, height: 300 })
secondView.webContents.loadURL('https://electronjs.org')
app.on('window-all-closed', () => {
win.removeBrowserView(secondView)
win.removeBrowserView(view)
app.quit()
})
}
app.whenReady().then(twoViews)
Once you create two BrowserView objects, you just add them to the window. You'll also want to remove the views when tearing down the application. If you don't you might get a segmentation fault.
What you are looking for is BrowserView
From the docs:
A BrowserView can be used to embed additional web content into a BrowserWindow. It is like a child window, except that it is positioned relative to its owning window. It is meant to be an alternative to the webview tag.
It looks like this is what you want, the views can render separate HTML pages and position them relatively inside the same browser window.
// In the main process.
const { BrowserView, BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600 })
win.on('closed', () => {
win = null
})
let view = new BrowserView({
webPreferences: {
nodeIntegration: false
}
})
win.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
view.webContents.loadURL('https://electronjs.org')

How to access jquery from the render process inside the preload script on Electron

I configured the Node integration to false in my window because, as described in the documentation, disabling is a good practice.
I want to access the jQuery of my render process in my preload script, but I don't know How I can do this.
Is it possible to activate a modal (for example) after some event occurs in the preload script?
Link to my example code in Github: https://github.com/JhonatanRSantos/Sample
As Janith said, you don't need to use a preload script. Here is a working sample based on your Github repo :
Index.html - in HEADER part, modify like this :
<script>window.$ = window.jQuery = require('jquery');</script>
<script src="./node_modules/popper.js/dist/umd/popper.min.js"></script>
<script src="./node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
Note that you have to use the path ./node_modules/popper.js/dist/umd/popper.min.js or you will get an error in the console (see popper.js in bootstrap 4 gives SyntaxError Unexpected token export)
Index.html - FOOTER, add :
<script src="./index.js"></script>
Rename preload.js to index.js
index.js can be :
console.log('Preload.js');
setTimeout(() => {
console.log('Open Boostrap Modal');
$('#myModal').modal('show');
}, 5000);
and finally, main.js can be :
const { app, BrowserWindow } = require('electron');
const path = require('path')
const url = require('url')
let createWindow = () => {
let win = new BrowserWindow({
width: 800,
height: 500,
center: true,
resizable: false,
show: false
});
win.setMenu(null);
const mainUrl = url.format({ // https://electronjs.org/docs/api/browser-window#winloadurlurl-options
protocol: 'file',
slashes: true,
pathname: path.join(__dirname, 'index.html')
})
win.loadURL(mainUrl);
win.once('ready-to-show', () => {
win.show();
win.webContents.openDevTools();
});
win.on('closed', () => {
win = null;
});
};
app.on('ready', createWindow);
I made a pull request to your repo to get changes.

Electron "require is not defined"

I'm making an application which I need to give access to the file system (fs) module, however even with nodeIntegration enabled the renderer gives me this error:
Uncaught ReferenceError: require is not defined
All similar problems I could find had a solution that said they needed to turn nodeIntegration on, however I already have it enabled.
This is my main.js:
const electron = require('electron');
const {app, BrowserWindow} = electron;
let win;
app.on('ready', () => {
var { width, height } = electron.screen.getPrimaryDisplay().workAreaSize;
width = 1600;
height = 900;
win = new BrowserWindow({'minHeight': 850, 'minWidth': 1600, width, height, webPreferences: {
contextIsolation: true,
webSecurity: true,
nodeIntegration: true
}});
win.setMenu(null);
win.loadFile('index.html');
win.webContents.openDevTools()
});
My index.js, linked in index.html as <script src="index.js"></script> currently only has require("fs"); in it, I've commented out all the other stuff.
I don't know why require still doesn't work even though nodeIntegration is enabled.
When you have nodeIntegration disabled but aren't using contextIsolation, you could use a preload script to expose a safe version of it on the global object. (Note: you shouldn't expose the entire fs module to a remote page!)
Here's an example of using a preload script in this way:
// main process script
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('my-safe-file.html')
// preload.js
const { readFileSync } = require('fs')
// the host page will have access to `window.readConfig`,
// but not direct access to `readFileSync`
window.readConfig = function () {
const data = readFileSync('./config.json')
return data
}
// renderer.js
const config = window.readConfig()
If you're only loading local pages, and those pages don't load or execute unsafe dynamic content then you might reconsider the use of contextIsolation for this strategy. If you want to keep contextIsolation on, however (and you definitely should if you have a chance of showing unsafe content), you can only communicate with the preload script with message passing via postMessage.
Here's an example of the same scenario above, but with contextIsolation on and using message passing.
// main process script
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('my-unsafe-file.html')
// preload.js
const { readFileSync } = require('fs')
const readConfig = function () {
const data = readFileSync('./config.json')
return data
}
window.addEventListener('message', (event) => {
if (event.source !== window) return
if (event.data.type === 'request') {
window.postMessage({ type: 'response', content: readConfig() })
}
})
// renderer.js
window.addEventListener('message', (event) => {
if (event.source !== window) return
if (event.data.type === 'response') {
const config = event.data.content
}
})
window.postMessage('request')
While this is definitely more verbose and difficult to deal with (and forces things to be async, because message passing is async), it's also much more secure. A pair of small JS wrappers around the postMessage API could make this easier to work with (e.g. via an RPC-like mechanism), but remember that the whole point of using contextIsolation is because you can't trust the renderer, so your preload script shouldn't trust just any message it gets via the postMessage API — you should always verify the event that you receive to ensure that you trust it.
This slide deck describers in detail why turning off Node integration without using context isolation is not always a good idea.

Communicating between two renderer processes in Electron

I am writing an Eletron program. In the program there is one index-window which is created by the main process (main.js). In this window there is a list of files (images). When I click on one of the files in that list I want to start a second window that displays that file.
That second window is started by the renderer process of the index-window (index.js). How can I communicate between the renderer process of the index-window and the renderer process of the second window?
Code:
Creating the index-window from the main process in main.js:
let win;
function createWindow(){
// Create the browser window.
win = new BrowserWindow({width: 1024, height: 768, minWidth: 800, minHeight: 600, show: false, icon: 'files/images/icon.png'});
win.loadURL(`file://${__dirname}/files/html/index.html`);
win.once('ready-to-show', () => {
win.show()
})
// Emitted when the window is closed.
win.on('closed', () => {
win = null;
});
}
app.on('ready', createWindow);
In the index.html index.js (renderer process) is started:
<script src="../javascript/index.js"></script>
In index.js the function create_sprite_window() is called which creates a child window:
const fs = require('fs');
const path = require('path');
const {BrowserWindow} = require('electron').remote
let child_windows = [];
function create_child_window(URL, width, height){
let rem_win = require('electron').remote.getCurrentWindow();
let new_win = new BrowserWindow({width: width, height: height, minWidth: 400, minHeight: 300, show: false, parent: rem_win, minimizable: true, maximizable: true, skipTaskbar: true});
child_windows[child_windows.length] = new_win;
console.log(child_windows);
new_win.loadURL(URL);
new_win.once('ready-to-show', () => {
new_win.show()
})
return new_win;
}
function create_sprite_window(){
new_win = create_child_window(`file://${__dirname}/../html/sprite_manager.html`, 800, 400);
}
The child windows are stored in the array child_windows.
Is it possible to then send the path of the image to the second window or, alternatively, edit the <img> tag of the second window (setting the source of the <img> tag in the second window to the image with getElementById.src = path;) from the index-window?.
I have found the answer by myself.
To show the correct image in the second renderer window, I add a GET parameter to the URL which contains the path of the image.

Categories

Resources