Better "text" testing.
This commit is contained in:
parent
56ccaea99b
commit
bd7f121c83
166
test/driver.js
166
test/driver.js
@ -22,80 +22,71 @@ var PDF_TO_CSS_UNITS = 96.0 / 72.0;
|
|||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
*/
|
*/
|
||||||
var NullTextLayerBuilder = (function NullTextLayerBuilderClosure() {
|
var rasterizeTextLayer = (function rasterizeTextLayerClosure() {
|
||||||
/**
|
var SVG_NS = 'http://www.w3.org/2000/svg';
|
||||||
* @constructs NullTextLayerBuilder
|
|
||||||
*/
|
|
||||||
function NullTextLayerBuilder() {}
|
|
||||||
|
|
||||||
NullTextLayerBuilder.prototype = {
|
var textLayerStylePromise = null;
|
||||||
beginLayout: function NullTextLayerBuilder_BeginLayout() {},
|
function getTextLayerStyle() {
|
||||||
endLayout: function NullTextLayerBuilder_EndLayout() {},
|
if (textLayerStylePromise) {
|
||||||
appendText: function NullTextLayerBuilder_AppendText() {}
|
return textLayerStylePromise;
|
||||||
|
}
|
||||||
|
textLayerStylePromise = new Promise(function (resolve) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', './text_layer_test.css');
|
||||||
|
xhr.onload = function () {
|
||||||
|
resolve(xhr.responseText);
|
||||||
};
|
};
|
||||||
|
xhr.send(null);
|
||||||
return NullTextLayerBuilder;
|
});
|
||||||
})();
|
return textLayerStylePromise;
|
||||||
|
|
||||||
/**
|
|
||||||
* @class
|
|
||||||
*/
|
|
||||||
var SimpleTextLayerBuilder = (function SimpleTextLayerBuilderClosure() {
|
|
||||||
/**
|
|
||||||
* @constructs SimpleTextLayerBuilder
|
|
||||||
*/
|
|
||||||
function SimpleTextLayerBuilder(ctx, viewport) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.viewport = viewport;
|
|
||||||
this.textCounter = 0;
|
|
||||||
|
|
||||||
// clear canvas
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = 'rgb(255,255,255)';
|
|
||||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleTextLayerBuilder.prototype = {
|
function rasterizeTextLayer(ctx, viewport, textContent) {
|
||||||
appendText: function SimpleTextLayerBuilder_AppendText(geom, styles) {
|
return new Promise(function (resolve) {
|
||||||
var style = styles[geom.fontName];
|
// Building SVG with size of the viewport.
|
||||||
var ctx = this.ctx, viewport = this.viewport;
|
var svg = document.createElementNS(SVG_NS, 'svg:svg');
|
||||||
var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
|
svg.setAttribute('width', viewport.width + 'px');
|
||||||
var angle = Math.atan2(tx[1], tx[0]);
|
svg.setAttribute('height', viewport.height + 'px');
|
||||||
var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
|
// items are transformed to have 1px font size
|
||||||
var fontAscent = (style.ascent ? style.ascent * fontHeight :
|
svg.setAttribute('font-size', 1);
|
||||||
(style.descent ? (1 + style.descent) * fontHeight : fontHeight));
|
|
||||||
|
|
||||||
ctx.save();
|
// Adding element to host our HTML (style + text layer div).
|
||||||
ctx.beginPath();
|
var foreignObject = document.createElementNS(SVG_NS, 'svg:foreignObject');
|
||||||
ctx.strokeStyle = 'red';
|
foreignObject.setAttribute('x', '0');
|
||||||
ctx.fillStyle = 'yellow';
|
foreignObject.setAttribute('y', '0');
|
||||||
ctx.translate(tx[4] + (fontAscent * Math.sin(angle)),
|
foreignObject.setAttribute('width', viewport.width + 'px');
|
||||||
tx[5] - (fontAscent * Math.cos(angle)));
|
foreignObject.setAttribute('height', viewport.height + 'px');
|
||||||
ctx.rotate(angle);
|
var style = document.createElement('style');
|
||||||
ctx.rect(0, 0, geom.width * viewport.scale, geom.height * viewport.scale);
|
var stylePromise = getTextLayerStyle();
|
||||||
ctx.stroke();
|
foreignObject.appendChild(style);
|
||||||
ctx.fill();
|
var div = document.createElement('div');
|
||||||
ctx.restore();
|
div.className = 'textLayer';
|
||||||
ctx.font = fontHeight + 'px ' + style.fontFamily;
|
foreignObject.appendChild(div);
|
||||||
ctx.fillStyle = 'black';
|
|
||||||
ctx.fillText(geom.str, tx[4], tx[5]);
|
|
||||||
|
|
||||||
this.textCounter++;
|
// Rendering text layer as HTML.
|
||||||
},
|
var task = PDFJS.renderTextLayer({
|
||||||
|
textContent: textContent,
|
||||||
|
container: div,
|
||||||
|
viewport: viewport
|
||||||
|
});
|
||||||
|
Promise.all([stylePromise, task.promise]).then(function (results) {
|
||||||
|
style.textContent = results[0];
|
||||||
|
svg.appendChild(foreignObject);
|
||||||
|
|
||||||
setTextContent:
|
// We need to have UTF-8 encoded XML.
|
||||||
function SimpleTextLayerBuilder_SetTextContent(textContent) {
|
var svg_xml = unescape(encodeURIComponent(
|
||||||
this.ctx.save();
|
(new XMLSerializer()).serializeToString(svg)));
|
||||||
var textItems = textContent.items;
|
var img = new Image();
|
||||||
for (var i = 0, ii = textItems.length; i < ii; i++) {
|
img.src = 'data:image/svg+xml;base64,' + btoa(svg_xml);
|
||||||
this.appendText(textItems[i], textContent.styles);
|
img.onload = function () {
|
||||||
}
|
ctx.drawImage(img, 0, 0);
|
||||||
this.ctx.restore();
|
resolve();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return SimpleTextLayerBuilder;
|
return rasterizeTextLayer;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -328,35 +319,44 @@ var Driver = (function DriverClosure() {
|
|||||||
self.canvas.height = viewport.height;
|
self.canvas.height = viewport.height;
|
||||||
self._clearCanvas();
|
self._clearCanvas();
|
||||||
|
|
||||||
var drawContext, textLayerBuilder;
|
var textLayerCanvas;
|
||||||
var resolveInitPromise;
|
var initPromise;
|
||||||
var initPromise = new Promise(function (resolve) {
|
|
||||||
resolveInitPromise = resolve;
|
|
||||||
});
|
|
||||||
if (task.type === 'text') {
|
if (task.type === 'text') {
|
||||||
// Using a dummy canvas for PDF context drawing operations
|
// Using a dummy canvas for PDF context drawing operations
|
||||||
if (!self.dummyCanvas) {
|
textLayerCanvas = self.textLayerCanvas;
|
||||||
self.dummyCanvas = document.createElement('canvas');
|
if (!textLayerCanvas) {
|
||||||
|
textLayerCanvas = document.createElement('canvas');
|
||||||
|
self.textLayerCanvas = textLayerCanvas;
|
||||||
}
|
}
|
||||||
drawContext = self.dummyCanvas.getContext('2d');
|
textLayerCanvas.width = viewport.width;
|
||||||
|
textLayerCanvas.height = viewport.height;
|
||||||
|
var textLayerContext = textLayerCanvas.getContext('2d');
|
||||||
|
textLayerContext.clearRect(0, 0,
|
||||||
|
textLayerCanvas.width, textLayerCanvas.height);
|
||||||
// The text builder will draw its content on the test canvas
|
// The text builder will draw its content on the test canvas
|
||||||
textLayerBuilder = new SimpleTextLayerBuilder(ctx, viewport);
|
initPromise = page.getTextContent().then(function(textContent) {
|
||||||
|
return rasterizeTextLayer(textLayerContext, viewport,
|
||||||
page.getTextContent().then(function(textContent) {
|
textContent);
|
||||||
textLayerBuilder.setTextContent(textContent);
|
|
||||||
resolveInitPromise();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
drawContext = ctx;
|
textLayerCanvas = null;
|
||||||
textLayerBuilder = new NullTextLayerBuilder();
|
initPromise = Promise.resolve();
|
||||||
resolveInitPromise();
|
|
||||||
}
|
}
|
||||||
var renderContext = {
|
var renderContext = {
|
||||||
canvasContext: drawContext,
|
canvasContext: ctx,
|
||||||
viewport: viewport
|
viewport: viewport
|
||||||
};
|
};
|
||||||
var completeRender = (function(error) {
|
var completeRender = (function(error) {
|
||||||
page.destroy();
|
// if text layer is present, compose it on top of the page
|
||||||
|
if (textLayerCanvas) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalCompositeOperation = 'screen';
|
||||||
|
ctx.fillStyle = 'rgb(128, 255, 128)'; // making it green
|
||||||
|
ctx.fillRect(0, 0, viewport.width, viewport.height);
|
||||||
|
ctx.restore();
|
||||||
|
ctx.drawImage(textLayerCanvas, 0, 0);
|
||||||
|
}
|
||||||
|
page.cleanup();
|
||||||
task.stats = page.stats;
|
task.stats = page.stats;
|
||||||
page.stats = new StatTimer();
|
page.stats = new StatTimer();
|
||||||
self._snapshot(task, error);
|
self._snapshot(task, error);
|
||||||
|
@ -323,12 +323,6 @@
|
|||||||
"lastPage": 1,
|
"lastPage": 1,
|
||||||
"about": "The same file as issue2337."
|
"about": "The same file as issue2337."
|
||||||
},
|
},
|
||||||
{ "id": "thuluthfont-text",
|
|
||||||
"file": "pdfs/ThuluthFeatures.pdf",
|
|
||||||
"md5": "b7e18bf7a3d6a9c82aefa12d721072fc",
|
|
||||||
"rounds": 1,
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{ "id": "freeculture",
|
{ "id": "freeculture",
|
||||||
"file": "pdfs/freeculture.pdf",
|
"file": "pdfs/freeculture.pdf",
|
||||||
"md5": "dcdf3a8268e6a18938a42d5149efcfca",
|
"md5": "dcdf3a8268e6a18938a42d5149efcfca",
|
||||||
|
38
test/text_layer_test.css
Normal file
38
test/text_layer_test.css
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* Copyright 2015 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Used in 'text' tests */
|
||||||
|
|
||||||
|
.textLayer {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.textLayer > div {
|
||||||
|
position: absolute;
|
||||||
|
white-space: pre;
|
||||||
|
-webkit-transform-origin: 0% 0%;
|
||||||
|
-moz-transform-origin: 0% 0%;
|
||||||
|
-o-transform-origin: 0% 0%;
|
||||||
|
-ms-transform-origin: 0% 0%;
|
||||||
|
transform-origin: 0% 0%;
|
||||||
|
border: solid 1px rgba(255, 0, 0, 0.5);
|
||||||
|
background-color: rgba(255, 255, 32, 0.1);
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
@ -1515,7 +1515,27 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#viewer.textLayer-visible .textLayer > div,
|
#viewer.textLayer-visible .textLayer {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer.textLayer-visible .canvasWrapper {
|
||||||
|
background-color: rgb(128,255,128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer.textLayer-visible .canvasWrapper canvas {
|
||||||
|
mix-blend-mode: screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer.textLayer-visible .textLayer > div {
|
||||||
|
background-color: rgba(255, 255, 0, 0.1);
|
||||||
|
color: black;
|
||||||
|
border: solid 1px rgba(255, 0, 0, 0.5);
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
#viewer.textLayer-hover .textLayer > div:hover {
|
#viewer.textLayer-hover .textLayer > div:hover {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
color: black;
|
color: black;
|
||||||
|
Loading…
Reference in New Issue
Block a user