function PdfParser(context, needRawText) {
//call constructor for super class
stream.Transform.call(this, {objectMode: true, bufferSize: 64 * 1024});
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = () => _id;
this.get_name = () => _name + _id;
// service context object, only used in Web Service project; null in command line
this.context = context;
this.pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started
this.data = null; //if file read success, data is PDF content; if failed, data is "err" object
this.PDFJS = new PDFJS(needRawText);
this.processFieldInfoXML = false;//disable additional _fieldInfo.xml parsing and merging
this.chunks = [];
this.flushCallback = null;
}n/a
p2jcmd = function () {
this.inputCount = 0;
this.successCount = 0;
this.failedCount = 0;
this.warningCount = 0;
this.p2j = null;
}n/a
pdf = function (needRawText) {
nodeEvents.EventEmitter.call(this);
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = () => _id;
this.get_name = () => _name + _id;
// public, this instance copies
this.pdfDocument = null;
this.pages = [];
this.pageWidth = 0;
this.rawTextContents = [];
this.needRawText = needRawText;
}n/a
pdfanno = function (field, viewport, Fields, Boxsets) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function () {
return _id;
};
this.get_name = function () {
return _name + _id;
};
}n/a
function CanvasRenderingContext2D_(canvasTarget, scaledWidth, scaledHeight) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.m_ = createMatrixIdentity();
this.mStack_ = [];
this.aStack_ = [];
this.currentPath_ = [];
// Canvas context properties
this.strokeStyle = '#000';
this.fillStyle = '#000';
this.lineWidth = 1;
this.lineJoin = 'miter';
this.lineCap = 'butt';
this.dashArray = [];
this.miterLimit = 1;
this.globalAlpha = 1;
if (!_.has(canvasTarget, "HLines") || !_.isArray(canvasTarget.HLines))
canvasTarget.HLines = [];
if (!_.has(canvasTarget, "VLines") || !_.isArray(canvasTarget.VLines))
canvasTarget.VLines = [];
if (!_.has(canvasTarget, "Fills") || !_.isArray(canvasTarget.Fills))
canvasTarget.Fills = [];
if (!_.has(canvasTarget, "Texts") || !_.isArray(canvasTarget.Texts))
canvasTarget.Texts = [];
this.canvas = canvasTarget;
this.width = scaledWidth;
this.height = scaledHeight;
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
this.lineScale_ = 1;
this.currentFont = null;
}n/a
pdffield = function (field, viewport, Fields, Boxsets) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.field = field;
this.viewport = viewport;
this.Fields = Fields;
this.Boxsets = Boxsets;
}n/a
pdffill = function (x, y, width, height, color) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}n/a
pdffont = function (fontObj) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.fontObj = fontObj;
let typeName = (fontObj.name || fontObj.fallbackName);
if (!typeName) {
typeName = _kFontFaces[0]; //default font family name
}
typeName = typeName.toLowerCase();
this.typeName = typeName;
let subType = typeName;
let nameArray = typeName.split('+');
if (_.isArray(nameArray) && nameArray.length > 1) {
subType = nameArray[1].split("-");
if (_.isArray(subType) && subType.length > 1) {
if (!this.bold) {
let subName = subType[1].toLowerCase();
this.bold = _boldSubNames.indexOf(subName) >= 0;
}
subType = subType[0];
}
}
this.subType = subType;
this.isSymbol = typeName.indexOf("symbol") > 0 || _kFontFaces[2].indexOf(this.subType) >= 0;
if (this.fontObj.isSymbolicFont) {
let mFonts = _stdFonts.filter( (oneName) => (typeName.indexOf(oneName) >= 0) );
if (mFonts.length > 0) {
this.fontObj.isSymbolicFont = false; //lots of Arial-based font is detected as symbol in VA forms (301, 76-c, etc.)
reset the flag for now
nodeUtil.p2jinfo("Reset: isSymbolicFont (false) for " + this.fontObj.name);
}
}
else {
if (this.isSymbol) {
this.fontObj.isSymbolicFont = true; //text pdf: va_ind_760c
nodeUtil.p2jinfo("Reset: isSymbolicFont (true) for " + this.fontObj.name);
}
}
this.fontSize = 1;
this.faceIdx = 0;
this.bold = false;
this.italic = false;
this.fontStyleId = -1;
this.spaceWidth = fontObj.spaceWidth;
if (!this.spaceWidth) {
var spaceId = Array.isArray(fontObj.toFontChar) ? fontObj.toFontChar.indexOf(32) : -1;
this.spaceWidth = (spaceId >= 0 && Array.isArray(fontObj.widths)) ? fontObj.widths[spaceId] : 250;
}
this.spaceWidth = PDFUnit.toFormX(this.spaceWidth) / 32;
}n/a
pdfline = function (x1, y1, x2, y2, lineWidth, color, dashed) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.lineWidth = lineWidth || 1.0;
this.color = color;
this.dashed = dashed;
}n/a
pdfunit = function () {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
}n/a
ptixmlinject = function () {
}n/a
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
this._transformState = new TransformState(this);
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
if (options) {
if (typeof options.transform === 'function')
this._transform = options.transform;
if (typeof options.flush === 'function')
this._flush = options.flush;
}
// When the writable side finishes, then flush out anything remaining.
this.once('prefinish', function() {
if (typeof this._flush === 'function')
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}n/a
p2jcmd = function () {
this.inputCount = 0;
this.successCount = 0;
this.failedCount = 0;
this.warningCount = 0;
this.p2j = null;
}n/a
complete = function (err) {
let statusMsg = "\n%d input files\t%d success\t%d fail\t%d warning.";
console.log(statusMsg, this.inputCount, this.successCount, this.failedCount, this.warningCount);
process.nextTick( () => {
console.timeEnd(_PRO_TIMER);
//let exitCode = (this.inputCount === this.successCount) ? 0 : 1;
process.exit(0);
});
}...
cls.prototype.processOneFile = function () {
let inputDir = path.dirname(argv.f);
let inputFile = path.basename(argv.f);
this.inputCount = 1;
this.p2j = new PDF2JSONUtil(inputDir, inputFile, this);
this.p2j.processFile( data => this.complete(data) );
};
cls.prototype.processFiles = function(inputDir, files) {
let fId = 0;
this.p2j = new PDF2JSONUtil(inputDir, files[fId], this);
this.p2j.processFile( function processPDFFile(err) {
if (err) {
...initialize = function (){
console.time(_PRO_TIMER);
let retVal = true;
try {
if (_.has(argv, 'v')) {
console.log(pkInfo.version);
retVal = false;
}
else if (_.has(argv, 'h')) {
optimist.showHelp();
retVal = false;
}
else if (!_.has(argv, 'f')) {
optimist.showHelp();
console.log("-f is required to specify input directory or file.");
retVal = false;
}
}
catch(e) {
console.log("Exception: " + e.message);
retVal = false;
}
return retVal;
}...
console.log("Exception: " + e.message);
retVal = false;
}
return retVal;
};
cls.prototype.start = function(){
if (!this.initialize()) {
console.timeEnd(_PRO_TIMER);
return;
}
try {
console.log("\n" + _PRO_TIMER);
...processFiles = function (inputDir, files) {
let fId = 0;
this.p2j = new PDF2JSONUtil(inputDir, files[fId], this);
this.p2j.processFile( function processPDFFile(err) {
if (err) {
this.complete(err);
}
else {
fId++;
if (fId >= this.inputCount) {
this.complete(null);
}
else {
if (this.p2j) {
this.p2j.destroy();
this.p2j = null;
}
this.p2j = new PDF2JSONUtil(inputDir, files[fId], this);
this.p2j.processFile(processPDFFile.bind(this));
}
}
}.bind(this));
}...
fs.readdir(inputDir, (err, files) => {
let _iChars = "!@#$%^&*()+=[]\\\';,/{}|\":<>?~`.-_ ";
let pdfFiles = files.filter( file => file.substr(-4).toLowerCase() === '.pdf' && _iChars.indexOf
(file.substr(0,1)) < 0 );
this.inputCount = pdfFiles.length;
if (this.inputCount > 0) {
this.processFiles(inputDir, pdfFiles);
}
else {
console.log("No PDF files found. [" + inputDir + "].");
this.complete(null);
}
});
};
...processOneDirectory = function () {
let inputDir = path.normalize(argv.f);
fs.readdir(inputDir, (err, files) => {
let _iChars = "!@#$%^&*()+=[]\\\';,/{}|\":<>?~`.-_ ";
let pdfFiles = files.filter( file => file.substr(-4).toLowerCase() === '.pdf' && _iChars.indexOf(file.substr(0,1)) < 0 );
this.inputCount = pdfFiles.length;
if (this.inputCount > 0) {
this.processFiles(inputDir, pdfFiles);
}
else {
console.log("No PDF files found. [" + inputDir + "].");
this.complete(null);
}
});
}...
let inputStatus = fs.statSync(argv.f);
if (inputStatus.isFile()) {
this.processOneFile();
}
else if (inputStatus.isDirectory()) {
this.processOneDirectory();
}
}
catch(e) {
console.error("Exception: " + e.message);
console.timeEnd(_PRO_TIMER);
}
};
...processOneFile = function () {
let inputDir = path.dirname(argv.f);
let inputFile = path.basename(argv.f);
this.inputCount = 1;
this.p2j = new PDF2JSONUtil(inputDir, inputFile, this);
this.p2j.processFile( data => this.complete(data) );
}...
try {
console.log("\n" + _PRO_TIMER);
let inputStatus = fs.statSync(argv.f);
if (inputStatus.isFile()) {
this.processOneFile();
}
else if (inputStatus.isDirectory()) {
this.processOneDirectory();
}
}
catch(e) {
console.error("Exception: " + e.message);
...start = function (){
if (!this.initialize()) {
console.timeEnd(_PRO_TIMER);
return;
}
try {
console.log("\n" + _PRO_TIMER);
let inputStatus = fs.statSync(argv.f);
if (inputStatus.isFile()) {
this.processOneFile();
}
else if (inputStatus.isDirectory()) {
this.processOneDirectory();
}
}
catch(e) {
console.error("Exception: " + e.message);
console.timeEnd(_PRO_TIMER);
}
}...
'use strict';
var P2JCMD = require('./lib/p2jcmd');
new P2JCMD().start();
...pdf = function (needRawText) {
nodeEvents.EventEmitter.call(this);
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = () => _id;
this.get_name = () => _name + _id;
// public, this instance copies
this.pdfDocument = null;
this.pages = [];
this.pageWidth = 0;
this.rawTextContents = [];
this.needRawText = needRawText;
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
destroy = function () {
this.removeAllListeners();
if (this.pdfDocument)
this.pdfDocument.destroy();
this.pdfDocument = null;
this.pages = null;
this.rawTextContents = null;
}...
PdfParser.prototype.getMergedTextBlocksStream = function() { return _createContentStream(this.getMergedTextBlocksIfNeeded()); };
PdfParser.prototype.destroy = function() {
this.removeAllListeners();
//context object will be set in Web Service project, but not in command line utility
if (this.context) {
this.context.destroy();
this.context = null;
}
this.pdfFilePath = null;
this.data = null;
this.chunks = null;
...getAllFieldsTypes = function () {
return PDFField.getAllFieldsTypes({Pages:this.pages || [], Width: this.pageWidth});
}...
PdfParser.prototype.parseBuffer = function(pdfBuffer) {
_startParsingPDF.call(this, pdfBuffer);
};
PdfParser.prototype.getRawTextContent = function() { return this.PDFJS.getRawTextContent(); };
PdfParser.prototype.getRawTextContentStream = function() { return _createContentStream(this.getRawTextContent()); };
PdfParser.prototype.getAllFieldsTypes = function() { return this.PDFJS.getAllFieldsTypes(); };
PdfParser.prototype.getAllFieldsTypesStream = function() { return _createContentStream(this.getAllFieldsTypes()); };
PdfParser.prototype.getMergedTextBlocksIfNeeded = function() { return {"formImage": this.PDFJS.getMergedTextBlocksIfNeeded
()}; };
PdfParser.prototype.getMergedTextBlocksStream = function() { return _createContentStream(this.getMergedTextBlocksIfNeeded()); };
PdfParser.prototype.destroy = function() {
this.removeAllListeners();
...getMergedTextBlocksIfNeeded = function () {
for (let p = 0; p < this.pages.length; p++) {
let prevText = null;
let page = this.pages[p];
page.Texts.sort(PDFFont.compareBlockPos);
page.Texts = page.Texts.filter( (t, j) => {
let isDup = (j > 0) && PDFFont.areDuplicateBlocks(page.Texts[j-1], t);
if (isDup) {
nodeUtil.p2jinfo("skipped: dup text block: " + decodeURIComponent(t.R[0].T));
}
return !isDup;
});
for (let i = 0; i < page.Texts.length; i++) {
let text = page.Texts[i];
if (prevText) {
if (PDFFont.areAdjacentBlocks(prevText, text) && PDFFont.haveSameStyle(prevText, text)) {
let preT = decodeURIComponent(prevText.R[0].T);
let curT = decodeURIComponent(text.R[0].T);
prevText.R[0].T += text.R[0].T;
prevText.w += text.w;
text.merged = true;
let mergedText = decodeURIComponent(prevText.R[0].T);
nodeUtil.p2jinfo(`merged text block: ${preT} + ${curT} => ${mergedText}`);
prevText = null; //yeah, only merge two blocks for now
}
else {
prevText = text;
}
}
else {
prevText = text;
}
}
page.Texts = page.Texts.filter( t => !t.merged);
}
return {Pages:this.pages, Width: this.pageWidth};
}...
PdfParser.prototype.getRawTextContent = function() { return this.PDFJS.getRawTextContent(); };
PdfParser.prototype.getRawTextContentStream = function() { return _createContentStream(this.getRawTextContent()); };
PdfParser.prototype.getAllFieldsTypes = function() { return this.PDFJS.getAllFieldsTypes(); };
PdfParser.prototype.getAllFieldsTypesStream = function() { return _createContentStream(this.getAllFieldsTypes()); };
PdfParser.prototype.getMergedTextBlocksIfNeeded = function() { return {"formImage": this.PDFJS.getMergedTextBlocksIfNeeded()}; };
PdfParser.prototype.getMergedTextBlocksStream = function() { return _createContentStream(this.getMergedTextBlocksIfNeeded()); };
PdfParser.prototype.destroy = function() {
this.removeAllListeners();
//context object will be set in Web Service project, but not in command line utility
if (this.context) {
...getRawTextContent = function () {
let retVal = "";
if (!this.needRawText)
return retVal;
_.each(this.rawTextContents, function(textContent, index) {
let prevText = null;
_.each(textContent.bidiTexts, function(textObj, idx) {
if (prevText) {
if (Math.abs(textObj.y - prevText.y) <= 9) {
prevText.str += textObj.str;
}
else {
retVal += prevText.str + "\r\n";
prevText = textObj;
}
}
else {
prevText = textObj;
}
});
if (prevText) {
retVal += prevText.str;
}
retVal += "\r\n----------------Page (" + index + ") Break----------------\r\n";
});
return retVal;
}...
};
// Introduce a way to directly process buffers without the need to write it to a temporary file
PdfParser.prototype.parseBuffer = function(pdfBuffer) {
_startParsingPDF.call(this, pdfBuffer);
};
PdfParser.prototype.getRawTextContent = function() { return this.PDFJS.getRawTextContent(); };
PdfParser.prototype.getRawTextContentStream = function() { return _createContentStream(this.getRawTextContent()); };
PdfParser.prototype.getAllFieldsTypes = function() { return this.PDFJS.getAllFieldsTypes(); };
PdfParser.prototype.getAllFieldsTypesStream = function() { return _createContentStream(this.getAllFieldsTypes()); };
PdfParser.prototype.getMergedTextBlocksIfNeeded = function() { return {"formImage": this.PDFJS.getMergedTextBlocksIfNeeded
()}; };
PdfParser.prototype.getMergedTextBlocksStream = function() { return _createContentStream(this.getMergedTextBlocksIfNeeded()); };
...load = function (pdfDocument, scale) {
this.pdfDocument = pdfDocument;
return this.loadMetaData().then(
() => this.loadPages(),
error => this.raiseErrorEvent("loadMetaData error: " + error)
);
}...
cls.prototype.parsePDFData = function(arrayBuffer) {
this.pdfDocument = null;
let parameters = {password: '', data: arrayBuffer};
PDFJS.getDocument(parameters).then(
pdfDocument => this.load(pdfDocument, 1),
error => this.raiseErrorEvent("An error occurred while parsing the PDF: " + error)
);
};
cls.prototype.tryLoadFieldInfoXML = function(pdfFilePath) {
let fieldInfoXMLPath = pdfFilePath.replace(".pdf", _sufInfo);
if ((fieldInfoXMLPath.indexOf(_sufInfo) < 1) || (!fs.existsSync(fieldInfoXMLPath))) {
...loadMetaData = function () {
return this.pdfDocument.getMetadata().then(
data => {
this.documentInfo = data.info;
this.metadata = data.metadata;
this.parseMetaData();
},
error => this.raiseErrorEvent("pdfDocument.getMetadata error: " + error)
);
}...
}
});
};
cls.prototype.load = function(pdfDocument, scale) {
this.pdfDocument = pdfDocument;
return this.loadMetaData().then(
() => this.loadPages(),
error => this.raiseErrorEvent("loadMetaData error: " + error)
);
};
cls.prototype.loadMetaData = function() {
return this.pdfDocument.getMetadata().then(
...loadPages = function () {
let pagesCount = this.pdfDocument.numPages;
let pagePromises = [];
for (let i = 1; i <= pagesCount; i++)
pagePromises.push(this.pdfDocument.getPage(i));
let pagesPromise = PDFJS.Promise.all(pagePromises);
nodeUtil.p2jinfo("PDF loaded. pagesCount = " + pagesCount);
return pagesPromise.then(
promisedPages => this.parsePage(promisedPages, 0, 1.5),
error => this.raiseErrorEvent("pagesPromise error: " + error)
);
}...
});
};
cls.prototype.load = function(pdfDocument, scale) {
this.pdfDocument = pdfDocument;
return this.loadMetaData().then(
() => this.loadPages(),
error => this.raiseErrorEvent("loadMetaData error: " + error)
);
};
cls.prototype.loadMetaData = function() {
return this.pdfDocument.getMetadata().then(
data => {
...parseMetaData = function () {
let info = this.documentInfo;
let metadata = this.metadata;
let pdfTile = "";
if (metadata && metadata.has('dc:title')) {
pdfTile = metadata.get('dc:title');
}
else if (info && info['Title'])
pdfTile = info['Title'];
let formAttr = {AgencyId:"", Name: "", MC: false, Max: 1, Parent:""};
if (metadata) {
formAttr.AgencyId = _getMetaDataString(metadata, 'pdfx:agencyid');
if (formAttr.AgencyId != "unknown")
pdfTile = formAttr.AgencyId;
formAttr.Name = _getMetaDataString(metadata, 'pdfx:name');
formAttr.MC = _getMetaDataString(metadata, 'pdfx:mc') === 'true';
formAttr.Max = _getMetaDataInt(metadata, 'pdfx:max');
formAttr.Parent = _getMetaDataInt(metadata, 'pdfx:parent');
}
this.raiseReadyEvent({Transcoder: _PARSER_SIG, Agency:pdfTile, Id: formAttr});
}...
};
cls.prototype.loadMetaData = function() {
return this.pdfDocument.getMetadata().then(
data => {
this.documentInfo = data.info;
this.metadata = data.metadata;
this.parseMetaData();
},
error => this.raiseErrorEvent("pdfDocument.getMetadata error: " + error)
);
};
cls.prototype.parseMetaData = function() {
let info = this.documentInfo;
...parsePDFData = function (arrayBuffer) {
this.pdfDocument = null;
let parameters = {password: '', data: arrayBuffer};
PDFJS.getDocument(parameters).then(
pdfDocument => this.load(pdfDocument, 1),
error => this.raiseErrorEvent("An error occurred while parsing the PDF: " + error)
);
}...
let _startParsingPDF = function(buffer) {
this.data = {};
this.PDFJS.on("pdfjs_parseDataReady", _onPDFJSParseDataReady.bind(this));
this.PDFJS.on("pdfjs_parseDataError", _onPDFJSParserDataError.bind(this));
this.PDFJS.parsePDFData(buffer || _binBuffer[this.pdfFilePath]);
};
let _processBinaryCache = function() {
if (_.has(_binBuffer, this.pdfFilePath)) {
_startParsingPDF.call(this);
return true;
}
...parsePage = function (promisedPages, id, scale) {
nodeUtil.p2jinfo("start to parse page:" + (id+1));
let pdfPage = promisedPages[id];
let pageParser = new PDFPageParser(pdfPage, id, scale, this.ptiParser);
function continueOnNextPage() {
nodeUtil.p2jinfo("complete parsing page:" + (id+1));
if (id === (this.pdfDocument.numPages - 1) ) {
this.raiseReadyEvent({Pages:this.pages, Width: this.pageWidth});
//v1.1.2: signal end of parsed data with null
process.nextTick(() => this.raiseReadyEvent(null));
}
else {
process.nextTick(() => this.parsePage(promisedPages, ++id, scale));
}
}
pageParser.parsePage(
data => {
if (!this.pageWidth) //get PDF width
this.pageWidth = pageParser.width;
let page = {Height: pageParser.height,
HLines: pageParser.HLines,
VLines: pageParser.VLines,
Fills:pageParser.Fills,
//needs to keep current default output format, text content will output to a separate file if '-c' command line argument is set
// Content:pdfPage.getTextContent(),
Texts: pageParser.Texts,
Fields: pageParser.Fields,
Boxsets: pageParser.Boxsets
};
this.pages.push(page);
if (this.needRawText) {
pdfPage.getTextContent().then(
textContent => {
this.rawTextContents.push(textContent);
nodeUtil.p2jinfo("complete parsing raw text content:" + (id+1));
continueOnNextPage.call(this);
},
error => this.raiseErrorEvent("pdfPage.getTextContent error: " + error)
);
}
else {
continueOnNextPage.call(this);
}
},
errMsg => this.raiseErrorEvent("parsePage error:" + errMsg)
);
}...
pagePromises.push(this.pdfDocument.getPage(i));
let pagesPromise = PDFJS.Promise.all(pagePromises);
nodeUtil.p2jinfo("PDF loaded. pagesCount = " + pagesCount);
return pagesPromise.then(
promisedPages => this.parsePage(promisedPages, 0, 1.5),
error => this.raiseErrorEvent("pagesPromise error: " + error)
);
};
cls.prototype.parsePage = function(promisedPages, id, scale) {
nodeUtil.p2jinfo("start to parse page:" + (id+1));
...raiseErrorEvent = function (errMsg) {
console.error(errMsg);
process.nextTick( () => this.emit("pdfjs_parseDataError", errMsg));
return errMsg;
}...
cls.prototype.parsePDFData = function(arrayBuffer) {
this.pdfDocument = null;
let parameters = {password: '', data: arrayBuffer};
PDFJS.getDocument(parameters).then(
pdfDocument => this.load(pdfDocument, 1),
error => this.raiseErrorEvent("An error occurred while parsing the
PDF: " + error)
);
};
cls.prototype.tryLoadFieldInfoXML = function(pdfFilePath) {
let fieldInfoXMLPath = pdfFilePath.replace(".pdf", _sufInfo);
if ((fieldInfoXMLPath.indexOf(_sufInfo) < 1) || (!fs.existsSync(fieldInfoXMLPath))) {
return;
...raiseReadyEvent = function (data) {
process.nextTick( () => this.emit("pdfjs_parseDataReady", data) );
return data;
}...
formAttr.Name = _getMetaDataString(metadata, 'pdfx:name');
formAttr.MC = _getMetaDataString(metadata, 'pdfx:mc') === 'true';
formAttr.Max = _getMetaDataInt(metadata, 'pdfx:max');
formAttr.Parent = _getMetaDataInt(metadata, 'pdfx:parent');
}
this.raiseReadyEvent({Transcoder: _PARSER_SIG, Agency:pdfTile, Id: formAttr});
};
cls.prototype.loadPages = function() {
let pagesCount = this.pdfDocument.numPages;
let pagePromises = [];
for (let i = 1; i <= pagesCount; i++)
pagePromises.push(this.pdfDocument.getPage(i));
...tryLoadFieldInfoXML = function (pdfFilePath) {
let fieldInfoXMLPath = pdfFilePath.replace(".pdf", _sufInfo);
if ((fieldInfoXMLPath.indexOf(_sufInfo) < 1) || (!fs.existsSync(fieldInfoXMLPath))) {
return;
}
nodeUtil.p2jinfo("About to load fieldInfo XML : " + fieldInfoXMLPath);
let PTIXmlParser = require('./ptixmlinject');
this.ptiParser = new PTIXmlParser();
this.ptiParser.parseXml(fieldInfoXMLPath, err => {
if (err) {
nodeUtil.p2jwarn("fieldInfo XML Error: " + JSON.stringify(err));
this.ptiParser = null;
}
else {
nodeUtil.p2jinfo("fieldInfo XML loaded.");
}
});
}...
PdfParser.prototype.loadPDF = function(pdfFilePath, verbosity) {
this.setVerbosity(verbosity);
nodeUtil.p2jinfo("about to load PDF file " + pdfFilePath);
this.pdfFilePath = pdfFilePath;
if (this.processFieldInfoXML) {
this.PDFJS.tryLoadFieldInfoXML(pdfFilePath);
}
if (_processBinaryCache.call(this))
return;
this.fq.push({path: pdfFilePath}, _processPDFContent.bind(this));
};
...pdfanno = function (field, viewport, Fields, Boxsets) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function () {
return _id;
};
this.get_name = function () {
return _name + _id;
};
}n/a
processAnnotation = function (annotation, item) {
if (item.fieldType == 'Btn') { //PDF Spec p.675
if (item.fieldFlags & 32768) {
setupRadioButton(annotation, item);
}
else if (item.fieldFlags & 65536) {
setupPushButton(annotation, item);
}
else {
setupCheckBox(annotation, item);
}
}
else if (item.fieldType == 'Ch') {
setupDropDown(annotation, item);
}
else if (item.fieldType == 'Tx') {
setupFieldAttributes(annotation, item);
}
}...
//END:MQZ. Sep.19.2012. comment out the fullname routin, replace it with getInheritableProperty('T') //PDF Spec P.689
//It matches a sequence of at least one period or space, which is then replaced by a single underscore
var itemNameStr = stringToPDFString(Util.getInheritableProperty(dict, 'T') || '');
itemNameStr = itemNameStr.replace(/[.\s\W]+/g, '_'); //replace spaces and non-word character (not [^a-zA-Z0-9_]) with
_
data.fullName = itemNameStr.replace(/^[\s_,:.;\/\\]+/, ''); //replace starting punctuation
PDFAnno.processAnnotation(dict, data);
}
var parent = Annotation.prototype;
Util.inherit(WidgetAnnotation, Annotation, {
isViewable: function WidgetAnnotation_isViewable() {
if (this.data.fieldType === 'Sig') {
TODO('unimplemented annotation type: Widget signature');
...clean = function () {
delete this.get_id;
delete this.get_name;
}...
console.warn("to be implemented: contextPrototype.measureText - ", text);
let chars = text.length || 1;
return {width: chars * (this.currentFont.spaceWidth || 5)};
};
contextPrototype.setFont = function(fontObj) {
if ((!!this.currentFont) && _.isFunction(this.currentFont.clean)) {
this.currentFont.clean();
this.currentFont = null;
}
this.currentFont = new PDFFont(fontObj);
};
contextPrototype.clearRect = function () {
...function CanvasRenderingContext2D_(canvasTarget, scaledWidth, scaledHeight) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.m_ = createMatrixIdentity();
this.mStack_ = [];
this.aStack_ = [];
this.currentPath_ = [];
// Canvas context properties
this.strokeStyle = '#000';
this.fillStyle = '#000';
this.lineWidth = 1;
this.lineJoin = 'miter';
this.lineCap = 'butt';
this.dashArray = [];
this.miterLimit = 1;
this.globalAlpha = 1;
if (!_.has(canvasTarget, "HLines") || !_.isArray(canvasTarget.HLines))
canvasTarget.HLines = [];
if (!_.has(canvasTarget, "VLines") || !_.isArray(canvasTarget.VLines))
canvasTarget.VLines = [];
if (!_.has(canvasTarget, "Fills") || !_.isArray(canvasTarget.Fills))
canvasTarget.Fills = [];
if (!_.has(canvasTarget, "Texts") || !_.isArray(canvasTarget.Texts))
canvasTarget.Texts = [];
this.canvas = canvasTarget;
this.width = scaledWidth;
this.height = scaledHeight;
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
this.lineScale_ = 1;
this.currentFont = null;
}n/a
arc = function (aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) {
let arcType = aClockwise ? 'at' : 'wa';
let xStart = aX + mc(aStartAngle) * aRadius;
let yStart = aY + ms(aStartAngle) * aRadius;
let xEnd = aX + mc(aEndAngle) * aRadius;
let yEnd = aY + ms(aEndAngle) * aRadius;
// IE won't render arches drawn counter clockwise if xStart == xEnd.
if (xStart == xEnd && !aClockwise) {
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
// that can be represented in binary
}
let p = this.getCoords_(aX, aY);
let pStart = this.getCoords_(xStart, yStart);
let pEnd = this.getCoords_(xEnd, yEnd);
this.currentPath_.push({type:arcType,
x:p.x,
y:p.y,
radius:aRadius,
xStart:pStart.x,
yStart:pStart.y,
xEnd:pEnd.x,
yEnd:pEnd.y});
}n/a
arcTo = function () {
// TODO: Implement
}n/a
beginPath = function () {
// TODO: Branch current matrix so that save/restore has no effect
// as per safari docs.
this.currentPath_ = [];
}...
contextPrototype.strokeRect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
let oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
...bezierCurveTo = function (aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) {
let p = this.getCoords_(aX, aY);
let cp1 = this.getCoords_(aCP1x, aCP1y);
let cp2 = this.getCoords_(aCP2x, aCP2y);
bezierCurveTo(this, cp1, cp2, p);
}...
function moveTo(x, y) {
js.push('c.moveTo(' + x + ',' + y + ');');
}
function lineTo(x, y) {
js.push('c.lineTo(' + x + ',' + y + ');');
}
function bezierCurveTo(x1, y1, x2, y2, x, y) {
js.push('c.bezierCurveTo(' + x1 + ',' + y1 + ',' + x2
+ ',' + y2 + ',' +
x + ',' + y + ');');
}
function parse(code) {
var i = 0;
while (i < code.length) {
var stackClean = false;
...clearRect = function () {
}...
// blend modes can look wrong since we'd be blending with a white
// backdrop. The problem with a transparent backdrop though is we then
// don't get sub pixel anti aliasing on text, so we fill with white if
// we can.
var width = this.ctx.canvas.width;
var height = this.ctx.canvas.height;
if (transparency) {
this.ctx.clearRect(0, 0, width, height);
} else {
this.ctx.mozOpaque = true;
this.ctx.save();
this.ctx.fillStyle = 'rgb(255, 255, 255)';
this.ctx.fillRect(0, 0, width, height);
this.ctx.restore();
}
...clip = function () {
// TODO: Implement
}...
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
ctx.setTransform.apply(ctx, path.transform);
ctx.translate(path.x, path.y);
path.addToPath(ctx, path.fontSize);
}
ctx.restore();
ctx.clip();
ctx.beginPath();
delete this.pendingTextPaths;
},
setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
this.current.charSpacing = spacing;
},
setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
...closePath = function () {
this.currentPath_.push({type:'close'});
}...
return;//try to remove the rectangle behind radio buttons and checkboxes
}
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
};
contextPrototype.strokeRect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
...createLinearGradient = function (aX0, aY0, aX1, aY1) {
let gradient = new CanvasGradient_('gradient');
gradient.x0_ = aX0;
gradient.y0_ = aY0;
gradient.x1_ = aX1;
gradient.y1_ = aY1;
return gradient;
}...
var r0 = raw[5];
var r1 = raw[6];
return {
type: 'Pattern',
getPattern: function RadialAxial_getPattern(ctx) {
var grad;
if (type == PatternType.AXIAL)
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
else if (type == PatternType.RADIAL)
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
var c = colorStops[i];
grad.addColorStop(c[0], c[1]);
}
...createPattern = function () {
return new CanvasPattern_;
}...
var temporaryPatternCanvas = this.createPatternCanvas(owner);
var ctx = this.ctx;
ctx.setTransform.apply(ctx, this.baseTransform);
ctx.transform.apply(ctx, this.matrix);
this.scaleToContext();
return ctx.createPattern(temporaryPatternCanvas, 'repeat');
}
};
return TilingPattern;
})();
...createRadialGradient = function (aX0, aY0, aR0, aX1, aY1, aR1) {
let gradient = new CanvasGradient_('gradientradial');
gradient.x0_ = aX0;
gradient.y0_ = aY0;
gradient.r0_ = aR0;
gradient.x1_ = aX1;
gradient.y1_ = aY1;
gradient.r1_ = aR1;
return gradient;
}...
return {
type: 'Pattern',
getPattern: function RadialAxial_getPattern(ctx) {
var grad;
if (type == PatternType.AXIAL)
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
else if (type == PatternType.RADIAL)
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
var c = colorStops[i];
grad.addColorStop(c[0], c[1]);
}
return grad;
}
...drawImage = function (image, var_args) {
//MQZ. no image drawing support for now
}...
var width = img.width;
var height = img.height;
var size = width * height;
var rgbaLength = size * 4;
var buf = new Uint8Array(size * components);
var tmpCanvas = createScratchCanvas(width, height);
var tmpCtx = tmpCanvas.getContext('2d');
tmpCtx.drawImage(img, 0, 0);
var data = tmpCtx.getImageData(0, 0, width, height).data;
if (components == 3) {
for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
buf[j] = data[i];
buf[j + 1] = data[i + 1];
buf[j + 2] = data[i + 2];
...fill = function () {
this.stroke(true);
}...
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.fill();
this.currentPath_ = oldPath;
};
contextPrototype.createLinearGradient = function (aX0, aY0, aX1, aY1) {
let gradient = new CanvasGradient_('gradient');
gradient.x0_ = aX0;
...fillRect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
let oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.fill();
this.currentPath_ = oldPath;
}...
var height = this.ctx.canvas.height;
if (transparency) {
this.ctx.clearRect(0, 0, width, height);
} else {
this.ctx.mozOpaque = true;
this.ctx.save();
this.ctx.fillStyle = 'rgb(255, 255, 255)';
this.ctx.fillRect(0, 0, width, height);
this.ctx.restore();
}
var transform = viewport.transform;
this.baseTransform = transform.slice();
this.ctx.save();
this.ctx.transform.apply(this.ctx, transform);
...fillText = function (text, x, y, maxWidth, fontSize) {
if (!text || text.trim().length < 1)
return;
let p = this.getCoords_(x, y);
let a = processStyle(this.fillStyle || this.strokeStyle);
let color = (!!a) ? a.color : '#000000';
this.currentFont.processText(p, text, maxWidth, color, fontSize, this.canvas, this.m_);
}...
let color = (!!a) ? a.color : '#000000';
this.currentFont.processText(p, text, maxWidth, color, fontSize, this.canvas, this.m_);
};
contextPrototype.strokeText = function(text, x, y, maxWidth) {
//MQZ. 10/23/2012, yeah, no hollow text for now
this.fillText(text, x, y, maxWidth);
};
contextPrototype.measureText = function(text) {
console.warn("to be implemented: contextPrototype.measureText - ", text);
let chars = text.length || 1;
return {width: chars * (this.currentFont.spaceWidth || 5)};
};
...getContext = function (ctxType) {
return (ctxType === "2d") ? this : null;
}...
cls.prototype.parsePage = function(callback, errorCallBack) {
if (this.renderingState !== RenderingStates.INITIAL)
error('Must be in new state before drawing');
this.renderingState = RenderingStates.RUNNING;
let canvas = createScratchCanvas(1, 1);
let ctx = canvas.getContext('2d');
function pageViewDrawCallback(error) {
this.renderingState = RenderingStates.FINISHED;
if (error) {
let errMsg = 'An error occurred while rendering the page ' + (this.id + 1) +
':\n' + error.message +
...getCoords_ = function (aX, aY) {
let m = this.m_;
return {
x: (aX * m[0][0] + aY * m[1][0] + m[2][0]),
y: (aX * m[0][1] + aY * m[1][1] + m[2][1])
};
}...
contextPrototype.getLineDash= function() {
return this.dashArray;
};
contextPrototype.fillText = function(text, x, y, maxWidth, fontSize) {
if (!text || text.trim().length < 1)
return;
let p = this.getCoords_(x, y);
let a = processStyle(this.fillStyle || this.strokeStyle);
let color = (!!a) ? a.color : '#000000';
this.currentFont.processText(p, text, maxWidth, color, fontSize, this.canvas, this.m_);
};
...getImageData = function (x, y, w, h) {
//MQZ. returns empty data buffer for now
return {
width:w,
height:h,
data:new Uint8Array(w * h * 4)
};
}...
var SMALL_IMAGE_DIMENSIONS = 200;
// Inlining small images into the queue as RGB data
if (inline && !softMask && !mask &&
!(image instanceof JpegStream) &&
(w + h) < SMALL_IMAGE_DIMENSIONS) {
var imageObj = new PDFImage(this.xref, resources, image,
inline, null, null);
var imgData = imageObj.getImageData();
operatorList.addOp(OPS.paintInlineImageXObject, [imgData]);
return;
}
// If there is no imageMask, create the PDFImage and a lot
// of image processing can be done here.
var uniquePrefix = this.uniquePrefix || '';
...getLineDash = function () {
return this.dashArray;
}...
for (var i = 0, ii = properties.length; i < ii; i++) {
var property = properties[i];
if (property in sourceCtx) {
destCtx[property] = sourceCtx[property];
}
}
if ('setLineDash' in sourceCtx) {
destCtx.setLineDash(sourceCtx.getLineDash());
destCtx.lineDashOffset = sourceCtx.lineDashOffset;
} else if ('mozDash' in sourceCtx) {
destCtx.mozDash = sourceCtx.mozDash;
destCtx.mozDashOffset = sourceCtx.mozDashOffset;
}
}
...lineTo = function (aX, aY) {
let p = this.getCoords_(aX, aY);
this.currentPath_.push({type:'lineTo', x:p.x, y:p.y});
this.currentX_ = p.x;
this.currentY_ = p.y;
}...
contextPrototype.rect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
};
contextPrototype.strokeRect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
...measureText = function (text) {
console.warn("to be implemented: contextPrototype.measureText - ", text);
let chars = text.length || 1;
return {width: chars * (this.currentFont.spaceWidth || 5)};
}...
scaledX = x / fontSizeScale;
scaledY = 0;
}
if (font.remeasure && width > 0) {
// some standard fonts may not have the exact width, trying to
// rescale per character
var measuredWidth = ctx.measureText(character).width * 1000 /
current.fontSize * current.fontSizeScale;
var characterScaleX = width / measuredWidth;
restoreNeeded = true;
ctx.save();
ctx.scale(characterScaleX, 1);
scaledX /= characterScaleX;
...moveTo = function (aX, aY) {
let p = this.getCoords_(aX, aY);
this.currentPath_.push({type:'moveTo', x:p.x, y:p.y});
this.currentX_ = p.x;
this.currentY_ = p.y;
}...
};
contextPrototype.rect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
};
contextPrototype.strokeRect = function (aX, aY, aWidth, aHeight) {
...quadraticCurveTo = function (aCPx, aCPy, aX, aY) {
// the following is lifted almost directly from
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
let cp = this.getCoords_(aCPx, aCPy);
let p = this.getCoords_(aX, aY);
let cp1 = {
x:this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
y:this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
};
let cp2 = {
x:cp1.x + (p.x - this.currentX_) / 3.0,
y:cp1.y + (p.y - this.currentY_) / 3.0
};
bezierCurveTo(this, cp1, cp2, p);
}...
function moveTo(x, y) {
js.push('c.moveTo(' + x + ',' + y + ');');
}
function lineTo(x, y) {
js.push('c.lineTo(' + x + ',' + y + ');');
}
function quadraticCurveTo(xa, ya, x, y) {
js.push('c.quadraticCurveTo(' + xa + ',' + ya + ',' +
x + ',' + y + ');');
}
var i = 0;
var numberOfContours = ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
var xMin = ((code[i + 2] << 24) | (code[i + 3] << 16)) >> 16;
var yMin = ((code[i + 4] << 24) | (code[i + 5] << 16)) >> 16;
...rect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
}...
this.curveTo(x1, y1, x3, y3, x3, y3);
this.current.setCurrentPoint(x3, y3);
},
closePath: function CanvasGraphics_closePath() {
this.ctx.closePath();
},
rectangle: function CanvasGraphics_rectangle(x, y, width, height) {
this.ctx.rect(x, y, width, height);
},
stroke: function CanvasGraphics_stroke(consumePath) {
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
var ctx = this.ctx;
var strokeColor = this.current.strokeColor;
if (this.current.lineWidth === 0)
ctx.lineWidth = this.getSinglePixelWidth();
...restore = function () {
copyState(this.aStack_.pop(), this);
this.m_ = this.mStack_.pop();
}...
}
var subglyph = font.glyphs[glyphIndex];
if (subglyph) {
js.push('c.save();');
js.push('c.transform(' + scaleX + ',' + scale01 + ',' +
scale10 + ',' + scaleY + ',' + x + ',' + y + ');');
compileGlyf(subglyph, js, font);
js.push('c.restore();');
}
} while ((flags & 0x20));
} else {
// simple glyph
var endPtsOfContours = [];
for (var j = 0; j < numberOfContours; j++) {
endPtsOfContours.push((code[i] << 8) | code[i + 1]);
...rotate = function (aRot) {
let c = mc(aRot);
let s = ms(aRot);
let m1 = [
[c, s, 0],
[-s, c, 0],
[0, 0, 1]
];
setM(this, matrixMultiply(m1, this.m_), false);
}n/a
save = function () {
let o = {};
copyState(this, o);
this.aStack_.push(o);
this.mStack_.push(this.m_);
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
}...
scale01 = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824;
scale10 = ((code[i + 4] << 24) | (code[i + 5] << 16)) / 1073741824;
scaleY = ((code[i + 6] << 24) | (code[i + 7] << 16)) / 1073741824;
i += 8;
}
var subglyph = font.glyphs[glyphIndex];
if (subglyph) {
js.push('c.save();');
js.push('c.transform(' + scaleX + ',' + scale01 + ',' +
scale10 + ',' + scaleY + ',' + x + ',' + y + ');');
compileGlyf(subglyph, js, font);
js.push('c.restore();');
}
} while ((flags & 0x20));
} else {
...scale = function (aX, aY) {
this.arcScaleX_ *= aX;
this.arcScaleY_ *= aY;
let m1 = [
[aX, 0, 0],
[0, aY, 0],
[0, 0, 1]
];
setM(this, matrixMultiply(m1, this.m_), true);
}...
if (!code || code.length === 0 || code[0] === 14) {
return noop;
}
var js = [];
js.push('c.save();');
js.push('c.transform(' + this.fontMatrix.join(',') + ');');
js.push('c.scale(size, -size);');
this.compileGlyphImpl(code, js);
js.push('c.restore();');
return js.join('\n');
},
...setFont = function (fontObj) {
if ((!!this.currentFont) && _.isFunction(this.currentFont.clean)) {
this.currentFont.clean();
this.currentFont = null;
}
this.currentFont = new PDFFont(fontObj);
}...
case 'RI':
this.setRenderingIntent(value);
break;
case 'FL':
this.setFlatness(value);
break;
case 'Font':
this.setFont(value[0], value[1]);
break;
case 'CA':
this.current.strokeAlpha = state[1];
break;
case 'ca':
this.current.fillAlpha = state[1];
this.ctx.globalAlpha = state[1];
...setLineDash = function (lineDash) {
this.dashArray = lineDash;
}...
for (var i = 0, ii = properties.length; i < ii; i++) {
var property = properties[i];
if (property in sourceCtx) {
destCtx[property] = sourceCtx[property];
}
}
if ('setLineDash' in sourceCtx) {
destCtx.setLineDash(sourceCtx.getLineDash());
destCtx.lineDashOffset = sourceCtx.lineDashOffset;
} else if ('mozDash' in sourceCtx) {
destCtx.mozDash = sourceCtx.mozDash;
destCtx.mozDashOffset = sourceCtx.mozDashOffset;
}
}
...setTransform = function (m11, m12, m21, m22, dx, dy) {
let m = [
[m11, m12, 0],
[m21, m22, 0],
[dx, dy, 1]
];
setM(this, m, true);
}...
trackTransform) {
var canvasEntry;
if (id in cache) {
canvasEntry = cache[id];
canvasEntry.canvas.width = width;
canvasEntry.canvas.height = height;
// reset canvas transform for emulated mozCurrentTransform, if needed
canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
} else {
var canvas = createScratchCanvas(width, height);
var ctx = canvas.getContext('2d');
if (trackTransform) {
addContextCurrentTransform(ctx);
}
cache[id] = canvasEntry = {canvas: canvas, context: ctx};
...stroke = function (aFill) {
if (this.currentPath_.length < 2) {
return;
}
let a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
let color = a.color;
// let opacity = a.alpha * this.globalAlpha;
let lineWidth = this.lineScale_ * this.lineWidth;
let min = {x:null, y:null};
let max = {x:null, y:null};
for (let i = 0; i < this.currentPath_.length; i++) {
let p = this.currentPath_[i];
switch (p.type) {
case 'moveTo':
break;
case 'lineTo':
if (!aFill) { //lines
if (i > 0) {
_drawPDFLine.call(this, this.currentPath_[i-1], p, lineWidth, color);
}
}
break;
case 'close':
if (!aFill) { //lines
if (i > 0) {
_drawPDFLine.call(this, this.currentPath_[i-1], this.currentPath_[0], lineWidth, color);
}
}
p = null;
break;
case 'bezierCurveTo':
break;
case 'at':
case 'wa':
break;
}
// Figure out dimensions so we can set fills' coordinates correctly
if (aFill && p) {
if (min.x == null || p.x < min.x) {
min.x = p.x;
}
if (max.x == null || p.x > max.x) {
max.x = p.x;
}
if (min.y == null || p.y < min.y) {
min.y = p.y;
}
if (max.y == null || p.y > max.y) {
max.y = p.y;
}
}
}
if (aFill) { //fill
_drawPDFFill.call(this, min, min, max, color);
}
}...
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
this.currentPath_ = oldPath;
};
contextPrototype.fillRect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
...strokeRect = function (aX, aY, aWidth, aHeight) {
if (_needRemoveRect.call(this, aX, aY, aWidth, aHeight)) {
return;//try to remove the rectangle behind radio buttons and checkboxes
}
let oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
this.currentPath_ = oldPath;
}n/a
strokeText = function (text, x, y, maxWidth) {
//MQZ. 10/23/2012, yeah, no hollow text for now
this.fillText(text, x, y, maxWidth);
}...
} else {
if (fillStrokeMode === TextRenderingMode.FILL ||
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
ctx.fillText(character, x, y);
}
if (fillStrokeMode === TextRenderingMode.STROKE ||
fillStrokeMode === TextRenderingMode.FILL_STROKE) {
ctx.strokeText(character, x, y);
}
}
if (isAddToPathSet) {
var paths = this.pendingTextPaths || (this.pendingTextPaths = []);
paths.push({
transform: ctx.mozCurrentTransform,
...transform = function (m11, m12, m21, m22, dx, dy) {
let m1 = [
[m11, m12, 0],
[m21, m22, 0],
[dx, dy, 1]
];
setM(this, matrixMultiply(m1, this.m_), true);
}...
scale10 = ((code[i + 4] << 24) | (code[i + 5] << 16)) / 1073741824;
scaleY = ((code[i + 6] << 24) | (code[i + 7] << 16)) / 1073741824;
i += 8;
}
var subglyph = font.glyphs[glyphIndex];
if (subglyph) {
js.push('c.save();');
js.push('c.transform(' + scaleX + ',' + scale01 + ',
x27; +
scale10 + ',' + scaleY + ',' + x + ',' + y + ');');
compileGlyf(subglyph, js, font);
js.push('c.restore();');
}
} while ((flags & 0x20));
} else {
// simple glyph
...translate = function (aX, aY) {
let m1 = [
[1, 0, 0],
[0, 1, 0],
[aX, aY, 1]
];
setM(this, matrixMultiply(m1, this.m_), false);
}...
case 14: // endchar
if (stack.length >= 4) {
var achar = stack.pop();
var bchar = stack.pop();
y = stack.pop();
x = stack.pop();
js.push('c.save();');
js.push('c.translate('+ x + ',' + y + ');');
var gid = lookupCmap(font.cmap, String.fromCharCode(
font.glyphNameMap[Encodings.StandardEncoding[achar]]));
compileCharString(font.glyphs[gid], js, font);
js.push('c.restore();');
gid = lookupCmap(font.cmap, String.fromCharCode(
font.glyphNameMap[Encodings.StandardEncoding[bchar]]));
...pdffield = function (field, viewport, Fields, Boxsets) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.field = field;
this.viewport = viewport;
this.Fields = Fields;
this.Boxsets = Boxsets;
}n/a
getAllFieldsTypes = function (data) {
function isFieldReadOnly(field) {
return (field.AM & kFBANotOverridable) ? true : false;
}
function getFieldBase(field) {
return {id: field.id.Id, type: field.T.Name, calc: isFieldReadOnly(field), value: field.V || ""};
}
let retVal = [];
_.each(data.Pages, function(page) {
_.each(page.Boxsets, function(boxsets) {
if (boxsets.boxes.length > 1) { //radio button
_.each(boxsets.boxes, function(box) {
retVal.push({id: boxsets.id.Id, type: "radio", calc: isFieldReadOnly(box), value: box.id.Id});
});
}
else { //checkbox
retVal.push(getFieldBase(boxsets.boxes[0]));
}
});
_.each(page.Fields, function(field){
retVal.push(getFieldBase(field));
});
});
return retVal;
}...
PdfParser.prototype.parseBuffer = function(pdfBuffer) {
_startParsingPDF.call(this, pdfBuffer);
};
PdfParser.prototype.getRawTextContent = function() { return this.PDFJS.getRawTextContent(); };
PdfParser.prototype.getRawTextContentStream = function() { return _createContentStream(this.getRawTextContent()); };
PdfParser.prototype.getAllFieldsTypes = function() { return this.PDFJS.getAllFieldsTypes(); };
PdfParser.prototype.getAllFieldsTypesStream = function() { return _createContentStream(this.getAllFieldsTypes()); };
PdfParser.prototype.getMergedTextBlocksIfNeeded = function() { return {"formImage": this.PDFJS.getMergedTextBlocksIfNeeded
()}; };
PdfParser.prototype.getMergedTextBlocksStream = function() { return _createContentStream(this.getMergedTextBlocksIfNeeded()); };
PdfParser.prototype.destroy = function() {
this.removeAllListeners();
...isFormElement = function (field) {
let retVal = false;
switch(field.subtype) {
case 'Widget': retVal = cls.isWidgetSupported(field); break;
default:
nodeUtil.p2jwarn("Unsupported: field.type of " + field.subtype);
break;
}
return retVal;
}...
INITIAL: 0,
RUNNING: 1,
PAUSED: 2,
FINISHED: 3
};
let _addField = function(field) {
if (!PDFField.isFormElement(field))
return;
let oneField = new PDFField(field, this.viewport, this.Fields, this.Boxsets);
oneField.processField();
};
// constructor
...isWidgetSupported = function (field) {
let retVal = false;
switch(field.fieldType) {
case 'Tx': retVal = true; break; //text input
case 'Btn':
if (field.fieldFlags & 32768) {
field.fieldType = 'Rd'; //radio button
}
else if (field.fieldFlags & 65536) {
field.fieldType = 'Btn'; //push button
}
else {
field.fieldType = 'Cb'; //checkbox
}
retVal = true;
break;
case 'Ch': retVal = true; break; //drop down
default:
nodeUtil.p2jwarn("Unsupported: field.fieldType of " + field.fieldType);
break;
}
return retVal;
}...
return retVal;
};
cls.isFormElement = function(field) {
let retVal = false;
switch(field.subtype) {
case 'Widget': retVal = cls.isWidgetSupported(field); break;
default:
nodeUtil.p2jwarn("Unsupported: field.type of " + field.subtype);
break;
}
return retVal;
};
...clean = function () {
delete this.get_id;
delete this.get_name;
delete this.field;
delete this.viewport;
delete this.Fields;
delete this.Boxsets;
}...
console.warn("to be implemented: contextPrototype.measureText - ", text);
let chars = text.length || 1;
return {width: chars * (this.currentFont.spaceWidth || 5)};
};
contextPrototype.setFont = function(fontObj) {
if ((!!this.currentFont) && _.isFunction(this.currentFont.clean)) {
this.currentFont.clean();
this.currentFont = null;
}
this.currentFont = new PDFFont(fontObj);
};
contextPrototype.clearRect = function () {
...processField = function () {
this.field.TI = _tabIndex++;
switch(this.field.fieldType) {
case 'Tx': _addAlpha.call(this, this.field); break;
case 'Cb': _addCheckBox.call(this, this.field); break;
case 'Rd': _addRadioButton.call(this, this.field);break;
case 'Btn':_addLinkButton.call(this, this.field); break;
case 'Ch': _addSelect.call(this, this.field); break;
}
this.clean();
}...
};
let _addField = function(field) {
if (!PDFField.isFormElement(field))
return;
let oneField = new PDFField(field, this.viewport, this.Fields, this.Boxsets);
oneField.processField();
};
// constructor
let cls = function (pdfPage, id, scale, ptiParser) {
nodeEvents.EventEmitter.call(this);
// private
let _id = _nextId++;
...pdffill = function (x, y, width, height, color) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}n/a
processFill = function (targetData) {
let clrId = PDFUnit.findColorIndex(this.color);
let oneFill = {x:PDFUnit.toFormX(this.x),
y:PDFUnit.toFormY(this.y),
w:PDFUnit.toFormX(this.width),
h:PDFUnit.toFormY(this.height),
clr: clrId};
//MQZ.07/29/2013: when color is not in color dictionary, set the original color (oc)
if (clrId < 0) {
oneFill = _.extend({oc: this.color}, oneFill);
}
targetData.Fills.push(oneFill);
}...
pL.processLine(this.canvas);
};
let _drawPDFFill = function(cp, min, max, color) {
let width = max.x - min.x;
let height = max.y - min.y;
let pF = new PDFFill(cp.x, cp.y, width, height, color);
pF.processFill(this.canvas);
};
let _needRemoveRect = function(x, y, w, h) {
let retVal = (Math.abs(w - Math.abs(h)) < 1 && w < 13);
if (retVal) {
nodeUtil.p2jinfo("Skipped: tiny rect: w=" + w + ", h=" + h);
}
...pdffont = function (fontObj) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.fontObj = fontObj;
let typeName = (fontObj.name || fontObj.fallbackName);
if (!typeName) {
typeName = _kFontFaces[0]; //default font family name
}
typeName = typeName.toLowerCase();
this.typeName = typeName;
let subType = typeName;
let nameArray = typeName.split('+');
if (_.isArray(nameArray) && nameArray.length > 1) {
subType = nameArray[1].split("-");
if (_.isArray(subType) && subType.length > 1) {
if (!this.bold) {
let subName = subType[1].toLowerCase();
this.bold = _boldSubNames.indexOf(subName) >= 0;
}
subType = subType[0];
}
}
this.subType = subType;
this.isSymbol = typeName.indexOf("symbol") > 0 || _kFontFaces[2].indexOf(this.subType) >= 0;
if (this.fontObj.isSymbolicFont) {
let mFonts = _stdFonts.filter( (oneName) => (typeName.indexOf(oneName) >= 0) );
if (mFonts.length > 0) {
this.fontObj.isSymbolicFont = false; //lots of Arial-based font is detected as symbol in VA forms (301, 76-c, etc.)
reset the flag for now
nodeUtil.p2jinfo("Reset: isSymbolicFont (false) for " + this.fontObj.name);
}
}
else {
if (this.isSymbol) {
this.fontObj.isSymbolicFont = true; //text pdf: va_ind_760c
nodeUtil.p2jinfo("Reset: isSymbolicFont (true) for " + this.fontObj.name);
}
}
this.fontSize = 1;
this.faceIdx = 0;
this.bold = false;
this.italic = false;
this.fontStyleId = -1;
this.spaceWidth = fontObj.spaceWidth;
if (!this.spaceWidth) {
var spaceId = Array.isArray(fontObj.toFontChar) ? fontObj.toFontChar.indexOf(32) : -1;
this.spaceWidth = (spaceId >= 0 && Array.isArray(fontObj.widths)) ? fontObj.widths[spaceId] : 250;
}
this.spaceWidth = PDFUnit.toFormX(this.spaceWidth) / 32;
}n/a
areAdjacentBlocks = function (t1, t2) {
let isInSameLine = Math.abs(t1.y - t2.y) <= DISTANCE_DELTA;
let isDistanceSmallerThanASpace = ((t2.x - t1.x - t1.w) < cls.getSpaceThreshHold(t1));
return isInSameLine && isDistanceSmallerThanASpace;
}...
return !isDup;
});
for (let i = 0; i < page.Texts.length; i++) {
let text = page.Texts[i];
if (prevText) {
if (PDFFont.areAdjacentBlocks(prevText, text) && PDFFont.haveSameStyle
(prevText, text)) {
let preT = decodeURIComponent(prevText.R[0].T);
let curT = decodeURIComponent(text.R[0].T);
prevText.R[0].T += text.R[0].T;
prevText.w += text.w;
text.merged = true;
...areDuplicateBlocks = function (t1, t2) {
return t1.x == t2.x && t1.y == t2.y && t1.R[0].T == t2.R[0].T && cls.haveSameStyle(t1, t2);
}...
cls.prototype.getMergedTextBlocksIfNeeded = function() {
for (let p = 0; p < this.pages.length; p++) {
let prevText = null;
let page = this.pages[p];
page.Texts.sort(PDFFont.compareBlockPos);
page.Texts = page.Texts.filter( (t, j) => {
let isDup = (j > 0) && PDFFont.areDuplicateBlocks(page.Texts[j-1],
t);
if (isDup) {
nodeUtil.p2jinfo("skipped: dup text block: " + decodeURIComponent(t.R[0].T));
}
return !isDup;
});
for (let i = 0; i < page.Texts.length; i++) {
...compareBlockPos = function (t1, t2) {
if (t1.y < t2.y - DISTANCE_DELTA) {
return -1;
}
if (Math.abs(t1.y - t2.y) <= DISTANCE_DELTA) {
if (t1.x < t2.x - DISTANCE_DELTA) {
return -1;
}
if (Math.abs(t1.x - t2.x) <= DISTANCE_DELTA) {
return 0;
}
}
return 1;
}n/a
getFontSize = function (textBlock) {
let sId = textBlock.R[0].S;
return (sId < 0) ? textBlock.R[0].TS[1] : _kFontStyles[sId][1];
}...
retVal = (typeof t1.R[0].RA === 'undefined') && (typeof t2.R[0].RA === 'undefined');
}
return retVal;
};
cls.getSpaceThreshHold = function(t1) {
return (PDFFont.getFontSize(t1)/12) * t1.sw;
};
cls.areAdjacentBlocks = function(t1, t2) {
let isInSameLine = Math.abs(t1.y - t2.y) <= DISTANCE_DELTA;
let isDistanceSmallerThanASpace = ((t2.x - t1.x - t1.w) < cls.getSpaceThreshHold(t1));
return isInSameLine && isDistanceSmallerThanASpace;
...getSpaceThreshHold = function (t1) {
return (PDFFont.getFontSize(t1)/12) * t1.sw;
}...
cls.getSpaceThreshHold = function(t1) {
return (PDFFont.getFontSize(t1)/12) * t1.sw;
};
cls.areAdjacentBlocks = function(t1, t2) {
let isInSameLine = Math.abs(t1.y - t2.y) <= DISTANCE_DELTA;
let isDistanceSmallerThanASpace = ((t2.x - t1.x - t1.w) < cls.getSpaceThreshHold
(t1));
return isInSameLine && isDistanceSmallerThanASpace;
};
cls.getFontSize = function(textBlock) {
let sId = textBlock.R[0].S;
return (sId < 0) ? textBlock.R[0].TS[1] : _kFontStyles[sId][1];
...haveSameStyle = function (t1, t2) {
let retVal = t1.R[0].S === t2.R[0].S;
if (retVal && t1.R[0].S < 0) {
for (let i = 0; i < t1.R[0].TS.length; i++) {
if (t1.R[0].TS[i] !== t2.R[0].TS[i]) {
retVal = false;
break;
}
}
}
if (retVal) { // make sure both block are not rotated
retVal = (typeof t1.R[0].RA === 'undefined') && (typeof t2.R[0].RA === 'undefined');
}
return retVal;
}...
cls.getFontSize = function(textBlock) {
let sId = textBlock.R[0].S;
return (sId < 0) ? textBlock.R[0].TS[1] : _kFontStyles[sId][1];
};
cls.areDuplicateBlocks = function(t1, t2) {
return t1.x == t2.x && t1.y == t2.y && t1.R[0].T == t2.R[0].T && cls.haveSameStyle(t1, t2);
};
// private
let _setFaceIndex = function() {
let fontObj = this.fontObj;
this.bold = fontObj.bold;
...clean = function () {
this.fontObj = null;
delete this.fontObj;
}...
console.warn("to be implemented: contextPrototype.measureText - ", text);
let chars = text.length || 1;
return {width: chars * (this.currentFont.spaceWidth || 5)};
};
contextPrototype.setFont = function(fontObj) {
if ((!!this.currentFont) && _.isFunction(this.currentFont.clean)) {
this.currentFont.clean();
this.currentFont = null;
}
this.currentFont = new PDFFont(fontObj);
};
contextPrototype.clearRect = function () {
...flash_encode = function (str) {
let retVal = encodeURIComponent(str);
retVal = retVal.replace("%C2%96", "-");
retVal = retVal.replace("%C2%91", "%27");
retVal = retVal.replace("%C2%92", "%27");
retVal = retVal.replace("%C2%82", "%27");
retVal = retVal.replace("%C2%93", "%22");
retVal = retVal.replace("%C2%94", "%22");
retVal = retVal.replace("%C2%84", "%22");
retVal = retVal.replace("%C2%8B", "%C2%AB");
retVal = retVal.replace("%C2%9B", "%C2%BB");
return retVal;
}...
let oneText = {x: PDFUnit.toFormX(p.x) - 0.25,
y: PDFUnit.toFormY(p.y) - 0.75,
w: PDFUnit.toFixedFloat(maxWidth),
sw: this.spaceWidth, //font space width, use to merge adjacent text blocks
clr: clrId,
A: "left",
R: [{
T: this.flash_encode(text),
S: this.fontStyleId,
TS: TS
}]
};
//MQZ.07/29/2013: when color is not in color dictionary, set the original color (oc)
if (clrId < 0) {
...processText = function (p, str, maxWidth, color, fontSize, targetData, matrix2D) {
let text = _processSymbolicFont.call(this, str);
if (!text) {
return;
}
this.fontStyleId = _getFontStyleIndex.call(this, fontSize);
// when this.fontStyleId === -1, it means the text style doesn't match any entry in the dictionary
// adding TS to better describe text style [fontFaceId, fontSize, 1/0 for bold, 1/0 for italic];
let TS = [this.faceIdx, this.fontSize, this.bold?1:0, this.italic?1:0];
let clrId = PDFUnit.findColorIndex(color);
let oneText = {x: PDFUnit.toFormX(p.x) - 0.25,
y: PDFUnit.toFormY(p.y) - 0.75,
w: PDFUnit.toFixedFloat(maxWidth),
sw: this.spaceWidth, //font space width, use to merge adjacent text blocks
clr: clrId,
A: "left",
R: [{
T: this.flash_encode(text),
S: this.fontStyleId,
TS: TS
}]
};
//MQZ.07/29/2013: when color is not in color dictionary, set the original color (oc)
if (clrId < 0) {
oneText = _.extend({oc: color}, oneText);
}
let rAngle = _textRotationAngle.call(this, matrix2D);
if (rAngle != 0) {
nodeUtil.p2jinfo(str + ": rotated " + rAngle + " degree.");
_.extend(oneText.R[0], {RA: rAngle});
}
targetData.Texts.push(oneText);
}...
if (!text || text.trim().length < 1)
return;
let p = this.getCoords_(x, y);
let a = processStyle(this.fillStyle || this.strokeStyle);
let color = (!!a) ? a.color : '#000000';
this.currentFont.processText(p, text, maxWidth, color, fontSize, this.canvas, this
.m_);
};
contextPrototype.strokeText = function(text, x, y, maxWidth) {
//MQZ. 10/23/2012, yeah, no hollow text for now
this.fillText(text, x, y, maxWidth);
};
...pdfline = function (x1, y1, x2, y2, lineWidth, color, dashed) {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.lineWidth = lineWidth || 1.0;
this.color = color;
this.dashed = dashed;
}n/a
processLine = function (targetData) {
let xDelta = Math.abs(this.x2 - this.x1);
let yDelta = Math.abs(this.y2 - this.y1);
let minDelta = this.lineWidth;
let oneLine = {x:0, y:0, w: PDFUnit.toFixedFloat(this.lineWidth), l:0};
//MQZ Aug.28.2013, adding color support, using color dictionary and default to black
let clrId = PDFUnit.findColorIndex(this.color);
if (clrId < 0) {
oneLine = _.extend({oc: this.color}, oneLine);
}
else if (clrId > 0 && clrId < (PDFUnit.colorCount() - 1)) {
oneLine = _.extend({clr: clrId}, oneLine);
}
//MQZ Aug.29 dashed line support
if (this.dashed) {
oneLine = _.extend({dsh: 1}, oneLine);
}
if ((yDelta < this.lineWidth) && (xDelta > minDelta)) { //HLine
if (this.lineWidth < 4 && (xDelta / this.lineWidth < 4)) {
nodeUtil.p2jinfo("Skipped: short thick HLine: lineWidth = " + this.lineWidth + ", xDelta = " + xDelta);
return; //skip short thick lines, like PA SPP lines behinds checkbox
}
oneLine.l = PDFUnit.toFormX(xDelta);
if (this.x1 > this.x2)
_setStartPoint.call(this, oneLine, this.x2, this.y2);
else
_setStartPoint.call(this, oneLine, this.x1, this.y1);
targetData.HLines.push(oneLine);
}
else if ((xDelta < this.lineWidth) && (yDelta > minDelta)) {//VLine
if (this.lineWidth < 4 && (yDelta / this.lineWidth < 4)) {
nodeUtil.p2jinfo("Skipped: short thick VLine: lineWidth = " + this.lineWidth + ", yDelta = " + yDelta);
return; //skip short think lines, like PA SPP lines behinds checkbox
}
oneLine.l = PDFUnit.toFormY(yDelta);
if (this.y1 > this.y2)
_setStartPoint.call(this, oneLine, this.x2, this.y2);
else
_setStartPoint.call(this, oneLine, this.x1, this.y1);
targetData.VLines.push(oneLine);
}
}...
this.currentFont = null;
}
//private helper methods
let _drawPDFLine = function(p1, p2, lineWidth, color) {
let dashedLine = _.isArray(this.dashArray) && (this.dashArray.length > 1);
let pL = new PDFLine(p1.x, p1.y, p2.x, p2.y, lineWidth, color, dashedLine);
pL.processLine(this.canvas);
};
let _drawPDFFill = function(cp, min, max, color) {
let width = max.x - min.x;
let height = max.y - min.y;
let pF = new PDFFill(cp.x, cp.y, width, height, color);
pF.processFill(this.canvas);
...pdfunit = function () {
// private
let _id = _nextId++;
// public (every instance will have their own copy of these methods, needs to be lightweight)
this.get_id = function() { return _id; };
this.get_name = function() { return _name + _id; };
}n/a
colorCount = function () {
return kColors.length;
}...
let oneLine = {x:0, y:0, w: PDFUnit.toFixedFloat(this.lineWidth), l:0};
//MQZ Aug.28.2013, adding color support, using color dictionary and default to black
let clrId = PDFUnit.findColorIndex(this.color);
if (clrId < 0) {
oneLine = _.extend({oc: this.color}, oneLine);
}
else if (clrId > 0 && clrId < (PDFUnit.colorCount() - 1)) {
oneLine = _.extend({clr: clrId}, oneLine);
}
//MQZ Aug.29 dashed line support
if (this.dashed) {
oneLine = _.extend({dsh: 1}, oneLine);
}
...findColorIndex = function (color) {
if (color.length === 4)
color += "000";
//MQZ. 07/29/2013: if color is not in dictionary, just return -1. The caller (pdffont, pdffill) will set the actual color
return kColors.indexOf(color);
}...
this.width = width;
this.height = height;
this.color = color;
};
// public (every instance will share the same method, but has no access to private fields defined in constructor)
cls.prototype.processFill = function (targetData) {
let clrId = PDFUnit.findColorIndex(this.color);
let oneFill = {x:PDFUnit.toFormX(this.x),
y:PDFUnit.toFormY(this.y),
w:PDFUnit.toFormX(this.width),
h:PDFUnit.toFormY(this.height),
clr: clrId};
...getColorByIndex = function (clrId) {
return kColors[clrId];
}n/a
pointToPixel = function (point) {// Point unit (1/72 an inch) to pixel units
return point * _pixelPerPoint;
}n/a
toFixedFloat = function (fNum) {
return parseFloat(fNum.toFixed(3));
}...
// adding TS to better describe text style [fontFaceId, fontSize, 1/0 for bold, 1/0 for italic];
let TS = [this.faceIdx, this.fontSize, this.bold?1:0, this.italic?1:0];
let clrId = PDFUnit.findColorIndex(color);
let oneText = {x: PDFUnit.toFormX(p.x) - 0.25,
y: PDFUnit.toFormY(p.y) - 0.75,
w: PDFUnit.toFixedFloat(maxWidth),
sw: this.spaceWidth, //font space width, use to merge adjacent text blocks
clr: clrId,
A: "left",
R: [{
T: this.flash_encode(text),
S: this.fontStyleId,
TS: TS
...toFormPoint = function (viewportX, viewportY) {
return [(viewportX / _pixelXPerGrid), (viewportY / _pixelYPerGrid)];
}n/a
toFormX = function (viewportX) {
return cls.toFixedFloat(viewportX / _pixelXPerGrid);
}...
else if (field.fieldType !== 'Ch') { //checkbox, radio button, and link button
rect[1] -= 3;
}
height = (height >= kMinHeight) ? height : kMinHeight;
return {
x: PDFUnit.toFormX(rect[0]),
y: PDFUnit.toFormY(rect[1]),
w: PDFUnit.toFormX(rect[2] - rect[0]),
h: PDFUnit.toFormY(height)
};
};
let _getFieldBaseData = function(field) {
...toFormY = function (viewportY) {
return cls.toFixedFloat(viewportY / _pixelYPerGrid);
}...
rect[1] -= 3;
}
height = (height >= kMinHeight) ? height : kMinHeight;
return {
x: PDFUnit.toFormX(rect[0]),
y: PDFUnit.toFormY(rect[1]),
w: PDFUnit.toFormX(rect[2] - rect[0]),
h: PDFUnit.toFormY(height)
};
};
let _getFieldBaseData = function(field) {
let attributeMask = 0;
...toPixelX = function (formX) {
return Math.round(formX * _pixelXPerGrid);
}n/a
toPixelY = function (formY) {
return Math.round(formY * _pixelYPerGrid);
}n/a
ptixmlinject = function () {
}n/a
getFields = function (pageNum) {
return ptiPageArray[pageNum];
}...
let errMsg = 'An error occurred while rendering the page ' + (this.id + 1) +
':\n' + error.message +
':\n' + error.stack;
errorCallBack(errMsg);
}
else {
if (this.ptiParser) {
let extraFields = this.ptiParser.getFields(parseInt(this.id) + 1);
_.each(extraFields, _.bind(_addField, this));
}
_.extend(this, ctx.canvas);
this.stats = this.pdfPage.stats;
nodeUtil.p2jinfo('page ' + (this.id + 1) + ' is rendered successfully.');
...parseXml = function (filePath, callback) {
fs.readFile(filePath, 'utf8', function (err,data) {
if (err) {
callback(err);
}
else {
xmlData = data;
var parser = new DOMParser();
var dom = parser.parseFromString(xmlData);
var root = dom.documentElement;
var xmlFields = root.getElementsByTagName("field");
var fields = [];
for(var i=0;i<xmlFields.length;i++){
var id = xmlFields[i].getAttribute('id');
var xPos = xmlFields[i].getAttribute('x');
var yPos = xmlFields[i].getAttribute('y');
var width = xmlFields[i].getAttribute('width');
var height = xmlFields[i].getAttribute('height');
var type = xmlFields[i].getAttribute('xsi:type');
var page = xmlFields[i].getAttribute('page');
var fontName = xmlFields[i].getAttribute('fontName');
var fontSize = xmlFields[i].getAttribute('fontSize');
var item = {};
var rectLeft = parseInt(xPos) - 21; //was 23.5
var rectTop = parseInt(yPos) - 20;//was 23
var rectRight = parseInt(rectLeft) + parseInt(width) - 4;
var rectBottom = parseInt(rectTop) + parseInt(height) - 4;
item.fieldType="Tx";
if (type == "Boolean") {
item.fieldType="Btn";
}
else if (type=="SSN" || type=="Phone" || type=="zip") {
item.TName = type.toLowerCase();
}
item.alternativeText = "";
item.fullName = id;
item.fontSize = fontSize;
item.subtype = "Widget";
item.rect = [rectLeft, rectTop, rectRight, rectBottom];;
fields.push(item);
ptiPageArray[parseInt(page)]=fields;
}
}
callback();
});
}...
if ((fieldInfoXMLPath.indexOf(_sufInfo) < 1) || (!fs.existsSync(fieldInfoXMLPath))) {
return;
}
nodeUtil.p2jinfo("About to load fieldInfo XML : " + fieldInfoXMLPath);
let PTIXmlParser = require('./ptixmlinject');
this.ptiParser = new PTIXmlParser();
this.ptiParser.parseXml(fieldInfoXMLPath, err => {
if (err) {
nodeUtil.p2jwarn("fieldInfo XML Error: " + JSON.stringify(err));
this.ptiParser = null;
}
else {
nodeUtil.p2jinfo("fieldInfo XML loaded.");
}
...