Skip to content

Commit

Permalink
Generate test images at different output scales.
Browse files Browse the repository at this point in the history
This will default to generating test images at the device pixel
ratio of the machine the tests are created on unless the
test explicitly defines and output scale using the
`outputScale` setting. This makes the test look visually
like they would on the machine they are running on. It
also allows us to test different output scales.
  • Loading branch information
brendandahl committed Feb 24, 2022
1 parent 1d1e50e commit f5c3abb
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 34 deletions.
61 changes: 49 additions & 12 deletions test/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,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))
Expand Down Expand Up @@ -102,15 +102,18 @@ 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) {
promises.push(
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);
});
Expand Down Expand Up @@ -198,6 +201,7 @@ class Rasterize {
static async annotationLayer(
ctx,
viewport,
outputScale,
annotations,
annotationCanvasMap,
page,
Expand All @@ -213,7 +217,8 @@ class Rasterize {

const annotationViewport = viewport.clone({ dontFlip: true });
const annotationImageMap = await convertCanvasesToImages(
annotationCanvasMap
annotationCanvasMap,
outputScale
);

// Rendering annotation layer as HTML.
Expand Down Expand Up @@ -600,13 +605,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,
Expand All @@ -631,15 +661,16 @@ 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,
0,
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
Expand Down Expand Up @@ -672,15 +703,16 @@ 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,
0,
annotationLayerCanvas.width,
annotationLayerCanvas.height
);
annotationLayerContext.scale(outputScale, outputScale);

if (!renderXfa) {
// The annotation builder will draw its content
Expand Down Expand Up @@ -709,6 +741,7 @@ class Driver {
viewport,
optionalContentConfigPromise: task.optionalContentConfigPromise,
annotationCanvasMap,
transform,
};
if (renderForms) {
renderContext.annotationMode = AnnotationMode.ENABLE_FORMS;
Expand All @@ -725,7 +758,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);
}
Expand Down Expand Up @@ -755,6 +788,7 @@ class Driver {
Rasterize.annotationLayer(
annotationLayerContext,
viewport,
outputScale,
data,
annotationCanvasMap,
page,
Expand Down Expand Up @@ -864,6 +898,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);
}
Expand Down
4 changes: 2 additions & 2 deletions test/resources/reftest-analyzer.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ <h1>Reftest analyzer</h1>
</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>
Expand Down
44 changes: 29 additions & 15 deletions test/resources/reftest-analyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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";
Expand All @@ -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 = "";
Expand Down Expand Up @@ -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];
Expand Down
14 changes: 9 additions & 5 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 | " +
Expand All @@ -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"
);
Expand Down Expand Up @@ -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({
Expand Down
12 changes: 12 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6115,6 +6115,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",
Expand Down

0 comments on commit f5c3abb

Please sign in to comment.