From ca578dce6792435b7bc95f5c1a8a8cfa1b47c183 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 8 Sep 2021 17:31:10 -0700 Subject: [PATCH] Enable/disable image smoothing based on image interpolate value. (bug 1722191) While some of the output looks worse to my eye, this behavior more closely matches what I see when I open the PDFs in Adobe acrobat. Fixes: #4706, #9713, #8245, #1344 --- src/core/evaluator.js | 2 ++ src/core/image.js | 6 ++++-- src/display/canvas.js | 31 ++++++++++++++++++++++++++++++- src/display/display_utils.js | 5 +++++ src/pdf.js | 4 ++++ test/driver.js | 4 +++- test/pdfs/.gitignore | 2 ++ test/pdfs/issue4706.pdf | Bin 0 -> 10527 bytes test/pdfs/issue9713.pdf | Bin 0 -> 49425 bytes test/test_manifest.json | 12 ++++++++++++ web/firefox_print_service.js | 9 +++++++-- web/pdf_print_service.js | 4 ++-- web/ui_utils.js | 4 +++- 13 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 test/pdfs/issue4706.pdf create mode 100644 test/pdfs/issue9713.pdf diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 3f378222d7fe1..48a1700168ed4 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -600,6 +600,7 @@ class PartialEvaluator { } const imageMask = dict.get("ImageMask", "IM") || false; + const interpolate = dict.get("Interpolate", "I"); let imgData, args; if (imageMask) { // This depends on a tmpCanvas being filled with the @@ -623,6 +624,7 @@ class PartialEvaluator { height, imageIsFromDecodeStream: image instanceof DecodeStream, inverseDecode: !!decode && decode[0] > 0, + interpolate, }); imgData.cached = !!cacheKey; args = [imgData]; diff --git a/src/core/image.js b/src/core/image.js index 830f7a39f3c04..e1b55057af450 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -139,7 +139,7 @@ class PDFImage { this.width = width; this.height = height; - this.interpolate = dict.get("Interpolate", "I") || false; + this.interpolate = dict.get("Interpolate", "I"); this.imageMask = dict.get("ImageMask", "IM") || false; this.matte = dict.get("Matte") || false; @@ -294,6 +294,7 @@ class PDFImage { height, imageIsFromDecodeStream, inverseDecode, + interpolate, }) { if ( typeof PDFJSDev === "undefined" || @@ -339,7 +340,7 @@ class PDFImage { } } - return { data, width, height }; + return { data, width, height, interpolate }; } get drawWidth() { @@ -593,6 +594,7 @@ class PDFImage { const imgData = { width: drawWidth, height: drawHeight, + interpolate: this.interpolate, kind: 0, data: null, // Other fields are filled in below. diff --git a/src/display/canvas.js b/src/display/canvas.js index 2938e0b998104..a165647c39e3e 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { CSS_PIXELS_PER_INCH, PDF_PIXELS_PER_INCH } from "./display_utils.js"; import { FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, @@ -871,6 +871,27 @@ function composeSMask(ctx, smask, layerCtx) { ctx.drawImage(mask, 0, 0); } +function getImageSmoothingEnabled(transform, interpolate) { + const scale = Util.singularValueDecompose2dScale(transform); + // Round to a 32bit float so that `<=` check below will pass for numbers that + // are very close, but not exactly the same 64bit floats. + scale[0] = Math.fround(scale[0]); + scale[1] = Math.fround(scale[1]); + const actualScale = Math.fround( + ((globalThis.devicePixelRatio || 1) * CSS_PIXELS_PER_INCH) / + PDF_PIXELS_PER_INCH + ); + if (interpolate !== undefined) { + // If the value is explicitly set use it. + return interpolate; + } else if (scale[0] <= actualScale || scale[1] <= actualScale) { + // Smooth when downscaling. + return true; + } + // Don't smooth when upscaling. + return false; +} + const LINE_CAP_STYLES = ["butt", "round", "square"]; const LINE_JOIN_STYLES = ["miter", "round", "bevel"]; const NORMAL_CLIP = {}; @@ -1183,6 +1204,10 @@ class CanvasGraphics { maskCanvas.canvas, fillCtx.mozCurrentTransformInverse ); + fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled( + fillCtx.mozCurrentTransform, + img.interpolate + ); fillCtx.drawImage( scaled.img, 0, @@ -2663,6 +2688,10 @@ class CanvasGraphics { } const scaled = this._scaleImage(imgToPaint, ctx.mozCurrentTransformInverse); + ctx.imageSmoothingEnabled = getImageSmoothingEnabled( + ctx.mozCurrentTransform, + imgData.interpolate + ); ctx.drawImage( scaled.img, 0, diff --git a/src/display/display_utils.js b/src/display/display_utils.js index c94834a743842..aabe7a24b0afd 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -32,6 +32,9 @@ import { const DEFAULT_LINK_REL = "noopener noreferrer nofollow"; const SVG_NS = "http://www.w3.org/2000/svg"; +const CSS_PIXELS_PER_INCH = 96.0; +const PDF_PIXELS_PER_INCH = 72.0; + class DOMCanvasFactory extends BaseCanvasFactory { constructor({ ownerDocument = globalThis.document } = {}) { super(); @@ -622,6 +625,7 @@ function getXfaPageViewport(xfaPage, { scale = 1, rotation = 0 }) { export { addLinkAttributes, + CSS_PIXELS_PER_INCH, DEFAULT_LINK_REL, deprecated, DOMCanvasFactory, @@ -637,6 +641,7 @@ export { LinkTarget, loadScript, PageViewport, + PDF_PIXELS_PER_INCH, PDFDateString, RenderingCancelledException, StatTimer, diff --git a/src/pdf.js b/src/pdf.js index 8bbf9efa8039a..3b708cb23c86f 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -16,6 +16,7 @@ import { addLinkAttributes, + CSS_PIXELS_PER_INCH, getFilenameFromUrl, getPdfFilenameFromUrl, getXfaPageViewport, @@ -23,6 +24,7 @@ import { isValidFetchUrl, LinkTarget, loadScript, + PDF_PIXELS_PER_INCH, PDFDateString, RenderingCancelledException, } from "./display/display_utils.js"; @@ -103,11 +105,13 @@ if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) { export { // From "./display/display_utils.js": addLinkAttributes, + CSS_PIXELS_PER_INCH, getFilenameFromUrl, getPdfFilenameFromUrl, isPdfFile, LinkTarget, loadScript, + PDF_PIXELS_PER_INCH, PDFDateString, RenderingCancelledException, getXfaPageViewport, diff --git a/test/driver.js b/test/driver.js index 7f0d651e30be7..6ed5143e770e7 100644 --- a/test/driver.js +++ b/test/driver.js @@ -20,15 +20,17 @@ const { AnnotationLayer, AnnotationMode, + CSS_PIXELS_PER_INCH, getDocument, GlobalWorkerOptions, + PDF_PIXELS_PER_INCH, renderTextLayer, XfaLayer, } = pdfjsLib; const { SimpleLinkService } = pdfjsViewer; const WAITING_TIME = 100; // ms -const PDF_TO_CSS_UNITS = 96.0 / 72.0; +const PDF_TO_CSS_UNITS = CSS_PIXELS_PER_INCH / PDF_PIXELS_PER_INCH; const CMAP_URL = "/build/generic/web/cmaps/"; const CMAP_PACKED = true; const STANDARD_FONT_DATA_URL = "/build/generic/web/standard_fonts/"; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index ee07ee273c90b..c8845e4eeb075 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -69,6 +69,7 @@ !issue8229.pdf !issue8276_reduced.pdf !issue8372.pdf +!issue9713.pdf !xfa_filled_imm1344e.pdf !issue8424.pdf !issue8480.pdf @@ -150,6 +151,7 @@ !complex_ttf_font.pdf !issue3694_reduced.pdf !extgstate.pdf +!issue4706.pdf !rotation.pdf !simpletype3font.pdf !sizes.pdf diff --git a/test/pdfs/issue4706.pdf b/test/pdfs/issue4706.pdf new file mode 100644 index 0000000000000000000000000000000000000000..56f0881713cf3979c9e89fd25a09d64b5a41d747 GIT binary patch literal 10527 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 literal 0 HcmV?d00001 diff --git a/test/pdfs/issue9713.pdf b/test/pdfs/issue9713.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5b6e0a5def9a1a61533b327711d93a28463a8bb3 GIT binary patch literal 49425 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 Gq!s|K3hXoh literal 0 HcmV?d00001 diff --git a/test/test_manifest.json b/test/test_manifest.json index 56fff46c4fe45..7a86fa7141ac0 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -3134,6 +3134,12 @@ "rounds": 1, "type": "eq" }, + { "id": "issue4706", + "file": "pdfs/issue4706.pdf", + "md5": "f3e90a3cf52550583fa2a07a138b8660", + "rounds": 1, + "type": "eq" + }, { "id": "issue11242", "file": "pdfs/issue11242_reduced.pdf", "md5": "ba50b6ee537f3e815ccfe0c99e598e05", @@ -4443,6 +4449,12 @@ "link": true, "type": "eq" }, + { "id": "issue9713", + "file": "pdfs/issue9713.pdf", + "md5": "a62bd42d12271105b26a68c8eae5ea5f", + "rounds": 1, + "type": "eq" + }, { "id": "issue1936-text", "file": "pdfs/issue1936.pdf", "md5": "7302eb9b6a626308e2a933aaed9e1756", diff --git a/web/firefox_print_service.js b/web/firefox_print_service.js index 227632447932a..9d938646f41f5 100644 --- a/web/firefox_print_service.js +++ b/web/firefox_print_service.js @@ -13,7 +13,12 @@ * limitations under the License. */ -import { AnnotationMode, RenderingCancelledException, shadow } from "pdfjs-lib"; +import { + AnnotationMode, + PDF_PIXELS_PER_INCH, + RenderingCancelledException, + shadow, +} from "pdfjs-lib"; import { getXfaHtmlForPrinting } from "./print_utils.js"; import { PDFPrintServiceFactory } from "./app.js"; @@ -29,7 +34,7 @@ function composePage( const canvas = document.createElement("canvas"); // The size of the canvas in pixels for printing. - const PRINT_UNITS = printResolution / 72.0; + const PRINT_UNITS = printResolution / PDF_PIXELS_PER_INCH; canvas.width = Math.floor(size.width * PRINT_UNITS); canvas.height = Math.floor(size.height * PRINT_UNITS); diff --git a/web/pdf_print_service.js b/web/pdf_print_service.js index 58acfeb613cbe..aa678d34a2776 100644 --- a/web/pdf_print_service.js +++ b/web/pdf_print_service.js @@ -13,8 +13,8 @@ * limitations under the License. */ +import { AnnotationMode, PDF_PIXELS_PER_INCH } from "pdfjs-lib"; import { PDFPrintServiceFactory, PDFViewerApplication } from "./app.js"; -import { AnnotationMode } from "pdfjs-lib"; import { compatibilityParams } from "./app_options.js"; import { getXfaHtmlForPrinting } from "./print_utils.js"; @@ -34,7 +34,7 @@ function renderPage( const scratchCanvas = activeService.scratchCanvas; // The size of the canvas in pixels for printing. - const PRINT_UNITS = printResolution / 72.0; + const PRINT_UNITS = printResolution / PDF_PIXELS_PER_INCH; scratchCanvas.width = Math.floor(size.width * PRINT_UNITS); scratchCanvas.height = Math.floor(size.height * PRINT_UNITS); diff --git a/web/ui_utils.js b/web/ui_utils.js index 8f2f473f2eee8..cd8e79d4dbe0d 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -13,7 +13,9 @@ * limitations under the License. */ -const CSS_UNITS = 96.0 / 72.0; +import { CSS_PIXELS_PER_INCH, PDF_PIXELS_PER_INCH } from "pdfjs-lib"; + +const CSS_UNITS = CSS_PIXELS_PER_INCH / PDF_PIXELS_PER_INCH; const DEFAULT_SCALE_VALUE = "auto"; const DEFAULT_SCALE = 1.0; const MIN_SCALE = 0.1;