Skip to content

Commit cd4b710

Browse files
authored
fix for #1789 (#2069)
1 parent 0bb9c46 commit cd4b710

File tree

16 files changed

+173
-138
lines changed

16 files changed

+173
-138
lines changed

src/jspdf.js

Lines changed: 97 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ var jsPDF = (function (global) {
632632
return documentProperties[key] = value;
633633
};
634634

635-
var objectNumber = 2; // 'n' Current object number
635+
var objectNumber = 0; // 'n' Current object number
636636
var offsets = []; // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
637637
var fonts = {}; // collection of font objects, where key is fontKey - a dynamically created label for a given font.
638638
var fontmap = {}; // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
@@ -643,6 +643,45 @@ var jsPDF = (function (global) {
643643
var additionalObjects = [];
644644
var events = new PubSub(API);
645645
var hotfixes = options.hotfixes || [];
646+
var newObject = API.__private__.newObject = function () {
647+
var oid = newObjectDeferred();
648+
newObjectDeferredBegin(oid, true);
649+
return oid;
650+
};
651+
652+
// Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
653+
var newObjectDeferred = API.__private__.newObjectDeferred = function () {
654+
objectNumber++;
655+
offsets[objectNumber] = function () {
656+
return content_length;
657+
};
658+
return objectNumber;
659+
};
660+
661+
var newObjectDeferredBegin = function (oid, doOutput) {
662+
doOutput = typeof (doOutput) === 'boolean' ? doOutput : false;
663+
offsets[oid] = content_length;
664+
if (doOutput) {
665+
out(oid + ' 0 obj');
666+
}
667+
return oid;
668+
};
669+
// Does not output the object until after the pages have been output.
670+
// Returns an object containing the objectId and content.
671+
// All pages have been added so the object ID can be estimated to start right after.
672+
// This does not modify the current objectNumber; It must be updated after the newObjects are output.
673+
var newAdditionalObject = API.__private__.newAdditionalObject = function () {
674+
var objId = newObjectDeferred();
675+
var obj = {
676+
objId: objId,
677+
content: ''
678+
};
679+
additionalObjects.push(obj);
680+
return obj;
681+
};
682+
683+
var rootDictionaryObjId = newObjectDeferred();
684+
var resourceDictionaryObjId = newObjectDeferred();
646685

647686
/////////////////////
648687
// Private functions
@@ -754,41 +793,7 @@ var jsPDF = (function (global) {
754793
var getFilters = API.__private__.getFilters = function () {
755794
return filters;
756795
};
757-
758-
var newObject = API.__private__.newObject = function () {
759-
// Begin a new object
760-
objectNumber++;
761-
offsets[objectNumber] = content_length;
762-
out(objectNumber + ' 0 obj');
763-
return objectNumber;
764-
};
765-
// Does not output the object until after the pages have been output.
766-
// Returns an object containing the objectId and content.
767-
// All pages have been added so the object ID can be estimated to start right after.
768-
// This does not modify the current objectNumber; It must be updated after the newObjects are output.
769-
var newAdditionalObject = API.__private__.newAdditionalObject = function () {
770-
var objId = pages.length * 2 + 1;
771-
objId += additionalObjects.length;
772-
var obj = {
773-
objId: objId,
774-
content: ''
775-
};
776-
additionalObjects.push(obj);
777-
return obj;
778-
};
779-
// Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
780-
var newObjectDeferred = API.__private__.newObjectDeferred = function () {
781-
objectNumber++;
782-
offsets[objectNumber] = function () {
783-
return content_length;
784-
};
785-
return objectNumber;
786-
};
787-
788-
var newObjectDeferredBegin = function (oid) {
789-
offsets[oid] = content_length;
790-
};
791-
796+
792797
var putStream = API.__private__.putStream = function (options) {
793798
options = options || {};
794799
var data = options.data || '';
@@ -849,24 +854,29 @@ var jsPDF = (function (global) {
849854
var dimensions = page.dimensions;
850855
var pageNumber = page.number;
851856
var data = page.data;
857+
var pageObjectNumber = page.objId;
858+
var pageContentsObjId = page.contentsObjId;
852859

853-
var pageObjectNumber = newObject();
860+
newObjectDeferredBegin(pageObjectNumber, true);
854861
var wPt = dimensions.width * k;
855862
var hPt = dimensions.height * k;
856863
out('<</Type /Page');
857-
out('/Parent 1 0 R');
858-
out('/Resources 2 0 R');
864+
out('/Parent ' + page.rootDictionaryObjId + ' 0 R');
865+
out('/Resources ' + page.resourceDictionaryObjId + ' 0 R');
859866
out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
867+
860868
events.publish('putPage', {
869+
objId : pageObjectNumber,
870+
pageContext: pagesContext[pageNumber],
861871
pageNumber: pageNumber,
862872
page: data
863873
});
864-
out('/Contents ' + (objectNumber + 1) + ' 0 R');
874+
out('/Contents ' + pageContentsObjId + ' 0 R');
865875
out('>>');
866876
out('endobj');
867877
// Page content
868878
var pageContent = data.join('\n');
869-
newObject();
879+
newObjectDeferredBegin(pageContentsObjId, true);
870880
putStream({
871881
data: pageContent,
872882
filters: getFilters()
@@ -876,16 +886,25 @@ var jsPDF = (function (global) {
876886
}
877887
var putPages = API.__private__.putPages = function () {
878888
var n, p, i, pageObjectNumbers = [];
889+
890+
891+
for (n = 1; n <= page; n++) {
892+
pagesContext[n].objId = newObjectDeferred();
893+
pagesContext[n].contentsObjId = newObjectDeferred();
894+
}
879895

880896
for (n = 1; n <= page; n++) {
881897
pageObjectNumbers.push(putPage({
882898
number: n,
883899
data: pages[n],
884-
dimensions: pagesContext[n].dimensions
900+
objId: pagesContext[n].objId,
901+
contentsObjId: pagesContext[n].contentsObjId,
902+
dimensions: pagesContext[n].dimensions,
903+
rootDictionaryObjId: rootDictionaryObjId,
904+
resourceDictionaryObjId: resourceDictionaryObjId
885905
}));
886906
}
887-
offsets[1] = content_length;
888-
out('1 0 obj');
907+
newObjectDeferredBegin(rootDictionaryObjId, true);
889908
out('<</Type /Pages');
890909
var kids = '/Kids [';
891910
for (i = 0; i < page; i++) {
@@ -948,9 +967,7 @@ var jsPDF = (function (global) {
948967
var putResources = function () {
949968
putFonts();
950969
events.publish('putResources');
951-
// Resource dictionary
952-
offsets[2] = content_length;
953-
out('2 0 obj');
970+
newObjectDeferredBegin(resourceDictionaryObjId, true);
954971
out('<<');
955972
putResourceDictionary();
956973
out('>>');
@@ -962,12 +979,10 @@ var jsPDF = (function (global) {
962979
events.publish('putAdditionalObjects');
963980
for (var i = 0; i < additionalObjects.length; i++) {
964981
var obj = additionalObjects[i];
965-
offsets[obj.objId] = content_length;
966-
out(obj.objId + ' 0 obj');
967-
out(obj.content);;
982+
newObjectDeferredBegin(obj.objId, true);
983+
out(obj.content);
968984
out('endobj');
969985
}
970-
objectNumber += additionalObjects.length;
971986
events.publish('postPutAdditionalObjects');
972987
};
973988

@@ -1235,6 +1250,8 @@ var jsPDF = (function (global) {
12351250
outToPages = true;
12361251
pages[++page] = [];
12371252
pagesContext[page] = {
1253+
objId: 0,
1254+
contentsObjId: 0,
12381255
dimensions: {
12391256
width: Number(width),
12401257
height: Number(height),
@@ -1340,11 +1357,13 @@ var jsPDF = (function (global) {
13401357
out('endobj');
13411358
};
13421359

1343-
var putCatalog = API.__private__.putCatalog = function () {
1360+
var putCatalog = API.__private__.putCatalog = function (options) {
1361+
options = options || {};
1362+
var tmpRootDictionaryObjId = options.rootDictionaryObjId || rootDictionaryObjId;
13441363
newObject();
13451364
out('<<');
13461365
out('/Type /Catalog');
1347-
out('/Pages 1 0 R');
1366+
out('/Pages ' + tmpRootDictionaryObjId + ' 0 R');
13481367
// PDF13ref Section 7.2.1
13491368
if (!zoomMode) zoomMode = 'fullwidth';
13501369
switch (zoomMode) {
@@ -1438,11 +1457,14 @@ var jsPDF = (function (global) {
14381457
var buildDocument = API.__private__.buildDocument = function () {
14391458
outToPages = false; // switches out() to content
14401459

1441-
objectNumber = 2;
1460+
//reset fields relevant for objectNumber generation and xref.
1461+
objectNumber = 0;
14421462
content_length = 0;
14431463
content = [];
14441464
offsets = [];
14451465
additionalObjects = [];
1466+
rootDictionaryObjId = newObjectDeferred();
1467+
resourceDictionaryObjId = newObjectDeferred();
14461468

14471469
events.publish('buildDocument');
14481470

@@ -1588,18 +1610,31 @@ var jsPDF = (function (global) {
15881610
if (isNaN(pageNumberOneBased) || (pageNumberOneBased % 1 !== 0)) {
15891611
throw new Error('Invalid argument passed to jsPDF.getPageInfo');
15901612
}
1591-
var objId = (pageNumberOneBased - 1) * 2 + 3;
1613+
var objId = pagesContext[pageNumberOneBased].objId;
15921614
return {
15931615
objId: objId,
15941616
pageNumber: pageNumberOneBased,
15951617
pageContext: pagesContext[pageNumberOneBased]
15961618
};
15971619
};
1620+
1621+
var getPageInfoByObjId = API.__private__.getPageInfoByObjId = function (objId) {
1622+
var pageNumberWithObjId;
1623+
for (var pageNumber in pagesContext) {
1624+
if (pagesContext[pageNumber].objId === objId) {
1625+
pageNumberWithObjId = pageNumber;
1626+
break;
1627+
}
1628+
}
1629+
if (isNaN(objId) || (objId % 1 !== 0)) {
1630+
throw new Error('Invalid argument passed to jsPDF.getPageInfoByObjId');
1631+
}
1632+
return getPageInfo(pageNumber);
1633+
};
15981634

15991635
var getCurrentPageInfo = API.__private__.getCurrentPageInfo = function () {
1600-
var objId = (currentPage - 1) * 2 + 3;
16011636
return {
1602-
objId: objId,
1637+
objId: pagesContext[currentPage].objId,
16031638
pageNumber: currentPage,
16041639
pageContext: pagesContext[currentPage]
16051640
};
@@ -1769,10 +1804,10 @@ var jsPDF = (function (global) {
17691804
align: align
17701805
};
17711806
}
1772-
1773-
flags = flags || {};
1774-
flags.noBOM = flags.noBOM || true;
1775-
flags.autoencode = flags.autoencode || true;
1807+
1808+
flags = flags || {};
1809+
flags.noBOM = flags.noBOM || true;
1810+
flags.autoencode = flags.autoencode || true;
17761811

17771812
if (isNaN(x) || isNaN(y) || typeof text === "undefined") {
17781813
throw new Error('Invalid arguments passed to jsPDF.text');
@@ -3158,6 +3193,7 @@ var jsPDF = (function (global) {
31583193
'out': out,
31593194
'f2': f2,
31603195
'getPageInfo': getPageInfo,
3196+
'getPageInfoByObjId': getPageInfoByObjId,
31613197
'getCurrentPageInfo': getCurrentPageInfo,
31623198
'getPDFVersion': getPdfVersion,
31633199
'hasHotfix': hasHotfix //Expose the hasHotfix check so plugins can also check them.

src/modules/acroform.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,8 @@
398398
object: object
399399
};
400400
var findEntry = function (entry) { return (entry.type === options.type && entry.object === options.object); };
401-
if (scope.annotationPlugin.annotations[scope.internal.getPageInfo(object.page).pageNumber].find(findEntry) === undefined) {
402-
scope.annotationPlugin.annotations[scope.internal.getPageInfo(object.page).pageNumber].push(options);
401+
if (scope.internal.getPageInfo(object.page).pageContext.annotations.find(findEntry) === undefined) {
402+
scope.internal.getPageInfo(object.page).pageContext.annotations.push(options);
403403
}
404404
};
405405

@@ -439,7 +439,7 @@
439439
// in case there is no fieldArray specified, we want to print out
440440
// the Fields of the AcroForm
441441
// Print out Root
442-
scope.internal.newObjectDeferredBegin(scope.internal.acroformPlugin.acroFormDictionaryRoot.objId);
442+
scope.internal.newObjectDeferredBegin(scope.internal.acroformPlugin.acroFormDictionaryRoot.objId, true);
443443
scope.internal.acroformPlugin.acroFormDictionaryRoot.putStream();
444444
}
445445

@@ -457,8 +457,7 @@
457457
}
458458

459459
// Start Writing the Object
460-
scope.internal.newObjectDeferredBegin(fieldObject.objId);
461-
scope.internal.out(fieldObject.objId + " 0 obj");
460+
scope.internal.newObjectDeferredBegin(fieldObject.objId, true);
462461

463462
fieldObject.DA = AcroFormAppearance.createDefaultAppearanceStream(fieldObject);
464463

@@ -541,7 +540,7 @@
541540
var key = i;
542541
var fieldObject = fieldArray[i];
543542
// Start Writing the Object
544-
scope.internal.newObjectDeferredBegin(fieldObject && fieldObject.objId);
543+
scope.internal.newObjectDeferredBegin(fieldObject && fieldObject.objId, true);
545544

546545
if (typeof fieldObject === "object" && typeof fieldObject.putStream === "function") {
547546
fieldObject.putStream();
@@ -675,7 +674,6 @@
675674
};
676675

677676
AcroFormPDFObject.prototype.putStream = function () {
678-
scope.internal.out(this.objId + " 0 obj");
679677
var keyValueList = this.getKeyValueListForStream();
680678
scope.internal.putStream({data: this.stream, additionalKeyValues: keyValueList});
681679
scope.internal.out("endobj");

0 commit comments

Comments
 (0)