From bd05b255fabfc313b194bfe9a17ccded4d90fb5a Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 11 Feb 2025 11:55:24 +0100 Subject: [PATCH] [api-major] Apply the `userUnit` using CSS, to fix the text/annotation layers (bug 1947248) Rather than modifying the "raw" dimensions of the page, we'll instead apply the `userUnit` as an *additional* scale-factor via CSS. *Please note:* It's not clear to me if this solution is fully correct either, or if there's other problems with it, but it at least *appears* to work. --- With these changes, the following CSS variables are now assumed to be available/set as necessary: `--total-scale-factor`, `--scale-factor`, `--user-unit`, `--scale-round-x`, and `--scale-round-y`. --- src/core/xfa/template.js | 2 +- src/core/xfa/xhtml.js | 2 +- src/display/annotation_layer.js | 10 +- src/display/display_utils.js | 11 +- src/display/editor/freetext.js | 6 +- src/display/text_layer.js | 2 +- test/driver.js | 28 +++-- test/pdfs/.gitignore | 2 + test/pdfs/bug1947248_forms.pdf | Bin 0 -> 12097 bytes test/pdfs/bug1947248_text.pdf | 156 ++++++++++++++++++++++++ test/test_manifest.json | 15 +++ web/annotation_editor_layer_builder.css | 4 +- web/annotation_layer_builder.css | 26 ++-- web/pdf_page_view.js | 15 ++- web/pdf_viewer.css | 2 + web/struct_tree_layer_builder.js | 2 +- 16 files changed, 237 insertions(+), 46 deletions(-) create mode 100644 test/pdfs/bug1947248_forms.pdf create mode 100644 test/pdfs/bug1947248_text.pdf diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 56fc4d4fc..4fd5031d6 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -1421,7 +1421,7 @@ class ChoiceList extends XFAObject { const field = ui[$getParent](); const fontSize = field.font?.size || 10; const optionStyle = { - fontSize: `calc(${fontSize}px * var(--scale-factor))`, + fontSize: `calc(${fontSize}px * var(--total-scale-factor))`, }; const children = []; diff --git a/src/core/xfa/xhtml.js b/src/core/xfa/xhtml.js index 1c1eab989..c7db14c5d 100644 --- a/src/core/xfa/xhtml.js +++ b/src/core/xfa/xhtml.js @@ -179,7 +179,7 @@ function mapStyle(styleStr, node, richText) { } if (richText && style.fontSize) { - style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`; + style.fontSize = `calc(${style.fontSize} * var(--total-scale-factor))`; } fixTextIndent(style); diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 8db5d994f..8afd21829 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -297,10 +297,10 @@ class AnnotationElement { const horizontalRadius = data.borderStyle.horizontalCornerRadius; const verticalRadius = data.borderStyle.verticalCornerRadius; if (horizontalRadius > 0 || verticalRadius > 0) { - const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`; + const radius = `calc(${horizontalRadius}px * var(--total-scale-factor)) / calc(${verticalRadius}px * var(--total-scale-factor))`; style.borderRadius = radius; } else if (this instanceof RadioButtonWidgetAnnotationElement) { - const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`; + const radius = `calc(${width}px * var(--total-scale-factor)) / calc(${height}px * var(--total-scale-factor))`; style.borderRadius = radius; } @@ -1194,7 +1194,7 @@ class WidgetAnnotationElement extends AnnotationElement { roundToOneDecimal(height / LINE_FACTOR) ); } - style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`; + style.fontSize = `calc(${computedFontSize}px * var(--total-scale-factor))`; style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); @@ -1553,7 +1553,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { const combWidth = fieldWidth / maxLen; element.classList.add("comb"); - element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`; + element.style.letterSpacing = `calc(${combWidth}px * var(--total-scale-factor) - 1ch)`; } } else { element = document.createElement("div"); @@ -2279,7 +2279,7 @@ class PopupElement { style: { color: this.#fontColor, fontSize: this.#fontSize - ? `calc(${this.#fontSize}px * var(--scale-factor))` + ? `calc(${this.#fontSize}px * var(--total-scale-factor))` : "", }, }; diff --git a/src/display/display_utils.js b/src/display/display_utils.js index 1bac155c9..6e625d867 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -213,8 +213,7 @@ class PageViewport { * @type {Object} */ get rawDims() { - const { userUnit, viewBox } = this; - const dims = viewBox.map(x => x * userUnit); + const dims = this.viewBox; return shadow(this, "rawDims", { pageWidth: dims[2] - dims[0], @@ -597,13 +596,13 @@ function setLayerDimensions( const { style } = div; const useRound = FeatureTest.isCSSRoundSupported; - const w = `var(--scale-factor) * ${pageWidth}px`, - h = `var(--scale-factor) * ${pageHeight}px`; + const w = `var(--total-scale-factor) * ${pageWidth}px`, + h = `var(--total-scale-factor) * ${pageHeight}px`; const widthStr = useRound - ? `round(down, ${w}, var(--scale-round-x, 1px))` + ? `round(down, ${w}, var(--scale-round-x))` : `calc(${w})`, heightStr = useRound - ? `round(down, ${h}, var(--scale-round-y, 1px))` + ? `round(down, ${h}, var(--scale-round-y))` : `calc(${h})`; if (!mustFlip || viewport.rotation % 180 === 0) { diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 1b31fbd8a..9700d902d 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -209,7 +209,7 @@ class FreeTextEditor extends AnnotationEditor { */ #updateFontSize(fontSize) { const setFontsize = size => { - this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`; + this.editorDiv.style.fontSize = `calc(${size}px * var(--total-scale-factor))`; this.translate(0, -(size - this.#fontSize) * this.parentScale); this.#fontSize = size; this.#setEditorDimensions(); @@ -570,7 +570,7 @@ class FreeTextEditor extends AnnotationEditor { this.editorDiv.contentEditable = true; const { style } = this.editorDiv; - style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; + style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; style.color = this.#color; this.div.append(this.editorDiv); @@ -878,7 +878,7 @@ class FreeTextEditor extends AnnotationEditor { return content; } const { style } = content; - style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; + style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; style.color = this.#color; content.replaceChildren(); diff --git a/src/display/text_layer.js b/src/display/text_layer.js index e1f111386..1e3ba2d82 100644 --- a/src/display/text_layer.js +++ b/src/display/text_layer.js @@ -342,7 +342,7 @@ class TextLayer { top = tx[5] - fontAscent * Math.cos(angle); } - const scaleFactorStr = "calc(var(--scale-factor)*"; + const scaleFactorStr = "calc(var(--total-scale-factor) *"; const divStyle = textDiv.style; // Setting the style properties individually, rather than all at once, // should be OK since the `textDiv` isn't appended to the document yet. diff --git a/test/driver.js b/test/driver.js index 1813ad825..67fb667d6 100644 --- a/test/driver.js +++ b/test/driver.js @@ -215,6 +215,18 @@ class Rasterize { return { svg, foreignObject, style, div }; } + static createRootCSS(viewport) { + const { scale, userUnit } = viewport; + return [ + ":root {", + " --scale-round-x: 1px; --scale-round-y: 1px;", + ` --scale-factor: ${scale};`, + ` --user-unit: ${userUnit};`, + ` --total-scale-factor: ${scale * userUnit};`, + "}", + ].join("\n"); + } + static async annotationLayer( ctx, viewport, @@ -232,9 +244,7 @@ class Rasterize { div.className = "annotationLayer"; const [common, overrides] = await this.annotationStylePromise; - style.textContent = - `${common}\n${overrides}\n` + - `:root { --scale-factor: ${viewport.scale} }`; + style.textContent = `${common}\n${overrides}\n${this.createRootCSS(viewport)}`; const annotationViewport = viewport.clone({ dontFlip: true }); const annotationImageMap = await convertCanvasesToImages( @@ -293,9 +303,7 @@ class Rasterize { svg.setAttribute("font-size", 1); const [common, overrides] = await this.textStylePromise; - style.textContent = - `${common}\n${overrides}\n` + - `:root { --scale-factor: ${viewport.scale} }`; + style.textContent = `${common}\n${overrides}\n${this.createRootCSS(viewport)}`; // Rendering text layer as HTML. const textLayer = new TextLayer({ @@ -322,9 +330,7 @@ class Rasterize { svg.setAttribute("font-size", 1); const [common, overrides] = await this.drawLayerStylePromise; - style.textContent = - `${common}\n${overrides}` + - `:root { --scale-factor: ${viewport.scale} }`; + style.textContent = `${common}\n${overrides}\n${this.createRootCSS(viewport)}`; // Rendering text layer as HTML. const textLayer = new TextLayer({ @@ -346,9 +352,9 @@ class Rasterize { let x = parseFloat(left) / 100; let y = parseFloat(top) / 100; if (isNaN(x)) { - posRegex ||= /^calc\(var\(--scale-factor\)\*(.*)px\)$/; + posRegex ||= /^calc\(var\(--total-scale-factor\)\s*\*(.*)px\)$/; // The element is tagged so we've to extract the position from the - // string, e.g. `calc(var(--scale-factor)*66.32px)`. + // string, e.g. `calc(var(--total-scale-factor)*66.32px)`. let match = left.match(posRegex); if (match) { x = parseFloat(match[1]) / pageWidth; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 83fa48a0b..efe327f76 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -559,6 +559,8 @@ !poppler-85140-0.pdf !issue15012.pdf !issue19176.pdf +!bug1947248_text.pdf +!bug1947248_forms.pdf !issue15150.pdf !poppler-395-0-fuzzed.pdf !issue14165.pdf diff --git a/test/pdfs/bug1947248_forms.pdf b/test/pdfs/bug1947248_forms.pdf new file mode 100644 index 0000000000000000000000000000000000000000..12876fda6a220e983636112767dd244442809571 GIT binary patch literal 12097 zcmeHtc{EjR|FW+8<_hB760=6NoZ zM25^#ne`qGcb@0=Jiqr@>;0|u{_&i(&R%=(>pNeceeM0dzTb;F3Q9s?VFca9$*G00Q7_9O)z_ff`s>3<{6Eg0TgFff@iX1SBQ`)CPb>fQkSS5DWs;BHlLufWac7 zB0v>@832ZW5l7(`($aJoSK9+AfWQCfXdtlPIutNyoGnHNg?I5FYDD6kJzZQq05G5q z9%G9|6L5HH#${uJ!~600Mkqjn{WSMS*%a9034Ed?(N+#s&a|0S$cJFhCPMj2+O} z3=Dt(z{Ci65bzk33muKaxQ&hN32SQ`>-OV}w577aYh)3kq})D=vPIJXYBNAXDVd6- zI&s`KnSl{)%`LlY|7M2?@PZ`Lm}-lTh8V}6l72?igMh*leDD}MIvNli&Bcp~+DddZ zzoJY>1BL#M?NLD^dIA0Am?)e$n<6lvJ^(5TGy;f${=sT%YioOJ>wXLg*$oWsgFxzt z%PgX7u}!bDlY<)W1ceV|?KsPwCikl>AyEm53zDm*mWRFxo=dtBw0hSycuRvRkogk% z^~cl(BXofzv%s|4#YvLaPh7gY;V#K2b(GFWI>R*l$p-YtX%Md+`2hJ0Z#vFiIiKyfN(;zDl z017$KLjWazhcNj0huOY?r@l%#D@)=ecBOf;Bn zK@8>Pcc^u8Wgn3*yqonODvQ7VqSfr2=)l-O(tLHA_)rvvTD|DzWlI|~!RCuz6{CzT z@tc3Bf5fO(4nl&feKw{SiW!o$IXRw7jqj`fVZK+?QZv2w{*!XR=K9;zou?w+D}(;N zTf3k3yQ+{KPH$q)oC%-Z@a7Hp`tkIfo)Qoer9 zJ0~MJyfGxLe(y^@UkpP}lE2SY&u809t9#yTdyA=X|GZl>nRg^=%{$LX?)vz9n4(>+ zLSvKCCXLZ^wdijakou=D3$3n+2W-1Z);{;$S@sb(m#P-u_fm^tFeXUX)zrvXm^)(C&pn8-td$y1ch$-~=d|Z7!l})Wc}^<8@7usysnPc8`>&gpnMo5K8CU&Z#`Lwk zPd$@V9G!o$nvMRQ`L)fjKI_X#2?W>hjw$vH?jbjytA2aSQ+gJ0JsEGmRZ8D@LZ5lM zP^#?p3}>cQeSOv5=eeEKn$(?|>FJkyQzHQ$ej4foKc5zcS1X>+R`&6}U$(V9N}8qP z7VF0?Wi}&>^TVe;b^*gVmpR9+-d%aMz;8kSesz)%;I}^W@oml66eE4HR?EgUt1HR# zD87D~H~yk;W!U#iIR)}(X0Lhet+mF=jGwFU-+lnMdb8&rbDitUv{O~I*M85?n%VP= zrM!o)RdnIBcNNbv_Zm(#JsF!HutpCbD24khJ+ z_q8h95}1*ln5yMH_o6%n&Tff{((?E_;u-+~4=V(P67vS1Gq=6bs4t{wn{hWbGgX^c zf3(1Zh-B-O{BD$$l0~B-IYpqc+ zppIcrIr}AA+|_tzr(Rk|sj=R~fa3@Z+$G3`P; za$GMLYNPRdT0WCGw*I-ZYg|OArUz4huC_Ro?>MAE*K?(0c-YjvKsW-rabiLDng?zH z-FFDTLQ z&bxc>=NhB{N@kcXp?KcOa_M&7sNPWaYfamV=OS8AokhKo5R=;?ImcNYrqht?1(IdW z2+s)K^|4-7yofI(tZ6kQf;q+W1>dVnDdwOMKY8~vn$PNP8(d<_?aNLeU@K4c+`ln(hn(=S>$p0{H7o6y&Uh_?eTZ+-Ug88E((6Nzj|A4+MqN z3tX0<;nbU3-qa173rVN#O}@Ya$knku={g&&Z@9wSnZSGk)2atko*4f#nf?R1Y}Sqe ztr!u`_r!=@Z{gH>Mc?J2o!X2TR1sI?z97bS>0MA(>)iqglJ?_j0?pklk2OqAvfK$- zZ+~{znrz31SLBYH#gpX#nv7ax+BZAk#}3SL+qE;~`w{&hcCVA77wHAH6LqOvF51+6 zF{5my%UYK1meM%RFVuseQIPvmOPbm&)CL$~D13L?m`j@9RG;5$xB+Upj2?}Qk1y$@ zjJts0kK|Zq=ggyOyQuvRVwuTfx6oUtT*OW`H6xiLVQ-d`C9EEFc|n0+r{w;6?pECg z;QEWV{a^Q`XM^$~wLOIs`_Em=fa^L>x@C4c(q8FYO+iRd{NKh)p4Wn?{fO`5!mJTnx9#@aI{F0H=oZBU?lJJA1uvB|evB*o(7sD|)09 zdtRH1QLb=4ghI49t742##d1k~3t-kQ-8C>q{l|%7YxjOGiDCZYad$)0aREr168-3@ znS@lNeOEx@b?ghb<3U}6@kR~pGlD8=7c+ zRm-pwyf=8q(3|8IW7zH+p0fok*zms5isH0$qZWRixew|db;sI92|O#jt>vktme?Sh zNh27u+*};2xkh5ITT-PI9cD)H+QLI=h#BPxDW-dtrox{m(90I4Q-D25wUQy7!t!9k z-zj*0hMsaN>QeNuiiO_x;w2jCX2YJBtpj@A7KnZW`Z8^m`{iZuW2} z#a`9N_l282*5L7FcjwvC?k9YzG% z^1)7hM-e{xFXf}tizVn2Wy&8(|75+#)T59xrP&*fP2Jk-ID0KC;4u}u>N>4sFv$yr zZ|53?s&MIrK?S804;Mce1|64dFz&tfRqaJU_TpTW<@C*T=%RV~t6UP?JW~ndA1Py( z0IHeY!*&(@c{{>Vv>yvD<$RTXwU|C{YXq{`W^XW`IXUyX&qw?8%pMt^D5oDoP$S6+ z8e9;h7BQ!T?1q10_NTH?zMI!`Lf{PVU?pAl$E**rFUWaE)$c!+Jxf6sBml7rSianV z61ZD*JN%DF@bf8SS%9DuG%89lg*_LYqI|O+&P_&o^@1Qq_*t?1*2OmoFj4mDMCpgF zX@eT_IZEbrc7U}B9Cv|iuwa4`xDJ`#`m9MY`F4RXJPi1xu7@uAviitfib>nqH_V3S zA1Ksx9+7wqSIWJm#`b^kbDYcCUiP*8Xv9Iw6QmTmjNUv9|UVGM91l1Msq0D*uwgb4Dfe=X-QP599WXD9BW#!GYC~Y;9t*;JwY{d%P%kTBX=1e3_%) zQc>Qcbw9w)3UZ1{<1~-yMAeO8bp4&6S?^xu)@hz3)lje-*=nyk+xz5}>*RHU3PvR} z=I9fCZ4CX2SHx{Y&w~UC#qark$>Uo<=K19wN3;z^vAJ~7GcrZ6%!p7H&Afyg$GjcX zCDf+Jz!psCSXnSY*Qif@JSY%bJa@yxJ`>3Ek*na?D4hF!N;pdv_ev7!ja9Agr^)e~ zN{mU@Ti2@SM)(uh{9mF1j>?%{#s5&wj(LL3f-M%tm=j|?F&#Y_Y)o;%{s^qzh z=zHm#rmH$4m{Thu(FKm=&vC@N=p!0tqRQ{J__j%=^|FfC&Q#D2>!3bc?RM=(hC-+? zte(Ah|73M6?Y9#)x=xqgUut|d(>IOO=1S*|YU;b44=zf_DDFZbd2xLku3A!GxfQUv zEx9%Xkxg{rJ4zxj$Bfc8gC4gje4%+4}&9Fjai4HPU=05j}$L_ z=VQkfPa6-}xWHf#sq!v6>I<#eDs_D{-;Sc`Ds;1Hbg;bKxT?7*!(xQoRnqIZ#rw+x zl;C9%uA#4EvDNQl8>w6CTz7#NL)X9Q?Qcx)?AE^=Tc}~0lKxZ-5PseBZBQ(Y_)+~~ ztS4!C$7N^l-Lfle?!n-!zO+@nmHDn&ASnswBfrcea-#2v=+9jBh@9wWDkmoTJMmGu zA$U`rCJ;ocupy?XrIU}%Io+tZmj>_?;hNiHu+HM}x%s}!;KFJL%=y_1-Ph+LF9&ly zaip$du1q~GOB1VXkK>a(sS@oI*S7QVadpSrE|>g=-v|{k^f^{UGqdwqE*xbh_;>2| z(#$_7%hCm+RpkOcpaK7vINB7Y^FNL7V{H2NT+1tEXyQMuu7=%=G^ z%vS#@Hq8LqfWK8XKGF4ve7@UMv7=1u^1|c=F0$azZ6!ve0v^d25vLp*8<;CEA1ql^ z3kJ}rEs&dGZ#La~T+{hLu~a~u!&u4jDTjA=(@WQl@kJSlGN|87CvJIV=lS|h`brIS zW76b>AZ^RqG3;9Eq~5@OY}OE#)gnH~*SpX(!5j4Q1#eOr9`Tw#`*o=3P7TSO_|{%U z7?a?q=WVC7R8E(!lZ_1F%2rh6uQ!x%Q!wEplc^GH>fbZ^zYiC%%QStcx1eE~Fs&Um zZqw`dMB{UNa&ieq^6}>>0hzEJaC=N&$DAPwg}Q1!-QqS{te9^S#p4+rXEYCCy`BNK zO&% z^`EJq+y91Ebn=AdE8+&QzISXUgO$XQ`KApIL06MBc(m;y4nxh;mxl9mO+Pz|rx-ol z__pF!6^afr;j5(Bk*Z|8**uI|DD`oM-U+;j;x{P>RQGTaMkHloA(f|~)-h`e{%%H% zMGL_~!Rn;y04MZ?b%E>> zM6YBvonO06x2x(FwB8rAK4MYALk#6`iAv;fT!Gk5~^_d$>jU)^+I_S=5s*~x1W8L9bmc0f#0^) zmT_Lf_{+JCQw(lK@PDORxGnZr)b&doG*z*Yfflz+#cO{$h$=OpBHhM1oFSyjWjUYQ zV4o(#*i2;bl0PW%WYd|Xq_8%mLL;-_F$=?%>IdLHlfqkK*$%DVoS$qX^Hw3Vn@z_| z7GC<+w^26`=w(_dl|E;6PDSwOa;JpEO_a94jgVV99OUN`ecN+d@8$v8XI5)6SA$r( ziEFs`pQ`0d3W?%eTRt}#YF8?&GGZ6?_IGpG)V43o8tt;OFRg8CP|RC0>XWB&z8jJX z&)=28^H3;^IbVJ2#cA?U*i$ps?cy`Le{Vy_aLm=_V)#(yh^Y^NuPlub@ z-Z3O>70gS%@o_RWm)^?hv!FsY&KGGO{|tX@6MDOXvnTV;<|-2?dhd+6pie^MMsi0) zvwER=B;y~%SNRo6Q-HLH7U0^n)WISQJ+qaSC_tK z<0+$Fv$=49kq&!s<_-8LpR^EL$jMim9K6yzYzo;q4r3>1W+!8;d%QtaOGX>$V*9~M zcdnxAu$y!j)StzK-=~l;wh0^lYU$CtHCy74Hite-i5W5cb8iUqxLC!DaS=Ffl5E6- z2$rBX4vptd>5~YYTTdn!D&m==a;cPQ6zVN^N|tZeJhB4!^S+^=vU@hYvpwtx?h7|E zPr4kHLUv2?>Jli89GUrhTzI6XYx~sfK~CGlME*~lY(NW^SoJvi#1Go}saD_EU~3+q$tPEIx-;UoOvki6 zXDjAe^(~{HcloHv)tW@zX?49_-60aoM;BtqJ+h$}EohceQS$oUQ@gRo&{JNiiLlC) zvyQcf=YT`Q{!WXFJ(Kr~3xVT2@87+!>}YA?9BJ$y&XGnuc!do+=JLimQ!4N#iW}TI z+`U3EkFL6vvOYTAFuHSwN9)EdwLk32t>mlYZpvg7Eig= z**Hvf4UvDe;cxf!(kg~e^)w$VAxK*}PeqQrl6BG)O|zRFU(yV(u^|UO6s*9_a0iw5 zTnRHwYv`#9Gz3!gcDN>lX8Stn&@xKOmreX>?DjQ)g>+WVL*#TeyKbGO!A6uZjqgmypbclTteS6}d=LV}C`dInMDI!sFCpB|Z4S=}7{~K5 z@pUXx`vb!z2r}<@gdrOETe?A>Z;CI=+(Jcti+m z0!RC`j`bv}9fxwSo>w=Ce~_w=EyAVm!IU@`qDmXJjFJ|O{g^5ty~^64`MrTo#>2|~R^49q+FE0{ON|J)pymCi) z_U}LUetIVQ%QFNN{_i~Vzuo0J2BK}TYSIQZtM=WOJqa+A5`581;^gNL&f##QGPK>O zAjWxQAm%jFt9*;a*xhd`kv8VSk&3!^nz{=3k`${~&hjncDsu2)mq9X)=qI6XT@_CPc$~et%Ce=vXy#< z+3q*Cb7=LatyiCyE@Aa~ETUfWc=~PW^_#0VI8)eG2UA{zpQn>5cypp6gNhz;?5^SN zlKf;4>+E?i_EbRwe#XxaiMy|@(o&WI-S<__I-T4c=at$RKo_T1DTQ5Eo0@*J>3@&Lfq+iPjPv0jX!4%^H%_x6~%oCLAe^nJSpNv|u%N?L@Ez zuN$u49=que{IsX>T;>m+0&PUfb*TnR;f>3^_Hvo_bL)+@Dyt7YGK}x~_ehwIk?;ws zvDgc$T~5=pK1crP-8zJlF`42%s|FKB0j+?xV}oKNT0kmdym(%`NJ34S^}TYjl)m#w z6*3;u0a^_v_jv10q^QN4`VR?&t*}`}4;oeNau7 z#M|15>nSMx)JMFM;&LDm+{A%EZ*Om5Z>TU1Zw~~EiHQM05Fi91M5GY%@O34ie1u#* zF8mPkOO67@1C7VJ5wJK{z=2$p4bGDw#l`hk+4?~BPsd!`{!--XA^hD*VKmMK=!0?t z5=)lAqnK>bfAP3^;+?;nW{U=5oG~s3MP4Ew_((otOf@zCVm(rwi_4LmZinLW4}8P{ zqB`i2Oek9i9NPPLApe2!p!jWrBFG)k7~}9xcFs8O9}a(?kAnyiXN737xF*gPYv-## zEEr1@rx7j$f(U^@24GQfn20zWehCB?2Z1Dk|3vva)Zb+wiMM?;+(W#F2}p6GjYKekC4v8V;|NMG4BmqnPif*4^_%+F&7bs=z=LP}&@=yILHT}{ zxS|kOGvHs#*&%HIK=}X4@wX%YE8>3${jZY$D7gO(*MGzHA4T9lBK|MB{u{3UC<6Zx z@qf|v|14ZIzY`*2f2$OiH!J_4~Z`Jid>@7;nBm=SS{ zq_oew8`yP87c{uht-&9SfE}lyY?==@IGsCxocn(M>9G+~V){4h^vC!S)*Nb){-3P@ z6Em%UYmV*@bA;721=~7%JX8gVdoQJsgHL-91O_SxArR$*2#|pYNat0^gLYuQn?{?q znxUJ9aJ#mepe_|(yKzJx2->IFE_kEQ00ilS4nZNH%JRX%jV|p5P+#MlL1tGj9h@2# zFR-RG`rR*PW021gejW72{w&Y^f6 zgSU8MCxj^6cVrvsvKI4-WlxinUx>2a4gV8E=FZAWLPkrXMn}bSM05@{UW37izgn;V E4-$s&a{vGU literal 0 HcmV?d00001 diff --git a/test/pdfs/bug1947248_text.pdf b/test/pdfs/bug1947248_text.pdf new file mode 100644 index 000000000..1354de747 --- /dev/null +++ b/test/pdfs/bug1947248_text.pdf @@ -0,0 +1,156 @@ +%PDF-1.6 +% +1 0 obj +<< /Metadata 4 0 R /Outlines 6 0 R /Pages 10 0 R /Type /Catalog >> +endobj +2 0 obj +<< /Type /ObjStm /Length 188 /N 1 /First 4 >> +stream +3 0 +<< /CreationDate (D:20250210183319+01'00') /Creator (Adobe Acrobat Pro \(64-bit\) 24.5.20399) /ModDate (D:20250210183349+01'00') /Producer (Adobe Acrobat Pro \(64-bit\) 24.5.20399) >> +endstream +endobj +4 0 obj +<< /Subtype /XML /Type /Metadata /Length 3200 >> +stream + + + + + 2025-02-10T18:33:49+01:00 + 2025-02-10T18:33:19+01:00 + 2025-02-10T18:33:49+01:00 + Adobe Acrobat Pro (64-bit) 24.5.20399 + application/pdf + uuid:5d245aed-b564-46a6-a3d0-73d2f014184a + uuid:3a1749f2-475c-4f8f-adf0-eebee674d901 + Adobe Acrobat Pro (64-bit) 24.5.20399 + + + + + + + + + + + + + + + + + + + + + + + + +endstream +endobj +5 0 obj +<< /Type /ObjStm /Length 165 /N 3 /First 15 >> +stream +6 0 7 56 8 105 +<< /Count 1 /First 7 0 R /Last 7 0 R /Type /Outlines >> +<< /A 8 0 R /Parent 6 0 R /Title (Blank Page) >> +<< /D [ 11 0 R /XYZ 0 792 null ] /S /GoTo >> +endstream +endobj +9 0 obj +<< /Type /ObjStm /Length 50 /N 1 /First 5 >> +stream +10 0 +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endstream +endobj +11 0 obj +<< /Contents 12 0 R /CropBox [ 0.0 0.0 612.0 792.0 ] /MediaBox [ 0.0 0.0 612.0 792.0 ] /Parent 10 0 R /Resources << /Font << /C0_0 18 0 R >> /ProcSet [ /PDF /Text ] >> /UserUnit 3/Type/Page >> +endobj +12 0 obj +<< /Length 144 >> +stream +BT +0 i +/RelativeColorimetric ri +/C0_0 12 Tf +142.501 559.499 Td +<00290046004D004D00500001003100250027000F004B00540001003800500053004D0045>Tj +ET +endstream +endobj +13 0 obj +<< /Type /ObjStm /Length 6941 /N 5 /First 34 >> +stream +14 0 15 59 16 339 17 6762 18 6773 +<< /Ordering (Identity) /Registry (Adobe) /Supplement 0 >> +<< /Ascent 989 /CIDSet 19 0 R /CapHeight 651 /Descent -360 /Flags 6 /FontBBox [ -290 -360 1684 989 ] /FontFamily (Minion Pro) /FontFile3 20 0 R /FontName /WKJDTW+MinionPro-Regular /FontStretch /Normal /FontWeight 400 /ItalicAngle 0 /StemV 80 /Type /FontDescriptor /XHeight 437 >> +<< /BaseFont /WKJDTW+MinionPro-Regular /CIDSystemInfo 14 0 R /DW 1000 /FontDescriptor 15 0 R /Subtype /CIDFontType0 /Type /Font /W [ 0 [ 500 227 276 318 ] 4 5 480 6 [ 756 711 223 ] 9 10 346 11 [ 404 580 228 356 228 331 ] 17 26 480 27 28 228 29 [ 552 580 552 379 753 691 588 665 735 568 529 715 766 341 329 673 538 891 743 747 563 745 621 474 617 736 703 971 654 634 603 345 333 345 566 500 224 439 508 423 528 425 296 468 534 268 256 496 253 819 547 510 524 511 371 367 305 531 463 685 472 459 420 347 263 347 580 276 ] 97 98 480 99 [ 159 ] 100 101 480 102 [ 477 480 169 398 444 ] 107 108 279 109 [ 535 533 520 490 489 226 497 390 239 429 401 445 970 1062 379 ] 124 136 400 137 [ 922 869 305 550 749 973 334 671 268 273 513 770 545 341 580 512 459 737 762 580 549 762 580 263 343 514 762 341 321 580 505 580 341 702 ] 171 176 691 177 [ 661 ] 178 181 568 182 185 341 186 [ 743 ] 187 191 747 192 [ 474 ] 193 196 736 197 198 634 199 [ 603 ] 200 205 439 206 [ 421 ] 207 210 425 211 214 268 215 [ 547 ] 216 220 510 221 [ 367 ] 222 225 531 226 227 459 228 [ 420 503 500 480 418 ] 233 238 762 239 [ 691 926 666 627 737 736 766 613 518 637 606 499 1029 763 493 267 526 541 533 525 547 303 385 669 1071 914 876 722 803 561 1071 1081 798 787 1045 801 852 814 535 520 778 533 582 522 856 664 804 814 533 777 ] 289 290 533 291 [ 578 ] 292 293 800 294 298 480 299 [ 828 439 790 565 511 531 584 482 456 565 621 306 297 558 460 709 580 584 484 585 528 408 510 582 567 761 551 511 493 611 621 306 582 510 579 611 481 431 815 723 776 268 606 603 622 242 235 345 346 530 340 446 406 486 403 499 437 466 486 473 468 529 486 481 489 528 483 481 519 710 1009 711 493 338 465 452 497 454 495 464 475 488 493 480 479 574 480 482 480 568 483 486 482 ] 392 411 486 412 [ 305 349 355 ] 415 416 292 417 [ 306 372 194 192 543 371 334 262 265 228 ] 427 436 341 437 [ 178 177 ] 439 440 341 441 [ 259 ] 442 443 245 444 453 341 454 [ 178 177 ] 456 457 341 458 [ 259 ] 459 460 245 461 470 341 471 [ 178 177 ] 473 474 341 475 [ 259 ] 476 477 245 478 487 341 488 [ 178 177 ] 490 491 341 492 [ 259 ] 493 494 245 495 497 606 498 [ 454 469 407 563 ] 502 507 691 508 [ 1058 813 ] 510 512 691 513 520 766 521 [ 566 766 ] 523 526 757 527 [ 640 757 598 681 ] 531 532 652 533 534 877 535 536 631 537 540 757 541 542 510 543 [ 256 ] 544 545 846 546 [ 753 922 520 276 444 445 ] 552 553 279 554 [ 356 379 ] 556 557 347 558 559 345 560 561 346 562 [ 226 ] 563 564 579 565 [ 586 587 760 556 375 490 718 561 536 641 757 531 568 ] 578 580 691 581 [ 722 ] 582 585 665 586 [ 735 ] 587 591 568 592 596 715 597 [ 766 ] 598 602 341 603 [ 329 673 ] 605 608 538 609 [ 891 ] 610 613 743 614 616 747 617 [ 749 ] 618 620 621 621 [ 474 477 ] 623 624 474 625 626 617 627 629 736 630 [ 733 ] 631 632 736 633 636 971 637 639 634 640 641 603 642 [ 869 ] 643 644 1071 645 647 439 648 [ 512 ] 649 652 423 653 [ 528 ] 654 657 425 658 [ 424 ] 659 663 468 664 [ 534 ] 665 668 268 669 [ 258 496 ] 671 673 253 674 [ 271 819 ] 676 679 547 680 682 510 683 [ 513 ] 684 686 371 687 [ 367 366 ] 689 690 367 691 692 305 693 698 531 699 702 685 703 705 459 706 707 420 708 [ 671 367 ] 710 711 492 712 724 400 725 728 565 729 [ 723 ] 730 731 565 732 [ 568 ] 733 734 565 735 [ 643 ] 736 737 531 738 [ 528 ] 739 740 531 741 [ 584 ] 742 749 482 750 [ 487 ] 751 755 565 756 [ 621 ] 757 760 306 761 [ 474 ] 762 763 306 764 [ 308 306 297 558 ] 768 771 460 772 [ 478 709 ] 774 778 580 779 785 584 786 [ 582 584 ] 788 790 528 791 792 408 793 [ 412 ] 794 795 408 796 797 510 798 804 582 805 [ 584 ] 806 807 582 808 811 761 812 816 511 817 819 493 820 [ 401 402 401 381 401 375 404 400 401 400 401 400 367 401 691 588 507 641 568 603 766 739 341 673 686 891 743 607 747 738 563 598 617 655 754 654 725 757 691 568 766 ] 861 862 341 863 [ 747 ] 864 865 655 866 [ 757 ] 867 873 691 874 882 910 883 887 691 888 895 568 896 901 766 902 910 972 911 914 766 915 926 341 927 932 757 933 941 1007 942 945 757 946 953 747 954 [ 563 341 ] 956 963 655 964 [ 341 329 889 959 776 650 653 741 691 580 588 512 649 568 954 518 ] 980 981 752 982 [ 650 645 891 766 747 735 563 665 617 523 510 495 497 403 381 509 490 245 ] 1000 1001 493 1002 [ 512 476 404 510 501 515 446 481 587 467 605 645 403 497 496 582 665 404 508 669 544 453 523 403 509 ] 1027 1028 245 1029 [ 510 ] 1030 1031 481 1032 [ 645 245 481 ] 1035 1042 523 1043 1048 403 1049 1056 509 1057 1064 245 1065 1070 510 1071 1078 481 1079 1086 645 1087 1088 523 1089 1090 403 1091 1092 509 1093 1094 245 1095 1096 510 1097 1098 481 1099 1100 645 1101 1108 523 1109 1116 509 1117 1124 645 1125 1130 523 1131 1135 509 1136 1141 245 1142 1145 481 1146 1147 501 1148 [ 481 ] 1149 1153 645 1154 [ 523 481 ] 1156 1159 230 1160 1171 400 1172 [ 353 ] 1173 1177 400 1178 1179 405 1180 [ 400 653 767 654 741 666 958 960 720 840 581 644 956 636 439 501 486 389 490 425 726 408 ] 1202 1203 555 1204 [ 500 494 640 553 510 552 524 423 441 459 672 472 556 507 771 775 566 681 468 440 707 500 425 500 389 449 367 ] 1231 1232 268 1233 [ 256 673 719 533 500 468 545 689 547 736 511 680 467 477 366 428 356 411 872 974 1124 1133 957 457 603 623 830 1006 806 1408 1744 1095 643 566 821 836 906 1602 1675 1584 427 892 ] 1275 1276 745 1277 [ 465 619 776 427 341 566 892 ] 1284 1287 400 1288 [ 747 736 525 547 ] 1292 1293 480 1294 1305 691 1306 1313 568 1314 1315 341 1316 1327 747 1328 1334 736 1335 1337 634 1338 1349 439 1350 1357 425 1358 1359 268 1360 1366 510 1367 1371 525 1372 1373 531 1374 1378 547 1379 1381 459 1382 1393 637 1394 1401 606 1402 1413 565 1414 1421 482 1422 1423 306 1424 1436 584 1437 1444 582 1445 1447 511 1448 1457 400 1458 [ 392 ] 1459 1480 400 1481 [ 565 511 531 584 482 456 565 621 306 297 558 460 709 580 584 484 585 528 408 510 582 567 761 551 511 493 611 621 582 510 579 611 481 431 723 776 603 ] 1518 1521 565 1522 [ 723 ] 1523 1524 565 1525 [ 568 ] 1526 1528 565 1529 1530 531 1531 [ 528 ] 1532 1533 531 1534 [ 584 ] 1535 1542 482 1543 [ 487 ] 1544 1548 565 1549 [ 621 ] 1550 1556 306 1557 [ 308 306 297 558 ] 1561 1564 460 1565 [ 478 709 ] 1567 1571 580 1572 1578 584 1579 [ 582 584 ] 1581 1583 528 1584 1585 408 1586 [ 412 ] 1587 1588 408 1589 1590 510 1591 1597 582 1598 [ 584 ] 1599 1600 582 1601 1604 761 1605 1609 511 1610 1612 493 1613 1624 565 1625 1632 482 1633 1634 306 1635 1647 584 1648 1655 582 1656 1658 511 1659 [ 477 366 617 305 356 227 400 159 226 306 159 ] 1670 1671 105 1672 [ 495 565 762 916 297 223 480 461 480 486 480 472 468 486 ] ] >> +[ 16 0 R ] +<< /BaseFont /WKJDTW+MinionPro-Regular /DescendantFonts 17 0 R /Encoding /Identity-H /Subtype /Type0 /ToUnicode 21 0 R /Type /Font >> +endstream +endobj +19 0 obj +<< /Length 11 >> +stream +@@endstream +endobj +20 0 obj +<< /Subtype /CIDFontType0C /Length 2042 >> +stream +WKJDTW+MinionPro-Regular.  q ";3 % $ AdobeIdentityCopyright 1990, 1991, 1992, 1994, 1997, 1998, 2000, 2002, 2004 Adobe Systems Incorporated. All rights reserved. Minion is either a registered trademark or a trademark of Adobe Systems Incorporated in the United States and/or other countries./FSType 8 defMinionPro-Regular%')18EFKLMPSTQRrO5N(0$PP8>>>>>>>>skksrkhtحn؅5%65pŹ hZM-$n\2$45 OBj‹og˯Lo؅4"5;o0IGToK~0$סovxcmoل866ޓגo݄7(7;o9\\76o:(ޓݒ䋧~ЫonoՆ3$8;o11xqdcRD:0];hJjp~v D&Pqoڂ}O{DTXaN@Q6S8nX7Q9V>_"dd|Ԕoȃ9FDH55D%U4Q˩ޔДrgww}kff~H|WsӆI!tn} BۗȯJoŠ֘/ysg{gKB %߲{hZTZige{cE= H8WUp]bb^rNF'Н=­[W}yQTSܚnvXw8upotumps&'ɂGW0P`hu|~ypk|sw׭`yY~Vw{w&6eqgi& +wЗ`qwlxpmduu|{y~mkd~Rz[tȅImKHqtKzH;XqBwKiMzWsʆGmILqU0@9 +5.j, X]M9T$?׎wI[v_ZuăIcIMqz>Ká|ȑsi[`SYm !ƃvpb/OPE>jots_aiofO^yqgG}|xX@^ccP + &|I%F_ + wnendstream +endobj +21 0 obj +<< /Length 535 >> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (Adobe) +/Ordering (UCS) /Supplement 0 >> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange +<0000> +endcodespacerange +15 beginbfchar +<0001> <0020> +<000F> <002E> +<0025> <0044> +<0027> <0046> +<0029> <0048> +<0031> <0050> +<0038> <0057> +<0045> <0064> +<0046> <0065> +<004B> <006A> +<004C> <006B> +<004D> <006C> +<0050> <006F> +<0053> <0072> +<0054> <0073> +endbfchar +endcmap CMapName currentdict /CMap defineresource pop end end +endstream +endobj +22 0 obj +<< /Type /XRef /Length 92 /W [ 1 2 1 ] /Info 3 0 R /Root 1 0 R /Size 23 /ID [<8a13b2ae921cb5419d1916dde4fddd31><4c541e535733f0f223294ad893dc8b62>] >> +stream +ak<0 C     ,,547~ +endstream +endobj +startxref +14206 +%%EOF diff --git a/test/test_manifest.json b/test/test_manifest.json index 9247b545b..f1b8f1d56 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -2749,6 +2749,21 @@ "rounds": 1, "type": "eq" }, + { + "id": "bug1947248-text", + "file": "pdfs/bug1947248_text.pdf", + "md5": "491f1df75b77d2762ff96ce51f5e019b", + "rounds": 1, + "type": "text" + }, + { + "id": "bug1947248-forms", + "file": "pdfs/bug1947248_forms.pdf", + "md5": "456c974d7d4351719f36ef10e603d29c", + "rounds": 1, + "type": "eq", + "forms": true + }, { "id": "issue4801", "file": "pdfs/issue4801.pdf", diff --git a/web/annotation_editor_layer_builder.css b/web/annotation_editor_layer_builder.css index 179f866de..d6a5e6827 100644 --- a/web/annotation_editor_layer_builder.css +++ b/web/annotation_editor_layer_builder.css @@ -123,7 +123,7 @@ background: transparent; position: absolute; inset: 0; - font-size: calc(100px * var(--scale-factor)); + font-size: calc(100px * var(--total-scale-factor)); transform-origin: 0 0; cursor: auto; @@ -512,7 +512,7 @@ } .annotationEditorLayer .freeTextEditor { - padding: calc(var(--freetext-padding) * var(--scale-factor)); + padding: calc(var(--freetext-padding) * var(--total-scale-factor)); width: auto; height: auto; touch-action: none; diff --git a/web/annotation_layer_builder.css b/web/annotation_layer_builder.css index 21c953b4f..ea2ed830f 100644 --- a/web/annotation_layer_builder.css +++ b/web/annotation_layer_builder.css @@ -50,7 +50,7 @@ } .popupAnnotation .popup { - outline: calc(1.5px * var(--scale-factor)) solid CanvasText !important; + outline: calc(1.5px * var(--total-scale-factor)) solid CanvasText !important; background-color: ButtonFace !important; color: ButtonText !important; } @@ -67,7 +67,7 @@ } .popupAnnotation.focused .popup { - outline: calc(3px * var(--scale-factor)) solid Highlight !important; + outline: calc(3px * var(--total-scale-factor)) solid Highlight !important; } } @@ -169,7 +169,7 @@ background-image: var(--annotation-unfocused-field-background); border: 2px solid var(--input-unfocused-border-color); box-sizing: border-box; - font: calc(9px * var(--scale-factor)) sans-serif; + font: calc(9px * var(--total-scale-factor)) sans-serif; height: 100%; margin: 0; vertical-align: top; @@ -296,7 +296,7 @@ .popupAnnotation { position: absolute; - font-size: calc(9px * var(--scale-factor)); + font-size: calc(9px * var(--total-scale-factor)); pointer-events: none; width: max-content; max-width: 45%; @@ -305,11 +305,11 @@ .popup { background-color: rgb(255 255 153); - box-shadow: 0 calc(2px * var(--scale-factor)) - calc(5px * var(--scale-factor)) rgb(136 136 136); - border-radius: calc(2px * var(--scale-factor)); + box-shadow: 0 calc(2px * var(--total-scale-factor)) + calc(5px * var(--total-scale-factor)) rgb(136 136 136); + border-radius: calc(2px * var(--total-scale-factor)); outline: 1.5px solid rgb(255 255 74); - padding: calc(6px * var(--scale-factor)); + padding: calc(6px * var(--total-scale-factor)); cursor: pointer; font: message-box; white-space: normal; @@ -323,7 +323,7 @@ } .popup * { - font-size: calc(9px * var(--scale-factor)); + font-size: calc(9px * var(--total-scale-factor)); } .popup > .header { @@ -336,19 +336,19 @@ .popup > .header .popupDate { display: inline-block; - margin-left: calc(5px * var(--scale-factor)); + margin-left: calc(5px * var(--total-scale-factor)); width: fit-content; } .popupContent { border-top: 1px solid rgb(51 51 51); - margin-top: calc(2px * var(--scale-factor)); - padding-top: calc(2px * var(--scale-factor)); + margin-top: calc(2px * var(--total-scale-factor)); + padding-top: calc(2px * var(--total-scale-factor)); } .richText > * { white-space: pre-wrap; - font-size: calc(9px * var(--scale-factor)); + font-size: calc(9px * var(--total-scale-factor)); } .popupTriggerArea { diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index f4605b141..85afeb845 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -147,6 +147,8 @@ class PDFPageView { #textLayerMode = TextLayerMode.ENABLE; + #userUnit = 1; + #useThumbnailCanvas = { directDrawing: true, initialOptionalContent: true, @@ -314,7 +316,16 @@ class PDFPageView { } #setDimensions() { - const { viewport } = this; + const { div, viewport } = this; + + if (viewport.userUnit !== this.#userUnit) { + if (viewport.userUnit !== 1) { + div.style.setProperty("--user-unit", viewport.userUnit); + } else { + div.style.removeProperty("--user-unit"); + } + this.#userUnit = viewport.userUnit; + } if (this.pdfPage) { if (this.#previousRotation === viewport.rotation) { return; @@ -323,7 +334,7 @@ class PDFPageView { } setLayerDimensions( - this.div, + div, viewport, /* mustFlip = */ true, /* mustRotate = */ false diff --git a/web/pdf_viewer.css b/web/pdf_viewer.css index 97dcb6cd7..4599f0795 100644 --- a/web/pdf_viewer.css +++ b/web/pdf_viewer.css @@ -100,6 +100,8 @@ } .pdfViewer .page { + --user-unit: 1; + --total-scale-factor: calc(var(--scale-factor) * var(--user-unit)); --scale-round-x: 1px; --scale-round-y: 1px; diff --git a/web/struct_tree_layer_builder.js b/web/struct_tree_layer_builder.js index 0741e598e..dbc9d7ee4 100644 --- a/web/struct_tree_layer_builder.js +++ b/web/struct_tree_layer_builder.js @@ -204,7 +204,7 @@ class StructTreeLayerBuilder { img.setAttribute("aria-label", removeNullCharacters(alt)); const { pageHeight, pageX, pageY } = this.#rawDims; - const calc = "calc(var(--scale-factor)*"; + const calc = "calc(var(--total-scale-factor) *"; const { style } = img; style.width = `${calc}${bbox[2] - bbox[0]}px)`; style.height = `${calc}${bbox[3] - bbox[1]}px)`;