I've got a new problem within my project. I want to create a pdf file via pdfmake. I was following this tutorial and everything works fine, except the "transformation" of the '&' sign. In my 'var docDefinition' I am getting content from mysql database. There are some enterprise names with '&'-sign in it. If I call the enterprise name within php, the '&'-sign is shown correctly. But within the javascript and through convert to base64, only 'ampersand' is shown in the generated pdf.
Other signs like 'ö', 'ä' etc. are shown correctly.
Any ideas?
### Update
Here is a snippet of my code 'generatePDF.blade.php':
#section('scripts')
<script type="text/javascript">
...
// Create the PDF
var docDefinition = {
pageSize: 'A4',
pageOrientation: 'portrait',
...
content: [
{ text: 'Ausführende Firma: '},
'{{ $report->user->enterprise->enterprise_name }}',
...
],
};
And then the stuff from the tutorial:
//save the pdf into base64 strings
var pdfstr;
try {
pdfMake.createPdf(docDefinition).getDataUrl(function (result) {
pdfstr = result;
console.log('Result: ' + pdfstr);
var pdfAsArray = convertDataURIToBinary(pdfstr);
console.log('Array: ' + pdfAsArray);
PDFJS.getDocument(pdfAsArray).then(function getPdf(pdf) {
console.log('pdf var: ' + pdf.getPage(1));
//
// Fetch the first page
//
pdf.getPage(1).then(function getPdfPage(page) {
var scale = 1;
var viewport = page.getViewport(scale);
//
// Prepare canvas using PDF page dimensions
//
var canvas = $("#pdfviewer").get(0);
var context = canvas.getContext('2d');
// Check device pixel ratio and then if the browser is doubleing them
console.log('Pixelratio is', window.devicePixelRatio);
console.log('The browser is doubleing the pixels:', context.webkitBackingStorePixelRatio);
var factor = 1;
if (window.devicePixelRatio >= 2 && context.webkitBackingStorePixelRatio < 2 || context.webkitBackingStorePixelRatio == undefined) {
factor = 2;
}
//canvas.height = viewport.height*factor;
//canvas.width = viewport.width*factor;
canvas.setAttribute('width', viewport.width*factor);
canvas.setAttribute('height', viewport.height*factor);
// context.scale(factor, factor);
context.transform(devicePixelRatio,0,0,devicePixelRatio,0,0);
//
// Render PDF page into canvas context
//
var renderContext = {
canvasContext: context,
viewport: viewport,
};
page.render(renderContext);
// $('#containerPDFViewer').modal({ backdrop: 'static' }); //disable backdrop so user need to make choice
});
});
});
}
catch (e) {
throw e;
}
var BASE64_MARKER = ';base64,';
function convertDataURIToBinary(dataURI) {
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for (var i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
// array[i] = raw.codePointAt(i);
}
return array;
}
### Update 2
I figured out, that my problem has noting to do with the javascript part but with the laravel framework I am working with. I am sorry for not telling you, I just forgot to.
So when I am getting my data from mySQL in my blade view (both UTF-8), everything is fine. But if I do the same not within the pure blade but into javascript my error occurs. So I checked via dd($myvar->value); and so I can see, that the '$'-sign is not converted but the "ampersand ($amp;)". How can I solve that blade prob?
Okay, now I found an answer for my problem:
some tables in DB were in collation of utf8_general_ci, but in laravel /app/config/database.php there is 'collation' => 'utf8mb4_unicode_ci'. So the solution is to change the tables into that collation and then call the values from DB in blade via {!! $myvar->field !!} and woooosh, everything works as it should!
Related
I'm trying to get jspdf lib to show a jpeg image.
I do this:
src2 ="domain.com/images/pxPrescription/active_tick.jpg";
tickedImage = new Image();
tickedImage.src = src2;
src="domain.com/images/pxPrescription/no_tick.jpg";
no_tick = new Image();
no_tick.src = src;
doc.setFont('helvetica');
doc.setFontSize(10);
var basestarty = 190;
var basestartx = 15;
var offsetextra = 6;
var extraadd = 0.5;
var extratextoffsetx = basestartx + 10;
var extratextoffsety = basestarty + 10.5;
doc.text(extratextoffsetx, extratextoffsety, "Distance");
if(jQuery('#extraDist').is(":checked")){
doc.addImage(tickedImage, 'JPEG', basestartx, (basestarty+(offsetextra)+extraadd), 5, 5);
}else{
doc.addImage(no_tick, 'JPEG', basestartx, (basestarty+(offsetextra)+extraadd), 5, 5);
}
I also load the images into a hidden diff at page load to make sure they are available on the page.
I get Error: Supplied data is not a JPEG
Can someone help me solve this?
I'm not 100% sure since there isn't an environment to tinker with, but you should call
await no_tick.decode()
or
await tickedImage.decode()
as applicable in either of your if/else statements prior to adding the image to your pdf to ensure that you have valid image data available and then make your handler code an async function.
You're also welcome to use .then() as shown here:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode
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 );
I'm trying to save the activeDocument as a .psd but its returning this error
ERROR: General Photoshop error occurred. This functionality may not be available in this version of Photoshop.
my script:
#target photoshop
var fileRef = new File(app.path.toString() + "/Samples/template.psd");
var docRef = open(fileRef);
//target text layer
var layerRef = app.activeDocument.layers.getByName("Text");
//user input
var newText = prompt("Editing " + layerRef.name, "enter new text: ");
//change contents
layerRef.textItem.contents = newText;
//save
var savePath = "/Samples/" + newText + ".psd";
var saveFile = new File(savePath);
var saveOptions = new PhotoshopSaveOptions();
saveOptions.alphaChannels = false;
saveOptions.annotations = false;
saveOptions.embedColorProfile = true;
saveOptions.layers = true;
saveOptions.spotColors = false;
app.activeDocument.saveAs(saveFile, saveOptions, true, Extension.LOWERCASE);
app.activeDocument.close();
what I want to do is basically, duplicate a template file over and over, only replacing the contents of a text layer then saving it under the string I replace in the text layer.
any tips or help is greatly appreciated.
Resolved
I fixed my problem, by a work around. I moved both the script and the template file into the Photoshop directory and added app.path.toString() to the saveFile output variable. So it seems that the path needed to be converted to a string before saving.
As of yet I am unsure how to work outside the Photoshop directory but for me this works so I'm happy. It's a fairly crude but I'm open to suggestion. So if anyone is having a similar issue they can use this for reference.
#target photoshop
var loop = true;
var filePath = "/Samples/template.psd";
while(loop) {
openTemplate(filePath);
var layerRef = app.activeDocument.layers.getByName("Text"); //target text layer
var newText = prompt("Editing " + layerRef.name, "enter new text: "); //user input
if(newText == "stop") { //stop loop by entering 'stop'
loop = false;
}
layerRef.textItem.contents = newText;
var savePath = app.path.toString() + "/Samples/" + newText + ".psd";
var saveFile = new File(savePath);
savePSD(saveFile);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
function openTemplate(filePath) { //open template.psd
var fileRef = new File(app.path.toString() + filePath);
var docRef = open(fileRef);
}
function savePSD(saveFile) { //saveas newText.psd
var saveOptions = new PhotoshopSaveOptions();
saveOptions.alphaChannels = false;
saveOptions.annotations = false;
saveOptions.embedColorProfile = true;
saveOptions.layers = true;
saveOptions.spotColors = false;
app.activeDocument.saveAs(saveFile, saveOptions, true, Extension.LOWERCASE);
}
I suspect the problem with your original attempt is that you are not specifying a full path. I always provide a full path - even if it is just to a temporary location like '/c/temp/myfile.psd'.
app.path returns a File object, not a string. In your case, you most likely want the platform-specific fullpath string:
var appPath = app.path.fsName; // "C:\Program Files\Adobe\Adobe Photoshop 2022"
Regardless if paths are correct, if you're attempting to save anything to a directory located inside the Photoshop installation directory, you will probably need to run Photoshop as administrator.
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.
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)