Ensure TextLayer hiddenCanvasElement is layout-neutral by default#21218
Ensure TextLayer hiddenCanvasElement is layout-neutral by default#21218RolandWArnold wants to merge 1 commit intomozilla:masterfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #21218 +/- ##
==========================================
- Coverage 55.97% 55.92% -0.06%
==========================================
Files 218 220 +2
Lines 59033 58991 -42
==========================================
- Hits 33041 32988 -53
- Misses 25992 26003 +11
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Snuffleupagus
left a comment
There was a problem hiding this comment.
Given that using the textLayer requires also using the pdf_viewer.css file, since otherwise things are simply not guaranteed to work, it's not clear (at least to me) why this effectively duplicated code should be added here!?
|
Thanks for the quick review. I understand that Consumers building custom viewer chrome may include the viewer CSS but scope/rewrite its selectors to avoid leaking PDF.js viewer styles into the surrounding application. In that setup, the rest of the text-layer CSS is present, but That's why I thought applying the essential layout-neutralizing property at the creation site might be appropriate: the body-appended helper would not depend on a viewer-subtree stylesheet rule to avoid participating in host-page layout. If the project’s position is that |
| ctx = canvas.getContext("2d", { | ||
| alpha: false, | ||
| willReadFrequently: true, | ||
| }); |
There was a problem hiding this comment.
If we removed canvas from the document after getting the ctx instead, does the Context2D retain the locale inherited from the body? If it does, then it might be preferable to just fully remove the canvas from the DOM rather than "trying harder" to hide it.
There was a problem hiding this comment.
Tested this - the CanvasRenderingContext2D does retain the relevant language state after detachment. With lang set on the canvas before getContext, measureText produces language-sensitive widths (for example, Korean differs from Japanese/Simplified Chinese for some CJK characters), and those widths are preserved after canvas.remove().
Detaching the canvas is the cleaner fix. I’ve updated this accordingly.
There was a problem hiding this comment.
Tested this - the
CanvasRenderingContext2Ddoes retain the relevant language state after detachment. Withlangset on the canvas beforegetContext,measureTextproduces language-sensitive widths (for example, Korean differs from Japanese/Simplified Chinese for some CJK characters), and those widths are preserved aftercanvas.remove().Detaching the canvas is the cleaner fix. I’ve updated this accordingly.
Tested this - the
CanvasRenderingContext2Ddoes retain the relevant language state after detachment. Withlangset on the canvas beforegetContext,measureTextproduces language-sensitive widths (for example, Korean differs from Japanese/Simplified Chinese for some CJK characters), and those widths are preserved aftercanvas.remove().Detaching the canvas is the cleaner fix. I’ve updated this accordingly.
Do you have a link around the canvas specification explaining that we've the guarantee the lang setting will be constant over the time ?
If not, I don't think we'll take this patch as is.
But I'm not opposed to have something like:
canvas.style.cssText =
"position:absolute;top:0;left:0;width:0;height:0;display:none";@Snuffleupagus can you imagine any drawbacks ?
There was a problem hiding this comment.
But I'm not opposed to have something like:
canvas.style.cssText = "position:absolute;top:0;left:0;width:0;height:0;display:none";@Snuffleupagus can you imagine any drawbacks ?
Not technically. (Only maintainability wise, since it's generally better to not define styles inline. In this case though the element is explicitly invisible, so that's probably less of a concern here.)
However, if that change is done then the hiddenCopyElement in the viewer ought to be handled the same way such that the CSS rules can be removed from the web/pdf_viewer.css file.
There was a problem hiding this comment.
Thanks both. I’ve updated the PR to:
- apply inline layout-neutral styles to
hiddenCanvasElement - apply the same treatment to
hiddenCopyElement - remove the now-redundant shared rule from
web/pdf_viewer.css
fcfd0f3 to
3b05d2e
Compare
Snuffleupagus
left a comment
There was a problem hiding this comment.
Please also remember to write a good commit message, since a single line is rarely sufficient.
| @@ -473,6 +473,8 @@ class TextLayer { | |||
| // OffscreenCanvas. | |||
| const canvas = document.createElement("canvas"); | |||
| canvas.className = "hiddenCanvasElement"; | |||
There was a problem hiding this comment.
Can't this line be removed now?
There was a problem hiding this comment.
Removed - confirmed no other references.
| @@ -967,6 +967,8 @@ class PDFViewer { | |||
| const element = (this.#hiddenCopyElement = | |||
| document.createElement("div")); | |||
| element.id = "hiddenCopyElement"; | |||
There was a problem hiding this comment.
Can't this line be removed now?
There was a problem hiding this comment.
Kept this one: #hiddenCopyElement is used by test/integration/copy_paste_spec.mjs as a loadAndWait selector and via document.getElementById in the selectAll helper, so removing the id would break those tests.
Set layout-neutral styles at the creation sites for the hidden TextLayer canvas and PDFViewer copy element rather than relying on the shared web/pdf_viewer.css rule. This keeps the helper elements invisible and out of layout when viewer CSS selectors are scoped or omitted, and removes the obsolete hiddenCanvasElement class and shared CSS rule.
3b05d2e to
ceb7875
Compare
|
Updated again:
|
TextLayercreates a helper canvas with classhiddenCanvasElementand appends it directly todocument.body, which can adversely affect the host-page layout/scrolling behavior.The stock viewer stylesheet
web/pdf_viewer.cssalready makes this element layout-neutral via.hiddenCanvasElement { ... display: none; }, but directpdfjs-dist/TextLayerconsumers are not required to loadweb/pdf_viewer.css. Consumers that scope the viewer stylesheet to avoid global CSS leakage also lose this protection, since the helper canvas is appended outside the scoped viewer subtree.This change applies the essential layout-neutralizing rule at the creation site, so the display-layer helper does not depend on viewer CSS to avoid participating in host-page layout.
Related context:
react-pdfadded the.hiddenCanvasElementCSS rule downstream in Hidden canvas element may break layout wojtekmaj/react-pdf#1815 to avoid layout side effects.