From 3808d02a530b010da3b0a4f596c5a9855b483b61 Mon Sep 17 00:00:00 2001 From: Srishti Date: Sat, 14 Sep 2013 23:28:58 +0530 Subject: [PATCH] implementing transform matrix to getTextContent --- src/core/evaluator.js | 129 ++++++++++++++++++++++++++++++++++++++ web/text_layer_builder.js | 7 +++ web/viewer.js | 1 + 3 files changed, 137 insertions(+) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index c099fe9d4..dafdddb50 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -725,14 +725,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var bidiTexts; var SPACE_FACTOR = 0.35; var MULTI_SPACE_FACTOR = 1.5; + var textState; if (!state) { + textState = new TextState(); bidiTexts = []; state = { + textState: textState, bidiTexts: bidiTexts }; } else { bidiTexts = state.bidiTexts; + textState = state.textState; } var self = this; @@ -752,6 +756,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var chunk = ''; var font = null; + var charSpace = 0, wordSpace = 0; while (!isEOF(obj = parser.getObj())) { if (isCmd(obj)) { var cmd = obj.cmd; @@ -759,6 +764,49 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // TODO: Add support for SAVE/RESTORE and XFORM here. case 'Tf': font = handleSetFont(args[0].name).translated; + textState.fontSize = args[1]; + break; + case 'Ts': + textState.textRise = args[0]; + break; + case 'Tz': + textState.textHScale = args[0] / 100; + break; + case 'TL': + textState.leading = args[0]; + break; + case 'Td': + textState.translateTextMatrix(args[0], args[1]); + break; + case 'TD': + textState.leading = -args[1]; + textState.translateTextMatrix(args[0], args[1]); + break; + case 'T*': + textState.translateTextMatrix(0, -textState.leading); + break; + case 'Tm': + textState.setTextMatrix(args[0], args[1], + args[2], args[3], args[4], args[5]); + break; + case 'Tc': + charSpace = args[0]; + break; + case 'Tw': + wordSpace = args[0]; + break; + case 'q': + textState.push(); + break; + case 'Q': + textState.pop(); + break; + case 'BT': + textState.initialiseTextObj(); + break; + case 'cm': + textState.transformCTM(args[0], args[1], args[2], + args[3], args[4], args[5]); break; case 'TJ': var items = args[0]; @@ -844,6 +892,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (chunk !== '') { var bidiText = PDFJS.bidi(chunk, -1, font.vertical); + var renderParams = textState.calcRenderParams(); + bidiText.x = renderParams.renderMatrix[4] - (textState.fontSize * + renderParams.vScale * Math.sin(renderParams.angle)); + bidiText.y = renderParams.renderMatrix[5] + (textState.fontSize * + renderParams.vScale * Math.cos(renderParams.angle)); + if (bidiText.dir == 'ttb') { + bidiText.x += renderParams.vScale / 2; + bidiText.y -= renderParams.vScale; + } bidiTexts.push(bidiText); chunk = ''; @@ -1552,6 +1609,78 @@ var OperatorList = (function OperatorListClosure() { return OperatorList; })(); +var TextState = (function TextStateClosure() { + function TextState() { + this.fontSize = 0; + this.ctm = [1, 0, 0, 1, 0, 0]; + this.textMatrix = [1, 0, 0, 1, 0, 0]; + this.stateStack = []; + //textState variables + this.leading = 0; + this.textHScale = 1; + this.textRise = 0; + } + TextState.prototype = { + push: function TextState_push() { + this.stateStack.push(this.ctm.slice()); + }, + pop: function TextStae_pop() { + this.ctm = this.stateStack.pop(); + }, + initialiseTextObj: function TextState_initialiseTextObj() { + var m = this.textMatrix; + m[0] = 1, m[1] = 0, m[2] = 0, m[3] = 1, m[4] = 0, m[5] = 0; + }, + setTextMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) { + var m = this.textMatrix; + m[0] = a, m[1] = b, m[2] = c, m[3] = d, m[4] = e, m[5] = f; + }, + transformCTM: function TextState_transformCTM(a, b, c, d, e, f) { + var m = this.ctm; + var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4], m5 = m[5]; + m[0] = m0 * a + m2 * b; + m[1] = m1 * a + m3 * b; + m[2] = m0 * c + m2 * d; + m[3] = m1 * c + m3 * d; + m[4] = m0 * e + m2 * f + m4; + m[5] = m1 * e + m3 * f + m5; + }, + translateTextMatrix: function TextState_translateTextMatrix(x, y) { + var m = this.textMatrix; + m[4] = m[0] * x + m[2] * y + m[4]; + m[5] = m[1] * x + m[3] * y + m[5]; + }, + calcRenderParams: function TextState_calcRenderingParams() { + var tm = this.textMatrix; + var cm = this.ctm; + var a = this.fontSize; + var b = a * this.textHScale; + var c = this.textRise; + var vScale = Math.sqrt((tm[2] * tm[2]) + (tm[3] * tm[3])); + var angle = Math.atan2(tm[1], tm[0]); + var m0 = tm[0] * cm[0] + tm[1] * cm[2]; + var m1 = tm[0] * cm[1] + tm[1] * cm[3]; + var m2 = tm[2] * cm[0] + tm[3] * cm[2]; + var m3 = tm[2] * cm[1] + tm[3] * cm[3]; + var m4 = tm[4] * cm[0] + tm[5] * cm[2] + cm[4]; + var m5 = tm[4] * cm[1] + tm[5] * cm[3] + cm[5]; + var renderMatrix = [ + b * m0, + b * m1, + a * m2, + a * m3, + c * m2 + m4, + c * m3 + m5 + ]; + return { + renderMatrix: renderMatrix, + vScale: vScale, + angle: angle + }; + }, + }; + return TextState; +})(); var EvalState = (function EvalStateClosure() { function EvalState() { diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index 41cf1be74..b1626637a 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -36,6 +36,7 @@ var TextLayerBuilder = function textLayerBuilder(options) { this.pageIdx = options.pageIndex; this.matches = []; this.lastScrollSource = options.lastScrollSource; + this.viewport = options.viewport; this.isViewerInPresentationMode = options.isViewerInPresentationMode; if(typeof PDFFindController === 'undefined') { @@ -158,6 +159,12 @@ var TextLayerBuilder = function textLayerBuilder(options) { } textDiv.textContent = bidiText.str; + // TODO refactor text layer to use text content position + /** + * var arr = this.viewport.convertToViewportPoint(bidiText.x, bidiText.y); + * textDiv.style.left = arr[0] + 'px'; + * textDiv.style.top = arr[1] + 'px'; + */ // bidiText.dir may be 'ttb' for vertical texts. textDiv.dir = bidiText.dir; } diff --git a/web/viewer.js b/web/viewer.js index 0d9e6601f..cd47b3aa7 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1912,6 +1912,7 @@ var PageView = function pageView(container, id, scale, textLayerDiv: textLayerDiv, pageIndex: this.id - 1, lastScrollSource: PDFView, + viewport: this.viewport, isViewerInPresentationMode: PDFView.isPresentationMode }) : null;