From 1bc41a433b9e112f1dd11b1afa9b25633b967caa Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Sun, 15 Dec 2024 11:13:06 -0500 Subject: [PATCH 1/2] Add visual tests for textToPoints, addTextToContours --- src/type/p5.Font.js | 19 +++- test/unit/visual/cases/typography.js | 87 ++++++++++++++++++ .../000.png | Bin 0 -> 1810 bytes .../metadata.json | 3 + .../000.png | Bin 0 -> 1744 bytes .../metadata.json | 3 + .../Fonts can be converted to points/000.png | Bin 0 -> 1540 bytes .../metadata.json | 3 + .../Sampling density can be changed/000.png | Bin 0 -> 1918 bytes .../metadata.json | 3 + 10 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 test/unit/visual/screenshots/Typography/textToContours/Fonts can be converted to points grouped by contour/000.png create mode 100644 test/unit/visual/screenshots/Typography/textToContours/Fonts can be converted to points grouped by contour/metadata.json create mode 100644 test/unit/visual/screenshots/Typography/textToPaths/Fonts can be converted to drawing context commands/000.png create mode 100644 test/unit/visual/screenshots/Typography/textToPaths/Fonts can be converted to drawing context commands/metadata.json create mode 100644 test/unit/visual/screenshots/Typography/textToPoints/Fonts can be converted to points/000.png create mode 100644 test/unit/visual/screenshots/Typography/textToPoints/Fonts can be converted to points/metadata.json create mode 100644 test/unit/visual/screenshots/Typography/textToPoints/Sampling density can be changed/000.png create mode 100644 test/unit/visual/screenshots/Typography/textToPoints/Sampling density can be changed/metadata.json diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 9b42b4e763..ab6f23d1f5 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -133,10 +133,23 @@ function font(p5, fn) { ({ width, height, options } = this._parseArgs(width, height, options)); // lineate and get the glyphs for each line - let glyphs = this.textToPaths(str, x, y, width, height, options); + let commands = this.textToPaths(str, x, y, width, height, options); // convert glyphs to points array with {sampleFactor, simplifyThreshold} - return pathToPoints(glyphs, options); + return pathToPoints(commands, options); + } + + textToContours(str, x, y, width, height, options) { + const cmds = this.textToPaths(str, x, y, width, height, options); + const cmdContours = []; + for (const cmd of cmds) { + if (cmd[0] === 'M') { + cmdContours.push([]); + } + cmdContours[cmdContours.length - 1].push(cmd); + } + + return cmdContours.map((commands) => pathToPoints(commands, options)); } static async list(log = false) { // tmp @@ -1122,7 +1135,7 @@ function font(p5, fn) { } let opts = parseOpts(options, { - sampleFactor: 0.05, + sampleFactor: 0.25, simplifyThreshold: 0 }); diff --git a/test/unit/visual/cases/typography.js b/test/unit/visual/cases/typography.js index 93014a4ed6..15e33079e2 100644 --- a/test/unit/visual/cases/typography.js +++ b/test/unit/visual/cases/typography.js @@ -409,4 +409,91 @@ visualSuite("Typography", function () { screenshot(); }); }); + + visualSuite('textToPoints', function() { + visualTest('Fonts can be converted to points', async function(p5, screenshot) { + p5.createCanvas(100, 100); + const font = await p5.loadFont( + '/unit/assets/Inconsolata-Bold.ttf' + ); + p5.background(255); + p5.strokeWeight(2); + p5.textAlign(p5.LEFT, p5.TOP); + p5.textSize(50); + const pts = font.textToPoints('p5*js', 0, 0); + p5.beginShape(p5.POINTS); + for (const { x, y } of pts) p5.vertex(x, y); + p5.endShape(); + screenshot(); + }); + + visualTest('Sampling density can be changed', async function(p5, screenshot) { + p5.createCanvas(100, 100); + const font = await p5.loadFont( + '/unit/assets/Inconsolata-Bold.ttf' + ); + p5.background(255); + p5.strokeWeight(2); + p5.textAlign(p5.LEFT, p5.TOP); + p5.textSize(50); + const pts = font.textToPoints('p5*js', 0, 0, { sampleFactor: 0.5 }); + p5.beginShape(p5.POINTS); + for (const { x, y } of pts) p5.vertex(x, y); + p5.endShape(); + screenshot(); + }); + }); + + visualSuite('textToContours', function() { + visualTest('Fonts can be converted to points grouped by contour', async function(p5, screenshot) { + p5.createCanvas(100, 100); + const font = await p5.loadFont( + '/unit/assets/Inconsolata-Bold.ttf' + ); + p5.background(200); + p5.strokeWeight(2); + p5.textAlign(p5.LEFT, p5.TOP); + p5.textSize(50); + const contours = font.textToContours('p5*js', 0, 0, { samplingDensity: 0.5 }) + p5.beginShape(); + for (const pts of contours) { + p5.beginContour(); + for (const { x, y } of pts) p5.vertex(x, y); + p5.endContour(p5.CLOSE); + } + p5.endShape(); + screenshot(); + }); + }); + + visualSuite('textToPaths', function() { + visualTest('Fonts can be converted to drawing context commands', async function(p5, screenshot) { + p5.createCanvas(100, 100); + const font = await p5.loadFont( + '/unit/assets/Inconsolata-Bold.ttf' + ); + p5.background(200); + p5.strokeWeight(2); + p5.textAlign(p5.LEFT, p5.TOP); + p5.textSize(50); + const cmds = font.textToPaths('p5*js', 0, 0) + p5.drawingContext.beginPath(); + for (const [type, ...args] of cmds) { + if (type === 'M') { + p5.drawingContext.moveTo(...args); + } else if (type === 'L') { + p5.drawingContext.lineTo(...args); + } else if (type === 'C') { + p5.drawingContext.bezierCurveTo(...args); + } else if (type === 'Q') { + p5.drawingContext.quadraticCurveTo(...args); + } else if (type === 'Z') { + p5.drawingContext.closePath(); + } + } + p5.drawingContext.fill(); + p5.drawingContext.stroke(); + screenshot(); + }); + }); }); diff --git a/test/unit/visual/screenshots/Typography/textToContours/Fonts can be converted to points grouped by contour/000.png b/test/unit/visual/screenshots/Typography/textToContours/Fonts can be converted to points grouped by contour/000.png new file mode 100644 index 0000000000000000000000000000000000000000..ab15a8ffbf52070d1dcdb5cac2bed18d6083bf16 GIT binary patch literal 1810 zcmd6o{UZ~I1ICBi&5-523v1(M-tuyhIxn@D$@?NSvSYZe$#CAbL$PMIVj=s=k=L&r zbKYu(c?-=;oR@Lw(2x!hhE?9q_rLi5@I0T-PtUJUx`&&i0{B}n002;M!q{VvNcdks zz@sM3J@z_+R5aER1*jR)Tmk@qgHHCBz2a}K;#}PbHu4|alni4#0(>K7-zil$$jF2sw50wsBqFMx6lqx|TiN5NjD3)a zI5JwtAz@e|k%$%-5t8`dF}0Xza}LW zwllE{!6&0VQTDy@pD9)Yb_!@iMtD>SpMg@kYl{FyKx84VHYKc$^_oZe?5)=w; zf0;h5sHhkgLf`+&L&AcPpgbw$lW~_o%8TRrB)=fZ6rbruwVhav=SQ zYYd3J_7s;PlC~^48L>TgSPH?mn*jVeUlw*sWv5u%_ll#kn8>ZLqqwUji! zMhrT5{|YE9YcBx!D0y~zdOt(2%71tNryz=Q$qHJ2=mDw>nqy^smuCbIH-}l9YN7qK zF{}o8-O9tCI*3Sy{6w^$wDiE!%jx5KMgMpY#ON>~Lr+Aq@p~gQJBFH#(!DWLvn|kN z4FiH8wHtUiks68_+1A>U!ozu)fVrsS>S8MhiwF1o!OTZCNItI>1D zQ{}!g!9qX70Ucc|aQxzCCxEWnAqTcFRWu5h0d1&V3cWV@zF0`@K$_)Bg(g>hqs6JF zLz)~o*3Okv+5!Jvr*C|v;ecN}Rq8lUo&qQ*i!QfC^>sVl+^ z(1@JNF=Q1{+2Ns12#zvnfGT14Xei=ML~~%|ADEddjkugxHPL1ZmwfVInJ|HU7@KDG z8i!TDr&#LgW}kC_HOx4o)2@*%qHx2;q01i4@^?7P&rh`oKX^mnw9;k9D06R-gU(!k zKrRpbv~Yq!m3v$IOTz_Np7*>|uBQ*8KFiV>0i3HFX{eIU(+n4#Pl$NkR#!E^=(3f+ zBlY4t*o18lfV@jr1&S$x2*^2t(%X;l8_g_Q`j0>_lhOXx`~v@+X&f~^Y>MQnX|;Z! z^4PN8@)HWv>rTOkJJHqq61eAE6R)Id35B@}(3a+E3o1bZb-}9y4Xwd)e}1E7_UH9u zxpg+BIesnW!?n2`tlj zOe_9ME^n*o#xGINMx{?Y%zTFIt4d;GZmXlZF=xFyo}XdYf@5@Qs|Bx?p$khGP*KPx zFtPRGMfV?tp;@n)Jfl;-X$;d|c(6(aIUku_qqnRIa%8_Y$1hnTb8m2E*;`!aoF3Yl z23082QGLH;;b$-KBiqQGdk37~iHJjA@Vkjm;cN9J$|xJDj1B%{wMXXC4uPuunoD;S zHeHhQmAk@VHy^8%iPrU^U!^;3T>VI%n?%z-)R%NeYwVcAS|bE;_Lyd3;dW>9a#chV zy(L9%-s}ZEDfD{6cnhNNmGUpAC@zFfgfLuUie>Y3yQ^j&Hj7Rhd*=M3)OBj( z`tyQ59-DaQ{>qH-M&8{vg2w*pKp@o@L{Q>q308?Y2%yjH5Z?t?=S@8}ixGJ^q~RX- zw68ccvQ_Z3(RFwaeDfViJz*?suToYY9jiP_Rk>31p9>roqD(`&XYkhjbFdx=GStnm zOje(E$t?N?%oSsHjFC46zJ+4IJO+Qej#0+S-wRr7L_J{zfmFV)Q2l?H_+aM}dG`%H Sb->{0rj_p|rF#pTYa^$t z>2v2x$+Zooq@zP&;f@ITE)-(*alZCQ?&JQ8&kwKH^Ox7-_2cW6>+j1#7+D!XAP@xG zo59&;)IS1tY`b{s$G~mq#dBD6h~`h5c?e{Ol+Ex6OcCfpJ$xPZ8y9~>?}=*)JGca! zG%enMiK2!92*-eEh2$bD%H^lRh2oLSE}#ac@!AXzH727K@(&74BWm* zNiGE|?^JVPsO+nW(x))OBL=1v*}aA@AJh*T-PwMXg@RSrR1CkYs;av7=KT533qolg z1)e(~m$1xA`}}C_SXj%2{52jJUM9=wnUEtdrK&0AF;{((s^HvHfi?n%s0RSb`G>sd z9q@ z5s;UYGo(bumfC^2HNcXTl-D;LqC#4ta1(G1ADk=3L@@pyzfl+h-N&i+KjTC@K*wvZ zn5!DiyJbG+IGeB3)ru<5=dKUVRd*$R-#_)p5_iW%?biIR@1j|^K9xR0`4>9haP=}%7@LGvAb#7& zn(VPjjsJjcSq&n+t)elqQ5T;H4E#JVM`Ya(T2}QZ$Y!vF>Rt3ne<%~^O5E&2e?M3oh(@`f&FlBR+_m58np>?@L?)BrgrAP>`6`&4Y%*YWrRe)uPL8E{ z|4ubqjd&q+ytnf{ho=+j9Q@Va*lG{j(TS)byp*ve$T@QR00&HybbU!rJC^`aWiFS; zjdhm*vZ4!()!LG_5pl@O9QMY8kMbmAmDrt+qk$=j%)*ICJsUSiF$-3A@wJ9{W)AqK zI?*TiJDsEwIGvwsA8NAiKNay7%^g8r0TGJ?wDfGsGpVdihW=t}3lFB$++*?Y94jsD z9!@R|c8qknZo`RCYHGw{lB{qZtmQn>nx?_#mmWO;#X|j#p2S>c;j0rDmL zzmZlxJ2c*!T6vDBzrs0p*JXX&2tyIFf3I9eUTSo3tF+$zY~k8zWy7av(O-SLdQB^6 z)p+_Ng{4QBQdaMHiSE>^2u<+*=Ln#JdswUSK}Th7d8@B!k^;}*X9sPat4e) zO7=W)kcWK#Fk9^0-a+Mwki;j;Q!k#$T$C6ihI;yxUzsz0<^B2J_A)vx#-V-|)3t6Y zajPuumeK|LcG5ml;}Kl17FT4(iF!k+w|yk9VL@=~iFxw!hG0k2U077{Kx$4R2cz>_ zZ2pN1#h4r@J9RHaTOhvVYNV$(89`szY?1Xp_Fop%%urNIv=+%zE#-p}drz^&ot)3g zy;c@E5)TTq9s?@wkRPdA^b0qU15v>%dGo`wS%`Eg06VspK;Vm_=n>flE6YlQ6- z{X1OrWZdn3k1WK! zU0}e>(hV1JEO3iI#w7pOqc@M~`Z|ShM#mmAmhd8D5<_qaqR*t`+Hu(*ho1OOe0*gg z*)td;_&n3kU}}Ha8Qkdn;g9oG;Y9?Q!edfBLJ2(G&FYZPdI?F?lSUZ3qw7-}{S`5m zz6xGDg6R#GFEy_Ag5%ZD+w1K1+&XyLU8meq;2ki~=Us#>Gu(5eceF2|9AIBQ;u3!p$MkkF zD7YSAM4%Vo6kWv}Z&ZPCX4Kn4x>OMbIJj1r{XbO9S}!L&9I}3xSi5~u5H{17p`nLg F`X7r65e)zU literal 0 HcmV?d00001 diff --git a/test/unit/visual/screenshots/Typography/textToPaths/Fonts can be converted to drawing context commands/metadata.json b/test/unit/visual/screenshots/Typography/textToPaths/Fonts can be converted to drawing context commands/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Typography/textToPaths/Fonts can be converted to drawing context commands/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Typography/textToPoints/Fonts can be converted to points/000.png b/test/unit/visual/screenshots/Typography/textToPoints/Fonts can be converted to points/000.png new file mode 100644 index 0000000000000000000000000000000000000000..72c80c74638f2c8b8d2a93ad0f0fe8fd6fe7e8bc GIT binary patch literal 1540 zcmd^<>pK$)0LGV;$z@|YZYy?iTdPxU4ci<`w#k$o(%dU-56(%Zv?RBkGppP$4Vh_) z5Sdcsl8S~h$!&>La?Rz5$)Vde*xUS z*^wV}JvX(5NhJ~h)jiM|0AO1S+1bhSLcqN57I*Xk;3zKtqNOZ&wsVz3*)J}`GU&qw z(&xO)`u&R!Ks;K_!;$&(GZI^%Utz{yj~=P?vA?tvAtHSv3JD@IR4QzLV>b~@7<>PI zaW|jO-zSksdVL{EFdxGuxtz6`2Ju4DhAo_KNQlmm&e;ry(IHOJ@yFHHz=UbN*5+n^ zJRZN`a9D-cT>X#LWGdjNLebdN(z0l?qb8ifKUI(1DaN;tWMswgD#G zZ2Pf#R_NyxOf#GEj+y0aF1jGwUHjaSky-SH!CL$Td+BU_R>9!dYn%6CMj4FS<~{kj|IcJOPXW+yCpO6#g||JnsMiyfZi!tTXbG^1Oi<3+mT za`OP4Y_xV&Q^i#va`79gy9+j`ab%vT7Hig1X$fz{3D<$cub<(qB@XSG6l*LDVcc|j z2A9nR7-n{6o`-<*a(c-E(2-GBXbi=B;PdQ720lWaerErlV6id6E=4Rzmt_&p2qHcX zv_IG1`QfCUvI$kScRHAsFjq@86(!8-v0A?E@KO|0`Vm}58_osx300;Ja*Mko_C=Tu zt6JsQEwB^0NUZ=7;@**ldgiIg6?4kC<}jDQ??w-_j(>;EJ9aHhX6z&!v&(NzB!P7N zmtUr8b5GiHBJHlB@;p8|qF_CLt5=Q;r55+ENvW5elT03pi4~wuOxl;6mwMseLS?KH z`|+Ab9go{>80%^z(tej|`DrImp=|Ua=C<6g?aEZ6U9btrImA>c;5TSx{%QGVZ*^_# zo`0oy6rn3xv!vZ)_r2(XWP`-LrN};2dRy(ORom-+fgMmOnk4F4&(JT*VS+yhDO$Kh zbBS2iV5T33K#JBs^YkOeg%Ih)^b4tQu`JZ2!`_3D%Gfq-Q)CddDi>ciaZcGc(7OO7 zUo*qejHIKDZ`MkomW^7sYOF#k_*P#NFD)u(3H)im4#KIA0sp(t@rT`rQ{Ty4_)b~ z=m=q&T~V6^-+vNVm!88eB(IorTMuskIb)@;U#Zaw^5O&x%;21sc1Mbeqc!Do>>F`E ziw>D;^;|MEUCua5A|2sLa)y#;TEg-(gnKxhl(q2NC3qAQ;U5yx8`a@rbPXOu0TRlG z$Jjbs)85&@fF-A5Ayjot)DYX4ePy0PIxsHwha`bf^#@_?35fs6cmuQsT>gFeC1qpt P)&b2OUEm6XDou+FXVvk6bD$ zm(7m3KJG=C?TI-xQpdGsE{h>Vx5x8uJnz@*^LhXF`So+_mve6Fs(Pv*5J=s_9q`#R z;eUaE_nI`v^4*g{qK_L6)G&N#5d>1U^8lQD$@mW;YUfZGXzY1#8PrafWLc4O;<9|> z$kg+z1)(b1tqn#0`P3`i+}OjC`_h9qZ11>lur> z48)TqpL^kyL_x6p0J&9yP1Y|546_7CwFr|VT(esIn#hBROdk5zr?swk4OIS|Oi;Ff z^60@q?v$67)YvW{mw$wNKFw`iF^E7b=AHh5I#U|R_1h$zOwRa*s7;;4+0Ye@y=UX% z9r6lL`ppN{^MFdu6%ND7$n;n1fhCxXvwap)i0q5JQ~W2$7bCYzXwcS$X7PTNZv z7bjEH|E5b9uwpYz!dSC_=a@Cl4%E~rwvMTA$s%LG4ep{*B9dZP-d^z0g)3$*uImEf zlSkXg*Ify~>&nO5C_*D`fkH_N0zxud&&j?kYi6XLy)?HP? zOk|*QoW`CI@rtR<*xbWC4hh)-yNzO!NAwU8gVocw*eQ+uo%i56y-cQcbH9kW4C67E zk!gH2;IbU?Q|1bq%`2Fe!TE>>kQ4x&s~M3C2F6sAP>mC(jA;n4!Me9$;p!avWGGMt ztp~=ZSes|x)W@yE%G(d313BP9&)e&hd}ljNKwN5T^T+QpHz?71JJ3+2im?&~OwF<; zAh|2ewgPy;jlMZ_%%drs9B5x|1e4smNRZ@Rwy-%l(_QwSU?+yXPo5wU*`4vKjU)vvd8 zY$2sU7N*sO;}n_jekW_Y(e=V&8$L00G#Pw86Jx^#n$R=$}GZ6rzlm^X(F zR|h0-`LBG`dk1~Vqm`+Yq{{Y3WHSyKij8H#ZCO=o>cf_XJ_KuCS`80;e_c15L9bk6 zn!tlJoRuHo= z>Mz{-{w+e;gW-1gLgYj%g|2d5wtOa9q7cv7G5`LLua7VGPuSZEyu3Eq&hFZcQ9JNz z-I=;wcr`t+=?m;HD9P_D%*TZtln(g~++LRssWllxJ5Wz3hWa^BeYIkWjc}z34RuDi zx@>EqCj12`K&%P(8z_~~m{FPaOEjk0r5EeY?pA$%oBt2<8EjrzC_Ujf&%HwhdAOVd J8gSuR{{?H Date: Sun, 15 Dec 2024 11:21:50 -0500 Subject: [PATCH 2/2] Maybe not using TOP align fixes discrepancies? --- test/unit/visual/cases/typography.js | 12 ++++-------- .../000.png | Bin 1810 -> 1865 bytes .../000.png | Bin 1744 -> 1848 bytes .../Fonts can be converted to points/000.png | Bin 1540 -> 1479 bytes .../Sampling density can be changed/000.png | Bin 1918 -> 1893 bytes 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/unit/visual/cases/typography.js b/test/unit/visual/cases/typography.js index 15e33079e2..fb19910e5f 100644 --- a/test/unit/visual/cases/typography.js +++ b/test/unit/visual/cases/typography.js @@ -418,9 +418,8 @@ visualSuite("Typography", function () { ); p5.background(255); p5.strokeWeight(2); - p5.textAlign(p5.LEFT, p5.TOP); p5.textSize(50); - const pts = font.textToPoints('p5*js', 0, 0); + const pts = font.textToPoints('p5*js', 0, 50); p5.beginShape(p5.POINTS); for (const { x, y } of pts) p5.vertex(x, y); p5.endShape(); @@ -434,9 +433,8 @@ visualSuite("Typography", function () { ); p5.background(255); p5.strokeWeight(2); - p5.textAlign(p5.LEFT, p5.TOP); p5.textSize(50); - const pts = font.textToPoints('p5*js', 0, 0, { sampleFactor: 0.5 }); + const pts = font.textToPoints('p5*js', 0, 50, { sampleFactor: 0.5 }); p5.beginShape(p5.POINTS); for (const { x, y } of pts) p5.vertex(x, y); p5.endShape(); @@ -452,9 +450,8 @@ visualSuite("Typography", function () { ); p5.background(200); p5.strokeWeight(2); - p5.textAlign(p5.LEFT, p5.TOP); p5.textSize(50); - const contours = font.textToContours('p5*js', 0, 0, { samplingDensity: 0.5 }) + const contours = font.textToContours('p5*js', 0, 50, { samplingDensity: 0.5 }) p5.beginShape(); for (const pts of contours) { p5.beginContour(); @@ -474,9 +471,8 @@ visualSuite("Typography", function () { ); p5.background(200); p5.strokeWeight(2); - p5.textAlign(p5.LEFT, p5.TOP); p5.textSize(50); - const cmds = font.textToPaths('p5*js', 0, 0) + const cmds = font.textToPaths('p5*js', 0, 50) p5.drawingContext.beginPath(); for (const [type, ...args] of cmds) { if (type === 'M') { diff --git a/test/unit/visual/screenshots/Typography/textToContours/Fonts can be converted to points grouped by contour/000.png b/test/unit/visual/screenshots/Typography/textToContours/Fonts can be converted to points grouped by contour/000.png index ab15a8ffbf52070d1dcdb5cac2bed18d6083bf16..0e53ac0a0f8c7ca58f7611eb8d8a52bb7a093bae 100644 GIT binary patch literal 1865 zcmdUw`6CmI1IFi?Ij0OUW^P`ZE3YHdWQ^2$qhnLfx!xplWH#4axpGC4+?{ewycOon z9L<@?5i*t)F~=Oa-uC)_|BLsB&+|M#JU>0ZJgI+RF%qH*q5uFu!oeQxbm-py0|Y$O zU{?OcL*ol|!kh-w4k|7J0Kx(eXzPnaH%^hYlNwSyY3@w*1Yd+_mqwgpJqz&;pigw-flNUlj8Af{BN`Jxw*8Sn>he&vv z2+1y;uTzomHarYivb_NQETyY?1U8J4b@rgHG~PLp-37r_pXB_%lGd`Zw?|sv-F?e@ z+uS_Kn`Saw&If{ZfFaS*(QATyENeV4rN3+$<2%)>ye_%>mPVry$>bJdOiUY*MA}>7 z_VxA65OzXk;lYGOh9P|3Nug3M-(p6jsCl(B~FTUL-m7;Yk!dle)tqR!9P&mC%vS= zLz(q?hMc06DbD(|W z3nhNfTV`Ucp(hcOMHFwx409~Z=vrqUP|2(o8DV+q_iwv9ZO*IYT}Ly1k-Umg{;!HY63 z8?Q{@#68W>Tm@BFMq(lo-B#){PJd-8Y}9+dqOe{@Dl)SPYfF8wx9ggWh@q2Ps{A|JnpNcd5%b~{~!lYtoPB)aD zFWqG!e~JYp9gIP*Nn|0_En0SN!MX1)oZVt=GB`AeBH=Daa%DE-w9T6Zc|C*GRMk1! zQV|&x68<MT^PR-e@G9ToVPMc$*ZIwI8p#m!l3mK%%HeI=&6D61O zi`O%&i87qSrG87eDlof|_WanN0|)jHM0(Jo(#Oy&TAR5PNRgKC@&mZyVu4sjPN41$ zJN#!V|IKK3d8x3xtee!?ji6mvd)#r?Ep~{dSONi;*u$Kaue3$>O5R~dU?Va5@pGKa zWoXh2CD+vXN&;oLEgwV_>DNRVENwYl%R4ie44Cj)m}n2JLI%_|7h4gisvTK?7qEpO z>W8`eynaAQJjA8D65Lr4TJR#R>3|O3rX9dAp%Lh zyicfHRO_URx9|3&E+INQLlV#H85m729H^P8v@K-0qvok*o8KeuDKMBD?pLa}+=c7| zmSiRhEk5%}#e|`aLq=l%X^zM%dtdzV){fl8XRYenbse+o_f3p4y Yyv$7aTvJ=RefW(42U{$<_OwU*zst>iiU0rr literal 1810 zcmd6o{UZ~I1ICBi&5-523v1(M-tuyhIxn@D$@?NSvSYZe$#CAbL$PMIVj=s=k=L&r zbKYu(c?-=;oR@Lw(2x!hhE?9q_rLi5@I0T-PtUJUx`&&i0{B}n002;M!q{VvNcdks zz@sM3J@z_+R5aER1*jR)Tmk@qgHHCBz2a}K;#}PbHu4|alni4#0(>K7-zil$$jF2sw50wsBqFMx6lqx|TiN5NjD3)a zI5JwtAz@e|k%$%-5t8`dF}0Xza}LW zwllE{!6&0VQTDy@pD9)Yb_!@iMtD>SpMg@kYl{FyKx84VHYKc$^_oZe?5)=w; zf0;h5sHhkgLf`+&L&AcPpgbw$lW~_o%8TRrB)=fZ6rbruwVhav=SQ zYYd3J_7s;PlC~^48L>TgSPH?mn*jVeUlw*sWv5u%_ll#kn8>ZLqqwUji! zMhrT5{|YE9YcBx!D0y~zdOt(2%71tNryz=Q$qHJ2=mDw>nqy^smuCbIH-}l9YN7qK zF{}o8-O9tCI*3Sy{6w^$wDiE!%jx5KMgMpY#ON>~Lr+Aq@p~gQJBFH#(!DWLvn|kN z4FiH8wHtUiks68_+1A>U!ozu)fVrsS>S8MhiwF1o!OTZCNItI>1D zQ{}!g!9qX70Ucc|aQxzCCxEWnAqTcFRWu5h0d1&V3cWV@zF0`@K$_)Bg(g>hqs6JF zLz)~o*3Okv+5!Jvr*C|v;ecN}Rq8lUo&qQ*i!QfC^>sVl+^ z(1@JNF=Q1{+2Ns12#zvnfGT14Xei=ML~~%|ADEddjkugxHPL1ZmwfVInJ|HU7@KDG z8i!TDr&#LgW}kC_HOx4o)2@*%qHx2;q01i4@^?7P&rh`oKX^mnw9;k9D06R-gU(!k zKrRpbv~Yq!m3v$IOTz_Np7*>|uBQ*8KFiV>0i3HFX{eIU(+n4#Pl$NkR#!E^=(3f+ zBlY4t*o18lfV@jr1&S$x2*^2t(%X;l8_g_Q`j0>_lhOXx`~v@+X&f~^Y>MQnX|;Z! z^4PN8@)HWv>rTOkJJHqq61eAE6R)Id35B@}(3a+E3o1bZb-}9y4Xwd)e}1E7_UH9u zxpg+BIesnW!?n2`tlj zOe_9ME^n*o#xGINMx{?Y%zTFIt4d;GZmXlZF=xFyo}XdYf@5@Qs|Bx?p$khGP*KPx zFtPRGMfV?tp;@n)Jfl;-X$;d|c(6(aIUku_qqnRIa%8_Y$1hnTb8m2E*;`!aoF3Yl z23082QGLH;;b$-KBiqQGdk37~iHJjA@Vkjm;cN9J$|xJDj1B%{wMXXC4uPuunoD;S zHeHhQmAk@VHy^8%iPrU^U!^;3T>VI%n?%z-)R%NeYwVcAS|bE;_Lyd3;dW>9a#chV zy(L9%-s}ZEDfD{6cnhNNmGUpAC@zFfgfLuUie>Y3yQ^j&Hj7Rhd*=M3)OBj( z`tyQ59-DaQ{>qH-M&8{vg2w*pKp@o@L{Q>q308?Y2%yjH5Z?t?=S@8}ixGJ^q~RX- zw68ccvQ_Z3(RFwaeDfViJz*?suToYY9jiP_Rk>31p9>roqD(`&XYkhjbFdx=GStnm zOje(E$t?N?%oSsHjFC46zJ+4IJO+Qej#0+S-wRr7L_J{zfmFV)Q2l?H_+aM}dG`%H Sb->{0E#gnd~rl$shKr~z#H18ef z{*S<}9gUhP^w}}!MQ=IcDa$>l}AV>S<3Nj2evyh?zYh6A{P=3YHqQu$+>b zYN${s5No7BBobNo<3}EHJRfD8eyRQA`uciQYU=kfx;9=_Zl$tk`A%44@@Ksm@eb-j zD=I6IOeV8#`d1Fe^YUn{gV7jH)>cMDz4ZtkdqXwa=zqqq*xugm?d$7vS-qB%0I(74 zp{c2Yx8xvFcO{%!D3SKTj8T08d zn#vrq;q4!ytjWpAGWGfG?F|*u-gGBt=U$gy_guMgO-M*w8qS^bDQ69g4xaTD~>r3jO-vo5Ku9wBNdy3vi7{GJT1c?MXvh zwL3&BQJz5jnb%vaGAC+Zw(4;!FsJjoLQwVBm^#fw#v1{_2t)JvllUlFrnd2aaWwgh zmH_W}h4r<3qK1rhQ7_;O4LDyhmvBimuf~@oN{)#>!MUMxx+Pec2^U1OK4t>%xu`|I zEXUH(p|JaiB6+Pxo`k_tW%yugtnb9S{Is+%tL_O@K38_LcEzD7Es2m_B%F5w(5rz{ zWnqKR6z)h;M@L89?Zx;l=Tndy^7~TJ0~=e-Kd}^&7&hcynSV*=N-Sv@d}6;oTq?;L z8XTHW5cil{`~f99%*rL~BHP&&2ZAOR3Sv2AOJFdi`H;oZcSW&D>FZFHE;1K=q!S&n zFD1m>b)}0yVGxK(+iBHchbuq-qsaOjLd<;Sw~B>yQoHpBI4;_0LW>T zuDbmPfS(FGK@C|F9e_CQjoix&ZPsuAwo2E8na|AXG_Q z>_(T;F6;V|k<>QKurNT7uu1o0*A$n-vz6y|zXz(_V8ojf{UTGn2X`>isj-{x`3Bq|F0E@i%_t{fT)&zeD6y33KnpnM0~JJf6?dcb>fP_EW%@#v zAo;y#(yjdiyM+sa-vjljM9I~bl@ih|^CZ-ssj@i3hH*`#VPPB^C zFaLLzU~_F$&!27=vmD`6mKuZ-9cU_(DWeW!90G3ShRwIJM=t`D!j!!uZoGU9wc&Lx z!L1_dOaXehvT$68Dns#@zn#u#waQjEX#%zJ_L_HF-!0=Go8#Mq4JKxawSqI9V>O$O zbEW&Fn=1$9*X{h2>Mv#*pW2*|^x`H>PB|UZDbI_9y4~DLDzf9@Hd><34l+(Z(#Jz! zH3(2;;IJ)PI?`gnl(faXAh_F91^`ob{{#~I-HOPVH%(_F#Cp$++>*<$`inl>XO5qE z++?dHr67x%9UmHhVPYxyX6=rI;C*sNWf3UWjd<0_UbX&HXv#IgbM&hYl1sa9-)($} ze~h2!-10CW=(f`JyMy*7ZFR#pF=6b`;K>Z9E453@kj<*VTKaYlYH+F)7;@=8+6j1%#fvs7Q+0 tFP@9x6wM%Ckqut!r~NV3|0nZ{B*XfF@nZjT;X79daXsoulTd=v{|51SE|CBL literal 1744 zcmd6o|3A}-1IK5m_srZj!tfazUF{}5v1^U3GMicDOXFl?!;yz>rj_p|rF#pTYa^$t z>2v2x$+Zooq@zP&;f@ITE)-(*alZCQ?&JQ8&kwKH^Ox7-_2cW6>+j1#7+D!XAP@xG zo59&;)IS1tY`b{s$G~mq#dBD6h~`h5c?e{Ol+Ex6OcCfpJ$xPZ8y9~>?}=*)JGca! zG%enMiK2!92*-eEh2$bD%H^lRh2oLSE}#ac@!AXzH727K@(&74BWm* zNiGE|?^JVPsO+nW(x))OBL=1v*}aA@AJh*T-PwMXg@RSrR1CkYs;av7=KT533qolg z1)e(~m$1xA`}}C_SXj%2{52jJUM9=wnUEtdrK&0AF;{((s^HvHfi?n%s0RSb`G>sd z9q@ z5s;UYGo(bumfC^2HNcXTl-D;LqC#4ta1(G1ADk=3L@@pyzfl+h-N&i+KjTC@K*wvZ zn5!DiyJbG+IGeB3)ru<5=dKUVRd*$R-#_)p5_iW%?biIR@1j|^K9xR0`4>9haP=}%7@LGvAb#7& zn(VPjjsJjcSq&n+t)elqQ5T;H4E#JVM`Ya(T2}QZ$Y!vF>Rt3ne<%~^O5E&2e?M3oh(@`f&FlBR+_m58np>?@L?)BrgrAP>`6`&4Y%*YWrRe)uPL8E{ z|4ubqjd&q+ytnf{ho=+j9Q@Va*lG{j(TS)byp*ve$T@QR00&HybbU!rJC^`aWiFS; zjdhm*vZ4!()!LG_5pl@O9QMY8kMbmAmDrt+qk$=j%)*ICJsUSiF$-3A@wJ9{W)AqK zI?*TiJDsEwIGvwsA8NAiKNay7%^g8r0TGJ?wDfGsGpVdihW=t}3lFB$++*?Y94jsD z9!@R|c8qknZo`RCYHGw{lB{qZtmQn>nx?_#mmWO;#X|j#p2S>c;j0rDmL zzmZlxJ2c*!T6vDBzrs0p*JXX&2tyIFf3I9eUTSo3tF+$zY~k8zWy7av(O-SLdQB^6 z)p+_Ng{4QBQdaMHiSE>^2u<+*=Ln#JdswUSK}Th7d8@B!k^;}*X9sPat4e) zO7=W)kcWK#Fk9^0-a+Mwki;j;Q!k#$T$C6ihI;yxUzsz0<^B2J_A)vx#-V-|)3t6Y zajPuumeK|LcG5ml;}Kl17FT4(iF!k+w|yk9VL@=~iFxw!hG0k2U077{Kx$4R2cz>_ zZ2pN1#h4r@J9RHaTOhvVYNV$(89`szY?1Xp_Fop%%urNIv=+%zE#-p}drz^&ot)3g zy;c@E5)TTq9s?@wkRPdA^b0qU15v>%dGo`wS%`Eg06VspK;Vm_=n>flE6YlQ6- z{X1OrWZdn3k1WK! zU0}e>(hV1JEO3iI#w7pOqc@M~`Z|ShM#mmAmhd8D5<_qaqR*t`+Hu(*ho1OOe0*gg z*)td;_&n3kU}}Ha8Qkdn;g9oG;Y9?Q!edfBLJ2(G&FYZPdI?F?lSUZ3qw7-}{S`5m zz6xGDg6R#GFEy_Ag5%ZD+w1K1+&XyLU8meq;2ki~=Us#>Gu(5eceF2|9AIBQ;u3!p$MkkF zD7YSAM4%Vo6kWv}Z&ZPCX4Kn4x>OMbIJj1r{XbO9S}!L&9I}3xSi5~u5H{17p`nLg F`X7r65e)zU diff --git a/test/unit/visual/screenshots/Typography/textToPoints/Fonts can be converted to points/000.png b/test/unit/visual/screenshots/Typography/textToPoints/Fonts can be converted to points/000.png index 72c80c74638f2c8b8d2a93ad0f0fe8fd6fe7e8bc..f616ac1350f856cc496bb22c8e98e50d2def4758 100644 GIT binary patch literal 1479 zcmcJP?LQL;0LSOTjZwBMspM%0na7WcY#Pf`PGKm+?L5tv6`6+}YM%0JkVr~lyI+>77u`|kJo{N8-CuDLrYE1pyY007D^&M40V zhI|cB{-7flXy^mTM0+~f1DITO5da{U?1Dm~;{(?*>+VoX<#>NPb#}n@D!L2-6rMHgxoQ$!cGceemWXg+Q5Fj^X5skf7e` z&0Rynm*t~K;L6j%~)RdNXy-Kw@mOu+G;u$xheL1 zGIQOk0osR53>nFZS5Lqb5M|(g%oDogERi|$5$=13OUY3=|EuBVPZrX4d!@Fi`Y+*8 z;ayidj%aV1nwoZp27U1dqa5`mS2nzokqPO>c$#@Vu?9B6DmMZWx;yMZeFhdSE8e>| zN@-vlzV5!FeSOFa)0en=eXfGKx-@v+Wr`r%e@wv}64x>#B#LL>XFRL1=$+2af<)=9 zx%eN=sjaZWe0wKi`JwE0obxOq|_%ai~fkPJ_Xk%hKWxHMv5S!isTk-JONzoOYx zk$(;`FPSGy22-m>o85_QX#m|$bn$iJDLw3KY&GuF981!n1;$oT)FX@EIX??v`$#PP zXtV8@mn5_kkKUdh+Jr;a3xnjb_kqM4uTn}+57n9YN;#dU6sx>>Bv)eC){*Yx0W0xI zA2%8xbEOW0vCZnaeAse9ZOVkkXg}w%5s|vjP0-l(?pk^uKn!(_uwLTs6|_X^(-C&j^u+v&Kf#^yUrX#wFA) zsLQ%TNY#%=SRGNUQs6SsAp2btrGEFFZ7Ux>_NBaM%asu~4B+6qgHG#_*kC6GyUM^Lb?w zT4<}|=j);=Y&KBrV>NIDXNkBoWm#pjU2)xQ2WOHVK{Ej%aZ7Zs>p8y|*u)h{SN7ki z>kv^zm5j4@8;N5%%tATDqlSP*B3oNFZ0Oh%D?_5gw53AWIzF}#y`FJ=F+6oBQS{vs z`fz0l3^blf2o)tr*Dm~B2wor>)%TyC3gqu(rk=nY-f%D?|53|^N-%5|ot$Q^`LIDfmnvvj1xi!9N;m$kCyUtQjfYxCIu>uQ19VRp)=*w0F&!*qd3v;il; zm$O}|h4bgS-HGPh0cj*s%NWXMyk6HOP><$gm}C!yuG&FnRzOuS{X=Y1}68v01H zpr<6zSLFcYYoL$iWP$-o?oQ62Zw_2lmS3PMn=4~>;q-rU*8eh~Vusw8NwIxrTFEU9 QIxsW9<+3}9X^&0$7l=Z*iU0rr literal 1540 zcmd^<>pK$)0LGV;$z@|YZYy?iTdPxU4ci<`w#k$o(%dU-56(%Zv?RBkGppP$4Vh_) z5Sdcsl8S~h$!&>La?Rz5$)Vde*xUS z*^wV}JvX(5NhJ~h)jiM|0AO1S+1bhSLcqN57I*Xk;3zKtqNOZ&wsVz3*)J}`GU&qw z(&xO)`u&R!Ks;K_!;$&(GZI^%Utz{yj~=P?vA?tvAtHSv3JD@IR4QzLV>b~@7<>PI zaW|jO-zSksdVL{EFdxGuxtz6`2Ju4DhAo_KNQlmm&e;ry(IHOJ@yFHHz=UbN*5+n^ zJRZN`a9D-cT>X#LWGdjNLebdN(z0l?qb8ifKUI(1DaN;tWMswgD#G zZ2Pf#R_NyxOf#GEj+y0aF1jGwUHjaSky-SH!CL$Td+BU_R>9!dYn%6CMj4FS<~{kj|IcJOPXW+yCpO6#g||JnsMiyfZi!tTXbG^1Oi<3+mT za`OP4Y_xV&Q^i#va`79gy9+j`ab%vT7Hig1X$fz{3D<$cub<(qB@XSG6l*LDVcc|j z2A9nR7-n{6o`-<*a(c-E(2-GBXbi=B;PdQ720lWaerErlV6id6E=4Rzmt_&p2qHcX zv_IG1`QfCUvI$kScRHAsFjq@86(!8-v0A?E@KO|0`Vm}58_osx300;Ja*Mko_C=Tu zt6JsQEwB^0NUZ=7;@**ldgiIg6?4kC<}jDQ??w-_j(>;EJ9aHhX6z&!v&(NzB!P7N zmtUr8b5GiHBJHlB@;p8|qF_CLt5=Q;r55+ENvW5elT03pi4~wuOxl;6mwMseLS?KH z`|+Ab9go{>80%^z(tej|`DrImp=|Ua=C<6g?aEZ6U9btrImA>c;5TSx{%QGVZ*^_# zo`0oy6rn3xv!vZ)_r2(XWP`-LrN};2dRy(ORom-+fgMmOnk4F4&(JT*VS+yhDO$Kh zbBS2iV5T33K#JBs^YkOeg%Ih)^b4tQu`JZ2!`_3D%Gfq-Q)CddDi>ciaZcGc(7OO7 zUo*qejHIKDZ`MkomW^7sYOF#k_*P#NFD)u(3H)im4#KIA0sp(t@rT`rQ{Ty4_)b~ z=m=q&T~V6^-+vNVm!88eB(IorTMuskIb)@;U#Zaw^5O&x%;21sc1Mbeqc!Do>>F`E ziw>D;^;|MEUCua5A|2sLa)y#;TEg-(gnKxhl(q2NC3qAQ;U5yx8`a@rbPXOu0TRlG z$Jjbs)85&@fF-A5Ayjot)DYX4ePy0PIxsHwha`bf^#@_?35fs6cmuQsT>gFeC1qpt P)&bIPa5j3qbmQf;Tq{Y;dh`orh#u{x1#}M~sY=vJ?melEK>9I3JPt zJz(*p9W~2#J%UKAGX@Q68a}%M0!hHJHdd|_pLI`$;~WYS{v5Jz@HamNxms3#hL~G{ zP4v#!t;{;Vx;9KXd|xxUoAAtMkqZ%>gtkCVw`W6uu+2*}y}o{^0j(kPrT=#ESp%Bh zk^HHY0Rb35^Lz4DzdXTAD0d{?^}* z*>0-Nzluin@%j9P_4V~zoTHKcDwj6DPCvT0k$82fFX;#q><4Z)54v@5kWg?z!~Pt1 zDRP%yRwM2x-qC+hsk{NZ4bTQs$R}LtF>3i1^(^#5b=SetnKGU*>RUq|CiXRy;w9V>L=jTw5^ zj1~k{D>#(L6`qPv4angoZk@1FKvOnYw!bq4N=%zDbs;&CLLvMj2Ykuo3=E||=U`nf zvrQ8m3XkV#xkSL{$t-zh=JrCq*hN28f3F8wwgV$JaQzR6;|*JeE%a?>j;`NU0rYpG zvfhGyHq7D9EsGpRj9)n#0ccArx>ag?)U{F>)Bx@&RAdCdGDs;8)L zNd-yRmh_N$QlzTvJ{5DQ>;RCjBq3G0x!X!U|1|~t3s0t@=d@uz_~ynT zLA||?rhEqH70c&9h>E*wA|3J+a8>-xuF+N1T+Hwjh12+ruPp1j5$Z#4O3}p&piDm$$m6 z+G|g*Rz;+o`){&-xnrF8Mo7pztILw!h}WwabOuG007wNG*1_vSL78IC$1miyyVlmR zSV-m3P=Xq@q*IL(6AM<_HKz@GM*qc1a2*wNMC!cd<67o8rXDjm#`?ajjVd{iA?0Aj zL1_HB;i;eu8ol<@zi-7=tGgZxQuPz`i!oI9BIBMSw@ck&%61_KIO5I5_6A6V4LbNP4t4^!9|t*@)%>7=`Ee(dYEXO zi{1He#0qP|wYRKiIHOH;T6#Fn*XbKOx~A>yEUDh@49WwHOmYf|F9*j2rU$0h8Qm#Q zw-YJ_4MAfP*kXc)J%&j~Ojvj56tsH94up{@AC->(*ZLpK@_&sM=`g$TS#A)I28-)N zH(xAiKGkOKPMro)M|^&p-xH5dd|G_i_oHCBSayu%6n<4DYRsixZ>or@V-C(M#OK*O zNd7P_o01<{ID)&`!V3^TH_+$-8cripbQNug8W}LdMKe+QZv)XqLuWDbN?pltwpN)vIk%&a#4Y zOI#}?Z_&l{jmj&8a+eCE-DU4&&nKYg93%+?FUeY%V80sqwRZZ3{~V?8>*vI()p#r^ zt=^oUeW0~hoS;d!k=}P$iVym-&JAG)(^j+qyzJ(5i_k$eXk5)$V`g)J?xcA4fa7FW zn4LWC{^n-B+$Q&!{wH@HKT)bWUk#uNA8Nb$DSy`+qB(i?lEb&Pt4O7hQk7>meTDY2 zBTCl8nrWz;#$u+cLVeJrU$TFl}u_A003<2U^dte|yqhKsg?*G~kMd*|HUdkh82OUEm6XDou+FXVvk6bD$ zm(7m3KJG=C?TI-xQpdGsE{h>Vx5x8uJnz@*^LhXF`So+_mve6Fs(Pv*5J=s_9q`#R z;eUaE_nI`v^4*g{qK_L6)G&N#5d>1U^8lQD$@mW;YUfZGXzY1#8PrafWLc4O;<9|> z$kg+z1)(b1tqn#0`P3`i+}OjC`_h9qZ11>lur> z48)TqpL^kyL_x6p0J&9yP1Y|546_7CwFr|VT(esIn#hBROdk5zr?swk4OIS|Oi;Ff z^60@q?v$67)YvW{mw$wNKFw`iF^E7b=AHh5I#U|R_1h$zOwRa*s7;;4+0Ye@y=UX% z9r6lL`ppN{^MFdu6%ND7$n;n1fhCxXvwap)i0q5JQ~W2$7bCYzXwcS$X7PTNZv z7bjEH|E5b9uwpYz!dSC_=a@Cl4%E~rwvMTA$s%LG4ep{*B9dZP-d^z0g)3$*uImEf zlSkXg*Ify~>&nO5C_*D`fkH_N0zxud&&j?kYi6XLy)?HP? zOk|*QoW`CI@rtR<*xbWC4hh)-yNzO!NAwU8gVocw*eQ+uo%i56y-cQcbH9kW4C67E zk!gH2;IbU?Q|1bq%`2Fe!TE>>kQ4x&s~M3C2F6sAP>mC(jA;n4!Me9$;p!avWGGMt ztp~=ZSes|x)W@yE%G(d313BP9&)e&hd}ljNKwN5T^T+QpHz?71JJ3+2im?&~OwF<; zAh|2ewgPy;jlMZ_%%drs9B5x|1e4smNRZ@Rwy-%l(_QwSU?+yXPo5wU*`4vKjU)vvd8 zY$2sU7N*sO;}n_jekW_Y(e=V&8$L00G#Pw86Jx^#n$R=$}GZ6rzlm^X(F zR|h0-`LBG`dk1~Vqm`+Yq{{Y3WHSyKij8H#ZCO=o>cf_XJ_KuCS`80;e_c15L9bk6 zn!tlJoRuHo= z>Mz{-{w+e;gW-1gLgYj%g|2d5wtOa9q7cv7G5`LLua7VGPuSZEyu3Eq&hFZcQ9JNz z-I=;wcr`t+=?m;HD9P_D%*TZtln(g~++LRssWllxJ5Wz3hWa^BeYIkWjc}z34RuDi zx@>EqCj12`K&%P(8z_~~m{FPaOEjk0r5EeY?pA$%oBt2<8EjrzC_Ujf&%HwhdAOVd J8gSuR{{?H