Is it possibile to use pdf.js to render a PDF in a html page and manage its navigation?
For navigation I mean zoom and pan. My goal is to add some button with pan (up, down, left, right) for zoom (zoom in ,zoom out, default zoom) and, if it's possibile, to automatically pan the document centering it in the click position.
The whole point of pdf.js is to render .pdf documents in HTML5. So what you're saying should be achievable. The project is quite well documented on Github and should be a good starting point. As far as the navigation and other toggle buttons for zooming and paning are concerned, they have been clearly implemented here and here.
So you could start by viewing the source of these demos first and then go through the documentation to fit your needs. Hope this gets you started in the right direction. I'm posting some relevant code snippet from one of the demos:
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 0.8,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* #param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function () {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
document.getElementById('page_num').textContent = pageNum;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
...
/**
* Asynchronously downloads PDF.
*/
PDFJS.getDocument(url).then(function (pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
}
Related
I'm trying to pan-zoom with the mouse a <div> that contains a PDF document.
I'm using PDF.js and panzoom libraries (but other working alternatives are welcome)
The snippet below basically does this task, but unfortunately, after the <div> is panzoomed, the PDF is not re-rendered, then its image gets blurry (pan the PDF with the mouse and zoom it with the mouse-wheel):
HTML
<script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
<script src="https://cdn.jsdelivr.net/npm/#panzoom/panzoom#4.3.2/dist/panzoom.min.js"></script>
<div id="panzoom-container">
<canvas id="the-canvas"></canvas>
</div>
<style>
#the-canvas {
border: 1px solid black;
direction: ltr;
}
</style>
JAVASCRIPT
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.js';
var pdfDoc = null,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
var container = document.getElementById("panzoom-container")
function renderPage(desiredHeight) {
pdfDoc.getPage(1).then(function(page) {
var viewport = page.getViewport({ scale: 1, });
var scale = desiredHeight / viewport.height;
var scaledViewport = page.getViewport({ scale: scale, });
canvas.height = scaledViewport.height;
canvas.width = scaledViewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: scaledViewport
};
page.render(renderContext);
});
}
// Get the PDF
var url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf';
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
// Initial/first page rendering
renderPage(250);
});
var panzoom = Panzoom(container, {
setTransform: (container, { scale, x, y }) => {
// here I can re-render the page, but I don't know how
// renderPage(container.getBoundingClientRect().height);
panzoom.setStyle('transform', `scale(${scale}) translate(${x}px, ${y}px)`)
}
})
container.addEventListener('wheel', zoomWithWheel)
function zoomWithWheel(event) {
panzoom.zoomWithWheel(event)
}
https://jsfiddle.net/9rkh7o0e/5/
I think that the procedure is correct, but unfortunately I'm stuck into two issues that must be fixed (then I ask for your help):
I don't know how to re-render correctly the PDF after the panzoom happened.
consecutive renderings have to be queued in order to make this work properly (so that the PDF doesn't blink when panzooming)
How could I fix 1 and 2 ? I could not find any tool for doing that on a PDF, apart the panzoom lib.
Thanks
After some days of attempts, I solved the problem.
The tricky part was to scale down the contained canvas after the container has been scaled, with a transform-origin CSS attribute set:
canvas.style.transform = "scale("+1/panzoom.getScale()+")"
Here is a fiddle of a PDF that can pan-zoomed. Each new render is done after a small delay (here it is set to 250ms) which corresponds to a sort of "wheel stop event".
function zoomWithWheel(event) {
panzoom.zoomWithWheel(event)
clearTimeout(wheelTimeoutHandler);
wheelTimeoutHandler = setTimeout(function() {
canvas.style.transform = "scale("+1/panzoom.getScale()+")"
if (pdfDoc)
renderPage(panzoom.getScale());
}, wheelTimeout)
}
https://jsfiddle.net/7tmk0z42/
I am trying to make a PDF viewer, and it runs perfectly when I run it through live server on vscode, but for some reason, when I go to my files, right click on the index.html and click open with chrome, firefox, or internet explorer it comes up with 2 errors:
1)GET file://mozilla.github.io/pdf.js/build/pdf.js net::ERR_FILE_NOT_FOUND
main.js:18
2)Uncaught TypeError: Cannot read property 'GlobalWorkerOptions' of undefined
at main.js:18
Here is my index.html code and my main.js code:
Index.html
<!DOCTYPE html>
<html>
<head>
<title>PDF Viewer Attempt 2</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1>PDF Name</h1>
<div>
<button id="prev">Previous</button>
<button id="next">Next</button>
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
</div>
<canvas id="the-pdf"></canvas>
<script src="//mozilla.github.io/pdf.js/build/pdf.js"></script>
<script src="js/main.js"></script>
</body>
</html>
main.js:
//Code adapted from https://jsfiddle.net/pdfjs/wagvs9Lf/
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
var url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'; //works with this internet pdf
//url = 'pdf.pdf'; // Works with local pdf
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 0.8,
canvas = document.getElementById('the-pdf'),
ctx = canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* #param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function() {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
document.getElementById('page_num').textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
});
Any help is appreciated! This was my first post so let me know if there is anything I can do to format better! Dont worry about that line that says error for now, that is just a paragraph block.
I'm using CefSharp 55.0.0 WinForms.
In order to take screenshots in CefSharp, I scroll the webpage using window.scroll within JavaScript and take an image of the current viewport. Once that has completed, it is then stitched back together again. This works fine for monitors that have their DPI setting set to 100%. However, when the monitor has a DPI greater than 100% screenshots do not work as expected and miss content.
Image 1 - 100%
Image 2 - 150%
Compare Image 1 to Image 2. While they both (practically) have the same width and height, Image 2 is missing a large portion of the content, compared to a perfect Image 1.
When DPI settings are above 100%, how can I correctly scroll and capture a screenshot that ensures I obtain everything it would if settings were at 100%?
Other details
The application has the correct DPI aware settings in theapp.manifest file and Cef.EnableHighDPISupport(); has been called in the Main method within Program.cs.
Screenshot Code (abridged)
int scrollHeight = GetDocHeight(); //some javascript that calcs the height of the document
int viewportHeight = ClientRectangle.Size.Height;
int viewportWidth = ClientRectangle.Size.Width;
int count = 0;
int pageLeft = scrollHeight;
bool atBottom = false;
while (!atBottom)
{
if (pageLeft > viewportHeight)
{
await GetBrowser().MainFrame.EvaluateScriptAsync("(function() { window.scroll(0," + (count * viewportHeight) + "); })();"); //I think the issue lies here
count++;
await PutTaskDelay();
using (Bitmap image = GetCurrentViewScreenshot())
{
//just a class that saves the partial images to a disk cache
cache.AddImage(count, image);
}
}
else
{
await GetBrowser().MainFrame.EvaluateScriptAsync("(function() { window.scrollBy(0," + pageLeft + "); })();");
atBottom = true;
count++;
await PutTaskDelay();
Rectangle cropRect = new Rectangle(new Point(0, viewportHeight - pageLeft), new Size(viewportWidth, pageLeft));
using (Bitmap src = GetCurrentViewScreenshot())
using (Bitmap target = new Bitmap(cropRect.Width, cropRect.Height))
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel);
cache.AddImage(count, target);
}
}
pageLeft = pageLeft - viewportHeight;
}
Current View Screenshot Method
private Bitmap GetCurrentViewScreenshot()
{
int width, height;
width = ClientRectangle.Width;
height = ClientRectangle.Height;
using (Bitmap image = new Bitmap(width, height))
{
using (Graphics graphics = Graphics.FromImage(image))
{
Point p, upperLeftDestination;
Point upperLeftSource = new Point(0, 0);
p = new Point(0, 0);
upperLeftSource = PointToScreen(p);
upperLeftDestination = new Point(0, 0);
Size blockRegionSize = ClientRectangle.Size;
graphics.CopyFromScreen(upperLeftSource, upperLeftDestination, blockRegionSize);
}
return new Bitmap(image);
}
}
While not a perfect solution, I found a workaround that involves setting the force-device-scale-factor flag to 1 in CefSettings. It's not perfect because the browser isn't scaled on high DPI displays, potentially making text hard to read for users. However, it does fix my more pressing issue of missing data in screenshots.
CefSettings settings = new CefSettings();
settings.CefCommandLineArgs.Add("force-device-scale-factor", "1");
Cef.Initialize(settings);
This question is still open for better suggestions, if there are any. :-)
I have problem with resize windows game. I've read various posts on the forum but I could not get the correct resize the view of the game.
If you rotate the device screen size, I would like to display the game has adapted to the width / height of the display device both horizontally and vertically.
When the game started, it is properly scaled up, just turning your exchange device scaling is incorrect. Return to the previous screen orientation device retains its proportions.
In template html I add:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
First script:
function preload() {
...
Global.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
Global.game.scale.pageAlignHorizontally = true;
Global.game.scale.pageAlignVertically = true;
Global.game.scale.forceOrientation(true, false);
Global.game.scale.setMaximum();
Global.game.scale.setScreenSize(true);
...
}
function resize() {
Global.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
Global.game.scale.pageAlignHorizontally = true;
Global.game.scale.pageAlignVertically = true;
Global.game.scale.forceOrientation(true, true);
Global.game.scale.setShowAll();
Global.game.scale.refresh();
}
When I use this code, game scale correct when game started. When in device:
change position with horizontal on vertical, game is bigges and show
scroll
change position with vertical on horizontal, game is flat
Second script:
function preload() {
...
Global.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
Global.game.scale.parentIsWindow = true;
...
}
function resize () {
Global.game.scale.refresh();
}
When I use this code, game scale correct when game started. When in device:
1. change position with horizontal on vertical, game is scaled but on left and right is see white background
2. change position with vertical on horizontal, game is scaled but on bottom I see white background
I use Phaser 2.3
Does anyone know the solution to my problem?
Thank you =)
BEST SOLUTION
Phaser 2.3 have a bug - not change resize site. I download 2.4.3 version.
I've added features that is loaded at the beginning, which guards the resolution change
var attacks.base.x = 110;
var attacks.base.y = 190;
$(document).ready(function () {
handleScreenResize();
});
function handleScreenResize() {
$(window).resize(function () {
clearTimeout(timeoutResize);
timeoutResize = setTimeout(function () {
var xButtonBase;
var yButtonBase;
/* resize game */
game.scale.scaleMode = Phaser.ScaleManager.RESIZE;
game.scale.pageAlignHorizontally = true;
game.scale.pageAlignVertically = true;
game.scale.forceLandscape = true;
game.scale.parentIsWindow = true;
game.scale.refresh();
/* get new width and height*/
var gameWidth = game.width < game.world.width ? game.width : game.world.width;
/** If you have static button you have change position use "cameraOffset.x" and "cameraOffset.y" set new position*/
/* change position buttons attack */
if(settings.control_option === 'RIGHTHAND') {
xButtonBase = attacks.base.x;
yButtonBase = game.height - attacks.base.y;
} else {
xButtonBase = gameWidth - attacks.base.x;
yButtonBase = game.height - attacks.base.y;
}
/** set position buttons with attack and assist (down screen)*/
attacks.base.button.cameraOffset.x = xButtonBase;
attacks.base.button.cameraOffset.y = yButtonBase;
}, 1000);
});
}
In preload add this script
function preload() {
...
Global.game.scale.scaleMode = Phaser.ScaleManager.RESIZE;
Global.game.scale.pageAlignHorizontally = true;
Global.game.scale.pageAlignVertically = true;
Global.game.scale.forceLandscape = true;
Global.game.scale.parentIsWindow = true;
Global.game.scale.refresh();
...
}
This sounds like an issue that my friend and I were getting when we were trying to re-size our game to a larger resolution if our window changed size.
See this issue: https://github.com/photonstorm/phaser/issues/1881
It should be fixed in Phaser 2.4 (RC1 is out now).
I'm trying to display a PDF within an iFrame, for a PHP, HTML and CSS based web "app" on the iPad. However, when viewing a PDF that's either in an object or in an iFrame, on the iPad, you can't change the page you are viewing. The scrolling just doesn't seem to work.
So my thought is that I need to create a next and prev button that uses javascript to change the currently viewed page. However, I can't seem to find any information on how to achieve this without embedding code in the PDF. This is not an option for the app though, as users will obviously not know how to embed code in the PDF's they upload.
I'd really love any information on how to achieve this solution w/o modifying the PDF's. Also, if there is an alternative to using an object or iFrame that will make this work on the iPad, that would be great too.
Thanks in advance.
This can be done using PDF.js
Their webpage can be found here: http://mozilla.github.io/pdf.js/
I didn't want to use their solution at first, because I heard it will have issues rendering some PDF's. However, so far, it appears to render our PDFs perfectly.
Here is some code I used to implement the process
PDFJS.getDocument('FILE_LOCATION').then(function(pdf) {
state = true;
cur_page = 1;
total_pages = pdf.numPages;
pdf.getPage(cur_page).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
$(".pdf_viewer").on("click", ".prev_page", function(e) {
e.preventDefault();
if( state && cur_page > 1 ) {
--cur_page
pdf.getPage(cur_page).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
}
});
$(".pdf_viewer").on("click", ".next_page", function(e) {
e.preventDefault();
if( state && cur_page < total_pages ) {
++cur_page;
pdf.getPage(cur_page).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
}
});
$(".pdf_viewer").on("click", ".close_window", function(e) {
e.preventDefault();
if( state ) {
state = false;
pdf.destroy();
}
});
EDIT: fixed a typo