Merge pull request #14545 from brendandahl/output-scale
Generate test images at different output scales.
This commit is contained in:
commit
889b761f22
@ -61,7 +61,7 @@ function loadStyles(styles) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function writeSVG(svgElement, ctx) {
|
||||
function writeSVG(svgElement, ctx, outputScale) {
|
||||
// We need to have UTF-8 encoded XML.
|
||||
const svg_xml = unescape(
|
||||
encodeURIComponent(new XMLSerializer().serializeToString(svgElement))
|
||||
@ -104,7 +104,7 @@ function inlineImages(images) {
|
||||
return Promise.all(imagePromises);
|
||||
}
|
||||
|
||||
async function convertCanvasesToImages(annotationCanvasMap) {
|
||||
async function convertCanvasesToImages(annotationCanvasMap, outputScale) {
|
||||
const results = new Map();
|
||||
const promises = [];
|
||||
for (const [key, canvas] of annotationCanvasMap) {
|
||||
@ -112,7 +112,10 @@ async function convertCanvasesToImages(annotationCanvasMap) {
|
||||
new Promise(resolve => {
|
||||
canvas.toBlob(blob => {
|
||||
const image = document.createElement("img");
|
||||
image.onload = resolve;
|
||||
image.onload = function () {
|
||||
image.style.width = Math.floor(image.width / outputScale) + "px";
|
||||
resolve();
|
||||
};
|
||||
results.set(key, image);
|
||||
image.src = URL.createObjectURL(blob);
|
||||
});
|
||||
@ -200,6 +203,7 @@ class Rasterize {
|
||||
static async annotationLayer(
|
||||
ctx,
|
||||
viewport,
|
||||
outputScale,
|
||||
annotations,
|
||||
annotationCanvasMap,
|
||||
page,
|
||||
@ -215,7 +219,8 @@ class Rasterize {
|
||||
|
||||
const annotationViewport = viewport.clone({ dontFlip: true });
|
||||
const annotationImageMap = await convertCanvasesToImages(
|
||||
annotationCanvasMap
|
||||
annotationCanvasMap,
|
||||
outputScale
|
||||
);
|
||||
|
||||
// Rendering annotation layer as HTML.
|
||||
@ -608,13 +613,38 @@ class Driver {
|
||||
ctx = this.canvas.getContext("2d", { alpha: false });
|
||||
task.pdfDoc.getPage(task.pageNum).then(
|
||||
page => {
|
||||
const viewport = page.getViewport({
|
||||
// Default to creating the test images at the devices pixel ratio,
|
||||
// unless the test explicitly specifies an output scale.
|
||||
const outputScale = task.outputScale || window.devicePixelRatio;
|
||||
let viewport = page.getViewport({
|
||||
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
||||
});
|
||||
this.canvas.width = viewport.width;
|
||||
this.canvas.height = viewport.height;
|
||||
// Restrict the test from creating a canvas that is too big.
|
||||
const MAX_CANVAS_PIXEL_DIMENSION = 4096;
|
||||
const largestDimension = Math.max(viewport.width, viewport.height);
|
||||
if (
|
||||
Math.floor(largestDimension * outputScale) >
|
||||
MAX_CANVAS_PIXEL_DIMENSION
|
||||
) {
|
||||
const rescale = MAX_CANVAS_PIXEL_DIMENSION / largestDimension;
|
||||
viewport = viewport.clone({
|
||||
scale: PixelsPerInch.PDF_TO_CSS_UNITS * rescale,
|
||||
});
|
||||
}
|
||||
const pixelWidth = Math.floor(viewport.width * outputScale);
|
||||
const pixelHeight = Math.floor(viewport.height * outputScale);
|
||||
task.viewportWidth = Math.floor(viewport.width);
|
||||
task.viewportHeight = Math.floor(viewport.height);
|
||||
task.outputScale = outputScale;
|
||||
this.canvas.width = pixelWidth;
|
||||
this.canvas.height = pixelHeight;
|
||||
this.canvas.style.width = Math.floor(viewport.width) + "px";
|
||||
this.canvas.style.height = Math.floor(viewport.height) + "px";
|
||||
this._clearCanvas();
|
||||
|
||||
const transform =
|
||||
outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
|
||||
|
||||
// Initialize various `eq` test subtypes, see comment below.
|
||||
let renderAnnotations = false,
|
||||
renderForms = false,
|
||||
@ -639,8 +669,8 @@ class Driver {
|
||||
textLayerCanvas = document.createElement("canvas");
|
||||
this.textLayerCanvas = textLayerCanvas;
|
||||
}
|
||||
textLayerCanvas.width = viewport.width;
|
||||
textLayerCanvas.height = viewport.height;
|
||||
textLayerCanvas.width = pixelWidth;
|
||||
textLayerCanvas.height = pixelHeight;
|
||||
const textLayerContext = textLayerCanvas.getContext("2d");
|
||||
textLayerContext.clearRect(
|
||||
0,
|
||||
@ -648,6 +678,7 @@ class Driver {
|
||||
textLayerCanvas.width,
|
||||
textLayerCanvas.height
|
||||
);
|
||||
textLayerContext.scale(outputScale, outputScale);
|
||||
const enhanceText = !!task.enhance;
|
||||
// The text builder will draw its content on the test canvas
|
||||
initPromise = page
|
||||
@ -679,8 +710,8 @@ class Driver {
|
||||
annotationLayerCanvas = document.createElement("canvas");
|
||||
this.annotationLayerCanvas = annotationLayerCanvas;
|
||||
}
|
||||
annotationLayerCanvas.width = viewport.width;
|
||||
annotationLayerCanvas.height = viewport.height;
|
||||
annotationLayerCanvas.width = pixelWidth;
|
||||
annotationLayerCanvas.height = pixelHeight;
|
||||
annotationLayerContext = annotationLayerCanvas.getContext("2d");
|
||||
annotationLayerContext.clearRect(
|
||||
0,
|
||||
@ -688,6 +719,7 @@ class Driver {
|
||||
annotationLayerCanvas.width,
|
||||
annotationLayerCanvas.height
|
||||
);
|
||||
annotationLayerContext.scale(outputScale, outputScale);
|
||||
|
||||
if (!renderXfa) {
|
||||
// The annotation builder will draw its content
|
||||
@ -716,6 +748,7 @@ class Driver {
|
||||
viewport,
|
||||
optionalContentConfigPromise: task.optionalContentConfigPromise,
|
||||
annotationCanvasMap,
|
||||
transform,
|
||||
};
|
||||
if (renderForms) {
|
||||
renderContext.annotationMode = AnnotationMode.ENABLE_FORMS;
|
||||
@ -732,7 +765,7 @@ class Driver {
|
||||
ctx.save();
|
||||
ctx.globalCompositeOperation = "screen";
|
||||
ctx.fillStyle = "rgb(128, 255, 128)"; // making it green
|
||||
ctx.fillRect(0, 0, viewport.width, viewport.height);
|
||||
ctx.fillRect(0, 0, pixelWidth, pixelHeight);
|
||||
ctx.restore();
|
||||
ctx.drawImage(textLayerCanvas, 0, 0);
|
||||
}
|
||||
@ -762,6 +795,7 @@ class Driver {
|
||||
Rasterize.annotationLayer(
|
||||
annotationLayerContext,
|
||||
viewport,
|
||||
outputScale,
|
||||
data,
|
||||
annotationCanvasMap,
|
||||
page,
|
||||
@ -871,6 +905,9 @@ class Driver {
|
||||
page: task.pageNum,
|
||||
snapshot,
|
||||
stats: task.stats.times,
|
||||
viewportWidth: task.viewportWidth,
|
||||
viewportHeight: task.viewportHeight,
|
||||
outputScale: task.outputScale,
|
||||
});
|
||||
this._send("/submit_task_results", result, callback);
|
||||
}
|
||||
|
@ -165,8 +165,8 @@ Original author: L. David Baron <dbaron@dbaron.org>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="magnify">
|
||||
<image x="0" y="0" width="100%" height="100%" id="image1" />
|
||||
<image x="0" y="0" width="100%" height="100%" id="image2" />
|
||||
<image x="0" y="0" id="image1" />
|
||||
<image x="0" y="0" id="image2" />
|
||||
</g>
|
||||
<rect id="diffrect" filter="url(#showDifferences)" pointer-events="none" x="0" y="0" width="100%" height="100%" />
|
||||
</svg>
|
||||
|
@ -228,10 +228,15 @@ window.onload = function () {
|
||||
});
|
||||
continue;
|
||||
}
|
||||
match = line.match(/^ {2}IMAGE[^:]*: (.*)$/);
|
||||
match = line.match(/^ {2}IMAGE[^:]*\((\d+)x(\d+)x(\d+)\): (.*)$/);
|
||||
if (match) {
|
||||
const item = gTestItems[gTestItems.length - 1];
|
||||
item.images.push(match[1]);
|
||||
item.images.push({
|
||||
width: parseFloat(match[1]),
|
||||
height: parseFloat(match[2]),
|
||||
outputScale: parseFloat(match[3]),
|
||||
file: match[4],
|
||||
});
|
||||
}
|
||||
}
|
||||
buildViewer();
|
||||
@ -335,16 +340,31 @@ window.onload = function () {
|
||||
const cell = ID("images");
|
||||
|
||||
ID("image1").style.display = "";
|
||||
const scale = item.images[0].outputScale / window.devicePixelRatio;
|
||||
ID("image1").setAttribute("width", item.images[0].width * scale);
|
||||
ID("image1").setAttribute("height", item.images[0].height * scale);
|
||||
|
||||
ID("svg").setAttribute("width", item.images[0].width * scale);
|
||||
ID("svg").setAttribute("height", item.images[0].height * scale);
|
||||
|
||||
ID("image2").style.display = "none";
|
||||
if (item.images[1]) {
|
||||
ID("image2").setAttribute("width", item.images[1].width * scale);
|
||||
ID("image2").setAttribute("height", item.images[1].height * scale);
|
||||
}
|
||||
ID("diffrect").style.display = "none";
|
||||
ID("imgcontrols").reset();
|
||||
|
||||
ID("image1").setAttributeNS(XLINK_NS, "xlink:href", gPath + item.images[0]);
|
||||
ID("image1").setAttributeNS(
|
||||
XLINK_NS,
|
||||
"xlink:href",
|
||||
gPath + item.images[0].file
|
||||
);
|
||||
// Making the href be #image1 doesn't seem to work
|
||||
ID("feimage1").setAttributeNS(
|
||||
XLINK_NS,
|
||||
"xlink:href",
|
||||
gPath + item.images[0]
|
||||
gPath + item.images[0].file
|
||||
);
|
||||
if (item.images.length === 1) {
|
||||
ID("imgcontrols").style.display = "none";
|
||||
@ -353,30 +373,24 @@ window.onload = function () {
|
||||
ID("image2").setAttributeNS(
|
||||
XLINK_NS,
|
||||
"xlink:href",
|
||||
gPath + item.images[1]
|
||||
gPath + item.images[1].file
|
||||
);
|
||||
// Making the href be #image2 doesn't seem to work
|
||||
ID("feimage2").setAttributeNS(
|
||||
XLINK_NS,
|
||||
"xlink:href",
|
||||
gPath + item.images[1]
|
||||
gPath + item.images[1].file
|
||||
);
|
||||
}
|
||||
cell.style.display = "";
|
||||
getImageData(item.images[0], function (data) {
|
||||
getImageData(item.images[0].file, function (data) {
|
||||
gImage1Data = data;
|
||||
syncSVGSize(gImage1Data);
|
||||
});
|
||||
getImageData(item.images[1], function (data) {
|
||||
getImageData(item.images[1].file, function (data) {
|
||||
gImage2Data = data;
|
||||
});
|
||||
}
|
||||
|
||||
function syncSVGSize(imageData) {
|
||||
ID("svg").setAttribute("width", imageData.width);
|
||||
ID("svg").setAttribute("height", imageData.height);
|
||||
}
|
||||
|
||||
function showImage(i) {
|
||||
if (i === 1) {
|
||||
ID("image1").style.display = "";
|
||||
@ -414,7 +428,7 @@ window.onload = function () {
|
||||
}
|
||||
|
||||
function canvasPixelAsHex(data, x, y) {
|
||||
const offset = (y * data.width + x) * 4;
|
||||
const offset = (y * data.width + x) * 4 * window.devicePixelRatio;
|
||||
const r = data.data[offset];
|
||||
const g = data.data[offset + 1];
|
||||
const b = data.data[offset + 2];
|
||||
|
14
test/test.js
14
test/test.js
@ -451,7 +451,8 @@ function checkEq(task, results, browser, masterMode) {
|
||||
if (!pageResults[page]) {
|
||||
continue;
|
||||
}
|
||||
var testSnapshot = pageResults[page].snapshot;
|
||||
const pageResult = pageResults[page];
|
||||
let testSnapshot = pageResult.snapshot;
|
||||
if (testSnapshot && testSnapshot.startsWith("data:image/png;base64,")) {
|
||||
testSnapshot = Buffer.from(testSnapshot.substring(22), "base64");
|
||||
} else {
|
||||
@ -492,8 +493,8 @@ function checkEq(task, results, browser, masterMode) {
|
||||
refSnapshot
|
||||
);
|
||||
|
||||
// NB: this follows the format of Mozilla reftest output so that
|
||||
// we can reuse its reftest-analyzer script
|
||||
// This no longer follows the format of Mozilla reftest output.
|
||||
const viewportString = `(${pageResult.viewportWidth}x${pageResult.viewportHeight}x${pageResult.outputScale})`;
|
||||
fs.appendFileSync(
|
||||
eqLog,
|
||||
"REFTEST TEST-UNEXPECTED-FAIL | " +
|
||||
@ -503,10 +504,10 @@ function checkEq(task, results, browser, masterMode) {
|
||||
"-page" +
|
||||
(page + 1) +
|
||||
" | image comparison (==)\n" +
|
||||
"REFTEST IMAGE 1 (TEST): " +
|
||||
`REFTEST IMAGE 1 (TEST)${viewportString}: ` +
|
||||
path.join(testSnapshotDir, page + 1 + ".png") +
|
||||
"\n" +
|
||||
"REFTEST IMAGE 2 (REFERENCE): " +
|
||||
`REFTEST IMAGE 2 (REFERENCE)${viewportString}: ` +
|
||||
path.join(testSnapshotDir, page + 1 + "_ref.png") +
|
||||
"\n"
|
||||
);
|
||||
@ -735,6 +736,9 @@ function refTestPostHandler(req, res) {
|
||||
taskResults[round][page] = {
|
||||
failure,
|
||||
snapshot,
|
||||
viewportWidth: data.viewportWidth,
|
||||
viewportHeight: data.viewportHeight,
|
||||
outputScale: data.outputScale,
|
||||
};
|
||||
if (stats) {
|
||||
stats.push({
|
||||
|
@ -6142,6 +6142,18 @@
|
||||
"forms": true,
|
||||
"lastPage": 1
|
||||
},
|
||||
{
|
||||
"id": "issue12716-hidpi",
|
||||
"file": "pdfs/issue12716.pdf",
|
||||
"md5": "9bdc9c552bcfccd629f5f97385e79ca5",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq",
|
||||
"forms": true,
|
||||
"lastPage": 1,
|
||||
"outputScale": 2,
|
||||
"about": "This tests draws to another canvas for the button, so it's a good test to ensure output scale is working."
|
||||
},
|
||||
{ "id": "xfa_issue13500",
|
||||
"file": "pdfs/xfa_issue13500.pdf",
|
||||
"md5": "b81274a19f5a95c1466db3648f1be491",
|
||||
|
Loading…
x
Reference in New Issue
Block a user