Render PDF inline mobile and desktop - ruby-on-rails

Hi
i'm struggling browsing a pdf inline well on mobile and Desktop
env is vite 3.1.3, rails 7.0.4
Desktop i brought to run on all Browsers by mozilla/pdfjs but on mobile it doesnt show anything.
My Code is (on Rails)
view
<canvas id="the-canvas"></canvas>
javascript
// NPM
import * as pdfjsLib from 'pdfjs-dist/build/pdf'
window.showMozillaPdf = function () {
var pdfData = atob(gon.doc_b64);
console.log(pdfjsLib)
pdfjsLib.GlobalWorkerOptions.workerSrc = '/documents/pdfjs_worker';
console.log('pdf-part-2!')
var loadingTask = pdfjsLib.getDocument({data: pdfData});
loadingTask.promise.then(function (pdf) {
console.log('PDF loaded');
// Fetch the first page
var pageNumber = 1;
pdf.getPage(pageNumber).then(function (page) {
console.log('Page loaded');
var scale = 1.5;
var viewport = page.getViewport({scale: 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
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
console.log('Page rendered');
});
});
}, function (reason) {
// PDF loading error
console.error(reason);
});
}
The workers js (/documents/pdfjs_worker) i inserted from a controller because of mime type conflicts. This can be made nicer but it runs now.
This runs well on desktop but doesnt show anything on Mobile (iPhone 11 / Safari)
Has anyone experience with mozilla/pdfjs on Mobile?
thanks,
Chris

found it
i had to change the way for getting the url to the worker asset:
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf'
import workerUrl from 'pdfjs-dist/legacy/build/pdf.worker.min?url'
...
pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;
this replaces the '/documents/pdfjs_worker'
and i had to use the /legacy/ for getting it to run on mobile.
See vite-docs

Related

How to show the Annotation links on pdfJs

I have implement the pdfjs library to show the pdf in my application.
I have shown the pdf successfully. But the links on the pdf are not clickable. As i want o click the links to visit on the link.
pdf vsrsion : 2.10.377
Below is my code hope you will understand.
<div id="canvasPDFViewer" class="canvasPDFViewer"></div>
<div class="annotationLayer" id=”annotation-layer”></div>
This is the js I have include.
<script defer>
window.pdfjsLib.GlobalWorkerOptions.workerSrc = '/_js/pdf.worker.min.js';
</script>
This is the code how i am showing the pdf, the pdf is visible but, the links are not clickable.
let loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function(pdf) {
let totalPageNumber = pdf.numPages;
for(let pageNumber = 1; pageNumber <= totalPageNumber; pageNumber++) {
pdf.getPage(pageNumber).then(function(page) {
let scale = 1;
let viewport = page.getViewport({ scale: scale }); // Prepare canvas using PDF page dimensions
let canvas = document.createElement("canvas"); // Render PDF page into canvas context
canvas.height = viewport.height;
canvas.width = viewport.width;
let canvasContext = canvas.getContext("2d");
let renderContext = {
canvasContext: canvasContext,
viewport: viewport
};
//page.render(renderContext);
page.render(renderContext).promise.then(function() {
return page.getAnnotations();
}).then(function(annotationData) {
if(annotationData.length == 0)
return;
var pdf_canvas = $(canvas).offset();
$("#annotation-layer").css({ left: pdf_canvas.left + "px", top: pdf_canvas.top + "px", height: pdf_canvas + "px", width: pdf_canvas + "px" });
pdfjsLib.AnnotationLayer.render({
viewport: viewport.clone({ dontFlip: true }),
div: $("#annotation-layer").get(0),
annotations: annotationData,
page: page
});
});
document.getElementById("canvasPDFViewer").appendChild(canvas);
});
}
}, function (reason) {
// PDF loading error
// console.error(reason);
});
And the error is i am getting.
pdf.min.js:22 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'externalLinkTarget')
at LinkAnnotationElement.render (pdf.min.js:22:1)
at Function.render (pdf.min.js:22:1)
at eval (eval at <anonymous> (jquery.min.js:2:1), <anonymous>:32:48)
Why i am getting the error.
I have tried the solution with below link.
[https://usefulangle.com/post/94/javascript-pdfjs-enable-annotation-layer]
But no luck.

How to display pdf on canvas using angular7 and fabric js

I want to display a pdf as an image on canvas using angular7 and fabric js
i am not able to find any code to try in angular7
Finally i am able to solve this...i have used pdfjsLib and i have imported in my ts file (import * as pdfjsLib from 'pdfjs-dist/build/pdf';)
public src="/assets/myPhotos.pdf";
showPdf(){
pdfjsLib.getDocument(this.src).then(doc =>{
// console.log("this file has "+ doc._pdfInfo.numPages+ "pages");
doc.getPage(1).then(page => {
var myCanvas = <HTMLCanvasElement>document.getElementById("my_canvas");
var context = myCanvas.getContext("2d");
var scale = 1.5;
var viewport = page.getViewport(scale);
myCanvas.width = viewport.width;
myCanvas.height = viewport.height;
page.render({
canvasContext : context,
viewport : viewport
})
})
});
}

PDFJS.getDocument not working and not throwing an error

It's not going into the .then afterwards, and it's not throwing any error.
Here's my calling code:
function loadPage(base64Data, pageIndex) {
var pdfData = base64ToUint8Array(base64Data);
// this gets hit
PDFJS.getDocument(pdfData).then(function (pdf) {
// never gets here
pdf.getPage(pageIndex).then(function (page) {
var scale = 1;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('pdfPage');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({ canvasContext: context, viewport: viewport });
});
});
}
function base64ToUint8Array(base64) {
var raw = atob(base64); // convert base 64 string to raw string
var uint8Array = new Uint8Array(raw.length);
for (var i = 0; i < raw.length; i++) {
uint8Array[i] = raw.charCodeAt(i);
}
return uint8Array;
}
At one point it worked. When I step through it in the debugger, I can step into PDFJS.getDocument but that's way over my head.
My base64Data looks like JVBERi0x...g==. It's a base64 encoded pdf document.
To solve this, I had to add
PDFJS.disableWorker = true;
to the beginning of my loadPage function.
From View PDF files directly within browser using PDF.js,
PDF.js uses Web Workers concept of HTML5 internally to process the
request. If this statement is set to false, it creates an instance of
Web workers. Web Workers run in an isolated thread. For more
information on web workers; please refer
http://www.html5rocks.com/en/tutorials/workers/basics/
Promise is missing in your code. Here how i fixed this probelm:
PDFJS.getDocument(pdfData).promise.then(function (pdf) {
// do your stuff
});

Cordova Phonegap inappbrowser losing File Api functionality

I am developing an app using Web Audio Api. I have discovered that there is a memory leak in the way that Safari handles audio and doesn't garbage college the Audio Context correctly. For this reason I wish to load a new page. Have that page create the Audio Context, complete the operation and then close the window, so that the memory is released.
I have done the following to achieve this.
ref = window.open('record.html', '_self'); This will open the record.html page in the Cordova WebView according to https://wiki.apache.org/cordova/InAppBrowser
1 window.open('local-url.html');// loads in the
Cordova WebView
2 window.open('local-url.html', '_self');
// loads in the Cordova WebView
The record.html page loads a javascript file, that runs the operations that I wish to run. Here is the recordLoad.js file that makes some calls to native operations ( The native API is only available if loaded in the Cordova Webview and as you can see I need to access the file system, so this is the only way I can see to do it.
window.onload = createAudioContext;
ref = null;
function createAudioContext(){
console.log('createAudioContext');
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
window.URL = window.URL || window.webkitURL;
audioContext = new AudioContext;
getDirectory();
}
function getDirectory(){
console.log('getDirectory');
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, getFileSystem, fail);
}
function getFileSystem(directory){
console.log('getFileSystem');
var audioPath = localStorage.getItem('audioPath');
directory.root.getFile(audioPath, null, getVocalFile, fail);
}
function getVocalFile(fileEntry){
console.log('getVocalFile');
fileEntry.file(readVocalsToBuffer, fail);
}
function readVocalsToBuffer(file){
console.log('readVocalsToBuffer');
var reader = new FileReader();
reader.onloadend = function(evt){
var x = audioContext.decodeAudioData(evt.target._result, function(buffer){
if(!buffer){
console.log('error decoding file to Audio Buffer');
return;
}
window.voiceBuffer = buffer;
buffer = null;
loadBuffers();
});
}
reader.readAsArrayBuffer(file);
}
//web
function loadBuffers(){
console.log('loadBuffers');
var srcSong = localStorage.getItem('srcSong');
try{
var bufferLoader = new BufferLoader(
audioContext,
[
"."+srcSong
],
createOffLineContext
);
bufferLoader.load()
}
catch(e){
console.log(e.message);
}
}
//
function createOffLineContext(bufferList){
console.log('createOfflineContext');
offline = new webkitOfflineAudioContext(2, window.voiceBuffer.length, 44100);
var vocalSource = offline.createBufferSource();
vocalSource.buffer = window.voiceBuffer;
vocalSource.connect(offline.destination);
var backing = offline.createBufferSource();
backing.buffer = bufferList[0];
backing.connect(offline.destination);
vocalSource.start(0);
backing.start(0);
offline.oncomplete = function(ev){
bufferList = null;
console.log('audioContext');
console.log(audioContext);
audioContext = null;
console.log(audioContext);
vocalSource.stop(0);
backing.stop(0);
vocalSource.disconnect(0);
backing.disconnect(0);
vocalSource = null;
backing = null;
window.voiceBuffer = null;
window.renderedFile = ev.renderedBuffer;
var bufferR = ev.renderedBuffer.getChannelData(0);
var bufferL = ev.renderedBuffer.getChannelData(1);
var interleaved = interleave(bufferL, bufferR);
var dataview = encodeWAV(interleaved);
window.audioBlob = new Blob([dataview], {type: 'Wav'});
saveFile();
}
offline.startRendering();
}
// This file is very long, but once it is finished mixing the two audio buffers it writes a new file to the file system. And when that operation is complete I use
function gotFileWriter(writer){
console.log('gotFileWriter');
writer.onwriteend = function(evt){
console.log('onwriteEnd');
console.log(window.audioBlob);
delete window.audioBlob;
console.log(window.audioBlob);
// checkDirectory();
var ref = window.open('index.html', '_self');
// ref.addEventListener('exit', windowClose);
}
writer.write(audioBlob);
}
I return back to the original index.html file. This solves the memory issues. However, once I try to run the same operation a second time. ie load in the record.html file, and run the recordLoad.js file I receive an error ReferenceError: Can't find variable: LocalFileSystem
It would appear that in reload index.html some, but not all of the links to the Cordova API have been lost. I can still for example use the Media Api but not the File Api. I understand that this is a bit of a hacky way, (opening and closing windows) to solve the memory leak, but I cannot find any other way of doing it. I really need some help with this. So any pointers very very welcome.

Why does this Actionscript code work on Chrome but not FF and IE?

Why does this example, (from adobe.com) work in Chrome, but not in FF and IE?:
does not work = I can see the permission window, but when I click 'allow' all I see is a blank white screen.
http://wonderfl.net/c/7624
or see code below:
// frame action
import flash.media.Microphone;
import flash.events.SampleDataEvent;
import flash.utils.ByteArray;
import flash.display.Graphics;
var nWidth:Number = stage.stageWidth;
var nCenter:Number = stage.stageHeight / 2;
var nScale:Number = 100;
var myGraphics:Graphics = graphics;
var my_mic:Microphone = Microphone.getMicrophone();
my_mic.rate = 22;
my_mic.gain = 100;
my_mic.addEventListener(SampleDataEvent.SAMPLE_DATA, drawSampleData);
function drawSampleData(eventObject:SampleDataEvent):void
{
var myData:ByteArray = eventObject.data;
myGraphics.clear();
myGraphics.lineStyle(0, 0x000000);
myGraphics.moveTo(0, nCenter);
var nPitch:Number = nWidth / myData.length;
while (myData.bytesAvailable > 0)
{
var nX:Number = myData.position * nPitch;
var nY:Number = myData.readFloat() * nScale + nCenter;
myGraphics.lineTo(nX, nY);
}
}
Thanks.
I tried the example in the link you included and it did not work in any of my browsers, Chrome included.
The source code in the link you have included shows most of your code (including a few imports) nested inside a function called FlashTest, in my experience I cannot understand why this would even run in Chrome.

Resources