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