Stream canvas to imgITools to make ico - javascript

So on WinXP I have been having a hard time converting a PNG file to an ICO with canvas. I found this method encodeImage. I don't know if it works but it looks promising but I can't figure out how to feed the image I drew on a canvas into imgITools.decodeData.
What should I use for aImageStream and/or aMimeType?
imgTools.decodeImageData(aImageStream, aMimeType, imgContainer);
This is more of my code:
img['asdf'].file = new FileUtils.File(myPathHere);
let iconStream;
try {
let imgTools = Cc["#mozilla.org/image/tools;1"]
.createInstance(Ci.imgITools);
let imgContainer = { value: null };
imgTools.decodeImageData(aImageStream, aMimeType, imgContainer);
iconStream = imgTools.encodeImage(imgContainer.value,
"image/vnd.microsoft.icon",
"format=bmp;bpp=32");
} catch (e) {
alert('failure converting icon ' + e)
throw("processIcon - Failure converting icon (" + e + ")");
}
let outputStream = FileUtils.openSafeFileOutputStream(img['asdf'].file);
NetUtil.asyncCopy(iconStream, outStream, netutilCallback);

Since you're having a canvas already(?), it would be probably easier to use either on of the following canvas methods:
toDataURI/toDataURIHD
toBlob/toBlobHD
mozFetchAsStream
There is also the undocumented -moz-parse-options:, e.g -moz-parse-options:format=bmp;bpp=32. (ref-tests seem to depend on it, so it isn't going away anytime soon I'd think).
So, here is an example for loading stuff into an ArrayBuffer.
(canvas.toBlobHD || canvas.toBlob).call(canvas, function (b) {
var r = new FileReader();
r.onloadend = function () {
// r.result contains the ArrayBuffer.
};
r.readAsArrayBuffer(b);
}, "image/vnd.microsoft.icon", "-moz-parse-options:format=bmp;bpp=32");
Here is a more complete example fiddle creating a 256x256 BMP icon.
Since you likely want to feed that data into js-ctypes, having an ArrayBuffer is great because you can create pointers from it directly, or write it to a file using OS.File.

Related

Using pdf.js to render a PDF but it doesn't work and I don't get any error messages to help me debug the issue

I'm trying to build a Flask app where I upload pdf's and I'm working on previewing them before submitting to the back-end.
The script I'm using is as follows:
const imageUploadValidation = (function () {
"use strict";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://mozilla.github.io/pdf.js/build/pdf.js";
const onFilePicked = function (event) {
// Select file Nodelist containing 1 file
const files = event.target.files;
const filename = files[0].name;
if (filename.lastIndexOf(".") <= 0) {
return alert("Please add a valid file!");
}
const fileReader = new FileReader();
fileReader.onload = function (e) {
const pdfData = e.target.result;
let loadingTask = pdfjsLib.getDocument({ data: pdfData })
loadingTask.promise.then(function (pdf) {
console.log("PDF loaded", pdf);
pdf.getPage(1).then((page) => {
console.log("page loaded", page);
// var scale = 1.5;
// var viewport = page.getViewport({ scale: scale });
var iframe = document.getElementById("image-preview");
iframe.src = page
// var context = canvas.getContext("2d");
// canvas.height = viewport.height;
// canvas.width = viewport.width;
// var renderContext = {
// canvasContext: context,
// viewport: viewport,
// };
// var renderTask = page.render(renderContext);
// renderTask.promise.then(function () {
// console.log("Page rendered");
// });
});
})
.catch((error) => {
console.log(error);
});
};
const pdf = fileReader.readAsArrayBuffer(files[0]);
console.log("read as Data URL", pdf);
};
const Constructor = function (selector) {
const publicAPI = {};
const changeHandler = (e) => {
// console.log(e)
onFilePicked(e);
};
publicAPI.init = function (selector) {
// Check for errors.
const fileInput = document.querySelector(selector);
if (!selector || typeof selector !== "string") {
throw new Error("Please provide a valid selector");
}
fileInput.addEventListener("change", changeHandler);
};
publicAPI.init(selector);
return publicAPI;
};
return Constructor;
})();
imageUploadValidation("form input[type=file]");
The loading task promise never seems to run. Everything seems to work up until that point. I'm not familiar with this Promise syntax, so I can't be sure if the problem is there or how I'm passing in the pdf file.
P.S. The commented out code is the original way I had this setup, what
s uncommented was just me testing a different way.
Check Datatype
First you might want to check what your getting back from your FileReader, specifically what is the datatype for pdfData. If you have a look at the documentation (direct link) getDocument is expecting a Unit8Array or a binary string.
Add Missing Parameters
The next problem you have is your missing required parameters in your call to getDocument. Here is the minimum required arguments:
var args = {
url: 'https://example.com/the-pdf-to-load.pdf',
cMapUrl: "./cmaps/",
cMapPacked: true,
}
I have never used the data argument in place of the url but as long as you supply the correct datatype you should be fine. Notice that cMapUrl should be a relative or absolute path to the cmap folder. PDFJS often needs these files to actually interpret a PDF file. Here are all the files from the demo repository (GitHub pages): cmaps You'll need to add these to your project.
Instead of using data I would recommend uploading your files as blobs and then all you have to do is supply the blob URL as url. I am not familiar with how to do that, I just know its possible in modern browsers.
Where Is Your Viewer / You Don't Need iFrame or Canvas
PDFJS just needs a div to place the PDF inside of. It's picky about some of the CSS rules, for exmaple it MUST be positioned absolute, otherwise PDFJS generates the pages as 0px height.
I don't see PDFViewer or PDFLinkService in your code. It looks like you are trying to build the entire viewer from scratch yourself. This is no small endeavor. When you get loadingTask working correctly the response should be handled something like this:
loadingTask.promise.then(
// Success function.
function( doc ) {
// viewer is holding: new pdfjsViewer.PDFViewer()
// linkService is: new pdfjsViewer.PDFLinkService()
viewer.setDocument( doc );
linkService.setDocument( doc );
},
// Error function.
function( exception ) {
// What type of error occurred?
if ( exception.name == 'PasswordException' ) {
// Password missing, prompt the user and try again.
elem.appendChild( getPdfPasswordBox() );
} else {
// Some other error, stop trying to load this PDF.
console.error( exception );
}
/**
* Additional exceptions can be reversed engineered from here:
* https://github.com/mozilla/pdf.js/blob/master/examples/mobile-viewer/viewer.js
*/
}
);
Notice that PDFViewer does all the hard work for you. PDFLinkService is needed if you want links in the PDF to work. You really should checkout the live demo and the example files.
Its a lot of work but these example files specifically can teach you all you need to know about PDFJS.
Example / Sample Code
Here is some sample code from a project I did with PDFJS. The code is a bit advanced but it should help you reverse engineer how PDFJS is working under the hood a bit better.
pdfObj = An object to store all the info and objects for this PDF file. I load multiple PDFs on a single page so I need this to keep them separate from each other.
updatePageInfo = My custome function that is called by PDFJS's eventBus when the user changes pages in the PDF; this happens as they scroll from page to page.
pdfjsViewer.DownloadManager = I allow users to download the PDFs so I need to use this.
pdfjsViewer.EventBus = Handles events like loading, page changing, and so on for the PDF. I am not 100% certain but I think the PDFViewer requires this.
pdfjsViewer.PDFViewer = What handles actually showing your PDF to users. container is the element on the page to render in, remember it must be positioned absolute.
// Create a new PDF object for this PDF.
var pdfObj = {
'container': elem.querySelector('.pdf-view-wrapper'),
'document': null,
'download': new pdfjsViewer.DownloadManager(),
'eventBus': new pdfjsViewer.EventBus(),
'history': null,
'id': id,
'linkService': null,
'loaded': 0,
'loader': null,
'pageTotal': 0,
'src': elem.dataset.pdf,
'timeoutCount': 0,
'viewer': null
};
// Update the eventBus to dispatch page change events to our own function.
pdfObj.eventBus.on( 'pagechanging', function pagechange(evt) {
updatePageInfo( evt );
} );
// Create and attach the PDFLinkService that handles links and navigation in the viewer.
var linkService = new pdfjsViewer.PDFLinkService( {
'eventBus': pdfObj.eventBus,
'externalLinkEnabled': true,
'externalLinkRel': 'noopener noreferrer nofollow',
'externalLinkTarget': 2 // Blank
} );
pdfObj.linkService = linkService;
// Create the actual PDFViewer that shows the PDF to the user.
var pdfViewer = new pdfjsViewer.PDFViewer(
{
'container': pdfObj.container,
'enableScripting': false, // Block embeded scripts for security
'enableWebGL': true,
'eventBus': pdfObj.eventBus,
'linkService': pdfObj.linkService,
'renderInteractiveForms': true, // Allow form fields to be editable
'textLayerMode': 2
}
);
pdfObj.viewer = pdfViewer;
pdfObj.linkService.setViewer( pdfObj.viewer );

WebKitSubtleCrypto error when using dispatchMessage in Safari Extension

I am attempting to build a Safari extension to share screenshots of webpages but when I try to pass the image back to Swift I get an error that makes Safari unstable and kills my task mid-process.
The idea is that when a user taps a tool bar button any selected text and a screenshot of the webpage are saved. I am trying to pass both of those through the userInfo dictionary. If I run my code as is with the dispatchMessage call commented out I do not see any errors. If I uncomment the dispatch call I see the following error:
WebKitSubtleCrypto is deprecated. Please use SubtleCrypto instead.
Here is my js code:
document.addEventListener("DOMContentLoaded", function(event) {
safari.self.addEventListener("message", handleMessage);
});
function handleMessage(event) {
var selectedText = window.getSelection().toString();
var screenshot;
if (window.top === window) {
html2canvas(document.getElementsByTagName('html')).then(function(canvas) {
screenshot = convertCanvasToImage(canvas);
console.log("canvas image: " + screenshot)
safari.extension.dispatchMessage("test", {"selectedText": selectedText, "screenshot" : canvas});
});
}
}
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
The html2canvas (latest - 0.5.0-beta4) script is in another file packaged with the extension.
Edit 1
After some more testing it looks as though this error only has to do with passing the 'screenshot' object in the messageDipatch call. If I take out screenshot and only pass the selectedText data it works as expected. I have also tried to pass the screenshot through as a canvas instead of running it through the 'convertCanvasToImage()' call but I am getting the same error with that.
The issue ended up being related to how I was init'ing the image data before converting the canvas to a data url.
Going from:
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
to:
function convertCanvasToImage(canvas) {
var imageData = canvas.toDataURL("image/png")
return imageData;
}
resolved the issue.
On the Swift side this is how I am decoding that data (be cautious of the all the forcing in this):
let imageString = userInfo?["screenshot"] as! String
let imageData = NSData.init(contentsOf: NSURL(string: imageString) as! URL)
let image = NSImage(data: imageData as! Data)

Nativescript get base64 data from native image

I try to use https://github.com/bradmartin/nativescript-drawingpad for saving a signature to my backend. But I am simply not capable to find a solution to get some "useful" data from getDrawing(), which returns a native image Object, for example UIImage on iOS.
I would love to "convert" the image data to some base64 (png, or whatever) string and send it to my server.
I tried someting like:
var ImageModule = require("ui/image");
var ImageSourceModule = require("image-source");
elements.drawingpad.getDrawing().then(function(a){
var image = ImageSourceModule.fromNativeSource( a );
api.post("sign", image.toBase64String());
});
I also tried to post simply a like seen in the demo stuff.
I would really love to see a demo of how to get my hands on the "image data" itself.
thanks!
Thanks to #bradmartin I found the solution:
var image = ImageSourceModule.fromNativeSource(a);
var base64 = image.toBase64String('png');
Actually after lots of digging with tons of error, I finally figured it out.
First you have to require the nativescript-imagepicker source module and also the nativescript image source.
var imagepicker = require("nativescript-imagepicker");
var ImageSourceModule = require("tns-core-modules/image-source");
a case where you want to update a user profile and also send a base64 string to your backend for processing
function changeProfileImage(args) {
var page = args.object;
var profile = page.getViewById("profile-avatar");
var context = imagepicker.create({ mode: "single" });
context.authorize().then(function() {
return context.present();
}).then(function(selection) {
profile.background = `url(${selection[0]._android})`;
profile.backgroundRepeat = `no-repeat`;
profile.backgroundSize = `cover`;
ImageSourceModule.fromAsset(selection[0]).then(image => {
var base64 = image.toBase64String('png');
// console.log(base64);
uploadMediaFile(base64);
});
}).catch(function (e) {
// process error
console.log(e);
});
}

resize image before upload via background transfer in winjs

I would like to resize an image picked from the gallery of the phone before uploading it via background transfer so far I have:-
filePicker.pickSingleFileAsync().then(function (file) {
uploadSingleFileAsync(uri, file);
}).done(null, displayException);
function uploadSingleFileAsync(uri, file) {
if (!file) {
displayError("Error: No file selected.");
return;
}
return file.getBasicPropertiesAsync().then(function (properties) {
if (properties.size > maxUploadFileSize) {
displayError("Selected file exceeds max. upload file size (" + (maxUploadFileSize / (1024 * 1024)) +
" MB).");
return;
}
var upload = new UploadOperation();
//tried this to compress the file but it doesnt work obviously not right for the object
//file = file.slice(0, Math.round(file.size / 2));
upload.start(uri, file);
// Persist the upload operation in the global array.
uploadOperations.push(upload);
});
}
and the rest then uploads the file. I tried adding in .slice but it doesn't work (im guessing because file is an object rather than) and i'm not sure how to compress this type of file object. I can't seem to find any examples or advice on msdn or the windows dev forums, I can obviously resize the photos once they are on the server but I would rather users are not waiting longer than they have to for their files to upload.
Do I need to save the image before I can manipulate it? Any advice would be greatly appreciated!
** EDIT *
my upload singlefileasync now looks like:-
function uploadSingleFileAsync(uri, file) {
if (!file) {
displayError("Error: No file selected.");
return;
}
return file.getBasicPropertiesAsync().then(function (properties) {
if (properties.size > maxUploadFileSize) {
displayError("Selected file exceeds max. upload file size (" + (maxUploadFileSize / (1024 * 1024)) +
" MB).");
return;
}
// Exception number constants. These constants are defined using values from winerror.h,
// and are compared against error.number in the exception handlers in this scenario.
// This file format does not support the requested operation; for example, metadata or thumbnails.
var WINCODEC_ERR_UNSUPPORTEDOPERATION = Helpers.convertHResultToNumber(0x88982F81);
// This file format does not support the requested property/metadata query.
var WINCODEC_ERR_PROPERTYNOTSUPPORTED = Helpers.convertHResultToNumber(0x88982F41);
// There is no codec or component that can handle the requested operation; for example, encoding.
var WINCODEC_ERR_COMPONENTNOTFOUND = Helpers.convertHResultToNumber(0x88982F50);
// Keep objects in-scope across the lifetime of the scenario.
var FileToken = "";
var DisplayWidthNonScaled = 0;
var DisplayHeightNonScaled = 0;
var ScaleFactor = 0;
var UserRotation = 0;
var ExifOrientation = 0;
var DisableExifOrientation = false;
// Namespace and API aliases
var FutureAccess = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList;
var LocalSettings = Windows.Storage.ApplicationData.current.localSettings.values;
//FileToken = FutureAccess.add(file);
FileToken = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.add(file);
id("myImage").src = window.URL.createObjectURL(file, { oneTimeOnly: true });
id("myImage").alt = file.name;
// Use BitmapDecoder to attempt to read EXIF orientation and image dimensions.
return loadSaveFileAsync(file)
function resetPersistedState() {
LocalSettings.remove("scenario2FileToken");
LocalSettings.remove("scenario2Scale");
LocalSettings.remove("scenario2Rotation");
}
function resetSessionState() {
// Variables width and height reflect rotation but not the scale factor.
FileToken = "";
DisplayWidthNonScaled = 0;
DisplayHeightNonScaled = 0;
ScaleFactor = 1;
UserRotation = Windows.Storage.FileProperties.PhotoOrientation.normal;
ExifOrientation = Windows.Storage.FileProperties.PhotoOrientation.normal;
DisableExifOrientation = false;
}
function loadSaveFileAsync(file) {
// Keep data in-scope across multiple asynchronous methods.
var inputStream;
var outputStream;
var encoderId;
var pixels;
var pixelFormat;
var alphaMode;
var dpiX;
var dpiY;
var outputFilename;
var ScaleFactor = 0.5;
new WinJS.Promise(function (comp, err, prog) { comp(); }).then(function () {
// On Windows Phone, this call must be done within a WinJS Promise to correctly
// handle exceptions, for example if the file is read-only.
return FutureAccess.getFileAsync(FileToken);
}).then(function (inputFile) {
return inputFile.openAsync(Windows.Storage.FileAccessMode.read);
}).then(function (stream) {
inputStream = stream;
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(inputStream);
}).then(function (decoder) {
var transform = new Windows.Graphics.Imaging.BitmapTransform();
// Scaling occurs before flip/rotation, therefore use the original dimensions
// (no orientation applied) as parameters for scaling.
// Dimensions are rounded down by BitmapEncoder to the nearest integer.
transform.scaledHeight = decoder.pixelHeight * ScaleFactor;
transform.scaledWidth = decoder.pixelWidth * ScaleFactor;
transform.rotation = Helpers.convertToBitmapRotation(UserRotation);
// Fant is a relatively high quality interpolation mode.
transform.interpolationMode = Windows.Graphics.Imaging.BitmapInterpolationMode.fant;
// The BitmapDecoder indicates what pixel format and alpha mode best match the
// natively stored image data. This can provide a performance and/or quality gain.
pixelFormat = decoder.bitmapPixelFormat;
alphaMode = decoder.bitmapAlphaMode;
dpiX = decoder.dpiX;
dpiY = decoder.dpiY;
// Get pixel data from the decoder. We apply the user-requested transforms on the
// decoded pixels to take advantage of potential optimizations in the decoder.
return decoder.getPixelDataAsync(
pixelFormat,
alphaMode,
transform,
Windows.Graphics.Imaging.ExifOrientationMode.respectExifOrientation,
Windows.Graphics.Imaging.ColorManagementMode.colorManageToSRgb
);
}).then(function (pixelProvider) {
pixels = pixelProvider.detachPixelData();
// The destination file was passed as an argument to loadSaveFileAsync().
outputFilename = file.name;
switch (file.fileType) {
case ".jpg":
encoderId = Windows.Graphics.Imaging.BitmapEncoder.jpegEncoderId;
break;
case ".bmp":
encoderId = Windows.Graphics.Imaging.BitmapEncoder.bmpEncoderId;
break;
case ".png":
default:
encoderId = Windows.Graphics.Imaging.BitmapEncoder.pngEncoderId;
break;
}
return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
}).then(function (stream) {
outputStream = stream;
// BitmapEncoder expects an empty output stream; the user may have selected a
// pre-existing file.
outputStream.size = 0;
return Windows.Graphics.Imaging.BitmapEncoder.createAsync(encoderId, outputStream);
}).then(function (encoder) {
// Write the pixel data onto the encoder. Note that we can't simply use the
// BitmapTransform.ScaledWidth and ScaledHeight members as the user may have
// requested a rotation (which is applied after scaling).
encoder.setPixelData(
pixelFormat,
alphaMode,
DisplayWidthNonScaled * ScaleFactor,
DisplayHeightNonScaled * ScaleFactor,
dpiX,
dpiY,
pixels
);
return encoder.flushAsync();
}).then(function () {
WinJS.log && WinJS.log("Successfully saved a copy: " + outputFilename, "sample", "status");
}, function (error) {
WinJS.log && WinJS.log("Failed to update file: " + error.message, "sample", "error");
resetSessionState();
resetPersistedState();
}).then(function () {
// Finally, close each stream to release any locks.
inputStream && inputStream.close();
outputStream && outputStream.close();
}).then(function () {
var upload = new UploadOperation();
upload.start(uri, file);
// Persist the upload operation in the global array.
uploadOperations.push(upload);
});
}
But I am getting an error when I reach this line return
file.openAsync(Windows.Storage.FileAccessMode.readWrite);
saying that I do not have write access? How do I get write access or move it so that I can have write access?
To resize an image you can use the image encoding APIs in WinRT, namely that in Windows.Graphics.Imaging. I suggest you look at scenario 2 of the Simple Imaging Sample (http://code.msdn.microsoft.com/windowsapps/Simple-Imaging-Sample-a2dec2b0) which shows how to do all manners of transforms on an image. Changing the dimensions is included there, so it'll just be a matter of chopping out the parts you don't need.
I have a discussion about all this in my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, in Chapter 13, section "Image Manipulation and Encoding". In there I try to separate out the main steps in the process into something a little more digestible, and provide an additional sample.
The process of encoding can look rather involved (lots of chained promises), but it's quite straightforward and is exactly what an email program would do to reduce the size of attached images, for instance. In any case, you should end up with another StorageFile with a smaller image that you can then pass to the uploader. I would recommend using your Temporary app data for such files, and be sure to clean them up when the upload is complete.

how can we do pdf merging using javascript

I wanted to do client side scrpting for merging and splitting pdf, so i wanted to use itextsharp. Can that be used with javascript. I am new to Javascript. Please help me with your valuable suggestions.
I found an entirely client-side solution using the PDF-LIB library: https://pdf-lib.js.org/
It uses the function mergeAllPDFs which takes one parameter: urls, which is an array of urls to the files.
Make sure to include the following in the header:
<script src='https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.js'></script>
<script src='https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js'></script>
Then:
async function mergeAllPDFs(urls) {
const pdfDoc = await PDFLib.PDFDocument.create();
const numDocs = urls.length;
for(var i = 0; i < numDocs; i++) {
const donorPdfBytes = await fetch(urls[i]).then(res => res.arrayBuffer());
const donorPdfDoc = await PDFLib.PDFDocument.load(donorPdfBytes);
const docLength = donorPdfDoc.getPageCount();
for(var k = 0; k < docLength; k++) {
const [donorPage] = await pdfDoc.copyPages(donorPdfDoc, [k]);
//console.log("Doc " + i+ ", page " + k);
pdfDoc.addPage(donorPage);
}
}
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
//console.log(pdfDataUri);
// strip off the first part to the first comma "data:image/png;base64,iVBORw0K..."
var data_pdf = pdfDataUri.substring(pdfDataUri.indexOf(',')+1);
}
There are several client-side JavaScript libraries supporting merging and splitting existing PDFs that I'm aware of which might be useful to you:
PDF Assembler supports this and has a live demo. The Prior Art / Alternatives part of its README is worth checking out, where several other JavaScript PDF libraries (not necessarily client-side) are mentioned and commented on.
pdf-lib is a newer library under active development. Similarly, the Prior Art part is worth checking out.
If you just want to display multiple PDFs as merged into a single document in the browser, this is surely possible with pdf.js - see my answer here.
Surely that example could also be used to show a specific subset of pages, thus giving the user the ability to split it.
However, if you need the result to be available for download, there's no way (afaik) around server-side processing - at least if you want to stay in the open source, free of charge realm.
Update: Using a combination of pdf.js (Mozilla) to render the pdf - which happens on the canvas by default - and jsPdf (parall.ax) one should be able to get the merged result (or even anything else that was drawn on your canvas/es) for download, printing, etc. by using the canvas' toDataUrl() approach found this answer to export page by page (using jsPdf's addPage function)
If you want to do a merge operation then you can use easy-pdf-merge below is the link :
https://www.npmjs.com/package/easy-pdf-merge
Here is an example:
var merge = require('easy-pdf-merge');
merge(['File One.pdf','File Two.pdf'],'File Ouput.pdf',function(err){
if(err)
return console.log(err);
console.log('Successfully merged!');
});
Hope this helps.
No, you can't use iTextSharp (a .Net port of iText, which was written in Java) with JavaScript in a browser.
You could use iText in a Java applet, or there are a couple of PDF libraries for JavaScript if you search (mostly experimental ones, I understand, such as this one Mozilla did, or this one).
You can use pure JS with pdfjs-dist:
function pdfMerge(urls, divRootId) {
//necessário pois para manter as promisses sincronizadas com await
(async function loop() {
for (url_item of urls) {
console.log("loading: " + url_item);
var loadingTask = pdfjsLib.getDocument(url_item);
//sem isso fica dessincronizado
await loadingTask.promise.then(function (pdf) {
pdf.getMetadata().then(function (metaData) {
console.log("pdf (" + urls + ") version: " + metaData.info.PDFFormatVersion); //versão do pdf
}).catch(function (err) {
console.log('Error getting meta data');
console.log(err);
});
console.log("páginas: " + pdf.numPages);
let i = 0;
while (i < pdf.numPages) {
var pageNumber = i;
pdf.getPage(pageNumber).then(function (page) {
var div = document.createElement("div");
var documentosDiv = document.querySelector('#' + divRootId);
documentosDiv.appendChild(div);
var canvas = document.createElement("canvas");
div.appendChild(canvas);
// Prepare canvas using PDF page dimensions
var viewport = page.getViewport({scale: 1, });
//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');
});
});
i++;
}
// Fetch the first page
}, function (reason) {
// PDF loading error
console.error(reason);
});
}
})();
}
html example:
<script src="pdf.js"></script>
<script src="pdf.worker.js"></script>
<h1>Merge example</h1>
<div id="documentos-container"></div>
<style>
canvas {
border:1px solid black;
}
</style>
Here https://codepen.io/hudson-moreira/pen/MWmpqPb you have a working example of adding multiple pdfs and combining them into one, all directly from the front-end using the Hopding/pdf-lib library. The layout needs some adjustment but in this example it is possible to dynamically add and remove files directly from the screen.
In short, this function combines the PDFs, but it is necessary to read them in Uint8Array format before applying this function.
async function joinPdf() {
const mergedPdf = await PDFDocument.create();
for (let document of window.arrayOfPdf) {
document = await PDFDocument.load(document.bytes);
const copiedPages = await mergedPdf.copyPages(document, document.getPageIndices());
copiedPages.forEach((page) => mergedPdf.addPage(page));
}
var pdfBytes = await mergedPdf.save();
download(pdfBytes, "pdfconbined" + new Date().getTime() + ".pdf", "application/pdf");
}
This is a library for javascript to generate PDFs. It works on the client:
http://parall.ax/products/jspdf#
Another solution would be to make an ajax call, generate the PDF on the server with whatever technology you want to use (itextsharp) and return the generated PDF to the client.
I achieved this using pdfjs on browser (i.e. compiled it to browser environment using parcel)
https://github.com/Munawwar/merge-pdfs-on-browser (folder pdfjs-approach)

Categories