@digital-blueprint/annotpdf
Version:
Javascript library for creating annotations in PDF documents
656 lines • 27.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("./util");
const object_util_1 = require("./object-util");
const writer_util_1 = require("./writer-util");
/**
* Creats the byte array that must be attached to the end of the document
* */
class Writer {
/**
* data : The data representing the original PDF document
* aannotations : The annotations to add to the document
* */
constructor(data, annotations, toDelete, parser) {
this.data = data;
this.annotations = annotations;
this.toDelete = toDelete;
this.parser = parser;
/**
* Holds the crossite reference table
* */
this.xrefs = [];
this.data = new Uint8Array(data);
this.cryptoInterface = parser.getCryptoInterface();
}
/**
* Sorts the annotations pagewise.
*
* This is necessary since the annotation arrays of the sites must be extended
* and it makes sense to do this update in one step.
* */
sortPageWise(annotations) {
let pageAnnots = {};
for (let annot of annotations) {
if (!pageAnnots[annot.page])
pageAnnots[annot.page] = [];
pageAnnots[annot.page].push(annot);
}
return pageAnnots;
}
/**
* It returns the page object either extended by a /Annots field, if this did not exist yet or with the annots field replaced by a rerference pointer
* to an array if the page object contains the list of annotations directly
*
* ptr : Pointer to the page object
* annot_array_reference : The reference to the annotation array
* */
adaptPageObject(page, annot_array_reference) {
if (!page.object_id)
throw Error("Page without object id");
let ret = [];
let lookupTable = this.parser.documentHistory.createObjectLookupTable();
let page_ptr = lookupTable[page.object_id.obj];
if (page_ptr.compressed) {
let obj = object_util_1.ObjectUtil.extractObject(this.data, page_ptr, lookupTable);
let obj_data = obj.stream.getData().slice(obj.pointer_stream_start, obj.pointer_stream_end + 1);
let ref_ptr = writer_util_1.WriterUtil.writeReferencePointer(obj.id, false).concat(32);
let new_data = new Uint8Array(ref_ptr.length + Writer.OBJ.length + obj_data.length + Writer.ENDOBJ.length);
new_data.set(ref_ptr);
new_data.set(Writer.OBJ, ref_ptr.length);
new_data.set(obj_data, Writer.OBJ.length + ref_ptr.length);
new_data.set(Writer.ENDOBJ, Writer.OBJ.length + obj_data.length + ref_ptr.length);
return writer_util_1.WriterUtil.replaceAnnotsFieldInPageObject(new_data, page, 0, annot_array_reference);
}
return writer_util_1.WriterUtil.replaceAnnotsFieldInPageObject(this.data, page, page_ptr.pointer, annot_array_reference);
}
/**
* Takes the annotations of >>one<< page and derives the annotations array from it.
* Thereby it also considers the potentially existing annotation array.
*
* toDelete := contains those annotations that must be deleted. It removes them from the reference array
* and marks them as removed
* */
writeAnnotArray(annots, toDelete) {
let page = annots[0].pageReference;
if (!page)
throw Error("Missing page reference");
if (!page.object_id)
throw Error("Page without object id");
let references = page.annots;
references = references.concat(annots.map((x) => {
if (!x.object_id)
throw Error("Annotation with object_id null");
return x.object_id;
}));
// remove annotation references from the array that must be deleted and mark them as deleted
references = references.filter((a) => {
let toDel = toDelete.find((t) => t.object_id.obj === a.obj && t.object_id.generation === a.generation);
if (toDel) {
toDel.is_deleted = true;
return false;
}
return true;
});
let refArray_id = page.annotsPointer;
let page_data = [];
if (!refArray_id) {
refArray_id = this.parser.getFreeObjectId();
page_data = this.adaptPageObject(page, refArray_id);
}
let ret = writer_util_1.WriterUtil.writeReferencePointer(refArray_id);
ret.push(Writer.SPACE);
ret = ret.concat(Writer.OBJ);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret.push(Writer.ARRAY_START);
for (let an of references) {
ret = ret.concat(writer_util_1.WriterUtil.writeReferencePointer(an, true));
ret.push(Writer.SPACE);
}
ret.push(Writer.ARRAY_END);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.ENDOBJ);
ret.push(Writer.CR);
ret.push(Writer.LF);
return { ptr: refArray_id, data: ret, pageReference: page.object_id, pageData: page_data };
}
/**
* Converts a subtype to its byte representation
* */
convertSubtype(st) {
switch (st) {
case 'Text':
case '/Text':
return [47, 84, 101, 120, 116]; // = '/Text'
case 'Highlight':
case '/Highlight':
return [47, 72, 105, 103, 104, 108, 105, 103, 104, 116]; // = '/Highlight'
case 'Underline':
case '/Underline':
return [47, 85, 110, 100, 101, 114, 108, 105, 110, 101]; // = '/Underline'
case 'Squiggly':
case '/Squiggly':
return [47, 83, 113, 117, 105, 103, 103, 108, 121]; // = '/Squiggly'
case 'StrikeOut':
case '/StrikeOut':
return [47, 83, 116, 114, 105, 107, 101, 79, 117, 116]; // = '/StrikeOut'
case 'Square':
case '/Square':
return [47, 83, 113, 117, 97, 114, 101]; // = '/Square'
case 'Circle':
case '/Circle':
return [47, 67, 105, 114, 99, 108, 101]; // = '/Circle'
case 'FreeText':
case '/FreeText':
return [47, 70, 114, 101, 101, 84, 101, 120, 116]; // = '/FreeText'
case 'Polygon':
case '/Polygon':
return [47, 80, 111, 108, 121, 103, 111, 110]; // = '/Polygon'
case 'PolyLine':
case '/PolyLine':
return [47, 80, 111, 108, 121, 76, 105, 110, 101]; // '/PolyLine
case 'Stamp':
case '/Stamp':
return [47, 83, 116, 97, 109, 112]; // = '/Stamp'
case 'Caret':
case '/Caret':
return [47, 67, 97, 114, 101, 116]; // = '/Caret'
case 'Ink':
case '/Ink':
return [47, 73, 110, 107]; // = '/Ink'
}
return [];
}
/**
* Writes an annotation object
* */
writeAnnotationObject(annot) {
if (!annot.author)
annot.author = "";
if (!annot.contents)
annot.contents = "";
if (!annot.object_id)
throw Error("No object_id");
let ret = writer_util_1.WriterUtil.writeReferencePointer(annot.object_id);
ret.push(Writer.SPACE);
ret = ret.concat(Writer.OBJ);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.DICT_START);
ret = ret.concat(Writer.TYPE_ANNOT);
ret.push(Writer.SPACE);
if (annot.rect && annot.rect.length > 0) {
ret = ret.concat(Writer.RECT);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNumberArray(annot.rect));
ret.push(Writer.SPACE);
}
ret = ret.concat(Writer.SUBTYPE);
ret.push(Writer.SPACE);
ret = ret.concat(this.convertSubtype(annot.type));
ret.push(Writer.SPACE);
ret = ret.concat(Writer.UPDATE_DATE);
ret.push(Writer.SPACE);
ret.push(Writer.BRACKET_START);
ret = ret.concat(Array.from(util_1.Util.escapeString(this.cryptoInterface.encrypt(new Uint8Array(util_1.Util.convertStringToAscii(annot.updateDate)), annot.object_id))));
ret.push(Writer.BRACKET_END);
ret.push(Writer.SPACE);
ret = ret.concat(Writer.AUTHOR);
ret.push(Writer.SPACE);
ret.push(Writer.BRACKET_START);
ret = ret.concat(Array.from(util_1.Util.escapeString(this.cryptoInterface.encrypt(new Uint8Array(util_1.Util.convertStringToAscii(annot.author)), annot.object_id))));
ret.push(Writer.BRACKET_END);
ret.push(Writer.SPACE);
if (annot.contents) {
ret = ret.concat(Writer.CONTENTS);
ret.push(Writer.SPACE);
ret.push(Writer.BRACKET_START);
ret = ret.concat(Array.from(util_1.Util.escapeString(this.cryptoInterface.encrypt(new Uint8Array(util_1.Util.convertStringToAscii(annot.contents)), annot.object_id))));
ret.push(Writer.BRACKET_END);
ret.push(Writer.SPACE);
}
ret = ret.concat(Writer.ID);
ret.push(Writer.SPACE);
ret.push(Writer.BRACKET_START);
ret = ret.concat(Array.from(util_1.Util.escapeString(this.cryptoInterface.encrypt(new Uint8Array(util_1.Util.convertStringToAscii(annot.id)), annot.object_id))));
ret.push(Writer.BRACKET_END);
ret.push(Writer.SPACE);
if (annot.annotation_flag) {
ret = ret.concat(Writer.FLAG);
ret.push(Writer.SPACE);
ret = ret.concat(util_1.Util.convertNumberToCharArray(annot.annotation_flag));
ret.push(Writer.SPACE);
}
if (annot.color) {
if (annot.color.r > 1)
annot.color.r /= 255;
if (annot.color.g > 1)
annot.color.g /= 255;
if (annot.color.b > 1)
annot.color.b /= 255;
ret.push(Writer.SPACE);
ret = ret.concat(Writer.COLOR);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNumberArray([annot.color.r, annot.color.g, annot.color.b]));
ret.push(Writer.SPACE);
}
if (annot.fill) {
if (annot.fill.r > 1)
annot.fill.r /= 255;
if (annot.fill.g > 1)
annot.fill.g /= 255;
if (annot.fill.b > 1)
annot.fill.b /= 255;
ret.push(Writer.SPACE);
ret = ret.concat(Writer.FILL);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNumberArray([annot.fill.r, annot.fill.g, annot.fill.b]));
ret.push(Writer.SPACE);
}
let opacity = annot.opacity;
if (opacity) {
ret = ret.concat(Writer.OPACITY);
ret.push(Writer.SPACE);
ret = ret.concat(util_1.Util.convertNumberToCharArray(opacity));
ret.push(Writer.SPACE);
}
if (annot.border) {
ret.push(Writer.SPACE);
ret = ret.concat(Writer.BORDER);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNumberArray([annot.border.horizontal_corner_radius, annot.border.vertical_corner_radius, annot.border.border_width]));
ret.push(Writer.SPACE);
}
if (annot.defaultAppearance) {
ret.push(Writer.SPACE);
ret = ret.concat(Writer.DEFAULT_APPEARANCE);
ret.push(Writer.SPACE);
ret.push(Writer.BRACKET_START);
ret = ret.concat(util_1.Util.convertStringToAscii(annot.defaultAppearance));
ret.push(Writer.BRACKET_END);
ret.push(Writer.SPACE);
}
if (!annot.pageReference)
throw Error("No page reference");
if (annot.pageReference.object_id) {
ret.push(Writer.SPACE);
ret = ret.concat(Writer.PAGE_REFERENCE);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeReferencePointer(annot.pageReference.object_id, true));
ret.push(Writer.SPACE);
}
if (annot.quadPoints) {
ret = ret.concat(Writer.QUADPOINTS);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNumberArray(annot.quadPoints));
ret.push(Writer.SPACE);
}
if (annot.vertices) {
ret = ret.concat(Writer.VERTICES);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNumberArray(annot.vertices));
ret.push(Writer.SPACE);
}
if (annot.stampType) {
ret = ret.concat(Writer.NAME);
ret.push(Writer.SPACE);
ret = ret.concat(Writer.DRAFT);
ret.push(Writer.SPACE);
}
if (annot.caretSymbol) {
ret = ret.concat(Writer.SY);
ret.push(Writer.SPACE);
ret.push(Writer.P);
ret.push(Writer.SPACE);
}
if (annot.inkList) {
ret = ret.concat(Writer.INKLIST);
ret.push(Writer.SPACE);
ret = ret.concat(writer_util_1.WriterUtil.writeNestedNumberArray(annot.inkList));
ret.push(Writer.SPACE);
}
ret = ret.concat(Writer.DICT_END);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.ENDOBJ);
ret.push(Writer.CR);
ret.push(Writer.LF);
return { ptr: annot.object_id, data: ret };
}
/**
* Takes all the cross site reference table entries and extracts the consecutive sequences
* */
computeXrefSequences() {
let seqs = [];
let ordered_xrefs = this.xrefs.sort((a, b) => {
if (a.id < b.id)
return -1;
if (a.id > b.id)
return 1;
return 0;
});
let seq = [ordered_xrefs[0]];
let last_id = ordered_xrefs[0].id;
seqs.push(seq);
for (let i = 1; i < ordered_xrefs.length; ++i) {
if (ordered_xrefs[i].id === last_id + 1) {
seq.push(ordered_xrefs[i]);
}
else {
seq = [ordered_xrefs[i]];
seqs.push(seq);
}
last_id = ordered_xrefs[i].id;
}
return seqs;
}
/**
* Constructs the pointers of the linked list that contains the ids of freed objects
* */
applyObjectFreeing(refs) {
// write free object head
let head = this.parser.documentHistory.createObjectLookupTable()[0];
let last_freed_object_id = head.id;
let freed_objs = refs.filter(r => r.free);
freed_objs = freed_objs.sort((a, b) => {
if (a.id < b.id)
return -1;
if (a.id > b.id)
return 1;
return 0;
});
let lastobj = undefined;
for (let obj of freed_objs) {
if (!lastobj) {
// set first object as list header
head.pointer = obj.id;
}
if (lastobj) {
lastobj.pointer = obj.id;
}
lastobj = obj;
}
if (freed_objs.length > 0)
freed_objs[freed_objs.length - 1].pointer = last_freed_object_id;
refs.push(head);
return refs;
}
/**
* Writes the crossite reference table
* */
writeCrossSiteReferenceTable() {
let ret = Writer.XREF;
ret.push(Writer.CR);
ret.push(Writer.LF);
this.xrefs = this.applyObjectFreeing(this.xrefs);
let ordered_sequences = this.computeXrefSequences();
for (let sequence of ordered_sequences) {
let head = sequence[0];
ret = ret.concat(util_1.Util.convertNumberToCharArray(head.id));
ret.push(Writer.SPACE);
ret = ret.concat(util_1.Util.convertNumberToCharArray(sequence.length));
ret.push(Writer.CR);
ret.push(Writer.LF);
for (let i = 0; i < sequence.length; ++i) {
let _ret = [];
_ret = _ret.concat(writer_util_1.WriterUtil.pad(10, sequence[i].pointer));
_ret.push(Writer.SPACE);
_ret = _ret.concat(writer_util_1.WriterUtil.pad(5, sequence[i].generation));
_ret.push(Writer.SPACE);
if (sequence[i].free)
_ret.push(Writer.F);
if (sequence[i].update)
_ret.push(Writer.N);
_ret.push(Writer.CR);
_ret.push(Writer.LF);
if (_ret.length < 20)
throw Error("XRef entry < 20 bytes");
ret = ret.concat(_ret);
}
}
return ret;
}
/**
* Writes the trailer
* */
writeTrailer(posXref) {
let ret = Writer.TRAILER;
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.DICT_START);
ret = ret.concat(Writer.SIZE);
ret.push(Writer.SPACE);
ret = ret.concat(util_1.Util.convertNumberToCharArray(this.parser.documentHistory.trailerSize));
ret.push(Writer.SPACE);
if (this.parser.documentHistory.isEncrypted()) {
ret = ret.concat(Writer.ENCRYPT);
ret.push(Writer.SPACE);
let enc_ref = this.parser.getCryptoInterface().getEncryptionDictReference();
if (!enc_ref)
throw Error("No reference pointer to encryption dictionary");
ret = ret.concat(writer_util_1.WriterUtil.writeReferencePointer(enc_ref, true));
ret.push(Writer.SPACE);
}
let trailer = this.parser.documentHistory.getRecentUpdate();
if (trailer.id) {
ret = ret.concat(Writer.DOCUMENT_ID);
ret.push(Writer.SPACE);
ret.push(Writer.ARRAY_START);
for (let val of trailer.id) {
ret.push(Writer.SPACE);
ret = ret.concat(Writer.HEX_STRING_START);
ret = ret.concat(util_1.Util.convertStringToAscii(util_1.Util.convertByteArrayToHexString(val)));
ret = ret.concat(Writer.HEX_STRING_END);
ret.push(Writer.SPACE);
}
ret.push(Writer.ARRAY_END);
ret.push(Writer.SPACE);
}
ret = ret.concat(Writer.ROOT);
ret.push(Writer.SPACE);
if (!trailer.root)
throw Error("No root object in trailer, although this is an cross reference table document");
ret = ret.concat(writer_util_1.WriterUtil.writeReferencePointer(trailer.root, true));
ret.push(Writer.SPACE);
ret = ret.concat(Writer.PREV);
ret.push(Writer.SPACE);
ret = ret.concat(util_1.Util.convertNumberToCharArray(this.parser.documentHistory.getRecentUpdate().start_pointer));
ret.push(Writer.SPACE);
ret = ret.concat(Writer.DICT_END);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.STARTXREF);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(util_1.Util.convertNumberToCharArray(posXref));
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.EOF);
ret.push(Writer.CR);
ret.push(Writer.LF);
return ret;
}
/**
* Writes the annations at the end of the PDF byte representation and returns
* the byte array
* */
write() {
let pageWiseSorted = this.sortPageWise(this.annotations);
let ptr = this.data.length;
let new_data = [];
// Fix case that there is no linebreak after the end of the file
if (this.data[ptr - 1] === 70) { // 70 = 'F' (from [%%EO]F
new_data.push(Writer.CR);
new_data.push(Writer.LF);
ptr += 2;
}
for (let key in pageWiseSorted) {
let pageAnnots = pageWiseSorted[key];
// write the array referencing to annotations of a certain page
// it also removes references of annotations that must be deleted
let annot_array = this.writeAnnotArray(pageAnnots, this.toDelete);
this.xrefs.push({
id: annot_array.ptr.obj,
pointer: ptr,
generation: annot_array.ptr.generation,
free: false,
update: true
});
new_data = new_data.concat(annot_array.data);
ptr += annot_array.data.length;
// add adapted page object if it exists -- In the case the page had no annotation yet there exists
// no such array referring to its annotations. A pointer to such an array must be added to the
// page object. If this was done the `pageData` paramater is set and contains the adapted page object
if (annot_array.pageData.length > 0) {
this.xrefs.push({
id: annot_array.pageReference.obj,
pointer: ptr,
generation: annot_array.pageReference.generation,
free: false,
update: true
});
new_data = new_data.concat(annot_array.pageData);
ptr += annot_array.pageData.length;
}
// writes the actual annotation object
for (let annot of pageAnnots) {
let annot_obj = this.writeAnnotationObject(annot);
this.xrefs.push({
id: annot_obj.ptr.obj,
pointer: ptr,
generation: annot_obj.ptr.generation,
free: false,
update: true
});
new_data = new_data.concat(annot_obj.data);
ptr += annot_obj.data.length;
}
}
// take all annotations that are not deleted yet
let _toDelete = this.toDelete.filter((t) => !t.is_deleted);
let pageWiseSortedToDelete = this.sortPageWise(_toDelete);
// adapt the remaining annotation reference tables
for (let key in pageWiseSortedToDelete) {
let del_data = this.updatePageAnnotationReferenceArray(pageWiseSortedToDelete[key]);
this.xrefs.push({
id: del_data.ptr.obj,
pointer: ptr,
generation: del_data.ptr.generation,
free: false,
update: true
});
new_data = new_data.concat(del_data.data);
ptr += del_data.data.length;
}
// at this point all references to annotation objects in pages should be removed and we can free
// the annotation object ids
for (let toDel of this.toDelete) {
if (!toDel.object_id)
continue;
this.xrefs.push({
id: toDel.object_id.obj,
pointer: -1,
generation: toDel.object_id.generation + 1,
free: true,
update: false
});
}
let crtable = this.writeCrossSiteReferenceTable();
new_data = new_data.concat(crtable);
let trailer = this.writeTrailer(ptr);
new_data = new_data.concat(trailer);
let new_data_array = new Uint8Array(new_data);
let ret_array = new Uint8Array(this.data.length + new_data_array.length);
ret_array.set(this.data);
ret_array.set(new_data, this.data.length);
return ret_array;
}
/**
* Removes the given annotation
* */
updatePageAnnotationReferenceArray(toDelete) {
let page = toDelete[0].pageReference;
if (!page)
throw Error("Missing page reference");
if (!page.object_id) {
throw Error("Page without object id");
}
let references = page.annots;
// remove annotation references from the array that must be deleted and mark them as deleted
references = references.filter((a) => {
let toDel = toDelete.find((t) => t.object_id.obj === a.obj && t.object_id.generation === a.generation);
if (toDel) {
toDel.is_deleted = true;
return false;
}
return true;
});
let refArray_id = page.annotsPointer;
let ret = writer_util_1.WriterUtil.writeReferencePointer(refArray_id);
ret.push(Writer.SPACE);
ret = ret.concat(Writer.OBJ);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret.push(Writer.ARRAY_START);
for (let an of references) {
ret = ret.concat(writer_util_1.WriterUtil.writeReferencePointer(an, true));
ret.push(Writer.SPACE);
}
ret.push(Writer.ARRAY_END);
ret.push(Writer.CR);
ret.push(Writer.LF);
ret = ret.concat(Writer.ENDOBJ);
ret.push(Writer.CR);
ret.push(Writer.LF);
return { ptr: refArray_id, data: ret };
}
}
exports.Writer = Writer;
Writer.N = 110;
Writer.F = 102;
Writer.SPACE = 32;
Writer.CR = 13;
Writer.LF = 10;
Writer.OBJ = [111, 98, 106];
Writer.ENDOBJ = [101, 110, 100, 111, 98, 106];
Writer.ENCRYPT = [47, 69, 110, 99, 114, 121, 112, 116];
Writer.ARRAY_START = 91;
Writer.ARRAY_END = 93;
Writer.DICT_START = [60, 60];
Writer.HEX_STRING_START = [60];
Writer.HEX_STRING_END = [62];
Writer.DICT_END = [62, 62];
Writer.TYPE_ANNOT = [47, 84, 121, 112, 101, Writer.SPACE, 47, 65, 110, 110, 111, 116];
Writer.RECT = [47, 82, 101, 99, 116];
Writer.SUBTYPE = [47, 83, 117, 98, 116, 121, 112, 101];
Writer.UPDATE_DATE = [47, 77]; // = '/M'
Writer.AUTHOR = [47, 84]; // = '/T'
Writer.CONTENTS = [47, 67, 111, 110, 116, 101, 110, 116, 115]; // = '/Contents'
Writer.BRACKET_START = 40;
Writer.BRACKET_END = 41;
Writer.FLAG = [47, 70]; // = '/F'
Writer.ID = [47, 78, 77]; // = '/NM'
Writer.DOCUMENT_ID = [47, 73, 68]; // = '/ID'
Writer.COLOR = [47, 67]; // = '/C'
Writer.FILL = [47, 73, 67]; // = '/IC'
Writer.OPACITY = [47, 67, 65]; // = '/CA'
Writer.BORDER = [47, 66, 111, 114, 100, 101, 114]; // = '/Border'
Writer.PAGE_REFERENCE = [47, 80]; // = '/P'
Writer.DEFAULT_APPEARANCE = [47, 68, 65]; // = '/DA'
Writer.INKLIST = [47, 73, 110, 107, 76, 105, 115, 116]; // = '/InkList'
Writer.TRAILER = [116, 114, 97, 105, 108, 101, 114]; // = 'trailer'
Writer.SIZE = [47, 83, 105, 122, 101]; // = '/Size'
Writer.ROOT = [47, 82, 111, 111, 116]; // = '/Root'
Writer.PREV = [47, 80, 114, 101, 118]; // ='/Prev'
Writer.STARTXREF = [115, 116, 97, 114, 116, 120, 114, 101, 102]; // = 'startxref'
Writer.EOF = [37, 37, 69, 79, 70]; // = '%%EOF'
Writer.XREF = [120, 114, 101, 102]; // = 'xref'
Writer.QUADPOINTS = [47, 81, 117, 97, 100, 80, 111, 105, 110, 116, 115]; // = '/QuadPoints'
Writer.VERTICES = [47, 86, 101, 114, 116, 105, 99, 101, 115]; // = '/Vertices'
Writer.NAME = [47, 78, 97, 109, 101]; // = '/Name'
Writer.DRAFT = [47, 68, 114, 97, 102, 116]; // = '/Draft'
Writer.SY = [47, 83, 121]; // = '/Sy'
Writer.P = 80;
//# sourceMappingURL=writer.js.map