From a976392b40dcba6ef19ab53af52bf1489a9619af Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Sat, 18 Jul 2026 16:45:05 +1000 Subject: [PATCH] Add multiline Text Shape with wrap, shrink, and surface following. Support Enter/newlines in Text Shape, auto-wrap/shrink-to-box controls, line gap, newline-safe 3MF persistence, and per-line surface contour placement for curved text. Includes a short feature demo GIF for reviewers under pr-media/. Co-authored-by: Cursor --- pr-media/pr-11592-text-shape-demo.gif | Bin 0 -> 123427 bytes src/libslic3r/Emboss.cpp | 199 +++++++++++++++++++++++-- src/libslic3r/Emboss.hpp | 19 +++ src/libslic3r/Format/bbs_3mf.cpp | 27 +++- src/libslic3r/Model.hpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoText.cpp | 107 +++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoText.hpp | 8 +- src/slic3r/GUI/Jobs/EmbossJob.cpp | 202 ++++++++++++++++++++++---- src/slic3r/GUI/Jobs/EmbossJob.hpp | 9 ++ 9 files changed, 537 insertions(+), 42 deletions(-) create mode 100644 pr-media/pr-11592-text-shape-demo.gif diff --git a/pr-media/pr-11592-text-shape-demo.gif b/pr-media/pr-11592-text-shape-demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..5eddb713f2af750d21319c65097d9ceb4c3ad436 GIT binary patch literal 123427 zcmWh!c{J4D8~%)$VT^6azK$h3A^SQr){rI%+4odP_H50Ju~TE0EtNg{mc6lrkOq;j zoscb5k|Oi#cg}O~{pbDTo^#JV=Xu}fd5o_ZscX1WXi7lw0I<8eyR);iy}iA)we{u8 zm(9)1jg5`<^?z7D9L63DWA}%y?+sr6(r@+o^XIj-wbj+tm6g@ym6he?<)x*ig@wiW z`T76;oBQ->c6w%JdV2cf$B&bfQxlVu6BGY1L7ABN@PRTu{$b^f^-{OZVz=$5b~{Ro z?fE3arP_iA`?r?j;EWl8Dt=P!$kUpy;* zUR+#URP?N%@L6F#sl329zn~yLKR++O;Avi7?$i97+`OEe-0Ym(tgM`@tn9qZz^9pk zd6@y}8CmJ+8EOBJmQG5`Af=@}Ny~gfN>3vZQy)J`d7PG#N=iw6l9Kv3IprTxlafid;5ji`K56AY2Wj?f7i$V&RxITcYJPp z-}CbJ@$|au;py$>aog3+)5X=(*~Q(-+0D_}-SOY<&dJ8s!P?g0 zrp+y@o40Pwd*%6uvS;E-MDIT-R$ahQ}chgW@=`^ds|D$R@21vnvxmX z$i(cDiLS1(v5}FHzLBZE;XjyQ3{24aCMb-N9>z!)ZKQ)T(9zS^)-%x3)z{WVYw4gh zwb3$qn(A68bxj>LO+7UY9aRlIRdsC@bsc3j9Thb#WmSz!Y8pz)8cLVd6qPj;E~_gj zsmNVcyQHWhuc#)gs4B0ZET^C*rRO)PFYb+jU66Jdu7BPij}SF2Fll)4HkteK-G$+XmqSmG`bqp(8cW_~NZzP& zc-L4unxo)8QgEfI?0vp^;LgIkrt%NZP%&@;)8>kamnIp~jw8*LQ{~u~riG?0RnxC* zo9-@-w7mLM=hB}fVAfjwUz7J#mE&k@&HQWs&m)CquWJ`O!+z{6j=rv2euD!u2%5Lm zul6TgkZ~GoYxq1w5;ZF_Z*SZf&AohYY3x67MZh~Q0ECj;|3c9;&e?Vap@0vT&t%GXfEEBRU?I$OFu)<0x+l`hp^(NV%!amV6Jh(7+3XrG6sL}jQ-|8z zxC~KNqQtQ7AA;g5&tSrAm}-S3NyhhJoE&xU1|SVh6&->AAN@&Lws&tK_yJkhx4G8Z z7%T@+Odh;I$G$R(qQLw?7yR}BGI-_h)DtF!!zq_rG}Q_SU0q)#a@WD*hU@ZLU^9`S64QU%w^+V5!(AAvJJi)9Mr1 zW(J@I4MwR#Kr}eO9k$|G-_IGbhw`3_!AhTO{9#dIDPH-~j9yJB4o z`AtJ+PRQ;^;{e89AlLx)yAzcvo*h!gha(Y)&V7;0imOL`N4^rP)6&@Ibrd@UGXQJC zigL~-k*mPlX^@yfUJu2=QMi=>h<*bAG$7p|vK)OP64y$ziX-T)B^mCg0?0J^yS#EB zR2u!pJ*mXpyK`wDVB2Fq(9{|BLo-V(vZfC*4Z=O8j%ia^fv!p{`{H?w5hJ}Pf5;GZ z9f!tn_+Ux$0GU+@Xo9bgyi<(#{HYhPXO2*@jHF_5wCEuH%e*TPMLhJzm}xQ-vsL|o zcFd>CgS5~D?9fy!fM7s>1CS6*eIFteEiO^stM~N2F80eamLoVbD+&iz)Lb)3VdY_O z1CL1lmyDIyH1S6AM9__sX;NBEQ9OMTf0XWtUDqfBamlbwKayf9Vy}HAkvgoD^Mx0!Ug3jQbe{ z{u~BAF#HP2rITMm_I~$9YLquNnoJ50P!;ZD-H_!g`4VMG>AtruKX^BY;=TVt!4o5Yy)Lz25)~5@InD0z4cY-pcwV6 zD(tPS-vsg+1jSx%(j-9y==@+9fW}`C-+UOWSRh5b7+0P;5qi;J)h5fpA!%PzzPX)5WvfOs6^*htHXGV)m{h##zCTd z%qRo3jt49;CX_snT^s};@-xMSO$_qEJ z)%`Pc`#hJ*!o6@Of=Z5u12|+9O)+qn8>Vv?04!l*!R9`T=)>!=06>PG@xdnAxUb`p z`hYaanBdZsw;gE(Df$zyg+b!9vj##zY$(>_>ts%0edG}t1#9Mrr7_Td`OOk$D3^47 zCshIgMjKq%zVqSHN`@8erlh{WpC_KfnPuUPQW#y{$+Yz0s>?M}fZttg^q(IffI||k zBMoFSdz8u<4$A=KWIFhW(7u0c`a)TuDD*iP6h_budi&~;q`QXujbRn)1oTqHDoheU zL83vXXI4LiK5qd)`fbuf;tFU+a+m|PX~AZRqc=b7MX8}uhz4L__{>~OW?ltPT>t@K zWp7x$skD%GP~t25yO>PjMg>P}BwwtzzkPXA^i@Ra4#9>CYH7j#6jBC3(|`;#N%p`B zGx!daKaCa8^-!x|5SI7J_Fwo=;gWR1je{Y+&&|@fyoUTvxleuWxDOxbT+uNJWLWz! zeUyU)#HW3rja_r+pIh6r{Lod&q-y}OdG)#lY=Z6@i2p0{C|8^pqi|Kj!6okxoaH~O zYErba2q4@xQzV^9M52)NCyK-BwkQC{Mr8Jvk+a!XO{Q@vt~_gl3M2twdVmZo;mtyA zj@aPINiMWECD12|0Q*@;hqDJvUZgWdN}Q|B$)O5HTi^G{07UJm_zi$+RS*w7wvmiu zm%$u_MZ9-5{sz(nA$dsPI?R5v#c}y*#>rC~W+Ir2+ax__VO=7_bToax5p^E<3?=08 z-Z%QkUG*CNYmbaZsKrX?T3Lj~)GcI)(>J}nKw(i~VfvdWh&lr&#l{w((KAfoJz~U( z@_RJMI+-L(${-@To`WGdtYED+%u=xr zmQ@~J2^NL|NVGI7974l?5@5_{$)ck~-=1jgmuJs~kEBD4|56P@Gh%zUg4b3NPud0~ z#=mGVLue3fF@tK+bVAgZeDx3H$v3)4(cqQkM5-xz;M~;@D7IhB_Ir&!j5ta_jKaQIM$G9I2n75Gl=NvH*1s z4S5uI0Qsi%MdyGtWL#uWc$;VP<3&UlgTd0#x$(xOH@7H%M{1K)xbyfE5FwPGLLqUM zZsm0dT@s(LCxDv!1pqQ#9mvC=y%XaM)?;O9BdAI5<` zcLYo_*BIf=5iR^Z`wuXt zWE$8Lg%B_rhhW8(t05|LqIbERLg;{F3B*Yb-g^+iFpFbpRM zx?VUy_UUCu(|%@V#e&Y?;sKO?JJ$ANsE^|(B><^T_l^02zeOAeU|5fEKmsYs(N+)C zYLEaM*^v_qIu>IAZ$$5x#XRJSJSBQcPlLc%?2Nzj@e#8W(gC_k+l13SC^nw_#WI9v z0H?(Z*T+ZAY6?-qz>||K&M>@NJWb0cvj`4=Ry?F$MwF4nTR2%fvA4O4X-+9D3IJ$K z7!jX~z)2<$Z3#4K;Z#!Gz1g@D8X#zkIm8zG%scVb8euKG~@B^`hSUGt~AfdxUS zhpu9HJdG;^z=Eu>bXSldre7?W!^kNS;DrQ2h$lVQ2*H_{#``YKGCM%#^^?CRPh{G_ zEI1PU9SOXefs)EJc#4pu2c+Mn%hM~n@nq`X%G8j2G+q%(MP?}9$xyG%fIa{@ZUq_H zB2MZ-4}uZK_0Hxv`895|zz+tXn8p)Fdx<12mBl2Fg$9rz94O#93L)YSbvS%l{F^?; z5*W66a#L8424Tjig!ql66LA3~4`JK&g!_^?A7=eVNnjQPNWC6^4A5r9)1)H2XrD7$ zt}+e7FP zOaTYfZ$m#Hu{;L=bANH}A9Z!DKcHH~o{H}KV-{PXZ zM4EjmtN+>y7k?LSw-iw1z5jR1bYDCRIoJ+^P#=`=;>|N;bT#O z%PdxaFKmS-QbFf{0_FlZ}!gQ>tR1Xk6WW~~3vpns2 zSx|U+SYCN}M|tF2dGw!h9B&0dvw~<>5f@&OkXMn^QISHOt3W?sA@NqGYgT63Rc41* z=H^xAbyOD2RTllJEat6xp;=X8S5+2XRgqU!)lpSFS5^C`s-E{%qvoq-yH~B@uiEln zb#%PyntRpr=M|Z^x=*uuz^-~Qym~mVdZeRzY_59zPc?T}}qg~BRc+G5H&0I&# z!d%VLpPCik+BMDEb-UWl@Y=1s+MSNty}8=`KedOvb>B7Xj_m4=!|P7+>i%@poz2yq z|EUA`>S?sS5pN z718RQ-+H&R)$`sf=hIeyzSjX-uY=5*Do0ww@?VE{zK)!K9ew&5WA-XotBq*i78lW` z7*U1mY)hGMdwkkPdiA<7sV&pKJv*Y^*}f{Rv%O%xz38+(pW#)mR!50_M_GR3OL#|B zXGisXN9}HNU3f>MR%f$)Tjgm-TYhIpXJ^4~Rm*87nXk*9ud2(wYcQf~IJvb(x@&B{ zYkahEAirx;tNWvUH@dUyLw@(%XxGHO?xoZ26>5IxY-jhneNWkZ_iBF6PG`?!t)9)( zp2LW?t2O~X*ri6!@J0gj4Y5yGJ?PN zQa+i5D#g#ovSfh+uP-*gUGIJ~v) zYR0CtWuWLS7v5GdRN3$kx-qugGH=WPAl?OoD1RpB&bLlyg940$?%G2^KFyxmjh{$E znF#uakxaWl(filR^VAG~umBxcW;fuK(#(~EY#C9jNRphiAXM$)jO4d(Q{Ncv~| zBci+Sip}3`-g}oDIi^|tu6J*^m!H1ZpNR}W-cpz|6zISDGexE_zce2aO=hl(Wa?FT zS7T1U!cU*U$Ves)P4kbIR}V!lFgKlzO=!O#;2#J`89Rx5JHq&3Z0|kU{JlfU`(6e5 zdfAaQpTtVf>JW zq_5(ikT9c1p~ktGCejod*U0o4)PG$yju~)lw3nR}b)4k++r_syfq73BdOt@0cPvVl zS(M?OZq1~GJ)^86g(y3sn95v!HbzRBy1_&-UL5*V{85kT<1g)x9&-JLR~xqg2-&}p zMvnfTFQqD_@mArKnZOi@ab}`=dQ6siykO?Oz|8SkrKirUV&sfi=aB8!Nk4wZgUFBS zR~tW}=->J?6(H#CQYTWP-lNF$D|@4MzT=8f`Woxl#C^I1?m8;gh|?f9>WYTflrea=xN)j_`hN+_CpGvctZ6 z;ehYshp5Gme2ZmTiys|(W~ix)D^Xo@jvWh&i}A; zyL6n|xU94Mr>5g))bhDb`tqrQL8-XtM@ur`I%=0nb*YqT19l%^c>eDme#ED*QA|3$nLKxpEoNAe$LeX ztkUxt?69JK{`qV1XKmf}o4cP;f}b%K>!!@jYSHVC1?yh(be6gsR!$q%(Hpi!8+JV# z4oe$O=Nm49n{K+B9!{HH(VI8U+pq4gUuWEKm)XcidGEO(Ub$3dfcFLl6Djsj=3%2KV(Uq@k%P{Xek=dzq+HH;AZ7bT%?%8=Vzf)J#(g4s) z^6e>_?bSPVJQi$euH7A5+8aOLYa8GFdT(8BcgKzoo`h?h6>OT1-Yd!9yVki!(cNEn z+TYaOnjCNI(QR}^GK3sL{8t$FoERP*LOfjJ_Y{u5HhCFSblgC z;@r6Sn9=cmBZUIRAJXT&`JFX?WPbYlrSsV!%g>wC*90Nj%10-X2ee~{OeHAjGKDz^ z2V2H5dSZ_&sE4$l{Tmmle|Jc6l6U%3_@6Zwawah#@d(WxI^+`CC z-}ca@FP2HjsDRzac`)LKuI|0pUmZuX5=mwIIn&CkNVZ@hnI{LJZZ`0>}pH}OKw=aGL7?id{`Exryp|NEC(m(+Ro=Z}2d z)5rfwprKqI^=LXFq5_6qqNEiWp|2CGGPBG*ZDmzpVyI%{7#fVd*_D288hb1LBB#0?U&~K%hlif6|5-*ECQiHH z!&_|c)qUNW#bm_#n%{l{{dLXzT6&1xv#PhMj=^t~ep&eCFsV5`EG1Shgsh@N9ix8T z+*@i~`WfPcrz?Blkg?~h<{ZssVPmh=epAMUAW?RLi7~^>I>hp9=Gw+D61kU>jKv@J zCSMr|n@iR5`fuvV8^&91u>VN-UL$1_5)%-z^fWKZ+Yhlt`{Ut29E=hBoH^Xoi+3N#jVSoG|k7v&!G+kYS$4gYV3X-p2J@;jZ1 zk$J_LF{N{N-@G1}GpVs()AVkBGx*!5pX>JFW4iQPq(Y|Lw$1?fk6Nj_m6d-xf4G7V z9{6X&ubm!3dab*H206ycX*75vJ#Zaz<=vmcRhG{jK7@v9gihFqM4?-Lp`%Vd#;QdL z-3Ab4y*@x&X~_1~9-Y?<8eeoyUq#V{?a~KtKK(M#yZkV=AVW_V?Eq|*^@al8p5eb0 zmpi9>T88wrC^yd>OIJ8NVrGZS7*$iQ;YKP3e_Q7idjL3Yra4+5O!&?A1WV|rew%aU z+*0b_*osF|zn{@?S!o~f+z&erBMZ31zI0pGh*Pl+J{XBD&M6sO}oNhuoqU=e$D_^lzbO_3G%sm-=^g_oE1=9UD zMxb&&|HmWY*+Dvtmz}NLJT7MsAKZJjLjuqaz%a}wV#sxn2y0fLP(vg`E?e&5K zdWR@K$Ak0xSYoz$k=08n-!WrRz~YYKSnpa%Kv0}{pP!wY%XBd z&`ZVss#C~J6Z4`B-WuLT4_%}AGixlRUeCg{C5+~S5x-+ci@z9N!i^vYB^1bU@2dRJ zzfmaBAg#*xb0w8;YHs`gp0t5o@5i5_XgH$1Vxp|T!1(OYpShvpUvHm8vLrZ%rhO`A zFbM6!N!JBMCNCWkIIq39iG>}JSQrK_anXbsi)ME|q17d!SqqSqMpiJt%S@7W^ALb~ z-~#z!{glD)7AG5ura~48^2H{lT&Q;!JeZ}wlBSsYjBUM=zmHymn+gK+y3mM>2|wBi z9`pZeEPQDe#|1qU(G_=r3k|;}NjbPO#Bx5SJ85XOR*nVwdfgth=N2e<@uWR-bLR)Q z4~3p9TCVS9>S#eP7`TuNA4U@Ny;(+N(v@IR9tZXPs-`w z-rol9&JWUdjcw{)Jn4E+^dR+q!Dk+uzUjJJi1?nYhi>^FsT}p*$5-p2`U-srUv2^o zn*xo##{tFN^%8NP6%R*X(1jI1HiwvzQXXsSU59%*>IkT8`upThhJ&)R3V1=~^pBBB zDg|0Ix63F`r<-e4x}Ns!mr}?>SHJ0l*{}q&!~I#eE2=~)5dfZDOyCE?jtGp2CWcCu zdO=H7p|QXAT;VGsSFDJ`njextemvz+!@1YVr*Mu1-& z7RsNq?X@P87#mQM>F}XdP>GV?7kEKz1#u74*S z>q0ouxyP;L$o3FIMo=urw!fHq+1|r z%qmL6z?L)tb1ELN|k!HXJQZJA88~$`2Iv$i{7avvXBe%3w%WqMTZG?(hu8KxbOLQ_ys~Vzh zPJD5>LsgwV{TCgPQ}A)*cF8bExMqGUS1Q8~;<2^?Z-W3QwO@X0b9BqET6z(dl!e75Lx+_n>cz zk-M1G$>|`j(I`|={~(ccTg;fSZG>Iz4RXL362*-?5Me3C_@~BKD~%(L@`KM3z4na# zl#RGzNk zy-C_0cRGLF2>(nsu)2|Byt3^z^i zkb2R~R}^&NnfXZYtr5c@<8lKNaEfrrI@cLre+fnHqE(WEj9#gNlGqI;@g{QJt|HVM zcY#8vYwpx?$FVBlg}r0y)^n@IhF%SrrsGF0rJ1sQixEf$N6stSo5Rfzs+SgsdJN6C z3=FJybuPGQvU`j!-NR+S;_YFSYdsyk-&-*D-$>OicT}=QnMnr(iyO5{Qk`uuFo8Qv zsW|;mak-%C7C}}IYSGUHQ?asU!VXwVm>Bk_Ly|ss=XWCZ^L&G|&pZFf)WeD=jMEgR z4GQy53d>*0Y1@bGUG72IzEa%Pwj)hOhtU@|%%`n1Kb5GNhf#zU-rG;TUUA4hap?SV zI3Wa`M7DgGBx+>~kCwXVNcdAAlC34U_hhEZ}E8Fi`Tb)Uy1jKiWlOnY_Sj3Jmw zm}5%u;uNyR1n2RTTJpoP#JksdU2BM4uY{~6x!Bor+AHUy!#6#7mGVKn`-;NN$4VR@ z(HB3Cs^7@+;E^q*Tt#TDMs%(CpdnFRT)s)4NA+z^TV9BQ^cF3VM7Y#q4E*iT2icF7 zD$_R>(^xO|X5Z;OH|zKwbM=y=rCy6wMZ%5Am!rb|S9LaSmQ)aV|4v=^wXnK45MYM~EdP&LR1C>@xm$yw{HSc5pXf0iXx?ZFGTzkf!46z#K{)E3cD&3V;uuVcF_XIVf|Zqu2IFF^`a%jhsW@JXSf+4Qvdo;3^U{a z7qh~I?@l@vn$cGi%b;_`Oty#W^Su`r(ATF0n)5gdY)Wj~FHqD{4dL2fF_BYhin1R} zQSWsGH1xdZXk9*C5||g1TbQ`$*sZZ3Rn0rYpj>MRZ(?ZXF0s_p2BFvuFv$4>$ z9nixmDsoC+`)G0LC`NX;CF<0M^ph(_dU1oprlcjk7sB>;%(CoIU*PLCEThi#qd5+r zUW zs#DuqXMHR?lHOO6Kv$1@EJ?2x=dK}dD_X=XwzQ_c?S_w{4>pN0rqKHN$J(2G?)0aX z@dxOTi6UE|d-bvK6Jol*he`H&+FN(l|J)^Z+ym?|lih*@BNJQwp%;-}7bRmtPG}7(XylQx*P|01 zP#YnVv515O%UK{jck^z~X8IuxgTsRlLH8tS=zxDJb%NjdroZ5qfau5On@l=`p3HDJ z#fS7BTLfk9#E@LZxGe&H7;JDD=XFAe4DrgsZ6;ueya_OS9FT6Z73=3s^~(l%AZQT) z2t%P&T)_nvZ6)<=r7U@W@&B@j`x3^<=D6gND@k{wf(9i?7nPkFG7Glyrt>1v1R@hG zC5Z&D#29fHhJ;Tjj!ihHBwZ%MX{q7n1j&M=pbqcsqJWl?QmVkF$NV_Ij6gd(Q98Sop{~`JDOq=;wdYen?Nfy#?`Bp!1T9xsKbue?rKm z^)R8tz2D#V_M_uJ#NYOgg%`($gwZk}<2IxBw~GAytiR6s`S!*64hZ_p6Wp5_x8z97UQC=tMVAjd`*u>RAZ;ZneD!8<}% z0$x4z3HJ-&dGvi+a$C}vRrpPo^s{d=)D+oI0cL9fRL^e#GIb9O1Oo5Z_^0XoV4(XU zb1P8QIY8}E;Kt)0gNPp*%a4@~vbE^GY11+3+I$cB?T?ahLqEEJS-!6Kgxetghq9WD zk+X$yd=R5eP|s|jsamSkC*Pcvmspl(X1ABjmy@r437R|%VyF9={m)n9>~w=G+sY>A zrqFlm+le;u!IN1(8&JV^*BI>&0vw#_9hZOheGP6n33kzAbX5ywdfdMbG+0R;`90_hjg52@+%!i;7jn$@+L~bexU_IU)UZ8TuTj% z4+*|T@0$y_`s41{(qHEO`tZ_Jcqm8YrEH%^L4KExshLRPn;EPVOd3Pj+Ba^~K}pg^(bL{1dIlDl3R2N6I4tcQ>o=WpNT zEn}(tQK2W^*XX&oejEG$4yg~jH6BJlo+Py41yHp2NHO%1?nLXY5FCh+J3&ezrt&e! z6$z6^L4C1I*(|;!R1f#{rK|x z@fG*u9n^8_;xKL`B%KsTL!rIxjTfEWX3zCw2|G*Yjt5Y2IZt5x^`G0z9|##QbUxwu z=i3d|qo27?C%+;p%^g|(>7@Ts*!AK~YJw!8ULuhjfMV3a*A78mWWePZ2pr!=V)67Y zf`{TTQHsyxlk5)TTOaSQc0CH~R-+FlUT1UR8%9YKs%u zLUW+6afiFQUqIPvvB(BcT{HB1=iUw^ab5P?YZI9&HI}#`UZo_tIhLmuxWD)NvUj$= zOhys^?`F$#z`DtAG4DBx82sg)cl@hZ5KlO{HDZu}D-WS0*%lXr5Bm@|U?YksRfVOH z4TARYN+saJ^2l191zHxcowMp&sl=WXfUh{S9 z;XrUnr)b{)LXoDs?_MKgm^4q2UPr=VF8EERu;*@}u5H5c0`gX?Iv0|1*@zeO!bFmo zOw~ogkwu%rDNJ{`MN?I>ZG@9$rE^7|==?bmwUF7geVUy1Uzk|>73nLi3HnKGqNMAl z8XJii&4*TL=5p;9sX2}_v+G%Om@5~bdgjsRWx29tZCE;W{1z`b#aDWTyB9r_D0;Y= zDPAbv@YJpV57*o>2t2MVDoT{@kTj1J)SP^Fg;i6kG?VwwmPd$Ehg7+xW|EXiT8NcY zWogGlsS3k8b5gHr@=oqly$JA-u4xfWyjw-z(IHb;w;7&Z&G(&X?646R;d>?-5d+mc8Z%s72A#|dBw#w{P>wMftxwda3S{ZFm z(&sbVu{E7_m0vqfS-XC~wf(!}mv%FI&ZM;~-%x3_6$Vx~7rylVHp<-Z<9YL6eJ6|Y z*|)|EQ@6euaI1L)P%))|f+_B@=6atLb9?oJO8)!1io+VOr5p3aBvSVE(F+uPgH+j^ z)klf%%>zqSMU$JxuU{Ss%++;1`+=D=N2Y+MP##9C$#T9Ev*s6ej_R;x-62I*&UaKO zHfjd23C%M!Ndr*VM~Jy>kn=>o+GY@G5g)Om7-E}xMJ1~9l{wq38UA|2@0j}6V1555^|;qdkw0ivF=~6b6qNvT;3}p= zos_KXkaB^Wq1CpjpDE-mt{_gZd8Lb4xc_(bUw~i=!!@~gMGIzQ95@T@C+4}82E<;+h_w1$vn0!1k5OcG%Y8}+o z)cgP3cx&8r8h{5Hq=cm7vz|=3Q?kzc?F9pT&oYBg)Q$C;#E7R-)6?-tapjj%@9$VP z<;`@7t2R}>_YG*uUpm`V%Al7L=WQyus$Zr)RXHBq*i^WERIKs&p>$A3Q_*pkgzk?k zykR3v&&<|JbivXTJR7^+`jG^t9YP@}H9u#Ls<^|PM@ba9^BmDFX>{2X8*SPAa_~jD zk-qd~8a(r*pw1nftm$M{V{<8chNP*xCNis|xlFbDPO^*iRN?pdGWEYw*q9{t!tc!$ ztKTZI8C6!DndNxj-kV#+$5%j;Xzkm!#iZOPF)ug&<&2NZv)YM3VF+RNzW26U*>@LNBo;4Yd z7-$>*%VzRa#%c7$H1B`D@uI*4vr798=D?AuyA7&K}B++An(IWNuzL1Y*RqxnWe z$5s(n+k?G_~wmg0KFh!r|sc`Il1$>(;%ug%mdV-1xntmYvz83EUeJo{_vj!0> z2g5T72L9&cRuwMmj93U!0K=K+l@ouhY!Bh=KF%yvcG>rgFK@hmM^q9vsy&^@p@X zWg3QszoA+__}JT#?(92D^?bSCm@KiY8n6e0+zJkKYRb8zn_C3YF5y&^2N%cv`lCqj zzMCpTf%dej`uMJtt&ik$dC2ovwjf2nE)E*F18Kj;rk7TdBFw|c0}eI2e#i5*J5Nwp zA1^V@(YFmxzns|KblWLVqg&&PS6$`33o*M5pLRBHKZ;UQ>w*IH7M4o#+X z@0SEbEPOPGM!f|P@F=c=4lQ=p|d~`O?`629)CROlzwh-&8L|wy(bI*_CcU_xY zgAYGsL_bSs>DDlI{xi)7J*rsF*|~KoLa*@rXI{|6Uf{E_h}F?!_P5>pG1N;-HUq7& zcXFK(r@1|sA#KdAx(0oCn4|-qooT`_kMG4}BNGtYd=7y>vS(I={>@p8gsx!`-{Q9Q zuZ~oFwBetg#dxF}V%fxq!<5X!&i9em$=$f+z{}VcPn-H6YTAH0Ts0x{Clx6{3+wGHk*Qu9z9_**yAd zgJa5lmeu=j07A%e;MTS!UbV##K}nU|$u0eANGgWIAOG_&pDNII!38Aik%K6kO={^+ z$jVLb<;HCFar?MJ#0P}1k6AToxJ=LjCcGk~Ts8`xcZr}jyW;Zq3d6WCp$o}q33LvlefdIagUaSJh${+M8EBkynFusQqe( zkhdo<5_r>d@AG_;3ldf^eTmlOBRRgr%=R^G^fj!es)^+4vq3~Y#%kobYb}7PE8TU9 z#7xUdOl!r=T1(8}$%a1mucPeSmJQ!d{88~=#Lh#8vl!k}V(oX)CZyCRdgF?dL7yByT6v-Wea^u3g13uy zgT4+{oa@)K4S7)nk*5V*B?}{0R26Of}Dra*&zaY7M2Dd#vP2bpChDxcxmQN6tN zxuJ^v(=~W`NzkooujJrDxwDHH?|vbLUM>oael<;S3s?ZP?LYS!ajO(jB#B*M$e!<}x&`;@!of)%UL)&NZY5N(*! z7iqm2zAy3Uxct$KdqmkwcLaf50nL-r*YjHte04*77T@hXOc|_58JA3*@lKv|i&J$wl0;icVwOtdq;8YuTpx>ke9SAAp<0=tE0t+fnQ5_|*yECJDVTt*%=VMY3Hj&X zuFOewQ>jEx3iis4u6P2>p~>Emop0kd`jHkL>38MtqVxI9 zs?vR_@*?S~vZ`m{cXPavbm7QKuj$e%>6)3Unh>d1eWi%G-V0>VUw&6MOKFVq>IEZV zw=HCh1~>6aZr&YXwEuIi!K&)Kof_*`Elx6Nn>&Gsx@^gFcTwARp0-vzx{S9u6^5@H~`pPtR3UTP?tOL&#i88~l z*eNnbV>^i*U|wx9I7m{j9#BcBpw+HUecZ=gAxW93AhYd>6l_r%y`_tB+QpUq7P7f_ zWu|MyG2$Mo0b&>kuiKe>&7vK2hgaWL4C5j;HuGvMPg0D#S7yN{KDv+Xu=>NymVg32 zX(fdQd7V|JVUc6V}ECyCWU{OcG>j_`v@lYQC8R^YZPb+!W zuRSdP^&Bi#Ox8UE%Mnq%G^qXXEg3QnoIsZGuYi#-%t#!g2RME(hSdt7O~FirZ|$`1 z?`qzcr~sI^H$kehz;eWQDARt2>GI{w{z2$H=84(@Q_o56;i^5xMt~6+rwr4dd`ZzwXyFQ~y2d z!xi|Q)$mVutNI;gJZF|lM;Z!LN<80gd)NmosFCkIp%-_}me`agc2`M$iLQX#Qe(FM z$jT6b_&I$DDQ2gSd}$Ue#SZ-3B(n|cs}O;`aPQ@FFo(S&=V?EaGC{WlCx7-Y7P!T? zj?qEVK#8^gbo$F<`iMOsevQo97Nf=9dmOUGr}u#UMjdPxQ1|!da#obF?FU=wYj>e_ zk&PmkmChRDKI~6&Kk8IQSZO;7SSdG%EoQ5G1e;0t4;RmtgrvYrpZnZaB04 z&VR3wtBFQ(;ol`uv<3ZoDd?Sm1G+~Fvd#g*?hoiM-F>@{#^iCs4?OSnc=<>}eBBm( zqx-#i1txuSClw2)Ru%l=5)Y0Q^w0d0XdB|!Pz&K(Tj%|{4uIkrRbSE}fEu#JjG$35 zKl}j20Qk9@(c?jZ+G%--)g_ZED8u1}ah|i|hC+8(}7U@r3Vs z12pikEw+z%w=X}Pe*JVl4RE1?Z~x1v&XZ+=2!hQR);4|Glb9PZ{$32N_BJgyZT6zc z<&nD2Z~?(?373-yD)F`Cae}udRbSNw(C`3GtNf&cycvTCN)EvNWbftZ(6CTR^&k|2 zj5dq$zHE+?vhCMqBs(+sAsAvHCrv8c$2$S9Ely=0erW+C_2cI-xc&prS3mE4{;7BN z`rd~lJ>`Cb&iRnW@)72sgg?iwbPwVge))gC zo`R^ri%15@gt{yI+6>v(qky3vzv3-^=`p5mTT+!&DEde%K<5a|kcrny@D?2)>->+s z`woZmU)P4u7z_rt8r|rG2%`7VqlH8e5j{~tLJCnLhSA&Tz4sC=(WAE^$`DZ#K}3sQ zq9*f9epzen-(GvIz4!Y(@4Mf;sHy}~KoJm~cM4C*WMcgh za)(N&{X|vI*mpAiat%sT%2X&ZPUm(#lV`zRGRfhMLF`_@NMo=W0ja}|(mWnizUqG7 zv+Peog|D84Cs9Yzl$s!~$1pc*SH zZbtxRXeR<=K~Ia$nvU+@-+_m{+I$t~U72>);9cy1?UED1Co=M*d@Y%;dG;C?@Da$$ zB*|4h<|hSjDgmANuAar%NB)97RcTF;7Een}#!HJFl-~F?q!n{5DF+;_wcE-M9F_Dw z^m+7DJ`ALSN^=l5Z@&~TpOxt|uyURrCh(RfJe)y*+Ekefuc<%l)iC?>^gW-E*?|$R zmUl|uv&`1UDBs4aw7j?cp31p4{z@m&s%7$pLXXq;iLZ{60WH&63UPIxr*4N8XSd9@ zJ&*4EJVQ*{*xvH-)AvY=;<;^wPe(1EunP0Ut@D(M3l~}!*cBJ~S{E-XewJ$etfaW4 z*}8O1@ry}D>laJKW&76Uhl(p6tt&4SSA$zuqZQYZTGz4^*Na-$D;2-iw|;F?+~{lF z7*X7uY~B2%xV6%{wXL{))VhsT+#zn;p;X$v(6-C2w8z)BcUfs)s%>9M=|HpX09SZ5 zX*;x3`extu?V-|(pPb*|ty%6GeQ`WaR#?7!B z#HXZR;`wT2YHwt z?ku3=?w7wjciK&mN|DOaZ}D2I>m#wxYWR5CJ)F@Jj=g;5^X4ShcSLo_LecIBQQD<> ziSlF4S^RdAmr3f2LZ3sP?<**WvAg401_E>tOC3;!RHi?k2LA$*mdJP^kl|kOLJ*7l z>OwFqh<`DJJJDe=l)tceF-)j#bunC|m;ZBw_=LmfNa^L`&r$N~DW+UmHR&zplOILFvB-dB-P#PE4^5c!2)(VpE zm97=0yRWYmWd~hcFV0VNS}!RsEL|@xuUlU)tLnvG{968c!s+YFhUL<)6>q++f30jM z65OcjroX>Yjpi-es2P;}y73C5A-MT^-1z=x?bN-p&AJcnUpMRLg9Nu4mJ;u8HLezx zZ8dGwecfu_=@s05b1-p#yXAPfY`gXR+t=+jJYu1pcJKw~oel_J`A#QPYGbDhVD`4^ zrZsWi?P0Jl-|c1b*x2oZ1q_XdROH}>9&^bLOP77L%U8WNj}*dLZZ ziijUp%p$hNsHKJ98Wk=KJ{Z%L+B|rdxWy$gE~MQ;`Cia0vNp`@VWiEZMH<9?iaScL zF63Soxy_7|H`;xcrIoxs=+Q`|^+$J;5RW;UE$om7(ii*EV&4D4gX4t|zKY|;2&t{( z&nQjdlcji*2Pa>W?JG`}(>=CMRg_UBG_eTY=Gd#i->+0#>?{C+TQ^6>lNlzrv*Zy!9izaPy9 zU;c5tl=Se&$!bxhqEk=hwy!Dcm-*2xa6h(HS@TmHJ!b8~aq|zn4_apvCP|^w(VSSa zY|sUCJCW=T0*J^4;}*+F&36152D6E(CELlpLU=D=`x{K{}Cs-Mp3*-lDb#pT9hG^;LQYWFid1ZG)4I*-J$VAUY zv)wS$!Cd-!bdRvtZnzaRkD*ViS1ffm!a+BWX%gKl*|Zz!8j;7cqSYs>O%^!U!9{o| z4OisUlkQ=j?|#zD@`F5I>b8Kzv$024RK>bu==TwNFwdCJT4ORigJ_;t@3YTOsQ9zw zmnc+El&@f{}&$BfB0~( z{(QIantjEk;sBSfCcw6pC&XmIJj9LiN3Bl7PG^HR zIgxMj)J}KDC1&P$AvaNj?kLae!-I|cdgx5maDCaPf|0Buoo4VyncDS9NJ3}kM@)^= z#1)r!b3@+cTXRNkx4)CWX>g0IiH@dg-Pssi8pzYhnmfDfRyT=%L#lDw^k{Q_R18ab zt@ZAwJ;SHX7RBPt)rW*_nyjDBtpm9k@rnKixpbgEC7abk(ErZEeYWv>@_~1^PQVj6 zBTdRB@jK0B6V1w9#1GGR9hWQQ_6!9o<@Y@gqf~dnGL#BN6PEw*;r?;DQNR7;Y^RSs z=lsB%er%i18mV+HJ^6`tbdNdH|6H>@)*#w%XLsZW#ONPA+*0Tc3g0bHHYkVs7N1Ij zGQS*|P!6dHx-)`xCI;EKkWXsuU+WJyN=c4Lr3 zlfVj2PXGg&)Rp2y!?h_IYhc-GG0fGGJhe4fv2y*>5pUnTPKd*J)_!|uUCkFbb0sRYro|&1Mo}T`z0X^~VmjOLGHr1D~ z&}RIlyu`)ZE+US3vKUX$n8Py-{b8`O$hR({$ zDamrL%yFv9b<9tFkdc{{k&&66p7B>9IxQt7^$#IBB`GOBF)=CruR?Ts0#1mI|2rN! zDjLT_2PDQmiHZvT6A%5m!^BjdP;yo`hIG3bk+zQ+9K4e&BZdLB!wN(EkvIw!Uj&W%Cys z`mZpwso5P9)7v-|+Q|6NB(#yf!Oed_Lf@5RH~C3IU(r$dKO>=){^CJPEBr$Y`ads0 z^Y97&hY~by@{ar0pUhPQ?AZQ>JgbcV4S5z2CFqX>dGJs{I)cz4gx0y{#rN*1CJ5b` zIp=>|)GbePMPxSw0mB4XJ>k@K8sz~ab%-hst{&P3+VPfoy@}!OLudBG{6+^v1aDPX z^xp%QB`y*nDpYzeJ+rsIgLIF*dD40GmdF1N(LK270;x)Y3k@GQw_)*R00Lg);)4=> z-$%rC!(BRMFEWUVJ}7f)(@HO}=t}Uo;m-E*WEHFLRpa~jR;Ci44_B~yFK+f`(Tim! zh`dxIBE$+ARREzX6O55ZnfeNh@fmHFD|4PLr(GLPwGr5RAp#4SAdLE!sY9Zb1#+!L zkVw`eJF#B9>n44%`uwGgkBs>m-h{go2CkHJJSYTYG`8!81wwP$9lTl7b%Xf1B#H+j zU<&w?-*I~nKjMkYas(ScTTrD?Nrv90w`pK_@dLlukcgH|d{(Q%@n))zG8-7~;8|{j zuRwH(N)x`q!*xf@mdCa)ibxcVi36{Rp_u!{#ZU>vh9dM7{iqZm7+~PdG?U@&T2Re% z6abPe*pLPAfRBvyF&dmQ2Jtu84wlty5*_*B0mAv*aRgU`3sXM}7};wWa^_o#B_VH^ zaW0q-jNT?NY(!;(2pN{{Sspj$Xy}mUUxZr;WAmp~t20Xt()x9uV#?@;1~B=c0yBf$ z{@1C{O3>PROpP7Y3dWnLMIzaTs>dxe;(>;Dv2$H4ErQP2G%**Rz|?K+L;t=Bcyd!b z|3P!|XqeH>25s>W^$jN@VXYniCtE7jTV&UtH@janDDpJ0s=6i|d4@6knA^=<=_!3= zWJEBe9Uf1FD88V&F3erj%!ZK7spF=nEm7yXq6UZ41t&mzLA7b9e!@e0s$uY&5UQ8u z1wSyt@-!wyW%#1q23&9JAhEwSh-FtF#P^tv!GnkPp~m&c`NDNMM7Oq8k{;;2e}5|) z>|Vu~qHvhTz#v8wT*#e&H=4kae|220AFBX#kTvSXB>4rXHRyS5i`S{h0~pd#Bc zSOgOueO5;-=uNkfw&ZZBqW3mqMxrE;Q_4l}?*L2Cf_tcSFqLkj>b+M{)D_J4FBy2I z-d^(3C?Z?(+9DsC%T|lMVAQO4U+{&qSo6lx`!DYF-5VOYSzz~72u~$2I`U4R=77k} z(qi!@CGB@Odh{!hME?G@?-7$*G;mDV@d9%KF34(And4$VqrRRxcT3HPaQAkSGiUuF z4EYEmulvnCHubU*%zyZC2nDFN9U-!O$+iRbjs^0%{>e&!fi@ASr3N9jdW~ZM(QftR zo?NU@6wg;Cbgym$z>C`9pnC)J!Ae7f0>qU7(p-j=V3@p8N1bEjvy1@SUVi;AV zp8PcY$CzROa@RhY3BneaqZv3w#q|Y__F97Qs%(v_`9@^(Fz@wBQRh1{iDq7#^Jf{R z$|vRm6&#PD|5a~)W-qbfHCi&(?X~&oUQ#=>P()_2!J27589ivhmYh6zPj){A6HzE$ zbalwdY(I5suu!tTf9QeNe%d^=NV@Op@T1iI^i|y=*~$K4_on@forog&m8&COGy9px zgGGu*{n!zls+@&KUaU;4jqztX$cE?@t6Uhs1j!!c0FlLNeA=U7W(TY zgrD1*s!npL+%fXLM$p1UDDauXHKEKxKxNV&BS9V8ASlWyimFYN;D`?aTN8pN+Sn4S z!=5|aPBTp*&TDJDdz>uEZckftf3Ap^-muyBLX8GVFvg?QOo_dxte?@>2J5*vUud3a z#(E{bPI2ql;-E) z3xIh$sEheduBK2^1{9_rsA(M)skzS-=eDBRz&4>5%}>YG6u8wzOo>dHB`BZnr7Md+ z2P`PNZTqFQ?R;GBr**2p02(x1>?M>IbN6(>l)oVuM0SU-Yr1E+uC9J)^MUsn4$P}> z=+oPJly>%ERlmMza%juF`RwCPRQ;P3y=|}Av$^BpdboTymF|?X#L)`na^>4$ zAok;{Vt$**+wt=Z*6w1QYpxM0=UgQf4V@Cy3fCOd^DbbIbyer6bZM_IQDb>d)W)mo zpjwbG#5YgW9smf+^s>gdLSp2%I=}0iOz* zLs>*>A@;!G5pu_Tk2c3gRfsZHUzdYJ)T+CLMH+Cf{s0qJLbG!#Q}xE(IfO2NwIuNg z5Fs#V7`mHt-2ja6QKs6nzBlKjk4od}J|a_juHt7&#|c4_W{W_u9P%)fp6=!b-!^2k z_8PEth9v)V4RR1i1^(v!?z<#@AS4z)V22NR_-#_}Sc+y}$`4!(0pY*EFV-V^Mr{iN zGUfr5DnDp3-JukSeBrsMGGcn%4;+KXKI$We0cb)HEYI}Vqyg9})tr<+R1|#f;g5bz zB5oQW$qx`X1V|iH@gyVq=l#XB^aS{+jqniA>KAwn$T4vw2fkPTJd%XNyT2W&&97^8 zOqH>Nuhak}uHbi?1nOwft0zNAp8@+)K`k7_xU(IgYuKp$=fxM(+43#SOaLu4(KuvyCH&C3jF*rq@)i4b65KqyIQQc8-J7$uW3s9=Yz*bj6Oh>iEu z^Jc(fa3yq{1`AjLPeky)iKG@Ndxtg#HT9>Nm4InP@EF<&w_MZ4WHQEY;hN|Vlu#Ly zj0V#u1hXUtIG7oWrOjNF0c?*03Gqpnl8|5oV$K9eF7ixY@iagKYyg2XCWL7hpGyRM z(#W`UE3;o2BK-^ir$Tv6fs&Ar3S#euDS%|1iVe>mIE)a1NDF>(7@XI?lS!Kzy<{I* zHkrd#O|mV62sO-GEb*e$&Obs;2+K(|slpVJKWReU#6QZonyB8qr_?AI@nN1BsQGD^b0ki(Ya6{SrV*OWob zgu*1@EttI6768&#N(`M!Avp+=YM)B2V)lic!&`J^UKz=$PzO`w_Epa+r~G~Z#i`{F z831y%soiI&y1X*dWr3a|KTC$f{_H@4QGas$09Yzu%0zvgp}bVK&~B!%b25ye8eu!- z^KK>p%27dtPZ}W-v~o(dpH!;o_Hv>zGJYjb-PDI+;JGcNl-RI^c>( z*-L&w>ird@_^i)$5&!S9LE*B1y>vyo6g8J}jod7K&x{YBPSClg@49wrpcxPrk_`<9 zU=0NP{Ayq`xkuaU?_YU>8I`r+t zRh80Z9%n`1oJi^plC%r8#5oN_qTpw<^`S9gX&%61r|h$-Xe_#(G{;XB-2me!u!Ys3 zej(4^O~q+{AkSsbr%@`J)%HK^5={Ak*Uf$}cAd_b1pBQLAPdbeb@YNHGVfQfJuXnojtBC1g390HqGh}GE*~S{ghO>S~)c-`T>UJ`@mzk z(_ER{)_tJDji`_h*ps_yZGd+i-Ub0cs@aWpA#Lh{z|n0y#nTtZX-IgLsN+r;URakTr;Op@RZn^n)wF3B5^@-gf#GA?nxm#R(ueuO{dq zVROZCPM!P_cv~y&fG$9-+o8w-Av7dv5=>g{?jWju+Mf&XI0HHxMn`0z6rd+egy>2H z-s9OEA{dC>054atoe)Rz!y1S=h%C+VAm(@mLOn+3J(Q(gB%@uD_?#1oKzm&WA%dV0 z!Dt)YhlmICQz8P`*QMm(X}euASO(l<1m9Iuw&bLSXGt_6B$%4^+P~LYe~{^Zh4YZ;=MXgcy9? zH0TjCL6Ujn4O2XhcCgtH@&TS(4Q9!9QF*tCw7<0o0R-iV zDAEUs42Lq_bQzNljOtRq)9t@v{#vEIA8vq$BpXcpK*DN(XJZvah{8*Q5hTk4n;V7O zy1=jxfO#{_DT=ZCfDe63LSlfoPYkg-X8wQVJu6SXHVlbrS)U|`v za92E%`Il?y+1ajd!@Wk+@OnG2sU#3szY+t+qI@u@6e7?vM#wQnRX)t)GRB|Y#wF6f zpbHoo;5B%SUN^%N3>&>z`tF<0xc;Rkm7y-RJL7EDT_R@fz?#u-dI!(O1=A3LxwHA6Ww%=*Bv-`>4SX0klmi*NjF6CGDwF)c@_^eVjKhb? z=R1=g-}B-W#udvanM5(z+js^LQkcl($r*kO>HD5^yiSw;yHitg3S-A)ff42-Ab2l_ z-?Zh+{0F&yj4>_~YD_h#;h2n+&8^ws|hfN`!UACjgxIf5C}Y7fVo@!7k#Lka zXanCC1k&Q5Nx`^Za>lcE%EsA+c4&dkE03{sWNo z!DCehlb-cZ>H`_TXnFldpWY>K%mP1j*(MW6acv}?k2Z4cx$`IF8E(%@W=*m|wu)XN z(!x5gR=*~Q{&pW!?OSl2Hus3FRu0gP@y&t-_Z#$uDbmOjbB1I})w}_?atY1BJgJEu zgdOBBCfB$Tmr9`PH(~}z;a4)%09k{zieVZk$Je&EjztE*O-<);+c(X=)PrT~{lGdX z#uuf0;U*p{Ee29bOW)G9*7txWFv)2^ADGSVAYGvD5Ek#RB7bZEys0Ahs1lprqB;~4 z`zQ=R{K2#UK5Fg?ItW<8@LEr9{rIts$G!tn+5ubc5WU!eWbKf)?La^6P+)g}EUI_R zyEGiDr0`YRERDg;-FF?U#0c0C1!3$KkeE%Q;Umiv4bY727EXxX&K30Hasy#=9By)H z6H~F*w|fgks}fLs#kJJw4An??H;AkufqlF43tx#5;I;r*OIL^Br+u&?{!!~bQOt(Y zrwu~*0a1)^zw%c|M@NVC25s9WA=P#)(U$e2LrVD9q8~fkihzUC_Mye0dEYk*_*d%e zuf{m!8GGc%e(bMwjI=xsdT|_*bsW}q9P#Nm3VV!VKZ#X3iMKp4mGF?fvd+iR9In)7 zj9GVr+XswrOy57@TM(k+xDOWXD^<$&tpl1Ql|Z{rRw%iMLT50%8@jC|cG+cw-kvF) zxVM?yr^wqmZZ`&B$P*?0?K&`*t=+bv>(RSI&Z!=*JtSsI~WJr{VY3T=Rp^)Brx{&@o17t%LXUAn`1z#}Z^(C!;gFNe|O1R*V=Nl{f|AIWLN3yB^ zGvv9&myj8UJlEXCAXn`U(`AOD6t&-J94Y+JNG|Ni4o$g}CLO)sA)WBH>Uj%#w_ z%sgLLcPX_lWkH||lOJQv7Ms|)zF!h|Vz(!o~-6Y_;1(2ksMudXcp+{zH-gKNw6Rrp{!lp7bJAi5ha8rZwld#tkAuO3j84tp_giO%;!>3 zKD~ZTPfC3qlQOUxb;D4zCHw;QRdtE?xph^XS8rMdN*Ld=FQ_%X{jldJ^8BUN#PS89 zq^Wf~3H|cQTvFf6pZ7<#cI-I!310$h zDLNsH^X+^d4}v#DPm9E?21cVdfk4p%Aat=V#RC z@pUG}Xa42ambb#vC0BJ1ug%Pzt#Ytr!A?vh<^)&){kd#u$i+MAxY_Gt&z2V0h$V~y z62rqy@MvKwWJ^U%*&kN!Cpn5HjO%Fo61F;gk40RIfSnDD1jekT(NN<7K!}97MRnQLoS(%p)!Dgou5oAunJS1J_`&G-3A=P_)UvBe z-Gx|46^x0gzVLh|>MtIJ>nBBsHX*q0iKD5MY(%{x_(FkcyicTJy`pU3$Ss+V--Kf{ zlRw08OE$15P%lVCSB*w;?Q*G=@^)OYp%M{Tve6QTqb}Fk0eNXlnip4UrBu`+fqpw4 z)iKG+7%^@j?lU8AY#=vS{cgK;$gdlzVa{UZEqHu zgjWm>JpDXY@$0LHUGz((I=YJ;#W=7UYgbRVEXN7 zaQL$-e5yQvY@5XVW#D4hDCgvw_vVCMs|%NT<~i4L>Uf+&<*rpH{2wdCC#Krtw!fES%)9CFz|Hh<bafDea{ zxg;9c7*^M#?A6zJ7Sx!;)E3|zkL);97*vW;_b=8Z*uW_$=(uc1GN$Meh8+CkD$-2x zi=hfNJ=WoBY~md#g_9&!ck&hnmhk@g)i?`H<0W<#0}BVk#E)06zO&`GSh9;zy%fl; zViBu`(2)^(!UN~2=(ww9Z+5dt?V-~JnlzdMVbB1q-(#Hg}ft&|Ld29{^%V4 zXO@Nj=o~xm4@s|-|6CT@_}MwWbbwL2;Bqjk#rN_r%R+y2j*U!Q4&R&FzdW2U_t-p~ zv2mb(*~-hKIiDl!=FulVV&UU?SN8*O2EwTNe7Gb9}bhuu^fh_2y_xu1ethj~$aR(n{CeUTnC- z-k{X>`94N-WH%kH^WvcmJ^0~2S{C|o+ID|L@lE$6a{a7D<-k$p{X6!}WWRMwVNXVB ztsm5jJXnRd&xwRCxIUq6FB;@WsC#z&kqi_QA^@T5&@t*#x`l_T^o;Yzyr#4afE}20 zP5Bn^gm=Rl1tZT#2aAQ!(ycpzta5WG81KPpWWo4y(8YPI5WkvBjsYjL5NCy-X&kr4 zQi%3zIvU4Cwculsuq+O1_16rv{IdCBrV$ggj4ItCsns|@AWuc!RU`K4j-g9@*ae#0 zPMO|nWpi^aHkz8g0AQEUSxbxYZKsm|n^o1oH=p4a^MFRsitIxnEhya@m8MvCED1aAZr3=Fm));C6E5M7P#b- zcs5uBaUx{B6_7kE#w(Uqpley<#*rZu0v3=)hdfh`WQODM#ZjS4#rKm&Uqd7rsfQ!w zA7A_wkw>nx*yT{M4|}fLE6p0)r!3}{sdjaRwd^VupwX{NgDmi=&fVgEo=KHW!kEV% zUJw!VO$?Gra*EqAJsS1OL zODmU(D5n{&hm%rYH*LwgQBu>9=x!Hae5X*;Jr#(Y5sjQMDk~q8jf)vZ7C-ukmhB@U zXr>1jK1~lXQ38#>S+=VJ<8#_z`ES0`M;3vnx$cn5(e|y|`6rue-4oEELu;$W^-u8{ zN~TREJo@&!Sns8i$=jKUrf;=XEq8>TGR_`h%k%Z?UtTJAbp+-rwjYnH3|k`0W*3=E zgc9|%t)JEe5rdqA%FR-_`n;&xmhBq31B9PgCTh}gCc-$|h4q@!BDRIq-iYHrsL$`> zn4z`vocY3JC?HFFAgLQq8hRNM%RH@fJ&EgFJ>+@xTNIY9yNM71hf&E-gKh2F!N%*F z=hFa8pS}|mwWG1aAZ-I~~8! z)!}Y9M zb&oP=p1j|^PTvJmKZrr7o_~f>Hlj84_hQq|m-w;Q z8V5{<_Y#}WzlcOP4%+MQr_P=)Kf7^x*h8Q0&{uy&9+!M}fevz*KL!XSeH;BF`TV|S z5ZyFhq<>g?yTx8R>HcWt@S*?lo3ErZvQs1!1-bUmHykWCr{4{Kt2vTNB=e71eQ(0e^t)v-Kda%Hnes`UOGb4&6ldr52?W09=X1B8)$zt{;E4KRd`cyB` z>^-!e(?gjtp-fl@-+MpfS=YeVtsEolItR=($7{FMB^b1Q)ylJ^lO3|S%Dmp{?%oKb z#e6Q)?}q4k4mGTvHq5=9%MRJO#uwtFFY_aQD? zp%!sIO7f#$@C$tB^Md#>QN0*G{$F+?{@H+mn<)M~WBjgG{;pR3Us0{R`>zHJm47^7 z?BE8B-atM^+<<}lIbiVq958Gt{Wj zhIVb~`!Z7!>LIfyJd>5ocW_IMIE~k|!^!t^zm)*rlLCLQ+Gd($wo;BpBj)#`pSJpZ7Dmu!nE=kYST#Je|{ zy0YK4bsD{~#O$r}o4Ic}mRYY^tZX;d&PpLEBz|=4v#yPdE18PT&Y!b#BXIb*oh1UI zuRf0;F;<^9EHr(6Gk~qyWIg~&*Gpr(ed%D@(CL=6VKAqR8mA@`uQ*2_w5220{2D8* zwc(YEK?Y%NqO0?2qD;8&c7oK%&(Utrj~8Qz^{n!*;Xg8oiz6g-G$?eLE%=3M_iYl2)E>LrGo;g4C!(5Xv(Zy7$zvfT%T1aMnh?D>qsL12p6L26noQTOL?O7WwRC<~^SVS6Q z789>?hz(sUjsHU+HcM!?DP1OM)0?##>?34g%Qfi zJ&6T-r5XiwOW^426@59d*oNozRo)XHmIj4XoQhVNcX6OV_Sf@>*?5RpwStJ%F!aP` zB5+Zw@DgaQLV!b&AFmw@5)UHqcNO7aSAH8ianSNv&sCiLO&GDi1cGXLCz$fH6&aS* z9}mVP>c$~$)1V;|16&+6A;E+~c{{uLzFiEHNXbm5^wCT8VdxYA{`jB;Z{7Q{JYhh z`gU5}ZFwnFb2~AyPte?CV*adNR?%L&Fy6j`HpA4|`=XBq!CATJ8{eEvD%29o`29;w zXTp>VvTZ}1e+?Mzucs)37zdrm_lbM*Cq(09Uf%s77Db9*02e8St7&u-q%A=}itTEW zRy`^0@yqHp(}3F}aHC$Hz>y3wfg0R7S2`ArSei71^}Ey31Xc~D2Q=J!uP|QK zg~+hRUJn)-P50th&N#MAlgU)$lK6dHwhVp%M5#EZ)};_bDz%$3t&celn#_&MiTw4FpHv&@>dHD>aKq*^o$$GR>l*;IsBen{9Q2uDw9MBU6g4u9l* z+Z>x)S#}1m2=O=X<=DK7dU8GB#U@%iuVHfZ=YSFU(%D{Tb_P51t*(~qy^z~%X2Mc` z{hM040IQT%>Y)2Fp6~~*NpC;YJfXjM?R!77c8&;@jFXeT(N}c_xwzGu!6x5RMwo}z z1l-Gn?=$OH_2II(vFb8e_9k*4R*=Px#icUZOQha{#seYaM-G>sYB}C4y%Pma<~(z$ zP$l&NwmREUfpnKt7zR;bL53(E#|1x{&Y4hG4-Wd)9TiD)peHdYF4jfs{{1EkDogGf z{zNUhYd$DCW^gYBNp(26&2T~(P4vrJQTX+6fOWe8#-1n|RJ(2+!O+;K*s=~El+nm5 zbt^NhK*vRYSWIJ6%bTEmq)9ROB?xuQ`Rp~XY>zJLN8&Eecl;Bi$zC4Yx- zc2cPk&J{D{?D2k&mT=+eE2X`(k2CjXV2dFprhzOW%HD{nZzMuDKX;kYBX2JJvub7Q z5qagE{j*P1)p6b*8R>SquKdq%{dU6h^jYDmcKa%@kxiwdE8NB&; z#M|Xn#Zl`S>qU2L^E~mj<4)!uJ0aKJEM6EnrX*_JmEx+X;*O?j)JfmNmdfpkRCcej zhA7Yg5hhNwr%2Si$g`plaJk*5vD-+kyvre9JH)&*#xox=1Vc-ow57%hrD+l_JXe1* zuC>6N&S7_I&_NeVJD1ieA*t-CntC~XL?U*dIL6noYD8e#=?(0?o7Vl~5i#FjS0yQ1 zYrTlqmt*!cDR?(VqTPn0W4?Ig3;1Nc{oLhFK=pi8pA4Uu$=XGtnh%ekD%ryi8bh5# zOGSE2MR_bk!|Aec6L{OFkSngd5u+U7PerSxJm#*-?m)%WApMEhQ%ciMlm_?@=8r!Q zAHM^R|Kw8F?|?&F_nNkjKGXl))vn(G$M1mSzg$E6?>&F~4mf@X9KQpO-vP((fa7<- z@&7#FSjH2g#WkpI{e6S#PnD^qUzMr(g@sS^Kd}g0W$I7k$93HJfh$OTZom2IuN|q6 zZ%luI64TQ&zch*Q@%Lk6??y+*Mn^|6qoe-{ju;$#`*yIuf1nSI?(IYO^!9c4^mcdm zbar+BA|raMod;_yaiuF{pMfx%o{)W7A*x##fb9)qky0y{s>^@Axwu@rN4m zi;T$5&aTRF%1eDxlHrn>nU$XL3yVm{VG${*X~{S#A_>PtBqaRDOhimV%#&Eu)2L`v zbd*PA6zZ?7sqpZKzhDs|p^;%>5jZR&EbK*4NNC_sEFu^g^uj;r1&)mPg+(9&f)M^l z93SBu;QQR)@248^0^#F}QzKq@`}lg{e!QPQ^YVG(>+k9v;O^n|z$?J+S)hiGk8q@e zJn}I?sTxV)Rq|Y2AWa7vu1MvpfHNijB1pJB!PyZG?!h=j!sVgcFNXP-BVqn9T=nte zzoR4^om}ky;z&5!-F2|BwYRdqYx%QEW%~<}Fu!AMW^Q@ww&g!UBuoqpZ|WNu>FFEl z>R;EKYmvsv6pA>e{O6S}JN|7gCg!misFrA)_cME&qojAulc|Dvah!OhCf#lptP%*w&U z0%K%mXJBH(*%3n|2L^csM0f&()B;pW7r{+bn#+$y6s_f!32Hoz@ z-$BX&rqR zDbFF_$GSkXB(xDS#XQ_0D)yIfO#)%eG&}A#p zToqmwI|r3k7G#i2SaMgcawI8j+D&CPAYYYK4T0jwDCoz~65q@La$`uv9SbNFuuUjh zcZ)7<%QnjcUEd=JB92ddDn7gKi3VB|IL(#1+&y9+AdTvR5ZgAhkr z_2^t&2wrJ0VDtv)6;tx~R8P<)nCnnNuM(LQ#U=j~=un#DgGkBu1`i+|w)Q2^n4&c0 zZ>}Pshs9xiqb-1pAkakol6hN&h#l;zaxNBFt-{PTF08r}(yYJPLAqp!smP6An=Rstiqnb}rv@88d7`-5d zi1hI%+Nz%;P|s5q(@^6e!$^S%fbJ4zl%oJdha{Vhdnv>2m~fA~9o3wNB=$$#QlN;>g|$-b(!-oWFigHz?mzCF^S` zY$-NPwNW{d-Ka^W=qwDgN%Dmv*4*In1N}iV@(M@;*>o-fq}tbaX`9Tt{`i2eV<^SX z*HPl}3gdJa<9#K<%a}okLOpQORS^0`b*6NwMP~jAcZ3w_P!W+O%WQBiirS*GiXZ3bxdW~0&4skZpMW6DH6V(9qxfZIFN zXb5#5a9d)D>*YE<-%tm;yjcFtv9Rex<6=}ERkTH1XT0AaKEWQMh9h$+#}Oe$()7j;MjpWX=DfoW9%@!KU{n0`bCZ#^UDl@On!RrIG#9jb3L>T zBp2W1?mda56H_j!P9b>4a?H!(S*49|B~ubd6v-1(V?pBCh$}8e5O%hcns>X^Vibl6 z)aXtiw_!7rn--!V!Kv^lHO^t#)LzcbJ}U5Dein9vUzK(M6AyiYPx#SBRk8{lryI(V zjqo?)y{nv_Y)g@QJw%p{Q_4-n&0x`Of#jGg;S)q8`jU3Ba=T5b@-r(PK3Z1R4ca)b z^v4_<>4y7rHnqC#*_q2|0hReBqpH-;Pjm~4m66)xfeZ;877}GDgc6zz1KF* z=A463J?_gCf~zUXyOVsqi`RIlu}YE)#o9iPthz=Qn4#7NV7Qg5e+y(GDJ zPRdn%U#X6!;u=&LLab4#zZz733OE{uUOw=6J3Dv!t-hVS!Zldu!y?mB16r@*5ia0Z zmOX03L{>a0()qY-M;UXyR_k@j ztmu3?ojGbf9;);`di&|e=}{XVMU@}1?mUS3xE-Qj<$qyto=EPv1Bj|Z^64&+-ahVR z7_JJE8eE|8KJJ21REKElE>fo*ck}C4hnWm6(l;OXh(uLK*z0~~nmz879do5~>Y7L1S{dRhp_3IJr*xyVrU}qYO#3bWa$*PN79PbEt5Cp5Atf5+%VO zMMOSh9CYZ7NT%Vur)AKIWmpy=;!bvs(oUadxuu8}<;N${V#!B8a=(&s^ub;m!-$D- zeVjr#wk%b2GD?2yic0wha_OY${*TFz(}zaMW!bw&u1nU)@Kc3qYcW#iN(PC_>XGn7 zdr=o|X(X)aI_jvNPC#N_E0>?!bJ=^1cv;lQ(0{i#A+sBV$OL6sevi7gojT>wdzklB zZ$s(I%#de=q)Mqdi9@*T@Y`WA9^UE=!xy-=K3nZ;x0m;M+4J6I48KZ`%y^)XbvF2j z9?d-`|HXnUAH5aiA+`5r%aJQ>l=S2&_q@FGWAQW&sMHaB@6J70%{T9f`^CGsEL`2$ z-wc!$ip6D=JM&&i`J9g3l8j4=d}u(HKNfd8mdVHW7LnGf9#9R70KXPQzNw$xhqf%;?|!&&)RdxgB)9A zGON7izD^lLTnO2|QZsTNl3&!DRc(uQGhTlOryAGn+T+M23LJ#r9j&a!+OFbNNo(cs zw2pj%J3oz7UW%fdo_%xLbq#wui~Vt&Rmsg1gN@p5!xA37z?-s(n}>iFAUI1l8s!&T za4gp*t)}pHrrsEg%$$)bEi>nZ%R59Xo84_vm zfUg)436Ka%Bbg*gqIVz~43BMj{g&qgWAR9I7?8{kep38ML5RQOGe70*K&Co>VFnTl zX@sP7V08Nn`()%*X@r4k;EfQ#+=0Sm75T`)IuaIedJh4@9zz5iD55aI*p&cML_q9u zfP(bBK!(sz2kY4Epu~`1=GDjIXe0zeBK!=AMONCyK*7YT$C zkP?biQM!PjfS^qSu&NG0#|d9C6oXZ z^*0vM{i=r%tu&hiN`MsgW-$#J8cNYX6OIP}UefFnfHGv6N^p=gLCZ269s~y^;#C|5 zb19su$%`pT8uGzB1;+qVb=KSfoXg!-x_toPH9j{|xgg35)(8h&&DT#4hm+<5PhDXb z#ZXc_lOLLNy9>a7BVT15Sc`@M;r85i88@Lt;20VSJXxbx#-uR32LK?abA2Z4@-d&T zg9y*7-^nc-%pAs3aEz0KCn!jfppOkSvjD(TLHsHJkP(RKsl z!9uvj)nXp9^GDDO8ZiFhYJP#RaEVAs?r|3-|2XOXd8^(As%u*Le4h#hLFdQyp|+&AMS>`2(f0TrN_3fe$J004TriX}<_zs&RZr2w@h zFp_Z#HIbqd6p2d7L-%TGo#e}p8bHNRa-EG)%!~S(W<`Tn@xCn#3J`1{+R>~w(wyP} z)XJp=Xh2OveqI+~n?O=02w_V~vyywdMM7F3Py_i+K~k?N&h(i3eT}el)n-!F{XsA_ zo-uKeRIY)>y&mwr8pbtF7O9e_3I|DzM_fgLP*Cawbgt)6{$?>HL`tqw1WrJ}WjAZM z66yqj6}JYBH(F^<4uBT*G{@tlRT!!+1ek=I@=Z96N*BP9k1DYXs9ayRutN>O0K|cL z9ue@y+b6xkpJwIokH$_q|%LDe!&PIW-)nN(g=U4G+x2>=bxKd8GH zrywb&IvpovzkKnt(-{)+Bosg}8=6KlIjyy;*y^Bq zTj`2AB_M7h;}S)qK29MQQOBa{vM7=Uqitr_YrfGAw=Q(Hi)@B8G&65D-}Hf@9GqPv zn{TT+dx)lau{--*Jn*|f-C24Nu!IVVd=T7@3cW}QFGNL(CPqb~yb5#McG7ea%8~gX zT|(MEj!M4^2r33D!zq)EN>eXdG78~Gax_`(Ee|PMBg~twgu`{Yn*jJyT49xxFD;Ls zxkme-V%qbQ9a`RmwmLf1M6)C+WxG&rV zz+!k_?O^F1z!VKHsU4(|0?O8tm2|-$5J2)$KpFs$9z|+`qM&Smm`IT_!hy|VpimU7 zQfj2vm%^SKt}s5-qc#GO2Evf!V9gP*IeES@Ifdp(o-c5cY6yk`f_Xq?9Ao*v1~<_| zmw-bO=P?*JkO4ueg(M$(ME7SYmqd+BnY&-5SSVvR{AD{$ETQ+}UKLRc{`kBaLqFc2 zGA=Vb;9@b(7CsK``oL(>r?}a?D>kGJ|F9u8mY*`lJ~|;+52cs@(Q=PzV}>EL6vhBx zfiJ97jr0L>7E(mj4z|^r(awG<8KbbMHkIK0#H;g&n0z2LZm?XY9MGg z86t{2sUC_$fsMIALgMfzNYZ>?QhN-f5Dy7NfG&UT7%&j+uK)pYU{d|a6F-;==csxo zga$>*KmfcnS(;xSm2q4$5Qi6E)``D@zH~rRQ|AXp=S{vYKfpjdxIuJ)X|@&GKR!9* z80I750gx9@(bGjCSWKb_tyZfCTu-8^1CW~f!cOt4UshI4M_vg3df|#$-Tg|r_jR>o za78Q#?lSV?9!33u_{U!+A9JYa`cmP9y`|Kzqp%4AxEqxJYiSceF(wAGOkHXgUw}!? zG>OBb$CsE7pb1|A3J6jdd|JtG>BHU{Ny)M?0#rUSHFTGDT$m9N5-7ffEvGAqpGi{san>XJOI5pQdyP(fZrr5>TsY@cZbsIpXrv zCSsIF;xkDMgcb!e#(!cVfS);%OWt2n@`qKRfv^K`;P_kxYT876z5LG>jX8yeIw@z` zF8%5#we#mE_vwC7V(w9IgPnnjPP;TETg8aW$6(xJ_|i=Z1p+{Rd`;46h>m;GBnr3* z7-JwnlVP7#)jx$204yk231K7@y~|*_8}S(KP(Q{p_T@#|7sz+$*v)w!^DnJavzv=pXYlZ*Gs%bZ5y)k)tueyzl;%V9=0x!14x!?^sua{ZZNkA@F9lExE{ z)T6!^7Js*aVu=!EteF9>p8 zXr-?AXiU9MhqGS+vg{wcQ`@FNoo@nuZsL#BOi$C&;hB!4rTAZfH3R_x-J~j*`qb5< znE+OVHccTv=>#D#$X=z1Pkkwz|4(*=w7pn9x)=*)$>Yz=tM3tftgaz#+)_V%Rgi{5 z={wF7hoHb2t22JWytk;aWG%2)iStG#lzJ-V1)yWGGT3RM$OfQy{ zy@aatA!>1N>KRKK*Sa?oEfLg6-k&4yS(DwG-#KdDMdc9WKO3m5%CW@nx;M)DdY;TG zQ_AoizV254W5pj?X$HP{?r;r8(QmxX4ZFvV^3&YwKS^fyqgXxv> z+egLDA_@nKadE28Yks|#^$=#8^_(~Z|3-@89N(n%J2R*N#F6KC#sA#0;R14wBV>ts@!G2Y%y9x9;t8=U8bKeY&CZ!KC zl%T21l>vSqc`Iw;=L(zE>biuvTXnBD3oM@YGb(A3kWI?2VG+P1E!7mZNOCg?h5js0 z@FzcR-c^YO4ocJKyS72L3WBsJV_~f=#~kP(26486ot}BM{D8gHy)2`{M$s$mBs_8c zi#}Cfnr(K+HLq~`M4IS#`pnpP6!BpCC{+aqzm{h6$kfY0Xa(Ji0VGggcu!gj8(kcf zE#fJLTo9zG@U8Rfy_#%FpuQ*xk|IZBlq*<^$#_JLjAIhbmFP@feytJb*_@lQznP zA7so}Eq5kCG+!`_q3pdvrmLj&#Q+~CC{{wI-BZSkmrzK0c^Ta-Ad8aKq&QLI zd($;ej5p;ruawMZ9SP2#EcDTFtFum`wJ}|%=`D3wDR(IgG92UZiFqt6AxEk=y7h>T z6{5y`JQAbx5*<)_bqbc*M0vZsD09lg8Tr#B;jz@C8Y((v&V!)@e2fA;s)v)V0jiVj z456v-z9+W$B*nj}>psaha`25MW*c+bt{(V+SAnHSx&infzLBm58O5$i5gGb5t2-s}(22m< z*Z2Dh>leLhm5O!`3P96od5m=|$~PS>)~({TW?tmq^z4$kF_V=2gFA_Zn)Y6WQ466L zNKAe>aKK`)oms(Bu#r*W7bbWNZ-uAU?#4;PfLNOS8WtGj`2l$7oPCE+)TYlwr0EB%xNd^Oa>IZ=?@(}x zg=@Pbe3XeV?jg>3v;kUnHmdhfL^9}ePgPY@yoL@+Ec^jf@x$h(@rO>syMmKW*bW=s zLwQNpreQ~t(EfG?;2qr;-uN~I!M#h}&W@wj=}m(>5+{qh!oeN$#I~aQ35NYnA^V)G zUWm9~{y;ognKMjr{T-ea9do}dCsY>C`!m4tvFW5YAvbyM6GE5ulQ zcuMDZ8cUNv!f=v6jTyfp|A5r=L#HdU~!6~bjlo&AQHCPaa==JLDH4lMo0nD zv^l21(Ap_yI|gDj1}c@kKN{$fgggi4z;4RZ1bWt<=-Eb*-K{MvxXlp7+`l#xV0p6a zt*eTdtQ?oO;aTx|R(yMe99Uy@ca*KhZE)tKh&9vy>h3bWjpx1fun&yR*C{kYKsqP5 zB_J=;UYGI>LpUs8K`f@^yveiUr@Xh6D`RL-?BttDLtzPdRYJYqD)Y0`PNI6$Euk%& zCF+USXP6*(xFSE{!5@XXKJ@^NXXVzvlohl$n|y+(wrA*mLf@FI1vpx7&_Adg!t@Gu zu!pPlnkl!wC^2z4XXEIjGXJ>;n-0KP@NZuEcC{~_i&s;zSC*b-wJVdRn-L^yyRIez^M!n1Sdmm z->v*u6pqmLm3n!?sl#AYf_OQ?ll%jLp{!O9ueW`7?$YU0m++cNkvW*S*2fxmyovtp z;=CF(U6@XhfHaQFvRV)(Ajqh;l<3)tT%1`0Hg_T(C=%J+Tz3;g?w zLMb+8J;_KxztUd{O3~-~l0iSlA!9HX+yxm)v`#URW>@f95gv!-QQN!zrs-vK+@!{j zFB(6yuI=L6ce4=J?4kGRaMJXt4M~8vvJfPkWRay1%o1x-OhW_G#O6V1n`pXQRh3jZ zD!*sYWOFb!NhTL_D!oi@bnX7GEob!w!AjLlo6-X1@97H@8dO#&EslX6RIs5*EDLcu z=vcOGPG}3}n1@v;OH1UaNYw300v2|)OaeU4=)Bk?a?~SM+f7Vr(Nt2!H9q57v1tFA zfs@_S67z+kMYUR{-l^X$g&e&@@be=Wbml~QwU-%mj_}tkbr7z-dLTZ7?A~1N-s@B< zM!mhsRlUZ)v`u?^^+$A&N4;<9bdALN%&HVLn5Pt|9l zqbs8+AU^;Kf`A<`y5d>7LEG_E2Js0(?_G`cET;P$_juCh6Iey#xIOifw_{QU^gQC; z?@j8;N+kv$K^Es^3FvrQC5X)dAV^0ai|kKQiepy7a7%&h7=Q`5{_tA;pcefA3tfYC z9{FvYhZIB?l6XTzKLv?Nu{B6WlGDOtw9y8*Yy)EQMZexUkBt;~GaqM*huE<6n;05k zs|K*12ARINEH~UOIS4Zf5-0`9kpt(dLGrSjABaH}Mb2Ba6%WtdPuT-Pz^(E;P&XwOYjdmLB)060>7B;5pp+Q@I0?4%apfeb^hW^O6~)H@ZSOJXI^Zt!eynnGMJW z)($g5yW#L|I3Gxy2*MzV5R=zCLd0&H1mOqUrVS_5ARk7CZNCgPdm|wt9-f>FYFNHohQ$^h3 zXYIIho#cV>gtW7T+HtA2xVhBvlx*r#_{J1V)btOX>0Jv;`rj6Ej?-uSM$*(4-NsXf z#~nuMBamY=S^+a9^+_{vOL4}T4}4a_>PS813BlV|V$>gyQ8Oj&mL*?i9LdB`sVTrk zoQu4*^Y6-AMM_YaS$8XIw@wRBGN^m7H8%i5ondVjVf|dE!kY|pXJyuxF$--q<{xa0 zKE9HAlPti`Tz%BkTYmOVr!{aA7~*FWnX2d1G@FXB0p1`F>PkpMkhb;ONCeLLms^K7 z&xZF|CrN>@+cAg%9FgFL6Wj(obi*l1;q2vb!Sc97d7PUXpvn!$#j+4fMjsfg6Xa+c zFFu=s0b0UM(^=-RRW{gS($Bt-%YVzUz9U#fLc%;Yp(_5Bj-6fgY`N89#qGt);Kir$ zi&YO7t1A|3nipMX=5qXIQ)~fP%wR|X4�hBTt+%hJ*{FOI<&jV2d$^Bn%conwpoFikd6> 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 offset_xy; if (bfc_fn) {// support_backup_fonts @@ -1487,11 +1494,13 @@ void Slic3r::Emboss::text2vshapes(EmbossShape & emboss_shape, } std::vector text_align_offsets; text_align_offsets.resize(text_scales.size()); - for (int i = 0; i < text_scales.size(); i++) { - float x = (float) offset_xy[i][0] * standard_scale; - float y = (float) offset_xy[i][1] * standard_scale; - text_align_offsets[i][0] = x; - text_align_offsets[i][1] = y; + for (int i = 0; i < (int) text_scales.size(); i++) { + if (i < (int) offset_xy.size()) { + text_align_offsets[i][0] = (float) offset_xy[i][0] * standard_scale; + text_align_offsets[i][1] = (float) offset_xy[i][1] * standard_scale; + } else { + text_align_offsets[i] = Vec2f::Zero(); + } } emboss_shape.text_align_offsets = text_align_offsets; std::vector no_use_text_scales; @@ -1693,6 +1702,172 @@ double Emboss::get_text_shape_scale(const FontProp &fp, const FontFile &ff) return scale * SHAPE_SCALE; } +namespace { +double measure_line_width_mm(FontFileWithCache &font_with_cache, const FontProp &font_prop, const std::wstring &line) +{ + if (!font_with_cache.has_value() || line.empty()) + return 0.; + const double scale = get_text_shape_scale(font_prop, *font_with_cache.font_file); + fontinfo_opt font_info_cache; + Point cursor(0, 0); + for (wchar_t letter : line) { + if (letter == '\n' || letter == '\r') + continue; + letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache); + } + return cursor.x() * scale; +} + +double measure_text_height_mm(FontFileWithCache &font_with_cache, const FontProp &font_prop, const std::string &text) +{ + if (!font_with_cache.has_value()) + return 0.; + const unsigned lines = std::max(1u, get_count_lines(text)); + const double scale = get_text_shape_scale(font_prop, *font_with_cache.font_file); + return get_line_height(*font_with_cache.font_file, font_prop) * scale * lines; +} +} // namespace + +std::string Emboss::apply_text_wrap(FontFileWithCache &font_with_cache, const std::string &text, const FontProp &font_prop, double wrap_width_mm) +{ + if (!font_with_cache.has_value() || wrap_width_mm <= 1e-3 || text.empty()) + return text; + + const std::wstring src = boost::nowide::widen(text); + std::wstring out; + out.reserve(src.size() + 8); + + size_t para_start = 0; + while (para_start <= src.size()) { + size_t para_end = src.find('\n', para_start); + if (para_end == std::wstring::npos) + para_end = src.size(); + const std::wstring paragraph = src.substr(para_start, para_end - para_start); + + std::wstring line; + auto flush_line = [&]() { + if (line.empty()) + return; + if (!out.empty()) + out.push_back('\n'); + out += line; + line.clear(); + }; + + size_t i = 0; + while (i < paragraph.size()) { + size_t start = i; + if (paragraph[i] == ' ' || paragraph[i] == '\t') { + while (i < paragraph.size() && (paragraph[i] == ' ' || paragraph[i] == '\t')) + ++i; + } else { + while (i < paragraph.size() && paragraph[i] != ' ' && paragraph[i] != '\t') + ++i; + } + const std::wstring token = paragraph.substr(start, i - start); + const std::wstring trial = line + token; + if (line.empty() || measure_line_width_mm(font_with_cache, font_prop, trial) <= wrap_width_mm) { + line = trial; + continue; + } + flush_line(); + if (measure_line_width_mm(font_with_cache, font_prop, token) <= wrap_width_mm) { + line = token; + } else { + for (wchar_t ch : token) { + std::wstring next = line; + next.push_back(ch); + if (!line.empty() && measure_line_width_mm(font_with_cache, font_prop, next) > wrap_width_mm) { + flush_line(); + line.assign(1, ch); + } else { + line.swap(next); + } + } + } + } + flush_line(); + + // Preserve blank lines from consecutive Enter presses + if (para_end < src.size() && paragraph.empty()) { + if (!out.empty()) + out.push_back('\n'); + } + + if (para_end >= src.size()) + break; + para_start = para_end + 1; + } + + return boost::nowide::narrow(out); +} + +std::string Emboss::fit_text_to_box(FontFileWithCache &font_with_cache, + FontProp & font_prop, + const std::string &text, + double wrap_width_mm, + double wrap_height_mm, + bool do_wrap, + bool do_shrink) +{ + if (!font_with_cache.has_value() || text.empty()) + return text; + + const float original_size = font_prop.size_in_mm; + std::string working = text; + // Work on a private glyph cache so fitting does not wipe the caller's style-manager cache. + FontFileWithCache local_font = font_with_cache; + local_font.cache = std::make_shared(); + + auto rebuild = [&](float size) { + font_prop.size_in_mm = size; + local_font.cache = std::make_shared(); + working = do_wrap && wrap_width_mm > 1e-3 ? apply_text_wrap(local_font, text, font_prop, wrap_width_mm) : text; + }; + + rebuild(original_size); + + if (!do_shrink || wrap_width_mm <= 1e-3) + return working; + + auto fits = [&]() { + // Width: every line must fit (wrap already enforces when enabled; still check for shrink-only) + std::wstring ws = boost::nowide::widen(working); + size_t start = 0; + while (start <= ws.size()) { + size_t end = ws.find('\n', start); + if (end == std::wstring::npos) + end = ws.size(); + if (measure_line_width_mm(local_font, font_prop, ws.substr(start, end - start)) > wrap_width_mm + 1e-3) + return false; + if (end >= ws.size()) + break; + start = end + 1; + } + if (wrap_height_mm > 1e-3 && measure_text_height_mm(local_font, font_prop, working) > wrap_height_mm + 1e-3) + return false; + return true; + }; + + if (fits()) + return working; + + float lo = 1.f; + float hi = original_size; + for (int iter = 0; iter < 16; ++iter) { + float mid = 0.5f * (lo + hi); + rebuild(mid); + if (fits()) + lo = mid; + else + hi = mid; + } + rebuild(lo); + if (font_prop.size_in_mm < 1.f) + font_prop.size_in_mm = 1.f; + return working; +} + namespace { void add_quad(uint32_t i1, @@ -2193,8 +2368,8 @@ void align_shape(ExPolygonsWithIds &shapes, std::vector &offset_xy, const // Speed up for left aligned text if (prop.align.first == FontProp::HorizontalAlign::left){ - // already horizontaly aligned - offset_xy.emplace_back(Point(0, y_offset)); + // already horizontaly aligned — still emit one offset per character for callers + offset_xy.assign(shapes.size(), Point(0, y_offset)); for (ExPolygonsWithId& shape : shapes) for (ExPolygon &s : shape.expoly) s.translate(Point(0, y_offset)); @@ -2216,9 +2391,11 @@ void align_shape(ExPolygonsWithIds &shapes, std::vector &offset_xy, const Point offset( get_align_x_offset(prop.align.first, shape_bb, get_line_bb(0)), y_offset); + offset_xy.reserve(shapes.size()); for (size_t i = 0; i < shapes.size(); ++i) { wchar_t letter = text[i]; if (letter == '\n'){ + offset_xy.emplace_back(offset); // keep 1:1 with shapes/text for multiline offset.x() = get_align_x_offset(prop.align.first, shape_bb, get_line_bb(i + 1)); continue; } @@ -2272,9 +2449,11 @@ void align_shape(ExPolygonsWithIds & shapes, // Align x line by line Point main_offset(get_align_x_offset(prop.align.first, shape_bb, get_line_bb(0)), main_y_offset); + offset_xy.reserve(shapes.size()); for (size_t i = 0; i < shapes.size(); ++i) { wchar_t letter = text[i]; if (letter == '\n') {//Enter the next line of text + offset_xy.emplace_back(main_offset); // keep 1:1 with shapes/text for multiline main_offset.x() = get_align_x_offset(prop.align.first, shape_bb, get_line_bb(i + 1)); continue; } diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 121ffaaab6..9c39e9c6b3 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -169,6 +169,25 @@ namespace Emboss /// Conversion to mm double get_text_shape_scale(const FontProp &fp, const FontFile &ff); + /// + /// Insert line breaks so each line fits wrap_width_mm. Preserves existing '\n'. + /// Word-wraps on spaces; breaks long words by character when needed. + /// + std::string apply_text_wrap(FontFileWithCache &font_with_cache, const std::string &text, const FontProp &font_prop, double wrap_width_mm); + + /// + /// Optionally wrap text to width and/or shrink FontProp::size_in_mm so the layout fits a box. + /// When wrap_height_mm <= 0, only width is enforced. + /// + /// Text used for shape generation (may include inserted '\n') + std::string fit_text_to_box(FontFileWithCache &font_with_cache, + FontProp & font_prop, + const std::string &text, + double wrap_width_mm, + double wrap_height_mm, + bool do_wrap, + bool do_shrink); + /// /// getter of font info by collection defined in prop /// diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 7f8fe32f31..7027b8883d 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -249,6 +249,10 @@ static constexpr const char* THICKNESS_ATTR = "thickness"; static constexpr const char* EMBEDED_DEPTH_ATTR = "embeded_depth"; static constexpr const char* ROTATE_ANGLE_ATTR = "rotate_angle"; static constexpr const char* TEXT_GAP_ATTR = "text_gap"; +static constexpr const char* WRAP_TEXT_ATTR = "wrap_text"; +static constexpr const char* WRAP_WIDTH_ATTR = "wrap_width"; +static constexpr const char* WRAP_HEIGHT_ATTR = "wrap_height"; +static constexpr const char* AUTO_SHRINK_ATTR = "auto_shrink"; static constexpr const char* BOLD_ATTR = "bold"; static constexpr const char* ITALIC_ATTR = "italic"; static constexpr const char *SURFACE_TYPE = "surface_type"; @@ -4979,6 +4983,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) text_info.text_configuration.style.prop.boldness = bbs_get_attribute_value_float(attributes, num_attributes, BOLDNESS_ATTR); text_info.text_configuration.style.prop.skew = bbs_get_attribute_value_float(attributes, num_attributes, SKEW_ATTR); } + if (bbs_has_attribute_value_int(attributes, num_attributes, LINE_GAP_ATTR)) + text_info.text_configuration.style.prop.line_gap = bbs_get_attribute_value_int(attributes, num_attributes, LINE_GAP_ATTR); + if (bbs_has_attribute_value_int(attributes, num_attributes, CHAR_GAP_ATTR)) + text_info.text_configuration.style.prop.char_gap = bbs_get_attribute_value_int(attributes, num_attributes, CHAR_GAP_ATTR); text_info.m_curr_font_idx = bbs_get_attribute_value_int(attributes, num_attributes, FONT_INDEX_ATTR); text_info.m_font_size = bbs_get_attribute_value_float(attributes, num_attributes, FONT_SIZE_ATTR); @@ -4986,6 +4994,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) text_info.m_embeded_depth = bbs_get_attribute_value_float(attributes, num_attributes, EMBEDED_DEPTH_ATTR); text_info.m_rotate_angle = bbs_get_attribute_value_float(attributes, num_attributes, ROTATE_ANGLE_ATTR); text_info.m_text_gap = bbs_get_attribute_value_float(attributes, num_attributes, TEXT_GAP_ATTR); + if (bbs_has_attribute_value_int(attributes, num_attributes, WRAP_TEXT_ATTR)) + text_info.m_wrap_text = bbs_get_attribute_value_int(attributes, num_attributes, WRAP_TEXT_ATTR) != 0; + if (bbs_has_attribute_value_int(attributes, num_attributes, WRAP_WIDTH_ATTR)) + text_info.m_wrap_width_mm = bbs_get_attribute_value_float(attributes, num_attributes, WRAP_WIDTH_ATTR); + if (bbs_has_attribute_value_int(attributes, num_attributes, WRAP_HEIGHT_ATTR)) + text_info.m_wrap_height_mm = bbs_get_attribute_value_float(attributes, num_attributes, WRAP_HEIGHT_ATTR); + if (bbs_has_attribute_value_int(attributes, num_attributes, AUTO_SHRINK_ATTR)) + text_info.m_auto_shrink = bbs_get_attribute_value_int(attributes, num_attributes, AUTO_SHRINK_ATTR) != 0; text_info.m_bold = bbs_get_attribute_value_int(attributes, num_attributes, BOLD_ATTR); text_info.m_italic = bbs_get_attribute_value_int(attributes, num_attributes, ITALIC_ATTR); @@ -8015,12 +8031,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) void _add_text_info_to_archive(std::stringstream& stream, const TextInfo& text_info) { stream << " <" << TEXT_INFO_TAG << " "; - stream << TEXT_ATTR << "=\"" << xml_escape(text_info.m_text) << "\" "; + // Preserve newlines/tabs in text attributes (XML attribute whitespace normalisation). + stream << TEXT_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(text_info.m_text) << "\" "; stream << FONT_NAME_ATTR << "=\"" << xml_escape(text_info.m_font_name) << "\" "; stream << FONT_VERSION_ATTR << "=\"" << text_info.m_font_version << "\" "; stream << STYLE_NAME_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(text_info.text_configuration.style.name) << "\" "; stream << BOLDNESS_ATTR << "=\"" << text_info.text_configuration.style.prop.boldness.value_or(0) << "\" "; stream << SKEW_ATTR << "=\"" << text_info.text_configuration.style.prop.skew.value_or(0) << "\" "; + if (text_info.text_configuration.style.prop.line_gap.has_value()) + stream << LINE_GAP_ATTR << "=\"" << *text_info.text_configuration.style.prop.line_gap << "\" "; + if (text_info.text_configuration.style.prop.char_gap.has_value()) + stream << CHAR_GAP_ATTR << "=\"" << *text_info.text_configuration.style.prop.char_gap << "\" "; stream << FONT_INDEX_ATTR << "=\"" << text_info.m_curr_font_idx << "\" "; @@ -8029,6 +8050,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << EMBEDED_DEPTH_ATTR << "=\"" << text_info.m_embeded_depth << "\" "; stream << ROTATE_ANGLE_ATTR << "=\"" << text_info.m_rotate_angle << "\" "; stream << TEXT_GAP_ATTR << "=\"" << text_info.m_text_gap << "\" "; + stream << WRAP_TEXT_ATTR << "=\"" << (text_info.m_wrap_text ? 1 : 0) << "\" "; + stream << WRAP_WIDTH_ATTR << "=\"" << text_info.m_wrap_width_mm << "\" "; + stream << WRAP_HEIGHT_ATTR << "=\"" << text_info.m_wrap_height_mm << "\" "; + stream << AUTO_SHRINK_ATTR << "=\"" << (text_info.m_auto_shrink ? 1 : 0) << "\" "; stream << BOLD_ATTR << "=\"" << (text_info.m_bold ? 1 : 0) << "\" "; stream << ITALIC_ATTR << "=\"" << (text_info.m_italic ? 1 : 0) << "\" "; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index d7b3117d3f..81d2114e57 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -854,6 +854,10 @@ struct TextInfo float m_embeded_depth = 0.f; float m_rotate_angle = 0; float m_text_gap = 0.f; + bool m_wrap_text = false; + float m_wrap_width_mm = 40.f; + float m_wrap_height_mm = 0.f; // 0 = no height limit + bool m_auto_shrink = false; bool m_is_surface_text = false;//for old bool m_keep_horizontal = false;//for old enum TextType { @@ -867,8 +871,8 @@ struct TextInfo RaycastResult m_rr; template void serialize(Archive &ar) { - ar(m_font_name, m_font_version, m_font_size, m_curr_font_idx, m_bold, m_italic, m_thickness, m_embeded_depth, m_rotate_angle, m_text_gap, m_surface_type, m_text, - m_rr,text_configuration); + ar(m_font_name, m_font_version, m_font_size, m_curr_font_idx, m_bold, m_italic, m_thickness, m_embeded_depth, m_rotate_angle, m_text_gap, m_wrap_text, + m_wrap_width_mm, m_wrap_height_mm, m_auto_shrink, m_surface_type, m_text, m_rr, text_configuration); } TextConfiguration text_configuration; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 29c7cddb63..e7d8fee8ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -1369,7 +1369,28 @@ std::unique_ptr GLGizmoText::create_emboss_data_base( base.from_surface = style.distance; FontFileWithCache &font = style_manager.get_font_file_with_cache(); - TextConfiguration tc{static_cast(style), text}; + + // Apply auto-wrap / shrink into the shape string; keep UI m_text as the editable source. + std::string shape_text = text; + if ((m_wrap_text || m_auto_shrink) && font.has_value()) { + FontProp fit_prop = style.prop; + fit_prop.size_in_mm = m_font_size; + shape_text = fit_text_to_box(font, fit_prop, text, m_wrap_width_mm, m_wrap_height_mm, m_wrap_text, m_auto_shrink); + style.prop.size_in_mm = fit_prop.size_in_mm; + m_font_size = fit_prop.size_in_mm; + m_style_manager.get_font_prop().size_in_mm = fit_prop.size_in_mm; + } + + // Multiline placement uses the Emboss 2D layout, so character spacing must live in FontProp. + // Single-line text still applies m_text_gap in GenerateTextJob::calc_position_points. + if (get_count_lines(shape_text) > 1) { + if (std::abs(m_text_gap) > 1e-4f) + style.prop.char_gap = static_cast(std::lround(m_text_gap)); + else + style.prop.char_gap.reset(); + } + + TextConfiguration tc{static_cast(style), shape_text}; auto td_ptr = std::make_unique(std::move(base), font, std::move(tc), style.projection); return td_ptr; } @@ -2357,6 +2378,38 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) + ImGui::AlignTextToFramePadding(); + bool wrap_changed = ImGui::Checkbox((_L("Auto wrap")).c_str(), &m_wrap_text); + ImGui::SameLine(); + bool shrink_changed = ImGui::Checkbox((_L("Auto shrink")).c_str(), &m_auto_shrink); + if (wrap_changed || shrink_changed) + m_need_update_text = true; + if (m_wrap_text || m_auto_shrink) { + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Wrap width")); + ImGui::SameLine(caption_size); + ImGui::PushItemWidth(temp_input_width); + float old_wrap_w = m_wrap_width_mm; + if (ImGui::InputFloat("###text_wrap_width", &m_wrap_width_mm, 0.0f, 0.0f, "%.2f")) { + m_wrap_width_mm = ImClamp(m_wrap_width_mm, 1.f, 1000.f); + if (old_wrap_w != m_wrap_width_mm) + m_need_update_text = true; + } + ImGui::SameLine(); + m_imgui->text(_L("Wrap height")); + ImGui::SameLine(); + ImGui::PushItemWidth(temp_input_width); + float old_wrap_h = m_wrap_height_mm; + if (ImGui::InputFloat("###text_wrap_height", &m_wrap_height_mm, 0.0f, 0.0f, "%.2f")) { + m_wrap_height_mm = ImClamp(m_wrap_height_mm, 0.f, 1000.f); + if (old_wrap_h != m_wrap_height_mm) + m_need_update_text = true; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(_u8L("Optional max height in mm. 0 means no height limit. With Auto shrink, font size is reduced to fit the box."), + m_gui_cfg->max_tooltip_width); + } + ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Text Gap")); ImGui::SameLine(caption_size); @@ -2522,7 +2575,8 @@ bool GLGizmoText::set_height() void GLGizmoText::draw_text_input(int caption_width) { - bool allow_multi_line = false; + // Enter creates a new line; Emboss::text2vshapes + GenerateTextJob baked layout consume '\n'. + bool allow_multi_line = true; bool support_backup_fonts = GUI::wxGetApp().app_config->get_bool("support_backup_fonts"); auto create_range_text_prep = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph, support_backup_fonts]() { if (text.empty()) { return std::string(); } @@ -2983,6 +3037,34 @@ void GLGizmoText::draw_advanced(float caption_size, float slider_width, float sl m_need_update_text = true; } } + + // Line gap (font points) — used by Emboss::get_line_height for multiline text. + std::optional &line_gap = m_style_manager.get_font_prop().line_gap; + float line_gap_value = static_cast(line_gap.value_or(0)); + const float min_line_gap = static_cast(limits.line_gap.min); + const float max_line_gap = static_cast(limits.line_gap.max); + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Line gap")); + ImGui::SameLine(caption_size + ad_space_size); + ImGui::PushItemWidth(slider_width); + if (m_imgui->bbl_slider_float_style("##text_line_gap", &line_gap_value, min_line_gap, max_line_gap, "%.0f", 1.0f, true)) { + if (std::abs(line_gap_value) < 0.5f) + line_gap.reset(); + else + line_gap = static_cast(std::lround(line_gap_value)); + } + is_stop_sliding = m_imgui->get_last_slider_status().deactivated_after_edit; + if (is_stop_sliding) + m_need_update_text = true; + ImGui::SameLine(drag_left_width + ad_space_size); + ImGui::PushItemWidth(1.5 * slider_icon_width); + if (ImGui::BBLDragFloat("##text_line_gap_input", &line_gap_value, 1, min_line_gap, max_line_gap, "%.0f")) { + if (std::abs(line_gap_value) < 0.5f) + line_gap.reset(); + else + line_gap = static_cast(std::lround(line_gap_value)); + m_need_update_text = true; + } } void GLGizmoText::init_font_name_texture() @@ -3058,6 +3140,10 @@ void GLGizmoText::reset_text_info() m_embeded_depth = m_style_manager.get_style().projection.embeded_depth; m_rotate_angle = get_angle_from_current_style(); m_text_gap = m_style_manager.get_style().prop.char_gap.value_or(0); + m_wrap_text = false; + m_wrap_width_mm = 40.f; + m_wrap_height_mm = 0.f; + m_auto_shrink = false; m_surface_type = TextInfo::TextType::SURFACE; m_rr = RaycastResult(); m_last_text_mv = nullptr; @@ -3361,6 +3447,11 @@ bool GLGizmoText::generate_text_volume() input_info.m_thickness = m_thickness; input_info.m_cut_plane_dir_in_world = m_cut_plane_dir_in_world; input_info.use_surface = m_surface_type == TextInfo::TextType::SURFACE_CHAR ? true : false; + if (m_style_manager.is_active_font() && m_style_manager.get_font_file_with_cache().has_value()) { + input_info.font_file = m_style_manager.get_font_file_with_cache().font_file; + input_info.font_prop = m_style_manager.get_font_prop(); + input_info.font_prop.size_in_mm = m_font_size; + } TextInfo text_info = get_text_info(); ModelObject *model_object = selection.get_model()->objects[m_object_idx]; @@ -3407,8 +3498,14 @@ TextInfo GLGizmoText::get_text_info() text_info.m_rr.mesh_id = m_rr.mesh_id; text_info.m_rotate_angle = m_rotate_angle; text_info.m_text_gap = m_text_gap; + text_info.m_wrap_text = m_wrap_text; + text_info.m_wrap_width_mm = m_wrap_width_mm; + text_info.m_wrap_height_mm = m_wrap_height_mm; + text_info.m_auto_shrink = m_auto_shrink; text_info.m_surface_type = m_surface_type; text_info.text_configuration = m_ui_text_configuration; + text_info.text_configuration.style.prop.line_gap = m_style_manager.get_font_prop().line_gap; + text_info.text_configuration.style.prop.char_gap = m_style_manager.get_font_prop().char_gap; text_info.m_font_version = CUR_FONT_VERSION; return text_info; } @@ -3432,6 +3529,10 @@ void GLGizmoText::load_from_text_info(const TextInfo &text_info) m_rotate_angle = (float) Geometry::rad2deg(limit_angle); m_text_gap = text_info.m_text_gap; + m_wrap_text = text_info.m_wrap_text; + m_wrap_width_mm = text_info.m_wrap_width_mm; + m_wrap_height_mm = text_info.m_wrap_height_mm; + m_auto_shrink = text_info.m_auto_shrink; m_surface_type = (TextInfo::TextType) text_info.m_surface_type; if (is_old_text_info(text_info)) { // compatible with older versions @@ -3446,6 +3547,8 @@ void GLGizmoText::load_from_text_info(const TextInfo &text_info) } m_custom_boldness = text_info.text_configuration.style.prop.boldness.value_or(0.f); m_custom_skew = text_info.text_configuration.style.prop.skew.value_or(0.f); + m_style_manager.get_font_prop().line_gap = text_info.text_configuration.style.prop.line_gap; + m_style_manager.get_font_prop().char_gap = text_info.text_configuration.style.prop.char_gap; if (is_text_changed) { process(true,std::nullopt,false); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp index acef915108..dea372b5a6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp @@ -27,8 +27,10 @@ enum class SLAGizmoEventType : unsigned char; //2.2 mean v2.2 bambu version(20250815) fix char gap of text // 2.3 mean v2.3 bambu version(20250826) add custom boldness and skew //2.5 mean v2.3 bambu version(20251130) fix text more accurate layout +//2.6 mean multiline Text Shape (Enter / line breaks + line gap persistence) +//2.7 mean auto-wrap / shrink-to-box + multiline surface curve following const std::string NEW_FONT_BEGIN_VERSION = "2.0"; -const std::string CUR_FONT_VERSION = "2.5"; +const std::string CUR_FONT_VERSION = "2.7"; class GLGizmoText : public GLGizmoBase { private: @@ -56,6 +58,10 @@ class GLGizmoText : public GLGizmoBase const float m_embeded_depth_max = 1000.f; float m_rotate_angle = 0; float m_text_gap = 0.f; + bool m_wrap_text = false; + float m_wrap_width_mm = 40.f; + float m_wrap_height_mm = 0.f; + bool m_auto_shrink = false; TextConfiguration m_ui_text_configuration; TextInfo::TextType m_surface_type{TextInfo::TextType ::SURFACE}; bool m_really_use_surface_calc = false; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 660e345d8c..43c7e5dbcd 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -26,6 +26,7 @@ #include "slic3r/GUI/format.hpp" //#include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/Jobs/Worker.hpp" +#include "slic3r/GUI/TextLines.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include // #define EXECUTE_UPDATE_ON_MAIN_THREAD // debug execution on main thread @@ -1282,11 +1283,10 @@ void create_all_char_mesh(DataBase &input, std::vector &result, st if (shape.shapes_with_ids.empty()) return; result.clear(); + result.reserve(shape.shapes_with_ids.size()); auto first_project_tr = calc_project_tran(input, input.shape.scale); - TextConfiguration text_configuration = input.get_text_configuration(); bool support_backup_fonts = std::any_of(shape.text_scales.begin(), shape.text_scales.end(), [](float x) { return x > 0; }); - const char *text = input.get_text_configuration().text.c_str(); wxString input_text = wxString::FromUTF8(input.get_text_configuration().text); wxRegEx re("^ +$"); bool is_all_space = re.Matches(input_text); @@ -1294,16 +1294,19 @@ void create_all_char_mesh(DataBase &input, std::vector &result, st if (input_text.size() != shape.shapes_with_ids.size()) { BOOST_LOG_TRIVIAL(info) <<__FUNCTION__<< "error: input_text.size() != shape.shapes_with_ids.size()"; } - for (int i = 0; i < shape.shapes_with_ids.size(); i++) { + // Keep a 1:1 mesh slot for every glyph/control character (including '\n'). + // Skipping empty shapes desyncs meshes from text_cursors and breaks multiline layout. + for (int i = 0; i < (int) shape.shapes_with_ids.size(); i++) { auto &temp_shape = shape.shapes_with_ids[i]; - if (input_text[i] == ' ') { + const bool is_space = i < (int) input_text.size() && input_text[i] == ' '; + const bool is_line_break = temp_shape.id == ENTER_UNICODE || + (i < (int) input_text.size() && (input_text[i] == '\n' || input_text[i] == '\r')); + if (is_space || is_line_break || temp_shape.expoly.empty()) { result.emplace_back(TriangleMesh()); continue; } - if (temp_shape.expoly.empty()) - continue; if (support_backup_fonts) { - if (i < shape.text_scales.size() && shape.text_scales[i] > 0) { + if (i < (int) shape.text_scales.size() && shape.text_scales[i] > 0) { auto temp_scale = shape.text_scales[i]; auto temp_project_tr = calc_project_tran(input, temp_scale); @@ -1467,19 +1470,28 @@ bool GenerateTextJob::update_text_positions(InputInfo &input_info) input_info.text_lengths = text_lengths; input_info.m_surface_type = GenerateTextJob::SurfaceType::None; + const unsigned count_lines = get_count_lines(input_info.m_text_shape.shapes_with_ids); + const bool is_multiline = count_lines > 1; + // Flat / horizontal text keeps the baked Emboss 2D multiline layout. + // Surface modes use per-line contour sampling instead of a flat stamp. + input_info.m_use_baked_layout = is_multiline && input_info.text_surface_type == TextInfo::TextType::HORIZONAL; + input_info.m_surface_multiline = is_multiline && input_info.text_surface_type != TextInfo::TextType::HORIZONAL; if (input_info.text_surface_type == TextInfo::TextType::HORIZONAL) { - input_info.use_surface = false; + input_info.use_surface = false; Vec3d mouse_normal_world = input_info.m_text_normal_in_world.cast(); - Vec3d world_pos_dir = input_info.m_cut_plane_dir_in_world.cross(mouse_normal_world); auto inv_ = (input_info.m_model_object_in_world_tran.get_matrix_no_offset() * input_info.m_text_tran_in_object.get_matrix_no_offset()).inverse(); - Vec3d pos_dir = Vec3d::UnitX(); auto mouse_normal_local = inv_ * mouse_normal_world; mouse_normal_local.normalize(); - calc_position_points(input_info.m_position_points, text_lengths, input_info.m_text_gap, pos_dir); - - for (int i = 0; i < text_num; ++i) { - input_info.m_normal_points[i] = mouse_normal_local; + if (input_info.m_use_baked_layout) { + for (int i = 0; i < text_num; ++i) { + input_info.m_position_points[i] = Vec3d::Zero(); + input_info.m_normal_points[i] = mouse_normal_local; + } + } else { + calc_position_points(input_info.m_position_points, text_lengths, input_info.m_text_gap, Vec3d::UnitX()); + for (int i = 0; i < text_num; ++i) + input_info.m_normal_points[i] = mouse_normal_local; } return true; } @@ -1492,6 +1504,113 @@ bool GenerateTextJob::generate_text_points(InputInfo &input_info) if (input_info.m_surface_type == GenerateTextJob::SurfaceType::None) { return true; } + // Multiline surface: place each line on its own parallel slice contour. + if (input_info.m_surface_multiline) { + if (!input_info.font_file || !input_info.mo) + return false; + ModelVolumePtrs volumes_to_slice; + for (int i = 0; i < (int) input_info.mo->volumes.size(); ++i) { + if (i == input_info.m_volume_idx) + continue; + ModelVolume *mv = input_info.mo->volumes[i]; + if (mv->is_text() || !mv->is_model_part()) + continue; + volumes_to_slice.push_back(mv); + } + if (volumes_to_slice.empty()) + return false; + + const unsigned count_lines = get_count_lines(input_info.m_text_shape.shapes_with_ids); + TextLines text_lines = create_text_lines(input_info.m_text_tran_in_object.get_matrix(), volumes_to_slice, *input_info.font_file, + input_info.font_prop, count_lines); + if (text_lines.size() != count_lines) + return false; + + std::vector bbs = create_line_bounds(input_info.m_text_shape.shapes_with_ids, count_lines); + const double em_2_mm = std::max(5., (double) input_info.font_prop.size_in_mm); + const int32_t em_2_polygon = static_cast(std::round(scale_(em_2_mm))); + const auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {-0.5 * M_PI, 0.0, 0.0}); + + input_info.m_position_points.assign(input_info.m_chars_mesh_result.size(), Vec3d::Zero()); + input_info.m_normal_points.assign(input_info.m_chars_mesh_result.size(), Vec3d::UnitZ()); + + size_t s_i_offset = 0; + for (size_t line_i = 0; line_i < text_lines.size(); ++line_i) { + const BoundingBoxes &line_bbs = bbs[line_i]; + const TextLine & line = text_lines[line_i]; + if (line.polygon.empty()) { + s_i_offset += line_bbs.size(); + continue; + } + PolygonPoints samples = sample_slice(line, line_bbs, input_info.m_text_shape.scale); + std::vector angles = calculate_angles(em_2_polygon, samples, line.polygon); + for (size_t i = 0; i < line_bbs.size(); ++i) { + const size_t global_i = s_i_offset + i; + if (global_i >= input_info.m_position_points.size()) + break; + if (!line_bbs[i].defined) { + if (i < samples.size()) { + Vec2d p2 = unscale(samples[i].point); + input_info.m_position_points[global_i] = rotate_tran * Vec3d(p2.x(), p2.y(), 0.); + } + continue; + } + const PolygonPoint &sample = samples[i]; + Vec2d p2 = unscale(sample.point); + input_info.m_position_points[global_i] = rotate_tran * Vec3d(p2.x(), p2.y(), 0.); + // Approximate surface normal from contour tangent in the text plane. + const double angle = (i < angles.size()) ? angles[i] : 0.; + Vec3d tangent(std::cos(angle), std::sin(angle), 0.); + Vec3d n = rotate_tran.linear() * Vec3d(-tangent.y(), tangent.x(), 0.); + if (n.norm() > 1e-6) + input_info.m_normal_points[global_i] = n.normalized(); + } + s_i_offset += line_bbs.size(); + } + + // Refine normals from the host mesh (same approach as single-line surface text). + TriangleMesh slice_meshs; + for (ModelVolume *mv : volumes_to_slice) { + TriangleMesh vol_mesh(mv->mesh()); + vol_mesh.transform(mv->get_matrix()); + slice_meshs.merge(vol_mesh); + } + slice_meshs.transform(input_info.m_text_tran_in_object.get_matrix().inverse()); + const bool is_mirrored = (input_info.m_model_object_in_world_tran * input_info.m_text_tran_in_object).is_left_handed(); + auto point_in_triangle_delete_area = [](const Vec3d &point, const Vec3d &point0, const Vec3d &point1, const Vec3d &point2) { + Vec3d p0_p1 = point1 - point0; + Vec3d p0_p2 = point2 - point0; + Vec3d p_p0 = point0 - point; + Vec3d p_p1 = point1 - point; + Vec3d p_p2 = point2 - point; + double s = p0_p1.cross(p0_p2).norm(); + double s0 = p_p0.cross(p_p1).norm(); + double s1 = p_p1.cross(p_p2).norm(); + double s2 = p_p2.cross(p_p0).norm(); + return std::abs(s0 + s1 + s2 - s); + }; + for (size_t i = 0; i < input_info.m_position_points.size(); ++i) { + double best = 1e9; + for (auto indice : slice_meshs.its.indices) { + Vec3d point0 = slice_meshs.its.vertices[indice[0]].cast(); + Vec3d point1 = slice_meshs.its.vertices[indice[1]].cast(); + Vec3d point2 = slice_meshs.its.vertices[indice[2]].cast(); + double abs_area = point_in_triangle_delete_area(input_info.m_position_points[i], point0, point1, point2); + if (best > abs_area) { + best = abs_area; + Vec3d n = (point1 - point0).cross(point2 - point0); + if (n.norm() > 1e-9) { + n.normalize(); + if (is_mirrored) + n = -n; + input_info.m_normal_points[i] = n; + } + } + } + } + input_info.slice_mesh = slice_meshs; + return true; + } auto &m_text_tran_in_object = input_info.m_text_tran_in_object; auto mo = input_info.mo; auto &m_volume_idx = input_info.m_volume_idx; @@ -1897,9 +2016,11 @@ Vec2f GenerateTextJob::calc_mesh_offset(const std::pair &align_type, int i) { Vec2f mesh_offset(Vec2f::Zero()); - if (i < text_absolute_cursors.size()) { + if (i < (int) text_absolute_cursors.size() && i < (int) text_cursors.size()) { if (align_type.first == (int) Slic3r::FontProp::HorizontalAlign::center) { - mesh_offset[0] = -text_absolute_cursors[i] - text_align_offsets[0][0] + text_cursors[i] / 2.f; + const float align_x = (i < (int) text_align_offsets.size()) ? text_align_offsets[i][0] : + (!text_align_offsets.empty() ? text_align_offsets[0][0] : 0.f); + mesh_offset[0] = -text_absolute_cursors[i] - align_x + text_cursors[i] / 2.f; } // else todo } return mesh_offset; @@ -1922,17 +2043,22 @@ void GenerateTextJob::generate_mesh_according_points(InputInfo &input_info) auto inv_text_cs_in_object_no_offset = (m_model_object_in_world_tran.get_matrix_no_offset() * text_tran_in_object.get_matrix_no_offset()).inverse(); ExPolygons ex_polygons; - std::vector bbs; - int line_idx = 0; + BoundingBoxes all_glyph_bbs; SurfaceVolumeData::ModelSources ms_es; DataBase input_db("", std::make_shared>(false)); if (input_info.use_surface) { EmbossShape &es = input_info.m_text_shape; if (es.shapes_with_ids.empty()) throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); - size_t count_lines = 1; // input1.text_lines.size(); - bbs = create_line_bounds(es.shapes_with_ids, count_lines); - if (bbs.empty()) { + // Flat bounds list indexed by global glyph index (supports multiline). + all_glyph_bbs.reserve(es.shapes_with_ids.size()); + for (const ExPolygonsWithId &shape_id : es.shapes_with_ids) { + BoundingBox bb; + if (!shape_id.expoly.empty()) + bb = get_extents(shape_id.expoly); + all_glyph_bbs.push_back(bb); + } + if (all_glyph_bbs.empty()) { return; } SurfaceVolumeData::ModelSource ms; @@ -1946,14 +2072,30 @@ void GenerateTextJob::generate_mesh_according_points(InputInfo &input_info) input_db.shape.scale = input_info.shape_scale; } auto cut_plane_dir = inv_text_cs_in_object_no_offset * m_cut_plane_dir_in_world; - for (int i = 0; i < m_position_points.size(); ++i) { + for (int i = 0; i < (int) m_position_points.size(); ++i) { auto position = m_position_points[i]; auto normal = m_normal_points[i]; TriangleMesh sub_mesh; auto local_tran = get_sub_mesh_tran(position, normal, cut_plane_dir, m_embeded_depth); - Vec2f mesh_offset = calc_mesh_offset(input_info.m_align_type, input_info.m_text_cursors, input_info.m_text_absolute_cursors, input_info.m_text_align_offsets, i); + Vec2f mesh_offset = Vec2f::Zero(); + if (input_info.m_use_baked_layout) { + mesh_offset = Vec2f::Zero(); + } else if (input_info.m_surface_multiline) { + // Glyph meshes still carry Emboss layout offsets; center each glyph on its curve sample. + if (i < (int) m_chars_mesh_result.size() && !m_chars_mesh_result[i].its.empty()) { + const auto bb = m_chars_mesh_result[i].bounding_box(); + mesh_offset = Vec2f(static_cast(-bb.center().x()), static_cast(-bb.center().y())); + } + } else { + mesh_offset = calc_mesh_offset(input_info.m_align_type, input_info.m_text_cursors, input_info.m_text_absolute_cursors, + input_info.m_text_align_offsets, i); + } if (input_info.use_surface) { - get_text_mesh(sub_mesh, input_info.m_text_shape, bbs[line_idx], ms_es, input_db, i, mesh_offset, text_tran_in_object, local_tran, input_info.slice_mesh); + if (input_info.m_surface_multiline && i < (int) all_glyph_bbs.size() && all_glyph_bbs[i].defined) { + const BoundingBox &glyph_bb = all_glyph_bbs[i]; + mesh_offset = Vec2f(-glyph_bb.center().x() * input_info.shape_scale, 0.f); + } + get_text_mesh(sub_mesh, input_info.m_text_shape, all_glyph_bbs, ms_es, input_db, i, mesh_offset, text_tran_in_object, local_tran, input_info.slice_mesh); } else { get_text_mesh(sub_mesh, m_chars_mesh_result, i, mesh_offset, local_tran); @@ -1974,10 +2116,15 @@ void CreateObjectTextJob::process(Ctl &ctl) { m_input.m_text_absolute_cursors = m_input.m_text_shape.text_absolute_cursors; m_input.m_text_align_offsets = m_input.m_text_shape.text_align_offsets; m_input.m_align_type = m_input.m_text_shape.align_type; + m_input.m_use_baked_layout = get_count_lines(m_input.m_text_shape.shapes_with_ids) > 1; if (m_input.m_chars_mesh_result.empty()) { return; } + if (m_input.m_use_baked_layout) { + m_input.m_position_points.assign(m_input.m_chars_mesh_result.size(), Vec3d::Zero()); + return; + } std::vector text_lengths; calc_text_lengths(text_lengths, m_input.m_text_cursors); calc_position_points(m_input.m_position_points, text_lengths, m_input.text_info.m_text_gap, Vec3d(1, 0, 0)); @@ -1989,11 +2136,14 @@ void CreateObjectTextJob::finalize(bool canceled, std::exception_ptr &eptr) { return create_message("Can't create empty object."); TriangleMesh final_mesh; - for (int i = 0; i < m_input.m_position_points.size();i++) { + for (int i = 0; i < (int) m_input.m_position_points.size();i++) { TriangleMesh sub_mesh; auto position = m_input.m_position_points[i]; auto local_tran = GenerateTextJob::get_sub_mesh_tran(position, Vec3d::UnitZ(), Vec3d(0, 1, 0), m_input.text_info.m_embeded_depth); - Vec2f mesh_offset = GenerateTextJob::calc_mesh_offset(m_input.m_align_type, m_input.m_text_cursors, m_input.m_text_absolute_cursors, m_input.m_text_align_offsets, i); + Vec2f mesh_offset = m_input.m_use_baked_layout ? + Vec2f::Zero() : + GenerateTextJob::calc_mesh_offset(m_input.m_align_type, m_input.m_text_cursors, m_input.m_text_absolute_cursors, + m_input.m_text_align_offsets, i); GenerateTextJob::get_text_mesh(sub_mesh, m_input.m_chars_mesh_result, i, mesh_offset, local_tran); final_mesh.merge(sub_mesh); } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index efc5c51e4a..96815b3f2c 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -273,6 +273,8 @@ struct CreateTextInput DataBasePtr base; std::vector m_position_points; + // When true, glyph meshes already contain the final 2D multiline layout from Emboss::text2vshapes. + bool m_use_baked_layout = false; }; class CreateObjectTextJob : public JobNew @@ -384,6 +386,13 @@ class GenerateTextJob : public JobNew bool first_generate = false; Emboss::DataUpdate m_data_update; + // When true, glyph meshes already contain the final 2D multiline layout from Emboss::text2vshapes. + // Placement keeps every character at the text origin and skips single-line re-spacing. + bool m_use_baked_layout = false; + // Multiline surface: place each line on its own slice contour (create_text_lines + sample_slice). + bool m_surface_multiline = false; + std::shared_ptr font_file; + FontProp font_prop; }; static bool update_text_positions(InputInfo &input_info);