From dc639e8d392756f031e44a44aaaeae921c77d13a Mon Sep 17 00:00:00 2001 From: jinye_huang Date: Mon, 4 Aug 2025 13:43:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=8E=A5=E5=8F=97?= =?UTF-8?q?=E7=9A=84=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/models/__pycache__/poster.cpython-312.pyc | Bin 5741 -> 5819 bytes api/models/__pycache__/tweet.cpython-312.pyc | Bin 9875 -> 10235 bytes api/models/poster.py | 4 +- api/models/tweet.py | 4 +- api/routers/__pycache__/tweet.cpython-312.pyc | Bin 17308 -> 18587 bytes .../database_service.cpython-312.pyc | Bin 51756 -> 47135 bytes .../__pycache__/poster.cpython-312.pyc | Bin 26354 -> 66448 bytes .../__pycache__/tweet.cpython-312.pyc | Bin 20543 -> 36834 bytes api/services/poster.py | 47 +- api/services/tweet.py | 376 ++++++++- config/database.json | 2 +- run_api.py | 2 +- 海报Fabric.js前端对接文档.md | 711 ++++++++++++++++++ 13 files changed, 1110 insertions(+), 36 deletions(-) create mode 100644 海报Fabric.js前端对接文档.md diff --git a/api/models/__pycache__/poster.cpython-312.pyc b/api/models/__pycache__/poster.cpython-312.pyc index a1f538f1f920b6b5f3fe4e9dc69817fb2fc05cf4..baa0d02f87808514096141f0da96eb606b22349f 100644 GIT binary patch delta 1216 zcmZ{i%TE(g6vpSy^xaNNAHzEc@@gYP3sME-8G{r7Z!i81;Ic+YJNwW)3L&AoHZ{hf2ZJNtFdb@i>P zIwb!4+_QB4_lEkAklV0EVpa#U6!F*alN_=;nVq?qg{e>^R}(jM%9X?eNv?TtQ?6CK z%m*8CiQ;Dgc*ma_f9}gABp>WbB`E-1nJg{ju-!+mc5mECjLouRO?xly?X6zly|w)9 z@mgwK@yM-BJY!j6Qk+mJE>l8fs!-FoOwH6nt<*;C;_skN>ROShy5g81apj6T{SW(ahqZ7r1(j`d|$>_#qBg#pXqbRv%j$$AR!>*?m zVMG+p_=})Moo~;W33Eg@qJTIeS`z%MS!0+SgRko0G%zNBRqmHLfr21dVo<@)0`?=Y zAIdxp4$&d*lXtyZYT9#3COSOx_W3a|5He>v2wq>ImW2`q#G%7iIww%V_q@zgSSy$B zXE7kUGvmsFat;M=V8=u!l9!BO+~@Lm5CbUK`mDia5kC5iv_FH~pT*|?ntfnU)+`Bx zCcqsiO>+{$=f1!-G6Zh|<-NIx2OpCd1=}$W=E*9+Fv=jTMcl$H+z*8OMi{2F-e&O; hh`SaPF`{Tm9QzlwV{4n5p_ebP4r3C&YgbLk`vVx+?I8dF delta 1164 zcmZ{i%}*0S7{+&Ym+kgrTj+MnhmDX{DFs?VP!R!*QL92AL5;DR5W;Li6=O2s!2=%$ zh>7tac@Gdn!p&ebF{X(J@#7EhqQL{<{8UY+-Cl(nhd^vTRVGrxJ?XLdXLqtp6i zSph}8%z~k9SG#n&K)2#+8D~8CSW+bzZ{_?&Bssejeb#4V8yB0!3EP9$1+$XhxPPr3vF0 zi}XptvhpqD>>f<-wj|-XFIV3o--)c0OucBuaT=u!e)>*8yE&LbOSGfV%1#`G{zzC3 zX1~KDv&l)OxAf2bqhFI_OCwL()_Yd7JNg)WFh8=6(t`i88uY?!Hr9s;jWoJ2W=f+*ob8AKUE(NRvLoI&YEIg4^s zQ4;0eD;w-ZyBD5?UK!MNFKpV4FlQ|ddb+?hR1FHBZI`;k-xF^SMgxD`v3Ig^y^T5N$`fwuSVSee5e)7PVDznaR5h`!@ScKrm%26 qnz2O=ww-(*T|3<_QK&^KiMIWV+A|04-sAUY+#zur`s4FD@_qwmQq9`{ diff --git a/api/models/__pycache__/tweet.cpython-312.pyc b/api/models/__pycache__/tweet.cpython-312.pyc index c556382903274fc45db51fbe17c1e20b8d91b343..c84d72c78f9e8cf38dbe31c1bde086d6dd735126 100644 GIT binary patch delta 3536 zcmc&$Sxl5y6rO+hXBcK*5g1^=VQ~Ou7fM}FP!`!*LD>|6`2`dPyfYf?L!nI*?UsNi zCgW454^6PP=|a;OThqSuscFLm8pEWCZK4mp)U-)!lRmWP+@Z4p#y2PVzuf!%ch3Fq zIp@3QK>I(P`tNl*wG4lMjl47d)viT-n($z;&@nfq>{kjhx8P2iOrBEptEN)=Qv{h@ z)+BR_Z^+yVNp&lU{w9<4t2wJgHaUjXa8`wEN(`&ztQuKO46Ea;7Fpc}+eC)-oYo_4 zh*@mltPxpL3~S`98CgpVYXX%};#7phwh6BhVO>mm@l2X!2v22>$X)b#7@ecf(aJn z4+Y)wr6jvhY(Trrgz!j?;8HR@5jKJy1hg-+6J){H;;i6+YDIm~E+UE$TxRB^aUMZF zLMVA?$UQweG{jPf(!+ZShl|XGk|sR9uuX~tp&7HQ7_M0D@U6yXmGqJ- zoM03VdL%V`W=xMHRpdSVtBEF6lyzyHCeuc$McLz8r^1Y^1%_=7!3x*4jT(w*Na35F znHU8_wo}Qnkmv@T?n7k`j#v%c)n!{>z~P^CxCeJWfB5mj!-Yjw2ak2t=WA)riwHN> zY#)vH6C5HqOwd5kNN|AQAVEFB5rS|qaMX=y-a^b4F=4IvA}$zhu%sWEiv+`E+ddZD zsFEy;1UxDV8u8HZ-J-d_hh8KIfHn__Bh%B9TwSb?*3j1AaVQOR6EOlhv3C`U(h8rq zFlrKov7VD3aIe(%uI2R2w_Kg|i*LS)+I%9~NG=FrxioRPj2Rzj%IQp$7OVnJm>vl^ zP;V{}YGBO#dR{NmA<;KH=Iy>IhNW_nRua$p5aQxA00ows-e{bR+s7$EGf|^FY&>FQ zp7Br~r%_l?Jv8irA1$7+plD8lf`geQD5qYvgw+R=lF0w!;9llA8=s_cPM4r=$Jk`l>DKL?{&aCfcYC@*!X;vq zK9*n>AMZU>!lCD_ge^)$I^{eP>GI>rftlkj`G9pkFYu#1#>@QbMl zycwyehqp5FlsvqH@I^+Y`7AA`V0E)$#JFgj1GBxRI}(i!mtp&GJgWv`(tn(`9)~aO z=fdevb6!tg!Fu9}>r_6;+^SPq&ve>1bt;ry^hhe$Z%f~zXW5Svvm_&z3vXt4i(`b4t(YWJnmr=4!nJGj&x z?~gPsW$qr z$mTpG^aTk;g2A*yVVI&91k3n%A45w{`SaFB`=%<9YWE*mDeYWK?ZPVITNS%D>V|tQ zM~c=B>4~T%4IzXzM8~3xTdvI235i0gq=Q#M&(6Owv>ndExr9rR--nT_5U;s}7NUXv zA8j1McO!8T$+7b|?RJT5h-iLgouN@50+n#W@Mx@XrX@9Q+& zE}7kEl^G2Qe!$0j1-`LoKx+O4J--5o@4yDV7_R3#26@9BQq5d;4|{wQqfAAAO(Ed@ zHuZ+Qw_v=;@e7vgou?DuC(?{C7cLH|_DxN@FHTAa*k#ZZWQbKi$tuBBa8{(v{{v%C B58D6$ delta 3425 zcmcgudq|sC6wggQlbH9rMs1=^nlx%_t@Pn)?L%klS|2TL6}5}j1hrGs-bDKNV@l~* z84Mx^Hp@2Ju?KeU(anF#9)FB7HW&j%VJM7^Q9JrqyRu?ogYBIAHTuLBK^4o2>&7+oiAvR)Em$qGIt6PGtRC5H1#1+n0a>GhH3`;)tXaWUn+0t_+Nz)} zg0&%QSFl#WI*@fLSR0sF36r3L!)&<|Na%KRHI-oPhwQ*D20Nc^F zJhWdx+xpPHtPU_6+)&Rm)5KLx9xJxszvecC+iHY}mYa!i5qJo42yzMX;f{2M`Jg9j z&rq0%2tvfp*O46{2qMhsqS08QD;ni`qQq)i;x)+>pR-1zodW}jRA;I;F_6Sbw9%ZV zcOWJ2MqyBHNw2gG4-tr6H=t$X6k-7;G?Le#Qd>0?jEn^*D-X>C>xU0q>`A9at4GdG zSqi76`q^;FSo7rW#+h)_@WFJ>XmB)nDKcdbPD@R*kxgT%$-ReXB1eYn(>q3Mt~f8% zO*uo;(vjIv@mS4dRl`iEarl|Ee$+MEb;&zr3rtIm3z8wra8HFGABv6x_q-k`*G(`Z z{HA-4`6pi0_cATHmKVTZ*{8zG{fQ$HH7}zH

SDkjxMqHagi>7&An>7Tw$sF)#Bo zFQUo9$6|$IdMzQKwYYGkL{=8T3O?keG*AjBjEA&hpHjHv^{R8_tg3S7LB7ciJ58B9 zGD8AwIgiZn4%#-6Q)c+l6wuh?4yf@ILeRY1XvBr*vQmkYy*f!%e%fvu z?6>$_JJ7tJjd1hx&*nc$&!9QZdW+pwz~6|W&P5kOIc6La}l2-WUVBEW*Q(kj!&`^;UyKsLT(sy z9h7C!EMyVP$f5R!#bqB0!Gk2EsG=BquP__ddx?Uy!_%)(CQ1 z(@C(D-^*2GH68I;0?M*M2iy&~eIkPg9Z1Z7{PwLY*Kdt}GM|3`*2VPvE0=i-l;w38 zD6Z#xCkA8P@KrERyNy<8gD>;S*Tl)Bcs275#76?s3%U6U4Kzfot4HL&ieT0J@1}zg zyoFDaPV|y$R$4a;lld)j{&x$RJKQDMX;fpE@%7rJF5^DhnJy#Z%iInIIOYrNvc>Ij zeauM}T&;>rF?-DQp){_>SNyd)#Hd>y#Xih~&CZ&|Q77E+#oeN!hz3G5DOUWByM$wyP&IeZ!n4+$+SRXb7W7Hb^V zuVuIYKKgzafw=SKqDQZbLgFXzwK*d3=ZO}zC`uOH0murWh|Y);Yf2(MqPjsiy`C>c z{r{*=EGy!?boGc`XpXnrfkhK1dIfQlQSlOT7O5k)Sk^kv=_~savBAE06{mX3@JXIVxT|4|{jOIt dX+f`I8>dwzKdaXNp>;Atx&qz^w@Y-ae*pLV(~STC diff --git a/api/models/poster.py b/api/models/poster.py index c955e02..187643c 100644 --- a/api/models/poster.py +++ b/api/models/poster.py @@ -14,7 +14,7 @@ from pydantic import BaseModel, Field class PosterGenerateRequest(BaseModel): """海报生成请求模型""" templateId: str = Field("vibrant", description="模板ID") - imagesBase64: Optional[str] = Field(None, description="图像base64编码") + imagesBase64: Optional[List[str]] = Field(None, description="图像base64编码列表") posterContent: Optional[Dict[str, Any]] = Field(None, description="海报内容,如果提供则直接使用此内容") contentId: Optional[str] = Field(None, description="内容ID,用于AI生成内容") productId: Optional[str] = Field(None, description="产品ID,用于AI生成内容") @@ -28,7 +28,7 @@ class PosterGenerateRequest(BaseModel): json_schema_extra = { "example": { "templateId": "vibrant", - "imagesBase64": "", + "imagesBase64": ["base64_encoded_image_1", "base64_encoded_image_2"], "numVariations": 1, "forceLlmGeneration":False, "generatePsd": True, diff --git a/api/models/tweet.py b/api/models/tweet.py index 2e8d6a9..2772b2d 100644 --- a/api/models/tweet.py +++ b/api/models/tweet.py @@ -69,7 +69,7 @@ class ContentRequest(BaseModel): audienceIds: Optional[List[Union[int, str]]] = Field(None, description="受众ID列表") scenicSpotIds: Optional[List[Union[int, str]]] = Field(None, description="景区ID列表") productIds: Optional[List[Union[int, str]]] = Field(None, description="产品ID列表") - autoJudge: bool = Field(False, description="是否自动进行内容审核") + autoJudge: Optional[bool] = Field(True, description="是否自动进行内容审核") class Config: schema_extra = { @@ -185,7 +185,7 @@ class PipelineRequest(BaseModel): scenicSpotIds: Optional[List[Union[int, str]]] = Field(None, description="景区ID列表") productIds: Optional[List[Union[int, str]]] = Field(None, description="产品ID列表") skipJudge: bool = Field(False, description="是否跳过内容审核步骤") - autoJudge: bool = Field(False, description="是否在内容生成时进行内嵌审核") + autoJudge: Optional[bool] = Field(None, description="是否在内容生成时进行内嵌审核") class Config: schema_extra = { diff --git a/api/routers/__pycache__/tweet.cpython-312.pyc b/api/routers/__pycache__/tweet.cpython-312.pyc index 7b00885124a2c29321fde408e99e1b92dd5dbd13..774d297a622da8bd15d62eea198a3febed27e13f 100644 GIT binary patch delta 5208 zcmcgvYj9J?6}~H7J*}55%WqkB{1kp*W1hhn3^vcygkX{&k|>Jq+6ZJx-7E1(L`0{w zB@9eTZYXIWDG=H?10g1+Q#FOgG<3qW(-~_#8JwGRl1#7(KROkmue3kfJu6!h1ZVoA zQ)%?=*>iT!IeX68hmQXM9XpKbe{VGEIr{m2=((+b>gKr5p)>t4PZ78C_`~(JjscBJ zgE#>Ryk9$@bLj^3F8x4`D~It^{=)6@~|2!u?AP;nm$D_9W?zDtTRgTXO!47 zlGt793UFm1UqgW8e<`4LDzDyhHzjZw|OM%Rp0>f!WfevJR;v6JM&4Rht67 zU|`tE|5ce&M(q(hK)K}9?nV{TYue6+7GTVw(Bm|c2Iw>bG*OT>ZnqHhy4|D*SY?t~ zS6!V_0DYK5W&{jI()+qS=!(>>clrcHn2J-M z@YMF^xGZbD7P(F`pjwFl#}MEqN*l?b zs+Ci!m1#XD>b4Es?lq-(nyBU;x^rgKZD6|jDcyWpk2xw`o@;B5n$sL@RC^E2#u+u6 zq-H~LjbSGZ5Mn{PH{~=(ek%D5?#{^<-+!cIUN|^;l!W(b@dg2{AoN~@f?-@2cDl2`0NIGaPyi<1# zB{>di%SwN@uTnV5MLAK?&u!%TxjiV#b#ae7XnqwsE}Whd&16s+Mcb4R=C*X`5qGeX zr>xjlqqR=dQo|N;(GD z&=jfm2mAXmku^I#B;X75J5{8Ms*;V~U|9Fa9W2j(A8#jz>7+EpuSbfd1CSh6g zucQ)&{8P-Qh7XlBDc&@9R;n(Rc9y9wm#HWjaZ&-T*U3ZYCf<=Ij~|*iaFV8G;`z6v zW@B}18I{TgPekzHfEV|81~BOa79Rj|IgmLa5)>ldFcYqz9H)W6fk)tFMKk~_0p?PW z4L;HB+2Qf|Jx}>DSquDiz|ZZ+VYe9G?Z=|~sog9&`IIHSYg~pb(!Xl)}e;6%8pTO`FMW$ zwfwe3e%t8WwPX3~q;4~v?U+4e9CJ`{*?3{)wZcV-!bPL^_ly;;m%a!Lm9W4EekwaSspXJ;|evVO`;JZJ-&CH=wfL?P+#_Bs>n6T$<)wvigk zi)-0F3Dcgeq$_4&A**QDX(B!t=myv&1f)uDLHnX?i>%aoIJ^Z6Oi^>K;BF6z<0eBVQC(^GP zE6@q)ump%|N~P+MU8Ce(kN`Y3ylu|=$_i$T4Zk}XbPRa7VgA?BU`nOI&+2Vz&3EL0 z>4Wq8e-LaIYjW|01^MbrpfM|`m!S{v^zhOJU-1hvm41(Em388FUuZ2Ph}B4slP4!Y zflmsyFTT6dEM#kDOBqNc;12mh*zcoTD`VvfOow_Sc>psOmLV%gGj1l9Jhpp&OO?rH zR!S`bym+9vX*E%{$X;Vv=SnK6?}K@O!Qp3@tVhZg(k6{|)X!#DzpUx=(5(>i5iJbU z0YK5q7RqZL2nyIQHnTrrQr*(3QnnU34`d=HJAAZ~Q&M7;o?KcL%TG@gFuechqr`_r zVgt1WAk&6+3!XsO=Ou-}gWHV#^OJS-;Cz5R3R8b2VGqh`5{!iD=>rFC>V2XLM?&;r z0+$@w3INBJ%!h(vm>wVxQ3l>dLm+u+_tjkol`AYuo9^)3@)8_^?ZeADLfN4AXjK6*W z@71EqOA{R%6Z3kL8Ya|obmrU<)rj|M+sM|-=2&9!gNZpClFV>Y%RPa-=ys|@5VsPo zMdPa<7+>2nzG~eqoej-95lnKl8Lw&>>>9ESiSZRfyUsfjjh%_AsKW;Ak=bnqA)ZsZjF26 dMdz0$npY%hx{_d%$~K^fhmUkB6+Ap1{sWs)1poj5 delta 3937 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 Vv`ju~{DZMRRa5x|gD!dE{{c~Xb^8DS literal 51756 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 diff --git a/api/services/__pycache__/poster.cpython-312.pyc b/api/services/__pycache__/poster.cpython-312.pyc index d966d66f66e2dd6509bacac8cc60d2a41447b776..24893a71fe44e6c490d097def88d137a101517e0 100644 GIT binary patch literal 66448 zcmce=UUoA;~J=tctq zMd$nGzNXZuK3#qGQ>RXysyhE*v6xi2{$*|dL(decRllbP>C#1jFC1!>>avPc9aM2@ zPSc@2sAj*KgBtd$J*Z{Bx`R6Qt3Rk`zexv^*l+T|Wc+G744uYo9eu98Bpn zA2fGb4q7@>52kin4_Z5I2W_2c2h%#!52iCbeMd%T#=#5*Cv{|YW*y98aB@d>XU@SK z1{*r;ow)~dJM#|av1emPey8IgvW#?09R;0*2MZaT(oxjuJm_SwxudwV;sKKkpbcnjsH>tLluwNAyQKBeNUZ)qZ7Gfr!&Y&{)#GEMxq_a~nF z!Nk?)$Dg}4{_c~`TIb}m&riJa)SdHv6IZXB#)qG{``XhJgCpagd}I8%SMPq~{@{g)p=%Q_e{$!0PmMn}G;!{U@n@bMfBR>5zWwS%U;p^GE{L&@ z_q{a!;RmKkN_}gUI+VQXw8!P%u&v4v(rs>YdqTQ3Z7rUVwxR2E$gu5%r>(oIxg(_8 zefoqeq~Ftp$B?e28F>2LJtsO`h{3=$dt9EjPJ(sYn>`QVSHGdN`M9e}6SA!C?rLp2 zzNNVf&wR+dz1!_^@nRsQ-mszJxU0(p(6X(^bE3x+!9+kRi(@Z$j+ROodo>T&388kdgKy7ZjR zmDHNV>7UY}hb29wI%wdMaW`@Xmx(jFQe5U-MAE9`Ou(>kDR`HPaI4GaGP%-PHT0HY zXgD)4(&g{s-|ILF-e(w9(jT{h;aZVK94===n2BQL(qt%IhB5$I#rF&+)0N3(xw5!y z{LhiG?07ONV{Wx`xlid2W^)_4JXcP8U8HUlKP*4_h>gl^XQeW54t$f#6-@Dk(Pcv2 z6ryB#?CT<=>U8C!MvD>VFsissEd2f@qB=-pWucVgYNOT6l^_oVEUr?QnJb$jFXhTF z<+4=BHIgXqRIUdbY`y%B1-Z*EX?itpU`G~D`6IYECoKs@gpuTHa zoh!=SYT)XSgJLf6+azul-j+=Hb~fIY#^pU4)4hGyz|FxIWs3Oj#Yy7k0;fEZ*G#nf zJUmzY)#aLxuPWt~79=S7LSW38BKL&9$d#XP1e&?U__B&yg0`#&-ctNuCgW$KA1%lG znQ<|%0LDuEe?Z2(x9n!F0bf>6@#U)fez_W7)=cr`{nK27FKgqzq%YRuztL6a!nhad z4|+=@aA$Fw?@D$ z9)IJT;}^cGEc(_eBX7q`o~Q_)M>oO^nYd$3ZgGAJnVLM2*~=Zukm07bF1M$-3)5UE zv#E_H&ZZtW-93DB%fpy8t-xsW$oTFmT}bD4b+pE%=cY0{aeXys7T?|NnYEj5KI!Vf zWa~j|tma+Ko^F0t^NF@uk^H!4onX^(-3iR~O=2QVXR>3OxK0W$L*(xDxSdnovOD{9 z7c9ZTnm%2?kT3n-%&Quz^XDzWJ8Lk{!G2213B?&KtDxWFpus$5aQF<4&kTiMATCvl z+^Tq>cANTp1nifr)szr0epi=c}yqNf@ zVZ53bH5W`egdkDSJnB84JPa^I4|R4S?Gy` zyKld)jK9a3Fm4fL{PlP5zW0NA=k7PbqPkACpnJD=(;#)=qfq{Fm#3+@#nX1u)g%sr z==)!N+Cp%{-LaXY69>rBS#V1Ci7E3d2^%Ww4M zH~RC}_ig^%n*aHt)&7z-?|O%e2j>j#zP4zn;adH$*H^NpU-xb6O;r#b@ zjbwgO^s^%Wf(^p_jW=crjy<2*_TI8(j@fE_wwi#gPQrC;`pmZZ|Nf&2=Qj)aOGg?;<_WngZ(48NhAAkDki4U)dNze-I>IcBtU6sUFAmxy*tGUw^GC$PZ#dWy&rW062g-k6S z&2D!Sdy~Ss+%0^Y*u{)(?nu`S8IJY1+q$qmVg(rg&NEm!MhZ}JPXQ#{XbEhqBA?4e zs6=kWB1}I0t?>^oQxW<;8NWJw`-3;A6f6ZX^Hmz2nuTAA8&;BR)V!%?{oYMf-6?i( z>H%lU<2rcaakLz$Zn~kP8&o;YpuHmKTpcW&6)f5sEGYhiCE1=DPFLj=vlnZEg|mZ2 z>%|wjsei~w%QyAup11O4h#{0BHC$607fRw?o*urdMP?d+L1dmyHaih2G8~B}o0Doj z*CT(|E;ADNI&yKCa)`7l2(^aTmv))3OPDIvNfmER{Km_sr(IJ_dE#4|#u83X@`Tf? zodQFiMtXH#EwAzF+8QYl42$^jZqby2-|7Q#9iKKPV^pP4%y|^dv(&J)0?DG zd6T5+uQ!=X<_sj+=`~oqFkPt?j5uaQllsn=!Mjar2XQqyZZYdozHe-#s?t%oEK5lM_< zY8-$4sXP78j9(e*p=o;J>fcP9|M_^|usCVeJGb(S5X>*e?N7v+is8eKDf~T&E!Dz!z56xpF`+4qF<=iZ1px*hF#yKOTt_!KF7;Zu=c^>77 zHdI;IIOk^q=4oQ$i46#8F`b5tZJj5&c~3{%v5-Xsn>xF>o(@+i8Lyi?9zK-P(#^Z- z#AN}?BVSFQr)*$fuBE9nl-dBywqrdW7kjQUF_OZsAVf=aRAW|-R#hk|l2WRS!0IHF zCO=C|wwUZ(#O7xF2Qkd-V&p26Eza@sXOX%-QzS8YW zsg>u`!cGBHu*9Z3+BbQ?2cEh zSFA%RKKuMWOE9NwEN6xazeC9b6`KI%mguF!~DZ{;^t46(}UBd1?!rljk zJ)SpKsdouO1bwHN-X8^`Uu^w;?S3p#oQSQS4E~`Aai&vtB0jE~b097LT zEzu&iiY*Q)W_Cd?t7Y5+31PoGtQ;J-Ke;yX#5-(&PxZ2{u5K&1KsHXOk26M(4HlkF zbI}6`5YDM?8&WT(oKF!l=L8IM<<(WdP zUtr%Nzk2aE;n!gAq_3xlAEMf6Fu*KI7@YBH5+*)%qUOicL~Vgft-5ep2g$=ORh6ly z7y)-*cz^ujcPGxDo49&@{Ng)UgMf1r39{G&_uO$%L(u?Zmc{)S`U~SgVbO6 z%|Lb2oTZ>+Ic$M*vbn9J`B(=;36k6)o)wz{?J0`#1>8W%(lYvzKtV3A{PxPe)xn(n z%a31re9-O7sqR}JwB}s&p7#!H8p{04T6Zh2WN`kKx}XD8#pkFAmR1jK@s-x&_l;%O zmJO$lYW$_EgT+;EIIcN{I!0Fci&qAnGu|*?GY=gaDf2s5g2O9L@3(;Tq~%<;T(S&w zerBr)=cqDE!*-P|yU!x_W|eFh-2w0!Suh>^L zVqf*h>;RZ6nLHrv$i?ZU9&}30r-6-0jFlu|NxxIzCAX=hemez*VOk6hx4$QLWc27eZ=ZuKOYG5G<2rN5;y#TXo6e>ae0S#wPbjkovZKg@+^It5 zwk~YlG+{>WZ1#jK5iy_m$i_%xRdRg97zpl2L~i~;R3+a`HyTkfLqK<|ySoD+G$0uh zz#jtSrglJg7bopB>cvtQXAAu^MX@CG9)#~uB+uf84o-dh(#o-n3SUOW;A8%bd3}b@ ztvTrOx6<452E&+;PX|C-xPs44?gA)QWdAKJc z-yrgl6sN`1vK4|(AuyIx!D>POvOJ8ZjVXz(GL$jN>z=P%~W@aB4=q*-+dCg2EA6rirK=Ya~W?Dk;Bz%^CtCc_{$zyu!|LRq}Owq53f*!IFkrlq|hE?PW#Q@vJi^7R#R%-P=T7@E0K%Hxx2 zEYoxF-;V#eTxM?`7CNgF#F&j)C{KwP=cbr z(x`1e5)G3eZk?1iM$P(YjeF!Rvi1ih+~}_qqx3DP>js(?UWNe0o8z@(_rMMgz~s#Z zluOWGQ!;MjOFC}T%NoXgY1~e<#j$AG64cH~Q1rLGH3~}*OFhaJMe90R|1#g9Xp>yI zY@4s@xh+?dcBvMr`gIpR#SQ^UhS}9lyIXM=b_$;C%~Fhi%<%CSH`|*PJN~D^pTo`d zkl=`2Qte}>C?z^yJ}%F)DDk|w*rT-ge+%`(9*3zn>ugrP=7LLv9#wm2Lxx?bkx!l63f)3Bl^ux|6u&$Ych@uH1NmW z_3{%%5fu}0^-fU+wqZ>SMyv>?-U+$yMBn=p7yEDj{3SVAQMvlxKI(&Dc=Fu&fBWcs zOdL_MO}$e@-LM9lEQktv+Dfe%QN}z?Y*E2ay%QVp<1apO-`GSHQJ^YbBU&)xmu6XK_6 zjPZ*Dw?BRnT0@i1UW{pgL;^1qa^Riu{vVEG8@2DJqE^x7&08Wph-u5m#39SSAQ_Xs z6_K~^S~K4F40QA-zWeO$4}Ti-Rh$^D-U(z8?e5QC9DnwMyYIg<{`0341->9Errdq@ zZ?Ib|#vk9ERm1q6hvZ^meV=IE&u=F>cPq|qJk=x zK{TI$?Zm_jKbZLTPqDSmy2+F1BlTyjapV6_w0+>50kzffSH4;A_ihy3-jycqlBMY;0v3a`EAJ-}&L451$dod%g3F-RVr+Q+r6RLP~Hy`OfW;4trcEcKI+@;`bK=5z3>dLu#5A!l*+U!oV$>q_5vfw7`b46N zk;TRX6iwUCz>s6|DUjgFVX($b!+_lqOy~H}T)T zi7vl!``UFP4G=$dQ-z$zNbVsKP7Epnm6&3#zc=`ABNv@ePPzNeGvj@4MYF)@3RM`R zd*iRaSMTg0?ueoklZq>+7&ove!>Fs_560gaksr~oL8tIY+Bm;^`GRv5+rNmEJ)*(M zy8Ptjwu|Dp8OHr|c>52iCMR&yx1r|aRa?nhy!v~%6625iBTKrQYD+aY~R zcSkqhLy{8FBuXU0ljkl@47_mn8y7Iwp*+-3QjVo8ip7i!Jt>Q%j63=5D|g>{N1`R6 zlt{K%i&C!;Ka6d1rcW-mdbA_meeOwA2{H4(lN&;0Ka^1*Z%W13xSUsML&?%6Qj$0) z^X-U{wU$UusCmEfu|ze`Pkg5j?JQ2mA^oFmoado7vYl!(Ko2RTX*wCwqOV{pn(8d1 zXK{v-BAa}VP?)i`4O`8$cXTFMq=vO;EaeBvJ7GCezP@}*`L2+cX2GoAq&l@(hSIjvJ`V*G`K1$WaM3s^tjxf4cID^ zxIDn=;->iqwm~NLuaE&zHX^g`P>LAaMr>`lLxxW1DG?=MBT7jSCocbV{Ng2qh>Z-j zVx|t&$oi%dQBs$xnjg|paiSU^c!m)jznyBzw70pVN7Qc$<;jJRcKwk1`By1((*!ug!51yvYIezckIYU?^4 z(mv!m#e;sUcbd@Ty4i6 z^6>lV!4?_0o^EO1Qj1cC(xVkZEl-^)lobtAG(WVpdQOn}VRZ8s_2Y5#EmR!ZokUi< z9}O80Z7b_sx}F~Fp6cm7pP5gDk}yhOp%~J$ZdX;L*l2c9EF^W{kJIfTy0y`b1`_`; z-8$&jNw+TCBF!WAW1G|mn<6S7AxkSNwL@xU{t5abs?kOhE&mur^*FsqVT0)*S2G7C zbc0As+PZm~(L#~Yh!&*xNMu25y?T|RK>z>A0aOkXCc zj8@WS>fZnbrkj#x(~5v$W#pm1d+_1GBf~R4+C5tBU%pjXw(Z6vLgtQuVW))9HF$7v z^RVfo`J*QP(oI7B<{J${=GK5=+t)=9vY}rm%vd~P`t|%9ss7dbgjM^6{Rf21g8{=K zDNlqm;t{G=3p3UTnQH@vM)}SB;iJO*_`o`0)@q?HHk1_-Du52>SpGs^{=(tM{Q0W| zgZ*=B(am&+P&9XV>2R&Ea+grB+n>HiFz*3wZe^e4w<$%}dLx>612YE?4Q>(UtP||( z1Lh6El8UjC246|TX!VUV!XcM%yeF(uol-YyzEG)3*J=J;r7Bpb>D%&KtAmm#X!NJA z6U^(v*{byHzNFjsBEdO(xMHN*Z*T0g{4T|Qb=P2lzhKsIvA{Ums0|vUU zw+z?%>mCql8-#{~g5yxYcKAz!s($&%5&x1+H<~|PvJDM2=hN~9!k&GCt>m{kRl=Or zV{p3nOeo*v&)Y0yZoVxUCv5n~Qeo?Z{*BGThGT-GC1B%FGH{1@J0nys_`vKB0EySnXP0?OK2BI)C|k zB#cg#nIq&>`7>q??eJyP4Hti!G4EDp#Zc*R+Izc%C7b*UHs5%}w_v+(&UW9-9lp#R zi0@`*-sMe~HVGxW1AF!h2OjqC=@52z3eK)TX1AQ(hF_Nojr;wp4p0RgY!lkOsO+=q z1`U257k?gBucD`R^=kTAt!bnmG{KiDRoZ$DvQ|_+R#@jNME_{;7q040k&G`2H~r&k zVf!Kf=EK6KBf^mu!NCP=E>@O)D5#VUKJsbC3|5J2{dtW-W+Q5#Xy#Dq&|IOgu0JK1 zQ8<=S<;$oVnia^XmrCPY`B~-z!7S%kR<$pydT3)HYmxkF#b=o-Z==*(FKrddcM7|X zj_o?;+jY#pixYObgwobP=5eI%C>>nt&!5$AxS3n{YTlK+!S(*!+WsUYF6wK2+e%sn#K-rz5|@F-zB)Zg%c;oPCVv2@tDx<6&$^v z+0K4hqOxRM+$ z!R(5$>{-6-SwVYwFsC3`R2xoK<;?yHI9M=aV9QYYP{YtXA#WB87RCzZ z`U>U_FY*^G4HlLL9Thi=p<(MUp7V#4q|)>+RY_?Xe@ItlI)laKW5tVo#fyXX8ABDr zwLyF7;Eo~d7kX_;cK^md7*zI>puKp^KEsFic>();DDh^_U}RvKuVmTCksF(Y11*B< zVSh%zj{pA)8t#% z6fi#sBxmL8r{3^h^S*X=ta62~a>dBw{>p7a#dcx)j=;`6fxQRE_CDy_`=EdCG5^jM zfom09t-{XZ!j6Z8g0^5`Rj^|A=T&pBY#7mfK5OX}?`Y=jnRV|}y;=2E^@zzgbJgfP zp=#aeBSPhd&mY+BJKR24_D1cs+Od*_zLJGwCCff7StgwD+^nb?n)zDy&GM?Ds@Ixs z*3KEO@z<_IT@|GN!D7iw4XZ4c)bJ4<4Xv-f+^#SI>07otVBW)Ksd?|M9#Q|Kad@#%u*{#nTre;HOE?)Z{)hV%Ci*8EQy!eD{yU5M zK#}h6Qs)Bxje29!ffAK3xgiNppQf&}VK3_QLJb~1FUdH%T>JTghDC?wYX7N*-v85F zGlKqU#j5#77i<4zE(QI|Vm*TXWw{pczp9K6R%-v%*pT~RiS|~B9uK!F=|w0>Tz-Vi zGJgc6xfpxnBxH|R;x8VAz~C~hfndf16((4Ek^q5S?XoI{6cXJePpFEfPy%~d2Yus& zCP0Y{c%nMUy-*)QE?}gCyB@J5L7^z=ObS#(lA*th-}uW5*A+^eP7krY>}r<{aHJ3@ z8W_CEN|8GR{-R-wO~PTuFa|{|2^4%_Y{S&IIMel%=+*%?C(ONuvxd`V@i!{Qud*;* z`P-koJpQ8rafL}HFHDAT=O-URqA#(uO(AvDSCKVilelK&zk#fs(bb*kI?g`@5H@DC zS`p1>n6zsA{U3`_W7SLx?U-~VGf=i(+X`zEsvrI*bbF6(dANlvOh2^y1e6@PHrRa9 z0w%h0V@n;j0)qJ{NjvzzryK1@@&BDZH+a~mSXr1Nj5ft0E{AD(#6L@@#JanUxXC=V zXd(9&g3jb^XNsQ@!}>bq63UAn=^gcK=EqC zFx%Nn1`qh`b$uIXPRJk=j{)OQdEcghdA2;eRu63$S}NEV1z?tv;SkJ@Ki{<4V{JQT z4XcOTLhif~j%+(tD{MPRbn;}w@>Q_#P#IU%YJX|WTQx)b%NcsW{L^?DW0^;?DmF9+ z0Vp*}siNqD!nQh*A~qR$Uze5su^R{Jg?J^h#e;G+HAdVerSVpT3?v$IoIKE>Sgm71 z&GF%9wr|)B@eJhTcR#*9@%;Di6+5!t%l}U#_?5oL(^|V2)&W#b-bc5WaSPGv-POfb z?^sWJPLh-$lq9a_B^_w0FD&IpqzeSyi_6cD-l4gG~7pnaR z%SF?9Q~$!j@;+0*Fyp2*Ys^~Yvla~|yZv)U`8_6P=36mLiF(<$}32gBGQ{z0Y%I`qKWc1 z={=(#RI7}Bh_vI!c)Z6pEj}fd#sD?^cAyg#F&LNMGTSb0JHKtryvS!>6m(Pu?L{!g z0E=u{{3n&pk}BDJtBuQv2oMKeAhJ;TOUKO1eCB0;32X84D>sqcCku;J*NhEj?cdlN zlC{4`)&p#j+Xo1wq(OxIi~kNtR2Bbg`a{&>0W)G!qCP{$;nG}$xiT@1R)K?=5DF{? z_uOc|YKL4R{=!^LhOqh=B}KU>ChU30VQkMkovcVB!5ofRnbI&S(`Zxm!ipn04@dny z&^`;7qIe_E-Sh^|dEkty*ECIv7RG0!GCsq~Mv6jt&a2_CPxHNveV^u$xdW&@M@S}0 zlCD%BMLSa$iMtZ1=SJh`O_?VB3~vgT9IvDf<*tp>e3$8=N@ABc#Y1D4T~g11v3G$K z=1plQDOlVUKS6Yv6m^gitplY&xHrXXWx$Wv2% zE5Pg;#?ey#V1N=Z3o99Knn^qS?AEYqZv>$N0kNz1e?nWtz; z%QP)%`Kw#fBIQ4>7GMU><+GNoQ)HcdqFm2l_)f9EpwtXQp9Z|NutcaAb z?d7+Gwtcap?XAzQkOQP`+tz&ha!PUb+AyHi~Rpu5dlZmmG$6&3P0&8Uq)&o;uHN;@8ngVNe z4A#6Uu-3$2t(^j^QHk|(Dhyrj-}_~?YqZGv-b}CliD#zyVuON*kX7JgsgHu<>7%@$ z%(oi4nEN+W8D`5}as%$-O+Z0$?N-W6TGN;MI_Glia!Q2tW^coTw-gs@^ zbZ_SMoziNzH+z~GcPVOF<`-jYd72n@NAold=AN&M=~Xz}nI_J!pZeafow{5P)6SoY z)R|18M(VNA-}YHiSc2f3D4W}V{XwafVBDF^j61cWapz&sgkF1ZF8#p_WsdY+^p|Uv zN(1v9d;7fT8wui$Nf=zq83gv`d-FJsnGtUF=0~6no&^$qJ2{<*yS#Ro#pLJ79tnFL z)3k<5vEp%j9bPH6r+FQ_l-xj~6?CiD4mVH7nQ7re$~ErkWJq;pN(F#ar=vGVk=sOZ z?epeP`e=y*672&|0BJOLa0o3drnbx} z!OQa641F2?XW-&c-Jxgh-YjxXSkYfd#3)Zj1t%ldEW%BU z*HNrU&`Z_5`m_4eI9h;yqa&^=rH8uQ_}g&7I0P}htOdMbjV!32_|AJ!%Y;JZu-o5cYgeHs2ATJ`97q%TYIQjqW-S<;f6I(+0{8~S2&q^=^5=|uA1rB!oFkt z_2=ORa>JV2BR@jtBrihW8Ai(3pemtQM7{10Ur-`kB@Ij&jhLE!IQ+9 zzf2z($;-d!gw3)I8(^&+-H%4K|M>3$2U`}uB~&xC=78aGaZPO;KS4psFx5DQEeU9! zF|AzjQIuMfBv{<_5|zem?&ek(9JP2z8IIyPvnp=?OI90;D6z^qk#);NCBzkV@o~J5 zG3@>$QaY#lZALB}6@@i+i{n;q5jk9WsgUWZU}qhg0fCIt+octQCtq9IzggaFpErzc z?2-AS=8>~P@zy}*wqSmxkXiXfk}9h(Sm=DU{Yv{#_HeepaB=^-&vOcIRn`qXa_!83 z`DW$Jp=JKc#enREubQuzh04{VjiZZi%onyFzHwG4KN_$%1&gW$do`uSG;G%0NE$sQ zlx`1X?tnMfKi|wOeBF3`xsb60J8Bk(te&uVBzwd#YWhdSwD%vD-KZ1x9}`+Q;h0M( zXbsqo-^|G;?`amtS8fu+Jl&AIs#^66OM?zSzf8B&y*hLCZ0#>sR<5qr{;FCJnCbtH ze{>EGtRwENnEzhbOGp*n23waisWovn4>kALJoM1u7S*7YpYkUV%nT`d$^hMK>jZio z!53Ab(VfYcLrmD@_n*4^Mjs3^&f$$n{^2N*SztxeQe;4BpTUNpI6_73`RGgPaY$~; zd_^#0@)Z*%Be>5=u z+$X>^HghN8F&N&KMBLjSybV{s^x`3CHg}MAGqa8{-TvV9$#dTmGm6(K@aEpy+SalG zjw>0@q%4S>`|-CgV2X(?vC^C`QHhoBr4ox_Qi($;kZ2{m$sSsr$s zZjkB>D$8;;xy9r}izuneLm3=&@S*a}t%=14RphXd8q7kZ z5zqXZn`Np*Ud~OPM_urwMsIp5kgd4yaTF0El}(tW(w^D=Uv1~d2&AYOB(wHnx$DJO zl0~jqF6kBR!pK!3e>jEd1}mV+H2(36u-_8BJ4Ps$3ATd@B7z1W<{v&F#ox<=O!Wv@ z%7F(gqV`TF!j(;ga1t<>On2YGc={Q+%4eP=#rBV}9AOO$OFpO@(3&ZfqVTmBGU0*N zG_K~(kovKZx|i<(&g6gM>LIEuuf4>?#YRHnB#*T^!s z04{$TH#Xj2gf;O>zx*xXOG*t2^O#6-@#ZmA1yL`SqGAJyRQPeaRnv{u_WV0^yG=LZ zEk!*E=F3l-JNXOCfRJsm!R23+TJH$Sax-t8Jv9zHq(&o8lSPJA3kpF)u+5d}f|1aIn(d|F!HW#;$0W;kR*gLuS|3Qx?Dk1t742wbr z2E*kje*mxCv==V&?Ni)EEtCPEotg1Y>+_05*vrwi*GM#t;>ueV$~ z5>^3x@6@lg@AiH{;Fl{R<|#tiJMxKPz;T#&8zX7&flb{5B;^ zR-LOEIykhM*^`+3t9J>jc9YL!``&GcLjEIzqe+@F}7r*Z^=gglFfnnTLL|&k(tNT>&Woq zq9n9arj1M+4mF7*FVByTZHT}8IZMz-OpSRZr%ynNc+n*QI4z>FWmh^3vGrMs3 ztgx_Au&)c4*MDw>vVG~Rk6(Fwtmpw>(F3D}{-W*uW;yGtMwbcu4hgFd39AkZjw1ov z(a$rABuj*nMS(p{!h;=wE?+BdkMNuACZ=!;oQE#Rq zE#5-+;;mG$tZkZLNnO9L|G>AbWNwgpDOD(5GBR@n1~rYJ+1AAsHdoI7?BPOT_5!^QIld$nY;lXCXek@>a`Mh|}aGk%nK`#6e z$4b}xO4kb;4*5$D3$TBXOj{a-b^C;-7GYhB(8vi6SHRZ#c}A&R=B0rn$AyPZ1IFn4(V`-|UQJhH(*uki*a%-bo<-6iZgA{;$7 zcC^)Zv{g8M!hiG;;dHNXv{yKC7O6h2X7#;Qvy<$Jwrcij{viI~-9C8GMgF#GT$Hm; z%_H=)Rs7ka;pr*MP1%LB{czG2wXsgBQ2X3~c^<}K{@lJzL2JgvQ|C_&SOV4xsRkX( z0=DH?(hqF$<;?8Y1xscRXTEQL*FI7)TH>FxR+!xwR;kyijX|U3qW!$RziMEo-{=$! z#Rx8uLu&`q{l+rEP>#?FIkb9!_Zv$DLn%Vb5{FvlJPq=G>r5fFI-H^}n$@53rI}1$ z=xi0ol7OwA)NAL#bR<#&W58MxTY|j8fzwxJ^(O~Q=L}cAU;S?NNau~6{<+(QIXjU0 zPAY)yV*dI3{v(5{{KiVbFayC=NIv`GW9J_mSURNfTdRfC8idx$q05I#{MK3_wGN@P zVNgP(6~=`;koUl^ zGe!^j8+Hf}>=f+eZvRVJjoJ_}7t39Ic0hKakG^fLh`bn>5-BBME>j|f9q{2FteE*m z=e5qUiUwarL$I)#a+hC(tW~a3BX5gm~!A8NcDPY?S3#)z$RVK5mX@qnOePhS&(X&G1USaP6q2OS^ zb_fIb&tEQKRwpKL?6aeUR*XC{(jw%py0M!S$9LW;nun8$!Yb{8G@=MaugXmjv=^5xzjMEkSsF;&4qp#%pqZ-3yywor09V3g@AYzJhI; zEpyCT>a&){YO*gJeqQSPu2ObeYa1f8g_9kqlZ#d>MC4UPjfi7aCfitpkt<~R`k)ZsXg z4tG(e7eQrCbCaR@8Tq=`;7#Ily-B@B?7JmU(C6g668C)Qr|6j5RAX0N)X`L;gK4C_ znuInnvIa!2iRskA<_3EV$zBugx|fj?>_*UmGRglHX<)xZ*PDX6u*KIE3`H~U4fD!6 zrM+ga*=uCBK$hr!N+La4*ymVc_hq25l`F*!eM=mgb=@rOWG{eYV$c1Zyc)czoQ0#K zZz8P(%@!N|NxcHQ?wL~j33HSdpCb>g*4gEibuHUTj2(B$Ek^>0xT_aA=uJb}qPd?D zhk{41fwNrCka7y`9a@00E0OC_Z+fELe@<=%X}>k$4!?{AN51#3k1VPWE*q{zbEri* zJKQ4X#%N7bkIQ2{F(0lMjoviiI#8AZa#vWYnuEQ|M{yqD0hJnm7e>W%`LO#|D3y-w zB3l*tD3;ztf4Ow8HMXZckm&RD9S75+zhI2oW&?;u3A)m=q}nL-s>KJ3C^EaUSoKR+31H`BaNTxp(rY zcaq1lx4(D$XXllU&0><49}+qpV7q0Y8>C9dV-crm%v~DXjEN3TL?3cEicXH2 zITqo0^g7;++ZjEapmlfg{|DfZu_@vS<0KPx@c)QFwgW3lNvG}w5XGt=<^LDZ+3}HN zk_CgG#z_rAaICr6)#dKtT}`5klq86YAj#RvVoubjA_j>}g8?!*k~xV%(Hf9MYdE{B z6dkpv2sqK67V#k^7G6S~GCA=(Nd(BRLR4)k5N>$gnbUekyJ=Mwv#AUvM`|aeqkKqb z5gN$x2(wyDM@E=a7$z?Z8JTQ|L`}>+*VL6ag~YWgkH3zri0$J%JW^v3#fH}ivLas)rS){4h+1ayq$mNyO}Kx7VHyN~WqC?rF+{xehz}6!qI0=v@|Wn7;#U;04ddk*=Tss#$@NPI`ltIL++gHYvIqx$ z)i;b*+^7uH?;NOjwdP9AP|`b=H!Z&0g~EZuSmO)(TbM~`e$iOod|%%D;e8_y->4V% z9r5QK?b`%r0=AN%b8f$Q^jNJgr*`OgAZKAPvv_cyFSF*-mfPY%CogRsbIkKO<_&N2 zJJ$3YgQazYwL_;z93!ct%{Pprj|n^X3C{gE*9S(a#nYOu26GD%7_O3$>JguPZr=uZ zbtU928#yqtN%VaZ)+Ax$JS%sgYH-)^qR+CHjy!TR(|&owr40iQ56u}_a3k%r%uWBU zRb?-eR(ypIjOLDF-M%?s+k(|xZo$BTmmq|!@!4zq_PR0qLZ5vhGl|VF>f037=q!!u zn}sDz4IqDXL7;F0+;tR|iT*o=9vhq0;G5MjTIrv)(OV#wWF9;_yidqp9&}W}=L38^1oNEm zB7yUf$lF1(CH3E{P!b_)>0eMAnFX<014>5+l7_%vzO*wuHI+gVj6E=#HmVhh){NQK z`fO{-oDXmR@}(nk)@$_Cw#Ly%MzJ2+DC{{vE3|_O)841iFP~gpv3{}YS5>L&9I9U} z%3EjC{yNPG_}6pF5&pM14W{)b)!!A@;o&zn{kq)b-()4@^=}-;^~u`bl-t*9w7*%d z!`mAgJwk3IYY8?P*DuiC$gr=Ut-VpLr;yot3R%G3E;eo`*Z#daZ-Z0&_f9=vehx-z zj2$s4ov;N>65fyjs$xL{H6c!OU8`V$H6FrcSE7ZC!ZeLepv7O5?eNIUn#Z*Xmn{0@ zvclF|$O~NwShzBog(LMIb}<%C=P^sq370QP@x~@#;oyD%hOWqEoS|!SPZi=4H9r!~ zROIRJouA!(_Zm12xKs_Pc{U01PGoy)D7pJsyQ{?$aZt$i`f27+ej!axRR;bVQi{-O z+C5=@%=onw$aY>BQD-z*Jl%{nAWx?!N>16>)`+M;q{nBBCW`t)*>DHuVUDVr#4KT3 zx2MZ_UWO37dq&3DdIr+Xgd+9{ntB(a0Pc<84>khSV>0^rwJeq%Xdm zk=_3oJM`={q+~e@{22=c^TL1EsMI3KCsP}; zRQ=fvM(qzv0R6(KZ%9x6MQSp^S;kc=?Jo-L4NJAZSgHri=A9NQx^z*;U|o6wWuPvN z;{b57cjRnyt$2P}Z*p&vqN^uyT3TViPlS$srEZILwvL|@?etz8t1P`^F7lP>M$*Ue zHc8O)jtms6(Deyd^hT+Z;~0igtaKBs;#oW>16^2kCyvJyjR&zH@9|eu=bWvmi3Nze zC>^Yzr5u~5NJ+L{lzLEvsXuHw?ZuLgtzfN;-b_bhhLX7?P9-rBib-1E46+#M`D z1BcsW;4dGfjeZNR8h)V0%AVA;*p={bltK^f9<$3U%R#X6EK=%edsErrxx}}!3uI|F zR%p?f<@L9OffX%m`(js?Tr$cA8`Rj|$`xPLw`f4J%(U8s?koF^t~#qqdfGt~WOiR< zJ8zG?5O@0a-8Y`O`vy~?d1p$HBQkX;t@GBMCGJKpQwLvd*r#>9}N;^W^Q0(Fy)7Y3#>KRE60-W{1f9p z`w5Qto1#ME$|d?rCGCRfqDpk$m<&x#H~e&drC(4tuWmNKm}s@>81#RR^B}(*({bu@ zpJ?Xk=sIik#Rl@dxiw_&Mg$$rr`co3(9+R%Vmq9L^0XERrJZbZJxb>X@5X`bZgMM% z1qsUqkqJena zZKqrv%I~o!ABk&sHx7np8W0vrWLs-14up5Xk7s6ES4&3^=UU@xZN}N4%(1AOdGQq& zfRV+Z)CXcm1PL{mlW!-}Zg$=t`I|f6yng4cZ;Deo)9FjJ&XZ?(J--~Cfb|4^B|%S8 zV^M=vrG~VfZCz~17jiTqv*f4qWZN;6LUP$F&fTIj&^!s{L`QRri<$ukO^OJik|s1k zOHT*IaGY=E+DLDLDJ+mz$u`!?z!8&>5u&|Aisznekh(|Yr5lUGQ&7m@6sye|Dhy** zNPDHqCax9FQRoV~{Vm;|z%7*8Djtz2W*9oqEzMmgalj=Tfz=F0xy)w@ z&dy}siz6jXPf+?)xkRkU@R%!ALF6N|vKVZgDl$zZVP;H|;wG*pY2kgQbgI>4q%5>h z9mQyKug49%Qr>E{F*R6F)VCE2mFfkS;0VU-)3+@`Go@=L%V#1vxjR( z%Y1Vi{dw!eMP&BX%E5}E%0S*+=mkV{0P@R+61nc zIaa>LSH5QSu(0bve|a<9ILbIe#o_?AdXiLyC1V9^d608!nMA_wWDy= z*etX>;xFX;wgt_3W9DL?xp;6EcD?ZxdD~>l7W*VT!F|@B|2Xz+Wz?F0c~&?nFF!SC z%NZ8(kKbX8i}r{#;n8T62;N^=fohO-FHrUbZf4#9R+nqy#hIG5l&Ra(_h zSva3y2Ziql7Z6-X-=>F)2zFA27KDolE>WeG4z3B85?rQomIX^HgO%06s@h;hb+B>< zdfGD6{+~zsW7=cjoNhkth zKHVMC!o~Af*(8drXXJAhoWm^@Wok8{lor0*?cT=n0(I@K?yl3F-97G52GghPIYEn2 zvRfg#%k(OF5eC0OrGloR-d%#7z~E zsaA=Tj-v&!+A{jr4eayV%KP*bN$KDosQdJpgZ8q)9?0dO188#$czw3& zp+|hy*?qd8)iKcPv(^l&eW`Q$v|(L=%@j1J_pb|U0o^iZ^*YpDdb-u5|Cx#uIFe0n#>t+Phteg!0sk5Ej2;da5r;AoMMR(2+(nQ zm&LHNxg5ZDc9%&ILJGJ-t_X`?2j`^49)2BMNrVKIGF4-d1c}R=@#hC`fBZZS>JW*K zI3#sEc=;%$;ajO6v(EgG)T6}}f;`AbiMONUOmb=K;?^at%^)@&zWZSp0yvjzu4^Hf zFcz?n&N*NtEo2o#)-<~xYT)_i(`*?qNn#MbgHq|lt{u$yjdadHCC?ogUI5){lE zOfI5)Ec0>5%#~m&GXCs`;)+fzFPyzGTKXtOd5UhQaSPcLpEIpw{xJl zVqF041JXU3xRKGSj8WtqSJ9Xy4PlamIWTlG^9CBjT0pll3kEQB0Ac846%JtN5NseA zLx*6KDk~q_MJWWERatohbHWyaQ&m}xfx}@d!8TPEF-vI#W9Ve`ZwO})%!W=|IE!Em z9r`whU<{qYLCs(T($1ylJc?^aIG-}v03#BWUTjDyLXB&uYSx93AT{pI;zp3HL zw<+ICw@Y;U7H%;eI+QETN6P5gI{r*$;z{Wq#ADRU|1HwX?cxN1UJfWSK>(5`2!f>v zf?zg5pqCTOCJ6L$g4qOtUQRHZAkfPRrd}>&)`c_Zfpv2(oJFuSLl7*@5Clsz1i@^E zaEA*BmSzZoF+(8I8R24jAcyFf5QDks+IfFS)}cq2Qh1Fj-xLjK^udtwJ0#Q70O%8Zi*# zUc=SmUdPSCeKt1-cbpK)&BHmw%&jCh1M}zt)MOI3kbSoZ-!0~rU}l|%zxu1m5c?Xv zx`YCAg=qf5|In$tIxGX0iq@BXFOhNAcb}cOdR?4)$NMhdy*4ySYlKM-w~+1_q%xR6 z=eEpqEtp-U3mIBr!?KIcEg_yVWTMA?;sGy2Tqa3iJoz4dn$!h>UNg0rFNTtNS7$T-Fit!%iEKkM2}X`}pSnL?oScg|6zn-gIZ1BbCQs1|Dv4OM z#^%npj?;g75TWBw58wXaM|XaFdGf5Ob}DLkQ_`<@62P@7b`^qj^QWqFGPuV z!_JT-UySdm(unb~sB~nV8A|GOacw=F_pl~Wqa`}Z2&X9VIFSa+v#MkTTSRk0bV(GY z@HnAK-v)uXo2UI)-axmjlrLrlwjU6ja~D&IDiP2U;O(( zi-wae6$H>!tcVbfHp`+*oVewU5^<{KjS}Z9O(Htf<2lu>oLsDE464%dp0yVza>W(% z&4S7y^Ls6Ug8Ha0g0QZrC_QK|95jZtfNt4~2N#9)fH2eLmJBWoClhQSctO}mFzm33 z1~uUnf*C&thtOA+hb{D+s)EmP$FSB{JTGje0AlP0R}U@uv~2#BVA@yc*f_P8_uNw@O$|Ko5T48 zgWq!w?Gom1_tkE{W*7Dz4i```Sij=I(r^*MVEu?aEG8JNAF+p}1ed9Bsz#BM)=Gt? zgR2J@3~agOtOyp*3>KFJXH*C4<_HVc`RdkPYrL_aj*-Yl0ZS`^{>SC`ygw!UfEq%$ zFP&`;0Fw71oW#^`o_-cJv3n)?}PMHD_mWyu{V4_6U9lRoYVR|7_Y>tYLh%UTQ*J&~HyV3ICc zv#@mvZ5Aj`fB+Rzlh|G2-9Z&JJ!^pkFa3AkzC7{HOLt!Q@XoisF);$4O9P^kFG-U= zew&2YP`3W&t8r=r*b|>T_u<{2UxH*i!ZiJUC5F*$L<-KR7(Z+)!cF;$2ub<<7@}up z$1gvp+oJpvb$-%n$GLaV8OrEiJ~+>pQQ^13%&$_c1S+_6$@VzJDSe&&j;}xAB-$4N zQBFy`H-a~qm=f=%&`t;5Td>!Wa9<_zK9x-t(qu+cnxx$lk%P#47m{8}9+su>na<^7 z$(e;eM?_#)&@&SiBuYc#nsV~B7vTLt){{`k51IV!{)>P(@gYw$68|vWrjh;ej}Q`1 zH#gl#^D1Oh2qBng5Ssw8vJWQZW7Hq~eC+;!bo|2;D5!!BK8uLmB#mTLnGt24xQ#P2 z<2&NiTkT&_7p%rdiG}p}vh*BKg!}-KX)(bPEhbnJju0#fM+j!Lc<<%ImmnaaXGV%! zFLz#okc6H=fgn*I-22AiYv7mXgfl3Rk>t+7RAy|@{5G+Ye2$tmP z1WQsAg5%}t6eh{l2?l+xyk(y$3kd&U)I*kHjz|X=5Q-{4B1Yklq0(ZuBdH}Ajd}*a z*O91uNw6nN)Vok5s*iu4z`MU25>N>k>FSfjd$=F*T7N7Ml%doO99PP3!{k+}bih)9s0D~lKbMMe=I zc@z;WjUs}jQA98sMJvJ{=mxg40@5It;vLj$Cns+V*@tzHVT!6Gw_@()-V_6ha}@YcaJ1PZ(q@`1ZNXw>(7%|6BALw@P zW~6HcPq$2Yri!dF)X%8}ai|yOsueF8W5^xjhj;4&TrG_q2fv<8;8z*YHDzM#m4kB& zA-(N5qjaha7-ft~{#}Ri{mep$?+WuReFm1h9Bl!UZM9XoNL!T&ZRN;2$EPq~04)^j znRnZ~p5wa(-`&`qsusCZRotE6-`lu-g7f2N%ehe2(`8Z^0Wp<-^!6VC#WPPR#*B|B zwjbYqPp*(G{LY7*Ua3bOJ@)#Kf75X6?|L4;hSD8JG!>{#={}cwerQqxU4-oVFL=p% zCW~4aW7EITvQTyYTc|L^pQ&1DYg-BHft>kY!Y!5eaMo{5vbX;alRW#N`v3X#*ytSO$9_Z!TXY@tKpOEy0>d0vOR?nzpi{8U%W*0`Hj>(l#$JLb*2j!S-N1@#`&#t6-)=Ju1I~tWA z^NF7U*P?FmY;j7VLAEC)pnuD=grbQ$qJC*=eUp_mAABqy;ct5$??6M3U2JX1_^Bc9 z*p=Uwo1zeC&jrld>IfU8m-sVm1~nFwZ&((6$9j9&fCc0_Cf?*F33G zp(rFR7a8K_vL8{WypK_W%1y?AlL)V+@i7Oq@bN*Gk>vQ9XX8pq>@S=4A@l8&i} zBgwu-mvA%;UX40ME>Y8y(pEGGxbxs-)D@?w6GJh5+<0k&SH(Dqx~+ep6zST$`Rn-QLhxTd9 z)`Y9N0m6;6ti0E9Kx;V=$#!E~`_QJDJUz+QdJwUH9IXiZ)8yN(7B*o`;i(A>X$AFa z)4CbE*3_#N^!l~}A6EoabAca5Ay7m$!zcs_&y!Q^hf#<$P|d}D7==g!)$H*v1wIZm zP|XFv(`Aw-s#z^+20#u40c&mwD7RJsB!@Df%^ut$k2H|Y@X6KPq=9TEQpZCY$mTNG zhAOr?0F*-+=;kv2Nxg(L(9I?O?EuPw2D;hfPXSO4G|gg1%X1!Ll9?sIq?h3)dC3+Gl zgM*+X2fOs8qy-1N^fc0fgQdZAt*AXTq!o7R8LSn&d`ZtF4PH_kaxQd4tLvFr3E;a{ zuvvHU5IF0;F{I~^23OrT&IERby0zN%GdWt#CaqwTp2tIAGEZ}p29t}_;#Inb1#p=h zFCq;Nd(^@edI<|)u_sd6AXx?w2{={8`Z}_8M6V~^Ajd%?Xy5aImsp&0VPjyyxwpmx z9l;WvUiwdP*!OOLoq-#B{nXrM6cZc;Ove9s_xR9hd+oKDsn)`e@aL3mZ9qw`+J zF|Fem2BKQnGVPezgAu&yI7jd*jNmrkbBIRHv zYA6t$&vD!90AdYgj${8Rpw&Qw!w66Zv>IqK*uMm5HP9Ht9JdvuF@`yAt4L!E%Up*Wmxe5!{Khd^`2-*n4o^b+(}2#kFq_2djfGYfatZy0u!-T3^=v z(uzPP%(ayr;nG!VYEh)9R;};Sin@Gc6v4Tjnx}Jm=}gJ&1@(n7_0m+>a~(L_)ckv? zC0Z(Muy<1%aVA{x3wyOf__fb&P>-Hg&t5^_ui3XS%3o2x19)8Pis$a8^5?`|r&no3 zGL*pjyGOBW{gCP%(+0=FM=xqSE~*OSWNvai$0mvg9KNhr69acsn;*JLOarTw%pPze zy~iYiWMS>Xf5Ue^(M(`U)7&lAYd;H1pOO`m(xI(RSoQ6-O*t+b>k6!b9k?b*tWThg z;Ggsgh`W9%d}(pMb(aM2QexZJ(_$^hwJ`4qVtxj3=Oib#G?iuonamvPgcP90GsMr&4xpsSSI6sp;v7!#gOkI>a<5`=z3qrbyrVpPQQl#T`G;8Rk(Lqs`({>)BVnFfD{UI#b%&)x!|l)c!=C%_=bj8SBb-GzuT^!na@5wn zlN9U?UYqXIR`i4$Hflv1@gpj)3bckgfK~{XcVbY&XyYkXi(5h&TJhR&!8%{YeOK;3 z?TEGYy`=B;VZVm8TGv2$)${7^W8t>rVb=?4>I=fU6SYr9?N5EK*4YYKjQ|P6_Ft3A}xos98ws*V2(L_tu|9*Ent(GgrCUuG$e(gvvl_uO&hpMj+H=O%c_Gtum3C&MMN8S*o;njz_q`n5G^&st8>+jsUxWw2CQR zYZg>o#kqxxXCms(z^e=Nf>We)$3d_x{&Wqc2#r*5f{Clavr5jP1@npdtOjmRU>o8# z_W@QffhRb#NjPmci#ry2m!qcG+mg_qR@7B_TTk1eMuAh$f<3arhCyYq@S9%|iy#3N zCHlcW#l5Y0pA(@<$&5SRmiXjw3y*#7R?a)-iiNq0ZQ~0Z=LrMHiI8hg@%`zskcn;W zT>MBH+|Qay1}CUUY&UAMh1+_qQ0UcpSK(-bP=_`F-Iznyanv-6QC8@EW-8mLDfV_Q z%HP~Y_?v5vbOcKC0`*7*OIMsB@uMGn``3ZjO-0H-`}J#Ie*DMpee}n#J-+?H$8WwT zagbcB$i>=;iOZMAE=&+i`;piSLB>E=gv#SXepGq8|7E*LOlLxM$+ZG=-Mw-Zmfh_u zjkUMXo+!Uy!(U;-FF5w`nZ&=V7C0pS`36sn3Kc+fiF{t6yD)sW*g%{8O$D7i{|G0mSRTpl)rKI=2^A6 zJv0z5=)+Xy!J016eyjWK@pn3cFNd4gPM^@4H)67C9#D7h)tdK)>-U8{`+eBOsKmbs zu34oBgVe&MYOdQv=zucpS)gzeKs(?VLfW)vkv8pFq)n?ZY15uXTH3Sp9MaO1wOr3* z!35@zHceTiVam$6QLPt|hAE45326(E!U6#){zIX(nO-fwU$0;lOj^XERFM|ooC&=K zv@Z<;H$p9EeL3sK)w>82y(F(h^m3b?1j^_oDA`LuZ%B(?0(wJQ_R{YRT+#{~G=TFn zSS$N`<2q_R4O(7E{yN=58r}E2l@xm~ zdJ${Eh`bv+^b*owM4`V>FC$GpsQHb01qaa-Z{&r!wIFxu^ciTYbI0%eo`4&|JfTGS+aQ zct4?qAZ@PUNQ*U`9UKNNSIz-@V5e5urg_@HVblsQYq#zqEf#Z)ffKh{^&FPXwH#@z z<;>-qn`Pl_eF{7Vjip?Pe;YUpnu}$Bt^czBq(2k#v-(db15{z*bwf z$CoAKMCCX5^(RP(78IyBCLtzI2`>crR4gnp*v&Z)#;VjCc4Z8P6Bf)Zf7r=V&46X9&c><-BUb9R=HiMUyMwlULN_*EQDz%mFa5kYLYfv1@7 zOGStr6UQpdo4~KK)f)U)OP44i7h3XdtUpt6a-k(5S^gg0Wru=usV7H;uJK zvt|gnFB8Fzmd1>rsCNppYva_?44b!!{6Sr_koj70rdiV}YE}r7!-7TjGNfE&PtHt-tY(GnH2s=ylfuz0O*ABur&m?|lu;qLy)t z#_Z2~?=)kZ@7?8HJMX=(!%??)JyMT%1JWX40xuTEZ1atGqP_yMU5rPuH%=4wLT^0Y zgiV>QM{Mn8xXQi$-FTT!Zf6CV?&EI-KmN`i3C9V;{PF7^MM!P$QXcp^#)Yzgr3>4^ z&_bZZ{7r@XsgNcZ?nFv|l;EV0_6e!SOej++Q6|EtBPK;rfckrgAGRNVOWL1=HOI0z z37-~e#d(!CkZ$+5W}or~Tl*%Hzh)vtxBp7&AriyD^ot(lSFGX(>%fn;F96==dGAGWu5|iKfag^2-%5M}5g;ht0ZqgklT|T}rNu)^{ zauT*vGW$bW%_=AzxOFtu=+RZHZ{l*;TSP}Gu;rj*mGl3E?I0H3Cc#)?2xm|0Ef%*p zwcx8V{_U6`7ZF-~PrdM(nzyi~07Fo=IBFvWR6uI$r6|vN_9= zwltN@P`aLWQjp<3fh_uj{Zr4jl3yo>Df4_gL->H|eM(9ls>J_9pzxrKz6=~{C z1D?>D8Hirn^cq&x8fyM}6rkpBGACGU`m+q(EGhM$xz!56R|)N)hNpR>R=kl>J#!>y zpv?zma0Zm*47B;A1!thmCoMS>mZz(`KQW9ni~I1x`_IUqKq2 zqWPu@G&lkAPKp`^Co1ax(et9l^#vwF=GJM8Cfqn49zJh@T z+VmMGEqumzVqk$5K1N*_SfEXxanizP{A_TORu1`%6~e6u0y1g16qTvv8+11d^e2Kc znf^r{mf=oR6==Z70u4{1ia-WN7HD`9mH2mHOo4{`P-Vb_F$Eg#L)6z-k%s$FN#OYO zVRiQ&t#mI273yjYr=WTiZlq&SK^n%6LjG%avzs2e3Jh^AK~l3E$7k_zlDfRhpyZ#| zo;Vu|*=+%t3|c|UKFq(SM@}qGgOt8fMQjEH)x+$Wfe?;P0_Z? zI74nm%Z93B-og`Kp1k5Wnr}AyN5Z+aYDTTE0|{1~9&5$vF~9SulizvRy-AxhAZcsG z3EG?iSufX_fvQ`@SZktetTl&jA*vV4<}64$VXcX>wble}tu;Z5wPq4lnV_vzCTOwB zbYgi4S}ZT!p`w}HTH#hKFHvPJFF{+&OHUT8{MPca^RBZ7T`aEplagB{fn(vKb~U5j z*Np@mDYrOMZgHeMCKKRzj3b~WNBmoErC}Y8vc(n930y%rfh#CmTmhZH6_hQmfVQ{- zT5u&DTmdb(au928(Bz6-b)z8IVoilrJZ`ZCw8fV4ECj8y*s=kZ|Je5%fha|w$HB=2 ze82T2r|1dyn}t_k@fMjla5CVW(tv1u8{y+67ZBXk zMed&(XAw$^vk1X?Cc!|NzR3O4rqYegV4yrasKXI6}oIw5#PyDpo##07j^L7TR@XtA!OyW}E z4pmSluqz(yrUxic`u#E}Era>DOT%TY@KlXd*WIh$p;hmQI00K}3hkMx0c@paCt)i! z=~`-qFCD5_Og|<5qW}|?wP?95ahmu*1GeKUSOGm_ZoI< z4Z9;wk6P3^ojY>^%{J~~vyEtWnJ@FkD`>W$JdhQt0-!^4clfB4&T}JP8Z6ZH!R~PR zGt(Zed?Res_baLcox!78P1khO%qv>={&2+s_3(>Y#f#W16$We^u2$>Tgv+~)lUE1Z zZ+ELJ`oa~P&8HH-e@}Ffaj}$it&%*@n+i|~o zS>S})v>pqDOWMX=TGOs@^={Q!alfW6FcDO4UsF4_&Ni!uk7(PEXdOqwHAhuv78x74AAFA01~fbv+%P%QkE;Dffrn`)N$(TXWXz40Gbs~z*xA+RB7Q%O)u za$m2c>rO9}aj{RC*HTNugnAmED0qG#E+p;%SAX;opS{?J&1zl7l#~Wki8aB82Oo7T zAiyE<#M#4+Nyph@{4QbY7DtS}h`QVh4n$vfO}VZ+W1l~KPBGZ|cRdqFYw?M5h>v9< zsLZpQ(N^ZdXS1Ct7bX>6oBrB}p!vbgcW?BP6& zP0r_RgW!a7FoGr&+Yr6nHRV8>J>^82BaF$ZX3w!qaZcrk4|whbMd#0k%>!U>VQ1vW zoPNgN{hH9LjlP`!-qv4T`(BUnP5y@-<)>h9ql2!#Hte=7@=w0<{;tAm^>lx z^s2}o|L8};Fgzgr-5gOzYg;s#u$*y>G@BXYF>M|9yPmwLY`C^%zK!vzPo`zmG||wl z>&}eQhH3BJ_JNsGKW`s+tM^XrKaLaV^2O{j z`&ZM;?8-hok+PqO03!+GIj2Cmx43Xwki3^%uf(S8A~sQX{eaLhitX01qkaAIO4#Us z9r)5m@BPI`Km2z01X1jW#}oJ7th)F9`Quyv#5fPJyOYKz&P(JIr%Pyf5uj&8u71QM z+Ju(GwnL4%awc5>3I(LK6`9+e2!g`yO2e1O6qvFR=Nl2o1wLv#nmK}Sr>DnH8O8h< zQYsEVm2__Git^`RM>OpOPLTc>>4KEg?oaV-z-nQ4bXTkaIg4gs-*VIOV4QK5%&9V# z%Sn^s!nO9_eFXfHuX_d;ziRuHD=&f>Z}*M0z$Rl>au>m}nrh4UAcD=4l%%S3OmW;? zr&Qyh2$wt`E_~jXNfYztH`4;oX@x5V7`m!eZQC+4qP4-Ow-Z5x)z-e5En4e#t$h2F zBzx5k`~894>YlxK_a9OZ9}n+;Av`dM`pOsWn6q#@m&cbBL6RKFFZHEG+y%b$N5Cp8 zUoZ0YGL%mK&F(;zmRsZNlT%>ro2S*L4O;nznLMq0^UOGI1B5Gl@=N`f0&8v|qG(H~ z;;!q&%ua6AeP~GAH>7Po@mH=BkLU85em6>jmhOvW0#vN7(;z zv0}>OG>G~I!#JY8R5*7+e`eV=D@2>7vnH*XwQvi|@Gg6c!Jg~zU%jwx($Zy3pyo>v z>mdBx_=o8$31Ox|p--HEiN*Sir0k=dJMgmT0(Cvg)bb%N)2g;b##HVcoohu?5-FAi=WdeY_o`{hx5Bd^rK4e zTF}+rbqW2aJ+qZoP2qI9Q19cNP8Zf6Y%s?ybfa$Ns|)a`X8|4+#CT}hY|VCr>nTS0 z{J$wTTu(Q8d*}V0=z5wwRU}3d-AxxCNpv^O7LKDi!re3%?xrS^&-@+G#WZmwNq18s zKh2Ru`){>(gZKr|(RBVVfUc$s@iN}klsehIiB0I^LbhJtqrdpkXE;JiOR$tG=nl!a zcS;Kdkq=3|%Y+N^34Ry*1`dDn{U40VXnJ8O-u-Tx-TwpQu(EAdJ|3@4$%)~;UY&q*iorZCA;^fKGlQI3UbPW6^`yMkX^Y6}k!$`p*LIHI~LHEEco)D$lTC5v(v* z_9fmWP3go@?4`nZkb?$upw#ZalNi$9X^V5aFAQ7UoHIH8d-LG5Wuif*5R(NBwRiyM|r z;oXfvJ$?$`2Sa`Sc?RZ35ND2s`m|-8;i|59_cMlS-}2RTwUjE0;e}n$N@}1YC@%A# z33kAk7cO3b;7kaf1b$bD6_%y98TM-g2BX#XnR!;*!Xkh7%~ygM znx_NsF^8wvC4Bm z$syA5s(Y4A)jesUy5FG}krt|Z*j`DS1`X1}ppm6lkf!E7QrZ;nfL6u&CR(z+dNb+e z5;WHW8YqS~lPBGGoptEy|6QBD2iVxspc9@;)Aq&f`d0$EH>ZM2L)CYjp((AQ4+w<% zXSIT77q#<`h8(xgg^p=;y|COY>(>hU|9`joKdNgCE}zcRn%0Hu)~neyF?YTE3bkt0 zv@*LX=B}5kWw+hPP|Mmd+)5ju3Dio~-$>Pyi!EFLC<7M&3U3X#06>OGo45ed0vGTs ze!QSfTmWg}0@%8Xg#=&#D@%`#!cWwDpC@~5O2nT(sUj165@{v|8? zOhQc7a@K#J)E|&K$i&a&M@;@5lS51nFkx6Fg)Y`g9_LBo+m#KZX!%tr?KdU9Gy1@Ig;WYaAoQxBhMKCNtzbgql6SQS~@1LBG2KvK$vNPUx@MB-sn zduY{k()8Zx)b}@NO`G%-*5Wr%zalt(8(T<;Vyan86UB5}b4RG-PK%x)icT9EsSS<4 z->5aN(=$a~maQ3XtAfn7tz50=2r?G|QuRDR=G&I93iaJtue(LDfG3GZ!w+MVz281F zJlipI>cjFmQTgQJlKhmy!0PX;y}eeqft*H{k*T|k{KyG)cvL%bYL2z~^5m30dtl@{ zXK$a?ZKOlPb0XKB=_%{UWn?qSB2#zmpJNd}w0`?``;(OZBsAZr+nCQD0$A-Q$7Lb1 za?Nzj`(=6(k3@Ds&2voKbzBx7u3kSq{QhxWT>Wt1fO_yK&`9c#S3Ty{4vuI8C+EZ= zecuw>9{T~@=2<)5_d4CieAfLRN`6uz=}1j|Fe$h}_{pEd-IY;tI{w{k;R&Zaa zCNv&uxbuQ;V|f;AVXht>R9_s{j(Uyah4FhAu4+`;@F=u2{f_Na*6eT$Fx%zWD@%JF zPk3w}oMy#|UPmj2=ITFNdk3!!BqT6q>JhM$`H7S2=o$6gm^wP9owzv1I{ig^ImYq0 zZbLTPAu@IELG{p4?mvC#MQ!iU91kJ(aWT$AgUI;Y$kaVx`Vlhy;GnkW#W~jLU7abV z!Ofw(&~T{m&VX)1k*6b5Ul@}4$usKNG4rC}yXw;`KNnq#J?SK%pF|6uL=-~h;3JTNj` zEAul@hbijiE9%%4?absHkLmsPl9cpm+!UF*_khe#VHmS~@iL$C+I4l}x_0rEIo9dj z&pA>`g|cmUy6}DUk!5wyeuFr9+8|zjMG|;r1LBqOJQNN&j`95+cMLH*;TT~y>Nq1y zXZTW&!vKTqkmDTQt+?!k)z%GbdPl@CioJZvBDu-Wk}vu%!@%my60Wob8WwArzT*%$14 z<%sv_UdQSbPwX45+fYR43ud!2Q}-VdMRoL?%r8x->=fRO*(q2F+3XcKup?9K{b(Hx z=ojoYDQl){r^jddrYApW)om!MyZ6cbMevN}Q|HwSm(=l>)C(_Zr>@Mg?#Xt0S4!P9 z?vHF1Zwi@u6u)`qV~WUe_gCyjhXH}LI=mVE#zVYfrzbql*Dr%g@l0=?$(unW`48l1 z6P#o{et%+&dDJuKC4r`{v)%YuAUohVEK7%Zvz?A3%(gm?LKn!G^?kDvPCf`C5bs+FRb+FMTz<~4gdGdu2 zL8wet=z*a?`DV=W#D5VkaQW0Zc&lf!l(cKxPAPY6<7q;+$rh&}BK1h?&}kv_L^CHY zpFTIUkH^X{j*TN&Pb(bmggg9rD|c1H`d=1> zfD|nox_G*ko5Mn1Fy6`y<-|!wjpAlQY1o2wC%7`A2q8`G4=Uu5o8(L_&Jm09XzDsC zylqf^53Iy7+xST&dXn94f0CMHPo8ty?5l5N{(-ISfvxj_ZOy~9VxQ+;TCtW^?C%Pv)xNsqzc1-6wtGIc JF%{48KLPI317QFF delta 9901 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 goR=x^v1{*MgnTMFcG2Sw<@c>&ggCnPDn=~dR z!8vJzX_6w^v{4ReB2L>AC8Vv=^t8@N-aUIGrBc-7JCIu&qN4k!mzovJ}~r+P4^GiEThGj>qZsiCo~YCt=v?bMREVnElagEaL( z+@QWwPtsxr;s+Bt69x^PhCySek)*{ABn~EZCXu*iAbHT#3Ee|I?Lf+4YG*2m>ju&W z(>v2iJZ>OkFtan0#PtJNgV~+gBpyGIGnm_%OX3Lwd4uLoGfgQd3*Emqe2_xiQg8*v zh*xK!g4#eahQ}zzcts(m6_K<=NK2B_iq9y@lSfuU3Ej+dPfY*d!t~c3pMLGlnHQd# ze)974)vxHLU0=Ta-N$b|?V5Sv?DUhDZh!SBzkB=K%=w9%KYhMsd;4wIS8sj8srwXr z=qOhQRa^S3wxFuDug4ZtwhWyKYPKD>^$ibo4+Pb_M~)9z%ITnn>9$#HeS;V`?6RB~ zu?+QCwszZk9@{rvictR3Bxhx*#T8eejI`82SYjq|5bS?JGyp zq0IF7i_%Dam1@l@kr( zprwQ!oPX)kK!m)D{|2cSD4Uc{^fB>0%C7jUW>q28hW7MxW>s;IS&jcsnbic`hweJ734ga)HQZ%Zm4DT&%BvG* z)i`HXmG_v{#Q&68O}gi-CjH%JHTf>Hs`{&DRbJ^ZtEM@#s=CLlru?VOYU({_HTCZ{ zt7!|*YS^Ah^(TaFKw?z-;>Wc2*k#h~YMAK^yB5OC7u8ZdnmUtJo;{+2^G#qt+(*2^*PnI5n9+{qEB#Lq|Yh!QX!^N11RfCT+x_;7<-G8Y|yUj(V3qK{z_arqR{ zi4hY7!Ym6lgjsGfkH(s-o6YU5p%%mEBdC|=3mau|nTE9_l$2VMK?U&3;Fl=?iI(i+akosHv6 zOMRNgKzhb_{J+Pgf2>bBzxnLu$dz_)S{TgfftpdEwME zrzRhEuJxwXPN)JI*_YBTra4T{=S;)|lheMn{^MmU-0QE!O|<$=#k{H5@epsSb*XsM zl7K1QZ>r=?mClVW=IwQ!m}{GTri}qpzGDY(Dw){&u_^Py=4Uo@1xsBvSFd}6XT4_` zXWsImY3r!N-*>^%gX8`OCOv8@cp$pMKMC(n>=BKH_A(X^W!*xT4v8 z^4ba+z+=2=v8#bMEeip-*=>5;>REd2VV`O9{Q_8q0OrjBaJjdjiOXNXt=P<^Z}I83 z{wA#=1mH;r09X+fz~&ZD$+crLfI~2OE`~R)2m#pPZv91}r}y1TpQ+=10jxv-v%&y$ z40{Wfa`}y1;|4B$qfg)do3xS;d}Gcd-n2zg;cHsu?!8ti!`IK77P*ReQ)39eO>WiO zJva!TY193}SNeGpm64%E^kKlc*4h;1hPU?7rR!uYPps@++K#;W8o|#oay9; znp12(eiND=ApVaIvuDWX#5O`&kHph~I3-gX{}cAY3xMe!rN>k@+!x8!kGpHc1$-EV zT_IuEKy#?=ieu0)n`l?{%d|zPl0EE{vn!%f6F|kw)C5o*_h>KXiU=P~-2+n997tpB zv0}@BKP?EYOtk>*fH)BBR6xJbcBNgxs9#V59fETfaoIKfaS=)IctuP?`sG})in7TR zj9tdNn3!X&Fn4R1*nY$O7J$bk-o95dU~F zY#z_(&*;nJN9v%3o9?%SJzvB;CLX)>@>9b8fAiY+X1?+I%(*YmI4|G&;gi$n-u(3O zr$%EB96osJp!E=22DO8-@1Bj`rBW(1Hu9rhg~?G?7|^upMLz+>F0k? zUR17Ro1h9?j`ib**}kKF!15v|2#cHY7m6zL9CigR;ClGGgFSAhXk56|AvhAofGuRF zVSv<&%ID|{G>~t46?KOGf$|g$`~|wS;}r_-ikiA0ebS0+TRJXk(OJ`%KjGRmQi+aZ{ zXMs1pdOSW5mp<8gJE`hbH?oTY zso8=2;?H7LbqV9Gq{j`HwqD%&{I=`*s#ztJ`rJtA4dbW3zG`yO^|oxD0Ol)Rw7cF1_JToB07xbt6N?}5cm_scspq>2~r%knO&I7$is808D9G2FF8j)+tm?;-xr zm@4|-E$utNJ&BQO_RIVmDFygJDze|G!zsvn)PP+cKGayd3PMddC3KH^TARGn!l-a& zA}(oHg2=w+CfGyr0%MeZs#DPn~ z7;)Qxcp}6JF9G7oFKR>Rr&?mf~XOP;lq`x;BFJr3rg z&Yl9{B6}j>pt1x2b`XuQi;kt*Q-!_Ao=A47ICv(zqn+$9FdmaVIqIrSm3P#|QX6EC z2-~M{7qus|iA;Szehd+pJy{wNyzPduw0n$di9HG0UusXAD>H!h>2trhM_pk@Ya}f! z`zy}BcBIs4OF&%2Rz6%t^|W#!0x)+fQGfaOV~ zq$rqw+%1>Je&mOp1lx*WoX(s(D-bTz<6paV?J?-Py)}Yk8dUcUF_zO`C_8FfK+q}h z7lBg+0#1oBNS+MgTvE)IxObRKxNHK46X*Z|Sn~*e-R9drd;a$2bI>gzvi^V!PAMPP ztr?UErq4eIkb&aC%nPpyiU&>RQg#jGj-)~`{gY>>&psuv0;k`4cKY1o(-T+BYz^ic zAqeFVrG=(ZrP*9MsxC#YC%Xy?j4I4!gfbhAsRBM|lUbn9=<;$EyAAS5p%CdtSgPz^ zY##S{vjF+@xJw`f0k$t<%w?n<909v;eo29CilacbD7$}t34wIP63F;uADCZ4pfRCD zbdqr~)R0L=nCUS(#6t+-!y@sR2*e{rh)24koF>#&xq@8_O$3$j`obwl7N~e?v?)5x z(+g__-!}-MLA(uEeiYL2eW-86V@)6(Gg|nXH4f_wg_Av(${;Edjwxw$^l>eNn)0}k za9nmEhH;`9dK5xKegKVIOCS&i zntzPn**{g}C~}PQ1&dv~_=08b29N5^rVk6+AV(Z`dL~Uc{hu;Wk!T&jiWFCv$jgSU z0v4Nj1ewf8Jq!DW%-9wR-+KCqCi54{nnk@qx>)$qgpAv-e}Cqyj{`>>cT-`-lBlpG zNmN*SBq}Vj5tIa(1-S$66j!gAM^*UwjK<;zV{I}A=`yw%Ax1p9Z03SP1eq|^%i#kv zk1F81LI%+Y@~|UH06$)UYv#L;!e-b6A_<{gL84&3_55oyjwgh!0d(MiP-e3{l6-ue zdEp<$KF3`*uU)(OlW&I4OM7d1Jd2-1FuBWe+QxSG*n|bzWy8mI$O$nL$od!A4ShDt zpq1T#HGwNJ7>qgDJum{=9ZIXk#%_Y-@^pdQzXOZJkwp)yA9l>3wikH(gmmmH zXbi=>1ZD==V~~S^;sH=_>S8Rm?!E!oVZ((BAx#mKNC?{za3h7?342Dl0qHVm5FWZu zLMz=z1}s6H&@L)Sv0bDiiCrpkLrA_wkY<2-hny=)7of#%7Nj3tQbsUFfH9~=5Uqqk zf|4dUaiU&=;39F}{xDj)8@Gu_&7Yci1)qe*t<`{?K1$7+sI=@0eb4kcVx1}8)Y|bl zV2@;GqvlFpejqCclvh%89Vl zn1H#;*~ObzOo*Dg(uf1Lm?orWxk1B=c~2{OgWPvEvfLE79q>O#AjMA3T+&v&Przvx3qn#YATo82P5d`4j$uoZmFhk-mCLW*&oA1Kt%8W7+HV3OOoMB$&$RMyXf^kG zO08?Xr<~hzh+lh{Z#WFia=Ep=LSSE&X{Dm6pk8v^e|xHO+iYv#(~2H>1Uy+A<#Z zac0g7J$(K#Zs%VA&Tf8Zw}0nD{LY7bJNv!)$Go}6yqN>zhCuUHZu^ew%{wNJIkxes z^{$@lsmr;2`^R9JAE#B5D z*5NIqRq;7R#kVQuRCNx!2i;v6-YBjPl+^_) z>jR~=u!f3L|EPn(J<~H;=Gf>>t)7jeQVL*26_-wIh598mfwKBQu>~mb6Z6yD10*g>~f`^6d9)nqs(JN4T9mTu(oDY>4Y0=FG=^rW3y{ zsGW=nWaT@GF0OReU(c#>Ep=D$^=(sm-unH4>~ep09iLt2TJK)XH*DtWw@kH6^>TY2 zcyD0pFn8b}x9HG^*@uBgSy=a(j#{;ecBo#~Ue-FRedcA{wj&em(#tJy9CB=PEq3*} z4shlcpK0yyW2o%%kJEE6#a)bZ9CaRZ9&+cqx4KtzoBFt-eqZ{rP+pI7k7JuJy+KR? z$))Q{+|mtP(MDf-`)^9>C)Wq^;3K_gcUrIKE&kYC@^akeIA^~*)ve*GT0S(d{k@th zZJ6CaQw8<2J88<80h`$8YAQZi5Y`IscN>?!-KXDi!;lWSgyA}e!;1Jd;jlgpkqgLY z0}d)fe}@L%De4zh07eBoQaep3Qj`a&5VrzaGy!BmMsl0<^2fVc+FeHJA!!cngdJL+|Kn0FoTWJKDFaUIq zTDYTtkQ{~N=wo}~aUmIf6pVy|IkawvIv^@YNgLPwK88-)OpnI_+degaye(ibO{$-K z#8K>2f43@N%=8=cd1F3E>n+Z<%LCqm`VWl_u$oiz;iNDopFe%}^rX&fDC9JSWJQ## zI^Zf#Ky^`qVsr&{U0s8qpgLgb>I&++x=xIA4~RLjU0uv@PgfUf25?x!8d-ehtO^4% zH#k&wIR+>XVIRf-$4k&_(xEU1NUX6Kp!$UMj}SaYeWcp-dzD75n@y!Q)7$94)*XTE zy8~Ocf2M6#pQZ!lAX0YO-0M8a?u}O;;TNyx%Qws_G3!QEgKM|D$ZhqkbDw^*hF{Xo zS8bYAV;1Z#b<5l;_ij(2$LcA2^Dw_`3tzi+HkM>+D5%-$Ho1E|_3nq>%;g(4@|Ep( zNPn|y6uIhyw6lJe!n>t3h)*R?-Cz-I;ub=z|k#NMj*7+m<+s7N1EP=x@(?23_ts&-#exc=D{b zXf5#(I_3!r<`@>tF)o;sXi0!R;w%w8i8|!KB?Ap~L)bVAm}F=xqBLW&7(ve*M--7m z#{T~X#M3OhraQ!T>63MKDy-(roL{AmrPGZaecj}p#@}i)AH!A3h79S(Xub*#515|ZT z)7{qv_AH)^Bmn4+<$RPzLNl_21-rCts3l#a9ak@?x4D4 ziK`8<0dhedgx-Yqunul{sbSzRxD}?Vmn7{lr`I%XX9-Sj5%X z^B7EEfU}J>J`m1bsLcUn{V>ZKF*OlDK)a-{bmYj8WEx7V9XTFHVk<;~u*)oAEaOwQzv zpc>82z>a5VWRP$stOBRftsWWOqVZ4+zoZ3m55E;qOiWJ|=->X_GUf zsVOkP$^WTWYAaoH24UdNJcl~N`MA*-c$=q|M{)nJ#{lkG`N9P)TU z2&^Z@NXC1NI38fH4Yeu7m;}Zky)hWS(ViUfMHu=QNo7NKdy)hTkepA0ur2}Cy)oLH z5@C0uDD2janeG8)lHCYUBu8rr(m`mY{J`P4T- z$RQN??W13we(78CBN!Eb`sjt}^KXD;;L~q?V`luh+vmPI{rr#Q{tzO0CxPjWc~vl5mjd4rGijOE8|Fjr$jlkL1UC0=R^pL zhY2xnVJ9(m5rZgoI{ttKLD~eXQvgGdHVNyAh^AygcSuGj;vt!JpbV9$1hWSajNKUQfq;nD%z^+=5N{DN^AXHKc7o82YzQL?41;tn(N#ua zp-@7QkC48~$1x#Fy!E;zX ziJ+sR$WB08*?+){ED-@lb85hctzD=3Y!7t_C4y?u6b8%~H{zgbWCYA>)EG91>cc(5 z$IlRMRZ!VIbcXH2-WFp(*nT)TfMxhpPy_0}y~Gtks(|f=7D#B(kTtXrav0PLD^Va2 zz?%Au737K%m~kV3flCjoli_w)xa?k^@#qbGmS11M z>kAysALBfwpXE~Rz=7qmYkySe>iuCQpS{YXyq>*|+r0-Q2_exZSG@+r5uU_f z)O$|8yTVh=ZF_hY)iAcA=%_=n18;VEC*F1{cHu2$H_DT=dlc{z({f(ef9c@GgU=uK z=QQv+4X!S4P8*lK-qZbo@`KiY+w|V1_qTXAKEQPzM6t}qL)?bLoT+PED+_CvpzOt? z`=Ea6OWw_0+@=RPQ@2lZBw$FNtmO>loThxXg{G1i`Y%=_a=)@JroDoGS3|d#s@~PE z&w~fyqXAD1wqfGFqemuipghJ2(Y6T)BT>(UwyII*95e(KsBIEW3c{!uwS=&OUNn3Q z!WB*C!Nd^zTd>h#6^sV-@6bjiOmh#=^>B#9mO?r^K*H$ekT4p%lC1+BW|7p8`o=pW zYkOPsKo3@rbgUhGcn~CAe2!W#{)*TdlujU}RSWhZBa6_7O76oT)DoVf_F)X`BaxI= zFW85S+Cv{|xsN2F)?NCL^&DXwCQ>?P!9G$Xc%TpPa3aoCicsq=eWXhDppP_CI;!nd zG|MTdkuI$pLfE3#dUP#c#g4_k}K35vMDLL0q7#D)`?Ax@o5 z)-3MF6C>i1NEmtav3*o8Xgb2W{zyxO^^5$;h>OW2{bq&t3*(lV7op-&wO2JEE#5G8 z03N{+SAP_89&~^;ge4%dCggcjhFAho*K0PDBfYCJ(4J;D-eZipWQ=)p#+cAAlXD?Z zrI~D{&Ku7s_9*+P9>pQxVv?~LfxtI{o;?tX#FD{iPkdH!&Zwfs;>IA4F@yelKCZf< z+%S0ZL#<0OMu4Xv93Cb|s?8LK$iMy)sZQuF!Pp;#ZNQXBYXH_#>EF737#&k460FH# zBZ;xG?kDkAL^} zS-BnTTt4-HDxG|NdgA*6O@eB*KR!48+T|I?T>asDlAl7qFbu%>fgZp4*3;9EzYV^@ z=65VOMha1l^C{E&ZRRUqQbayCZ7M zgk8UDqz4>IS#Lf0&ohsHIecD&^rA0xi|ZCQRM$0D*DZQQAyA4h!O{-VjBF=7{sAp= zMdpmq1Qq9joJHc$3f|GK?$Y$1w zT_MrTzJkF&Vhxc17iLv#>KmAmAtXg>=P2gKp6X_Yz^7YK+jhFgLfrhZc+9guz~D6q z%1t4)8um?0#qBxLbB3To+m4#G?2j?ug~3$}-i08TEWYqLly#zz7*1a&kR+x{1epB= zR;G`!>&V^0z5#cu2?A-W4!;Z3u`zP?MsuQ*$6_DjfJ$4BD@FSFvl7af63ESWt^{Wj z<83!mGA}&v%mb6BoN8}M&A3h`v`QSKjsaISzvnOyv>VgQtv)>-qe8SBEcuU7)(jy@;<~HQq7V%-pOCo=3d3doin$*H?WMm+N@MU%H$xUCuRi_)510>eqWt@b&FjpqAIy1xgW$(pCP_ z7QPf5T6jw#YjPQ{FZw7h)6wiNT*4PF;g+uV6>bRBHoH&owJlQHfwD$7&6hR#%bNMJ zX7`x4Y%5e<&g+X|20~Q+R=#-a)DU<0sJHkbiHJvYX@7bRpI+ln-^{0Pp6cKZ_HoBf zdecw&lTMAteq>0M==iKk=O*V$*BF=4=F_i_An5yD1zd6ir*FXNO_^0>#Mg-wHkVuD z9CZ$GO?$cQeLmxU0I8|jzkCb7e2ag17r(rVdvJ(5e#*Q2G;drAggIBd6pEBp`AeJl z(k6fDZoYIkx91>tcmN3GVL|<}Rk25L2h`{HD*i}O1$z|}3821}U+Xt7=FN*;t37RA z^QMXTkCM%jHe#u>!kNu2*~ppO!NbOlx@Lb}D__^@t6T5O?c(d!zoK%|&P7xCKq_xK zJbA*Af6>O(t)JMy=XM2Z7Ef%L?6_{KL>R-H{i6N(NBnt9`8?2D^yY2gayL%V9~6GT{9FHf{qGNWHyz*(9_9`m<~DV4 z?MRi&L_6qG_fH<=%G;;Pr?NQH9-n3}lIsKEUO4a+bacz0cTxqoQ^qb~54D z#?rsc#zKYvu;Rh2%cJ zjK(*R+OnpFM8o}Y;VlU9a1V=i5b*$?1966ki&6AZ684LFqU@5eR-;g;42W?7YnC-W zh!{8uj*Z$U;44HND^O%A!r_o8G7yRH0$+@%mC6o-g^wC(bGAQ3X-H`{+_}jmLCJy+ zt5BYa!CFHgRu-PYql+@C_*pH_Dfnms%kG${Ga(Cw(Jv(UOwka^AGkV<7Gw9A7s(4F zdP&oVTSbDsyfn)AD5E543yhNFg^iLtgB`sxIt?w4Qft@3D78^XDT|?rHNAonxH>u* zrSU#SnP}I+_=vB`h*2iVqa@b&5$}?uEig)w7dA@r3`!2lsG@(F{{{B`dKjfX>L_I{ z5vUdDVQd1YC>~;j`*V+XnM&;Rf!i|gU8c#SOpEd^N!kLVBza+@B+sC%pp44V2)!lb zOElUIQAa6@l|gyH0AtG_E7l0H%=>tkSwsjPN36W(tXTXu38noZP}ZmoL@uDLi^mef zW-b~>B3rQWWpZB4mA+S+0!msnmiR~_`Q}M1f|kx30gu^XwDeDY281UF{brte4eUR~ z$alUy{kCXnDiO%%ZoWM^{rLDzQhvU`P4pgo{weS?+}ezpv=Tx!hG$>T#M3X?o&mSwJduCf6E4oINxT#5wm(H~2Jq9p;CKNgXpk z{HD-MIJ87yG=g-TkSYK@Elxz#*#loaC>8@U5u{0BB;wDY5ke;t+UDl9r)6-!x#R7h z{6r9{3yEM4cnFIhgdi9vk(JmUg2H+*K)P~)rUdqZ)D+0nh`Z%o;B6V3sKVOsV1PKl z0!;{VZn+7aNTFhVLY@8`>p!}1eL?Om)gLK@lJjSG=E6VEr44tpzl4J2i7Xz?tO)}m z6V>-wyJV9+L16k{Fq4QR>mU})kSRYxu1U+P53CM!eb&Aqs|_WFLDc}dR|gp$sP+*` z{>T{+CJ>>0gKVdoc zLx9+t1VK1mC&UG9#rGg@v}O)LCr`T|#hp)Z|DUiQSq*@_5D6$4C}!qe%DkBQeD-+4 ztZJEd7kx9M%xUtMujI>DdNWpyZ@Q73<4>;OlPi45RY*}JJOi@4-9F=HL#9l6v`@--8s#kyPs9eYOZ?d@`RtYMectT03GGM8 z*#UF;%kh`voxNW3l8N@f3IYSn$+AE#&dxG06%2!Bxu@#&C zFxitMyrIaM;?K|LWJUF?>v69b0U~=lYBS*Q1Z2Xat30)ws+@H6C z&s*W%1Vq_{{-fl)Kw+&PCZMq1wbfg=c4AAQah)_Jfx49d&T_D`3zuHyftE_ZLhN>Z+nYH0G#{whPg^d_|O0wZ2j*S?x21Xo( zstkF)A44jm6_kQ$QTnWn3aRWV3(Cdc$A}YvZizNxxg9cM@O2MLwDK$f6&n2=8^T7c zv+L%JSQjzkI2dsh3K=Am;#wjUM=Q`1v_LBX?GX)9S>+bW5qY-l?(aDHUhR+(BQ0`2 zBQ}MN82pXR8F75Xh|vNx3XLC9RVGr=R-i3tfo=jiB-)5W@^V^`m)~Q=>Gx`fj2OL7 z%^C4MTFsE&6RZY$7fJ3g?d{|-ov1Y}cgOpeT_ROV`kf#T{<9~UYw zL`xumA+!#Jg)(_1{N~SV-@f_Nuge``=`h3(6=RW#g2ss)@~?ABBmj0dItP(^QOT0f zauTT`%u_gNBeX@F^$1i7Ay8y#)(A=(1ksMv#@*=@;>?HjK&_D6Yr#h5QYggBk3f*L!hHUH&h_U~U!GFczw-|(|heUyTAR?Tbm^F>TUJPb1xP`$!3_iu+ z|HfcH2LBC%|Bk@}82k};r&%|vs?v7TH_{6)87>-}aju=-^kw7i9~sg=&dQxU<4nA0_hwZ^$DPBm<=2N^ z8FC-+*0e{W&8?o!UmbquFt@wQ+x8&W+Rb$XmUo;xdV)K_a%QW~WCJ8IyY$lPi>sZ@ z?#14$=J72b8L~dk&39BePh76`<}QwoEziM@{f!&>#*I_C-p2hBRw(0wt)4^vw!M5C zAcGcfTQApolsh`e4V~czN4e35IkVko8Uq;eDlQFQ9Oi0Pdy>4lt>fE1GUS4{ipgPT zH=kSQ&0G{6MV^BczdrWLn0v3c77*pR*m0}pfnObZ=McB+0dHF;*Lr|E07OY2*WJ(c zALGmeKGProkX3T2_F}EG+MVRhTq)2-Ic7(ebI0XeZ%$owq<9Vz?5|tHgOn@BTemF| zA8z#=^tbInFD!d|ylo8E3a-YFaRVp0W2d-Nr#bT(pK0`_;2@t%Z}91tNSN}UK93bK zJ9nT9QRD|X{exjBayOvJwXi(DlG?7OFK$@A(L_xpDIh*&(zYv< zQ#qL%S1G3&V=!fv8dBaQ$MAL+%z)!T~FVxCl)P_E~KGeGKMmEJP?rl*fi~#9UbXDh3<|?_xl73Xm!lPbcAA z!iK>mNSI}KO{8K$xHSWsSy|tLIs%U=ol4CT6$iB5l*M6+1I_MH_rO#&XWH-6JOG8)(>#o4|1kMKF#5|65eK#K%6+Dnxnxlq|^Zix$yK@spEp26U)UXxsq(Zf8P|8*;pp+d0TZFi| z-BT$WeqfCd5c1D&Az5%Yfhr2tK)9%6bV&MxBCnuSt%5X_oTewly&BTgavC9Pf&ABi zlCe}*P_d4#02`I81|xcLWPgL&utHQE7RAZ1EgR-+%OJ{%CYENvJ%ZHhm>#S$<3Jo+ z17+o{7bZfyu84O~X^lUh5TVi<4}z?C#we?_#@iEQl~&BtgHkFXg~8@hCcW;qnMV`j zGNvoy77y<=#fC45Tp^bM{n=7l=q|x6)nyD!s(3sY?r}V%*$vQBI$1{<=(IchhbMq2 z2Gvx%zE_39vU5Ka6;cz%;!g>VH#0{n01A0)oF=N6!Zv0un||Y;K7HaTI6cbc0_-&? z&c2PoPaq)bd|;IZTP6JFs!$Jp0YYu-vkh2;U1&F!{R5W8ITcmvNH@YhDEd`|(@Cfv zMlgA>>N^5Phn7s%@c7SR4r>*vry>PwP$C( z{Q~U7hRB^5UklM{V?V&yH!%1=FgS|A-FIhW*zQcW;{vQxh(Qqs#Tbyynh+)O1|EzP zRmD)$KnBbDu;@Ar$d+z|n7F0OH$g>AR6i6Iz(zCY?DB$AnwV#Y&)ABqjcn_m8Rsa6 zfu2&pyohTC*KXU#H-uEhB6j?8)B|#La;7ytO$%)3A?d?Map@6yV4&?4sow=kT#;&9 zpqv${j&-BBJlwDfRjPWt#f+$ObtBvC&#vXOYyH{ne0KX(DYvVe>p9M`W8UmX{Hc$O z$K6OPxbUTCzU26lTkTD29@pQ9%LF%qapi%kMt{{RzG{`PYPBzOKVP-_a;u}oal*4` z%HUnMZ*tLO_r(UTYW4VLK68JdV(Iwi$pl_sdLuRa?_o(4I<*cCaD!*KflhW841^%EI@Wi56T05$(Lz<}l25s2*omJ=5zaZAlpCGJ8|EVdlxy%u zJi~t8hEaunz%Lq#F70m7P<1e13hkF+XnE+Tb)xKV>!?NRarzPQKsQDb`egCB4J zF9}0np9X#&qPgJ*qXXF?{F5mFaayQtDF*KGHLvQWlW!iZN@!gN{|t5w+W@Re`J|ER zhkhXGE(RX)@@W_{@JK=f4||ebe^xL;GT7sV*tvc=2kL<9mBDWy}ydW5-i^ zX(sV8FxY98e7 zm611<49ZN&@?0K*A8e4jjVe}2Cvz39#1=MBMD?YUgt6gfCUD9(0aMv5H4RMV(m9?c z@C1su7!w(5irob720Tis-rFT zH-99b=mI@7^W!hixPEl&t)Knw?QaPw;`#qyzxEGvOa#oB5RQn*t;>=5oMt#IE$H&# z|99v>e(y7iYY6EmkS%cD_#s#k2soE8+cQ7--lzZg5-1VfbU!T{?A(0oA7-BWUby)O zzH2#KY7Kr@Kxj&Q*%$tKzIBBB`mhE0;r@tv!6^ee;t=d8$WIQSEJ8q7C!P*Zm)jmG zft4T#)Dx!$s4*pdDp8yoM0ZcW1v>jCEmEx1p)9nFUVu84 z#l>e8E`y&vAiq6fCUZt?a)>&dAZtK`UzlUEJcxUY5EF-xJd$5|VL6dv zvcO^HvGda}JtrB}2#Qn0_UCRd1{9Q#r%H@v?oJx8ZY<}-04JCIH3mpgz;D)~TX>>= z^#vxP`~rSslI^qg!7t&J-8pJqpTC3iDkM#tog9? zIxKCJe<)C-7}!5!-iH_<)v~ZNhcyB%q7PpLqZtdXkMHAR_$ma#V)(p95`Ltvbnk&5 zVr`o0n>xVle~9a2xreNr$>!6HAos}plICUoW&L*({N_g9+~_uV%^SJA_Nl}V>OZjk z+vt0v@7ukb!54TJY65M3klWPFnT~*ec3|V@`7>+y%o^v2tIv}&Q!lQ^O)as z?5uX29$y;ia3PSKJ$b}YAbDDVHcYAG;M_cGJV9`$kdb>S<6?%R(b?|JSPFVp1ttE1 zM!ulYt%l|c8odR({Moy}4|~h_iHXvGH>5)gOIN*d=<1;#b@>VpUhR5iw{wS!o}#A? zdp90HGURfrtBW(5`ND&N(nj!for&Jks~aW~CP%LuE1-H=Ev8;K7R_d%7X>pIid@v9 zh7A0I?$zZys8y{!$Ymbl4hjCYH=sv^F?u83GTMow?M(`hAqCRRjuK~?tAk5h%Nf_+ z5Fa;kX)8J7O6Uh1Mt#*p%+Yj~0R{#%tO&-u6*)x};DB zPVy>T+1#pwT;3s{@o>PHKC4p2pP~cRi~QBA`Rdgkwda(#dYiv=8w_fKo>&B4#1{ps zmq~+5q>36omt|n^1FXwkm7J;Br&*2UQHi6>*})l_I8D=R7fq!Mfayj$m9v}v3y?`g zhv?Zls%$&`=bOpdpfv>DB3OoJ42}xH(Buf$!*EAX6^lDDGGXu=Px8i-f0}NoB>gfyWkZGH zm(}!!vY21iBw&1HKBWIDkH)Yd2E#HM(tlN<-I%HVRejFJ6!nx*4e3)UYDk&NR6@L5 z*YT+uAV4?Cc$LR=tS5I(2df4u3FGV6m#4Ag_!XYT;7JU=jzK&IIt&spFk^spj=(j( zgE8E;1qaI53yZ8_vKIwaM}~(7gnbD05LuL63LI#%4XL2-NUzFIVG#_BkZJu4aNNhh zt8(6~DowqHcBIWxcz0I|ckY4SJ0xLtF|~`{O$VBq1FKpBYgz*<*MQgMz_wk1oqGcv zJ3mY8q}3J9Wv+FvteT}TKDAD`2a2kkyIqB^Jir$<&MGnIMk)NVvuoE?Ef_Y6PqL@Slm2E0 z-?*8t*)khXG83r9P2hLg=2dmucOkmEmpFTu$hhVGe<&J`6ZOtB<2R3zt z`K8`Xt82j5&-L6;(MHx^1l+v^8&7oNe9;N9T;ZC06PmBVygg*+-UmyoIvlc}g?rG~4 z?ReX)=n$Sd6rW+#>~;icNUi0_~8eZP@Bs#ddr;O>f61mfitx*3qCY%ReVmm7W}E> z9#~#N_TXSFc>RZ8S3XKCn1eC4Gsp2EV%sH~GTD0(xn<^# zSxXJzUA=urg)Kb}OX^8UlGoiw_WNM`hz)+R2Y$B(4CniX$<7~4v7v0QMo=0TMSI|m z%z(AigggOS6ABYNz$)_{&adhnH;^&+Vgy@cfB7g=b* zoH=bEdz+Y!OJz)0MhhkY@LLE+!G#(*OP?wKA~zp zp;mrEE%}66enXo%p61tP^4iSFWnOL3qcMMs8CKFopHUe8u{ZWeEWP6Q6ozC8{4c#O B9(Di# delta 4554 zcmaJ^3viUx75@Kyzc-J~bMx2`NU}TcF7usnHrCMyJQ*0eg9H&aP_0K>@{l{ACh}Ic9vlXXx?8Dx3H=CrW{F!`v z&pG#=d+s^+o^$W}PiS5|$2(rL+btaZ-gy3jfjxIUyJE9L;B38dOV)NS~ zcE6qH1YR(N9TBJB$=b$nP9)c#%i5-JUL@b2&)Vj&D^lPuU~NmdFjC|%Vr^@z5@QxxCI(-JIhx>PGbT5<+fks=ZiMcEZX}tCA6?8xw(IW2leq~P|>u*Tu$W$qXuskj5Q5t z(j0uyl!i~2|F+If3aMsNgiws&prAVC*pL(o^ovoE1mzeZPIzxc(Y_p%IYP0hEJo$P zU_$5@3CUy3ArgxW$$_{?4oM-A471;>!d)X(*4c5s3bCfC$x1{c88s{CqGcJw2hV7}#etJzY@J;FVvOF2Q& zSLR!jPC7;-*Jfu*I$%j_QLZ!Tz-d-;-?{L#c9{3t#&@^=ShLhZ>S#xTy~zTE8iZN2nO%_yH7SLu_S>jmuPJA|VN?93ZJIT9b^&ZXY-L|rz(~gmiBHj@k_c z{7YVbL%!~%g&hkiK~{}hHok3Ir0S)pAdZmLXz&&aW7@8U_5>b)>(3v({*#j*T>kaP zM^AkG)L9SwyQ^lm%5|x>xI7vb1F`*sVn`-<nLP*13UkL z>D_?2OfAWc)St2#iL1upAc;!Re$~8TBqR>WQY^~$3u#B~9TdE-%r04l zj7@CPo3{i`Wnp`<6N98W#OOefx>U$)P_hAsZbaw-b=w+O00-~~RQ{04i{uVU#a$4r zEz%GVY}nqUImnG4-tN@#7C3qL4|fWhB!7q_$s{kZtV@PvI~~HlEKkNP$-ZoAC#I}S z&|aui7m()I7R^ZlUQ$$Li)RXl|eNrRnu!bb{FnPEDJcrE|$}%8JHi1%BrW1@a zGMi*d>M*%HrqAl3CCM^lss$&#$yCPs22+`Aj;V}J8Isz;BFJsYch985lrd?f)*7j4 zR%R{Dd(_&$iOgF2Tx%W4tjr-AzGAC$##ZOo*vfd{U@Mc&v6ay&Q&J1=*=J_<{kf0Xmp9kG{A5<#WqY0%n>OP0KIL$|=8Su6@Nx)}d{t1NEUPX}Q^xNm>@FIk}7moPG0YB}mz1 zOuP7jk}7BsN|H8OWHdtQ&)nH9%5&YEP?oZN%Z3^n9<E~lrmI{us?fc zfrEjfh8fcXw4B+q>j=P!16(jMc+` z4-|s0x47aJO2*%zrQxsK=SR7;iz}^sqV)TvkCZ*&Nb8osXzz0PXKyDY!wcqhOfol#x-zGj`2&7;^V-hNF=hs(1A(t&^?Fw%u2Kdz6lDr8}VX_A7w_Wk6Cq zgV$UK;BngZ%Sc^A$Hba9eLJRnJCvQltG@k8M@R`t%Al-B2_8Zc1+k2V0#`nc^AQQw2|~u95b+jx#a&ACJomeIZcJ^va#56{h22}Ja_q{ z-@u2DKicf^Iy0MlcF{@Nm0i-v0JbnI5D8%r;nt3rzL2p}%S@lHw6xw&UmsBia||Ar z*}|$?nc)IE8Eh$pV23!u9ndvW!h2v~q+V0TkDndMG4l;@;okYRGx6&pczGkYA!OV| zTV9@QLPjrqcJIr}kljUD7-O$i)zmLld70S~*HV_pbCiWeHLt-}%E%CDgX_oYHTUyS zaNje05MI8o_QX)LyFkaX3`9&T^wU~P{ zPAQ}}nquubdZqd7_+rgQeth1y7ieuyQh68sHg5D5LS94t5qv5UGXiETQiWhZpfRVn zc8MxR_Yxg~9sv&m^N_0BAB%+v^A6^<%u!>0IY-G09GQn)4?;l}@Jz-}QfBZnYQ5k5_SDjyV$#Fy2=N%hlD^mu*d<{n^AJ9yz#IsE68 ziO&PW>FSPrRAi=)gU#&8NS>l}rpU6uo=0*f*s5twguY0^VjKA-74Y;wz8i*4SHPLm P%bde{zU5O6k&XBt0N|cy diff --git a/api/services/poster.py b/api/services/poster.py index e77cce2..ce34516 100644 --- a/api/services/poster.py +++ b/api/services/poster.py @@ -234,26 +234,37 @@ class PosterService: # 获取模板的默认尺寸,如果获取不到则使用标准尺寸 template_size = getattr(template_handler, 'size', (900, 1200)) - if images_base64 and images_base64.strip(): + if images_base64 and len(images_base64) > 0: try: + logger.info(f"🎯 接收到 {len(images_base64)} 张图片的base64数据") + + # 处理第一张图片(目前模板只支持单张图片) + # 未来可以扩展为处理多张图片 + first_image_base64 = images_base64[0] if len(images_base64) > 0 else "" + + if not first_image_base64 or not first_image_base64.strip(): + raise ValueError("第一张图片的base64数据为空") + + logger.info(f"📊 处理第一张图片,base64长度: {len(first_image_base64)}") + # 移除可能存在的MIME类型前缀 - if images_base64.startswith("data:"): - images_base64 = images_base64.split(",", 1)[1] + if first_image_base64.startswith("data:"): + first_image_base64 = first_image_base64.split(",", 1)[1] # 彻底清理base64字符串 - 移除所有空白字符 - images_base64 = ''.join(images_base64.split()) + first_image_base64 = ''.join(first_image_base64.split()) # 验证base64字符串长度(应该是4的倍数) - if len(images_base64) % 4 != 0: + if len(first_image_base64) % 4 != 0: # 添加必要的填充 - images_base64 += '=' * (4 - len(images_base64) % 4) - logger.info(f"为base64字符串添加了填充,最终长度: {len(images_base64)}") + first_image_base64 += '=' * (4 - len(first_image_base64) % 4) + logger.info(f"为base64字符串添加了填充,最终长度: {len(first_image_base64)}") - logger.info(f"准备解码base64数据,长度: {len(images_base64)}, 前20字符: {images_base64[:20]}...") + logger.info(f"准备解码base64数据,长度: {len(first_image_base64)}, 前20字符: {first_image_base64[:20]}...") # 解码base64 - image_bytes = base64.b64decode(images_base64) - logger.info(f"base64解码成功,图片数据大小: {len(image_bytes)} bytes") + image_bytes = base64.b64decode(first_image_base64) + logger.info(f"✅ base64解码成功,图片数据大小: {len(image_bytes)} bytes") # 验证解码后的数据不为空 if len(image_bytes) == 0: @@ -262,11 +273,11 @@ class PosterService: # 检查文件头判断图片格式 file_header = image_bytes[:10] if file_header.startswith(b'\xff\xd8\xff'): - logger.info("检测到JPEG格式图片") + logger.info("✅ 检测到JPEG格式图片") elif file_header.startswith(b'\x89PNG'): - logger.info("检测到PNG格式图片") + logger.info("✅ 检测到PNG格式图片") else: - logger.warning(f"未识别的图片格式,文件头: {file_header.hex()}") + logger.warning(f"⚠️ 未识别的图片格式,文件头: {file_header.hex()}") # 创建PIL Image对象 image_io = BytesIO(image_bytes) @@ -279,16 +290,16 @@ class PosterService: image_io.seek(0) images = Image.open(image_io) - logger.info(f"图片解码成功,格式: {images.format}, 尺寸: {images.size}, 模式: {images.mode}") + logger.info(f"✅ 图片解码成功,格式: {images.format}, 尺寸: {images.size}, 模式: {images.mode}") except binascii.Error as e: - logger.error(f"Base64解码失败: {e}") - logger.error(f"问题数据长度: {len(images_base64) if 'images_base64' in locals() else 'unknown'}") + logger.error(f"❌ Base64解码失败: {e}") + logger.error(f"问题数据长度: {len(first_image_base64) if 'first_image_base64' in locals() else 'unknown'}") # 创建一个与目标大小一致的透明底图 images = Image.new('RGBA', template_size, color=(0, 0, 0, 0)) logger.info(f"创建默认透明背景图,尺寸: {template_size}") except Exception as e: - logger.error(f"图片处理失败: {e}") + logger.error(f"❌ 图片处理失败: {e}") logger.error(f"错误类型: {type(e).__name__}") if 'image_bytes' in locals(): logger.error(f"图片数据大小: {len(image_bytes)} bytes, 前20字节: {image_bytes[:20].hex()}") @@ -296,7 +307,7 @@ class PosterService: images = Image.new('RGBA', template_size, color=(0, 0, 0, 0)) logger.info(f"创建默认透明背景图,尺寸: {template_size}") else: - logger.warning("未提供图片数据,使用默认透明背景图") + logger.warning("⚠️ 未提供图片数据,使用默认透明背景图") # 创建一个与目标大小一致的透明底图 images = Image.new('RGBA', template_size, color=(0, 0, 0, 0)) logger.info(f"创建默认透明背景图,尺寸: {template_size}") diff --git a/api/services/tweet.py b/api/services/tweet.py index 028afbe..e07d3c4 100644 --- a/api/services/tweet.py +++ b/api/services/tweet.py @@ -10,6 +10,8 @@ import logging import uuid from typing import List, Dict, Any, Optional, Tuple from datetime import datetime +import re +from difflib import SequenceMatcher from core.config import ConfigManager, GenerateTopicConfig, GenerateContentConfig from core.ai import AIAgent @@ -24,6 +26,249 @@ from api.services.database_service import DatabaseService logger = logging.getLogger(__name__) +class TopicIDMappingManager: + """选题ID映射管理器 - 专门处理选题中的对象ID映射""" + + def __init__(self): + """初始化映射管理器""" + self.name_to_id = {} # 名称 -> ID映射 + self.mapping_data = { + 'styles': {}, + 'audiences': {}, + 'scenic_spots': {}, + 'products': {} + } + + def add_objects_mapping(self, + style_objects: Optional[List[Dict[str, Any]]] = None, + audience_objects: Optional[List[Dict[str, Any]]] = None, + scenic_spot_objects: Optional[List[Dict[str, Any]]] = None, + product_objects: Optional[List[Dict[str, Any]]] = None): + """ + 批量添加对象映射关系 + + Args: + style_objects: 风格对象列表 + audience_objects: 受众对象列表 + scenic_spot_objects: 景区对象列表 + product_objects: 产品对象列表 + """ + logger.info("开始建立选题ID映射关系") + mapping_count = 0 + + if style_objects: + logger.info(f"处理 {len(style_objects)} 个风格对象") + for obj in style_objects: + name = obj.get('styleName', '') + obj_id = obj.get('id') + if name and obj_id: + self.mapping_data['styles'][name] = str(obj_id) + self._add_name_variants('styles', name, str(obj_id)) + mapping_count += 1 + logger.info(f"添加风格映射: {name} -> ID {obj_id}") + + if audience_objects: + logger.info(f"处理 {len(audience_objects)} 个受众对象") + for obj in audience_objects: + name = obj.get('audienceName', '') + obj_id = obj.get('id') + if name and obj_id: + self.mapping_data['audiences'][name] = str(obj_id) + self._add_name_variants('audiences', name, str(obj_id)) + mapping_count += 1 + logger.info(f"添加受众映射: {name} -> ID {obj_id}") + + if scenic_spot_objects: + logger.info(f"处理 {len(scenic_spot_objects)} 个景区对象") + for obj in scenic_spot_objects: + name = obj.get('name', '') + obj_id = obj.get('id') + if name and obj_id: + self.mapping_data['scenic_spots'][name] = str(obj_id) + self._add_name_variants('scenic_spots', name, str(obj_id)) + mapping_count += 1 + logger.info(f"添加景区映射: {name} -> ID {obj_id}") + + if product_objects: + logger.info(f"处理 {len(product_objects)} 个产品对象") + for obj in product_objects: + name = obj.get('productName', '') + obj_id = obj.get('id') + if name and obj_id: + self.mapping_data['products'][name] = str(obj_id) + self._add_name_variants('products', name, str(obj_id)) + mapping_count += 1 + logger.info(f"添加产品映射: {name} -> ID {obj_id}") + + total_variants = len(self.name_to_id) + logger.info(f"ID映射关系建立完成: {mapping_count} 个对象,生成 {total_variants} 个名称变体") + + def _add_name_variants(self, category: str, name: str, obj_id: str): + """为名称添加各种变体以支持模糊匹配""" + variants = [name] + + # 去除标点符号版本 + clean_name = re.sub(r'[^\w\s]', '', name) + if clean_name != name: + variants.append(clean_name) + + # 去除空格版本 + no_space_name = name.replace(' ', '') + if no_space_name != name: + variants.append(no_space_name) + + # 添加简化版本(去除常见后缀) + simplified = re.sub(r'(风格|类型|系列|产品|景区|公园)$', '', name) + if simplified != name and simplified: + variants.append(simplified) + + # 为所有变体添加映射 + for variant in variants: + self.name_to_id[variant.lower()] = { + 'category': category, + 'id': obj_id, + 'original_name': name + } + + # 记录生成的变体(仅在有多个变体时记录) + if len(variants) > 1: + variants_str = ', '.join(f"'{v}'" for v in variants) + logger.info(f" 为 '{name}' 生成 {len(variants)} 个变体: {variants_str}") + + def find_ids_in_topic(self, topic: Dict[str, Any]) -> Dict[str, List[str]]: + """ + 在选题中查找相关的对象ID + + Args: + topic: 选题字典 + + Returns: + 按类型分组的ID列表 + """ + topic_index = topic.get('index', 'N/A') + + found_ids = { + 'style_ids': [], + 'audience_ids': [], + 'scenic_spot_ids': [], + 'product_ids': [] + } + + # 提取选题中的所有文本 + topic_text = self._extract_topic_text(topic) + topic_text_lower = topic_text.lower() + + logger.info(f"开始为选题 {topic_index} 进行ID匹配") + logger.info(f"选题 {topic_index} 提取的文本内容: '{topic_text}'") + logger.info(f"当前映射库包含 {len(self.name_to_id)} 个名称变体") + + # 显示映射库内容 + logger.info("映射库内容:") + for variant, info in self.name_to_id.items(): + logger.info(f" '{variant}' -> {info['original_name']} ({info['category']}, ID: {info['id']})") + + # 记录匹配过程 + match_details = [] + + # 在文本中查找匹配的名称 + for name_variant, mapping_info in self.name_to_id.items(): + if name_variant in topic_text_lower: + category = mapping_info['category'] + obj_id = mapping_info['id'] + original_name = mapping_info['original_name'] + + # 记录匹配详情 + match_details.append({ + 'variant': name_variant, + 'original_name': original_name, + 'category': category, + 'id': obj_id + }) + + # 映射到返回格式 + if category == 'styles' and obj_id not in found_ids['style_ids']: + found_ids['style_ids'].append(obj_id) + logger.info(f"选题 {topic_index} 匹配到风格: '{name_variant}' -> {original_name} (ID: {obj_id})") + elif category == 'audiences' and obj_id not in found_ids['audience_ids']: + found_ids['audience_ids'].append(obj_id) + logger.info(f"选题 {topic_index} 匹配到受众: '{name_variant}' -> {original_name} (ID: {obj_id})") + elif category == 'scenic_spots' and obj_id not in found_ids['scenic_spot_ids']: + found_ids['scenic_spot_ids'].append(obj_id) + logger.info(f"选题 {topic_index} 匹配到景区: '{name_variant}' -> {original_name} (ID: {obj_id})") + elif category == 'products' and obj_id not in found_ids['product_ids']: + found_ids['product_ids'].append(obj_id) + logger.info(f"选题 {topic_index} 匹配到产品: '{name_variant}' -> {original_name} (ID: {obj_id})") + + # 统计匹配结果 + total_found = sum(len(ids) for ids in found_ids.values()) + total_available = len(set(info['category'] + '_' + info['id'] for info in self.name_to_id.values())) + match_rate = (total_found / total_available * 100) if total_available > 0 else 0 + + # 输出详细的匹配结果 + if total_found > 0: + logger.info(f"选题 {topic_index} ID匹配完成: 找到 {total_found} 个相关对象,匹配率: {match_rate:.1f}%") + logger.info(f"选题 {topic_index} 匹配详情: {match_details}") + + # 按类别显示匹配结果 + for category, ids in found_ids.items(): + if ids: + category_name = { + 'style_ids': '风格', + 'audience_ids': '受众', + 'scenic_spot_ids': '景区', + 'product_ids': '产品' + }.get(category, category) + + # 获取匹配的原始名称 + matched_names = [] + # 建立正确的分类映射关系 + category_mapping = { + 'style_ids': 'styles', + 'audience_ids': 'audiences', + 'scenic_spot_ids': 'scenic_spots', + 'product_ids': 'products' + } + target_category = category_mapping.get(category) + + logger.info(f"选题 {topic_index} 处理分类 {category} -> {target_category}, IDs: {ids}") + + for detail in match_details: + if detail['id'] in ids and detail['category'] == target_category: + matched_names.append(f"{detail['original_name']}({detail['id']})") + + # 显示分类结果 + names_str = ', '.join(matched_names) if matched_names else '(无匹配名称)' + logger.info(f" {category_name}: {names_str}") + + # 如果没有匹配名称,输出调试信息 + if not matched_names: + logger.info(f"选题 {topic_index} {category_name}无匹配名称,详情检查:") + logger.info(f" IDs: {ids}") + logger.info(f" 目标分类: {target_category}") + for detail in match_details: + if detail['id'] in ids: + logger.info(f" 详情: {detail}") + else: + logger.info(f"选题 {topic_index} 未匹配到任何相关对象ID") + + return found_ids + + def _extract_topic_text(self, topic: Dict[str, Any]) -> str: + """提取选题中的所有文本内容""" + text_parts = [] + + # 提取主要字段的文本 + text_fields = ['object', 'style', 'targetAudience', 'product', 'logic', + 'productLogic', 'styleLogic', 'targetAudienceLogic'] + + for field in text_fields: + if field in topic and topic[field]: + text_parts.append(str(topic[field])) + + return ' '.join(text_parts) + + + class TweetService: """文字内容服务类""" @@ -49,6 +294,9 @@ class TweetService: self.prompt_service = PromptService(config_manager) self.prompt_builder = PromptBuilderService(config_manager, self.prompt_service) + # 初始化选题ID映射管理器 + self.topic_id_mapping_manager = TopicIDMappingManager() + async def generate_topics(self, dates: Optional[str] = None, numTopics: int = 5, styles: Optional[List[str]] = None, audiences: Optional[List[str]] = None, @@ -84,6 +332,14 @@ class TweetService: topic_config.topic.date = dates topic_config.topic.num = numTopics + # 建立ID映射关系(用于后续的ID反向映射) + self.topic_id_mapping_manager.add_objects_mapping( + style_objects=style_objects, + audience_objects=audience_objects, + scenic_spot_objects=scenic_spot_objects, + product_objects=product_objects + ) + # 使用PromptBuilderService构建提示词 system_prompt, user_prompt = self.prompt_builder.build_topic_prompt( products=products, @@ -103,12 +359,35 @@ class TweetService: if not topics: logger.error("未能生成任何选题") return str(uuid.uuid4()), [] + + # 为每个选题添加相关的对象ID + logger.info(f"开始为 {len(topics)} 个选题进行ID映射分析") + enhanced_topics = [] + total_mapped_topics = 0 + + for topic in topics: + enhanced_topic = topic.copy() + + # 查找选题中涉及的对象ID + related_object_ids = self.topic_id_mapping_manager.find_ids_in_topic(topic) + + # 只有当找到相关ID时才添加字段 + if any(related_object_ids.values()): + enhanced_topic['related_object_ids'] = related_object_ids + total_mapped_topics += 1 + logger.info(f"选题 {topic.get('index', 'N/A')} 找到相关ID: {related_object_ids}") + + enhanced_topics.append(enhanced_topic) + + # 统计总体映射情况 + mapping_coverage = (total_mapped_topics / len(topics) * 100) if topics else 0 + logger.info(f"选题ID映射完成: {total_mapped_topics}/{len(topics)} 个选题包含相关对象ID,覆盖率: {mapping_coverage:.1f}%") # 生成请求ID requestId = f"topic-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}" - logger.info(f"选题生成完成,请求ID: {requestId}, 数量: {len(topics)}") - return requestId, topics + logger.info(f"选题生成完成,请求ID: {requestId}, 数量: {len(enhanced_topics)}") + return requestId, enhanced_topics async def generate_content(self, topic: Optional[Dict[str, Any]] = None, autoJudge: bool = False, style_objects: Optional[List[Dict[str, Any]]] = None, @@ -198,10 +477,10 @@ class TweetService: async def _enhance_topic_with_database_data(self, topic: Dict[str, Any]) -> Dict[str, Any]: """ - 使用数据库数据增强选题信息 + 使用数据库数据增强选题信息,优先使用related_object_ids中的反射ID Args: - topic: 原始选题数据 + topic: 原始选题数据(可能包含related_object_ids字段) Returns: 增强后的选题数据 @@ -216,46 +495,119 @@ class TweetService: logger.warning("数据库服务不可用,无法增强选题数据") return enhanced_topic + # 优先使用related_object_ids中的反射ID(更精确) + related_ids = topic.get('related_object_ids', {}) + if related_ids: + logger.info(f"选题包含反射ID信息,优先使用: {related_ids}") + enhanced_topic = await self._enhance_with_related_ids(enhanced_topic, db_service, related_ids) + return enhanced_topic + # 处理风格ID if 'styleIds' in topic and topic['styleIds']: style_id = topic['styleIds'][0] if isinstance(topic['styleIds'], list) else topic['styleIds'] style_data = db_service.get_style_by_id(style_id) if style_data: + style_name = style_data.get('styleName') enhanced_topic['style_object'] = style_data - enhanced_topic['style'] = style_data.get('styleName') - logger.info(f"从数据库加载风格数据: {style_data.get('styleName')} (ID: {style_id})") + enhanced_topic['style'] = style_name + logger.info(f"从数据库加载风格数据: {style_name} (ID: {style_id})") # 处理受众ID if 'audienceIds' in topic and topic['audienceIds']: audience_id = topic['audienceIds'][0] if isinstance(topic['audienceIds'], list) else topic['audienceIds'] audience_data = db_service.get_audience_by_id(audience_id) if audience_data: + audience_name = audience_data.get('audienceName') enhanced_topic['audience_object'] = audience_data - enhanced_topic['targetAudience'] = audience_data.get('audienceName') - logger.info(f"从数据库加载受众数据: {audience_data.get('audienceName')} (ID: {audience_id})") + enhanced_topic['targetAudience'] = audience_name + logger.info(f"从数据库加载受众数据: {audience_name} (ID: {audience_id})") # 处理景区ID if 'scenicSpotIds' in topic and topic['scenicSpotIds']: spot_id = topic['scenicSpotIds'][0] if isinstance(topic['scenicSpotIds'], list) else topic['scenicSpotIds'] spot_data = db_service.get_scenic_spot_by_id(spot_id) if spot_data: + spot_name = spot_data.get('name') enhanced_topic['scenic_spot_object'] = spot_data - enhanced_topic['object'] = spot_data.get('name') - logger.info(f"从数据库加载景区数据: {spot_data.get('name')} (ID: {spot_id})") + enhanced_topic['object'] = spot_name + logger.info(f"从数据库加载景区数据: {spot_name} (ID: {spot_id})") # 处理产品ID if 'productIds' in topic and topic['productIds']: product_id = topic['productIds'][0] if isinstance(topic['productIds'], list) else topic['productIds'] product_data = db_service.get_product_by_id(product_id) if product_data: + product_name = product_data.get('productName') enhanced_topic['product_object'] = product_data - enhanced_topic['product'] = product_data.get('productName') - logger.info(f"从数据库加载产品数据: {product_data.get('productName')} (ID: {product_id})") + enhanced_topic['product'] = product_name + logger.info(f"从数据库加载产品数据: {product_name} (ID: {product_id})") except Exception as e: logger.error(f"增强选题数据时发生错误: {e}", exc_info=True) return enhanced_topic + + async def _enhance_with_related_ids(self, enhanced_topic: Dict[str, Any], db_service, related_ids: Dict[str, List[str]]) -> Dict[str, Any]: + """ + 使用related_object_ids中的反射ID进行数据库查询和增强 + + Args: + enhanced_topic: 待增强的选题数据 + db_service: 数据库服务实例 + related_ids: 反射的ID字典 + + Returns: + 增强后的选题数据 + """ + logger.info("开始使用反射ID进行选题数据增强") + + try: + # 处理风格ID + style_ids = related_ids.get('style_ids', []) + if style_ids: + style_id = int(style_ids[0]) # 取第一个ID + style_data = db_service.get_style_by_id(style_id) + if style_data: + enhanced_topic['style_object'] = style_data + enhanced_topic['style'] = style_data.get('styleName') + logger.info(f"通过反射ID加载风格数据: {style_data.get('styleName')} (ID: {style_id})") + + # 处理受众ID + audience_ids = related_ids.get('audience_ids', []) + if audience_ids: + audience_id = int(audience_ids[0]) # 取第一个ID + audience_data = db_service.get_audience_by_id(audience_id) + if audience_data: + enhanced_topic['audience_object'] = audience_data + enhanced_topic['targetAudience'] = audience_data.get('audienceName') + logger.info(f"通过反射ID加载受众数据: {audience_data.get('audienceName')} (ID: {audience_id})") + + # 处理景区ID + scenic_spot_ids = related_ids.get('scenic_spot_ids', []) + if scenic_spot_ids: + spot_id = int(scenic_spot_ids[0]) # 取第一个ID + spot_data = db_service.get_scenic_spot_by_id(spot_id) + if spot_data: + enhanced_topic['scenic_spot_object'] = spot_data + enhanced_topic['object'] = spot_data.get('name') + logger.info(f"通过反射ID加载景区数据: {spot_data.get('name')} (ID: {spot_id})") + + # 处理产品ID + product_ids = related_ids.get('product_ids', []) + if product_ids: + product_id = int(product_ids[0]) # 取第一个ID + product_data = db_service.get_product_by_id(product_id) + if product_data: + enhanced_topic['product_object'] = product_data + enhanced_topic['product'] = product_data.get('productName') + logger.info(f"通过反射ID加载产品数据: {product_data.get('productName')} (ID: {product_id})") + + logger.info("反射ID数据增强完成") + + except Exception as e: + logger.error(f"使用反射ID增强选题数据时发生错误: {e}", exc_info=True) + + return enhanced_topic async def generate_content_with_prompt(self, topic: Dict[str, Any], system_prompt: str, user_prompt: str) -> Tuple[str, str, Dict[str, Any]]: """ diff --git a/config/database.json b/config/database.json index fc9ad20..4ec8470 100644 --- a/config/database.json +++ b/config/database.json @@ -1,7 +1,7 @@ { "host": "localhost", "user": "root", - "password": "civmek-rezTed-0hovre", + "password": "Kj#9mP2$", "database": "travel_content", "port": 3306, "charset": "utf8mb4", diff --git a/run_api.py b/run_api.py index 2d0a05d..6886aae 100644 --- a/run_api.py +++ b/run_api.py @@ -32,7 +32,7 @@ def build_arg_parser() -> argparse.ArgumentParser: parser.add_argument( "--port", type=int, - default=int(os.getenv("PORT", 8000)), + default=int(os.getenv("PORT", 2714)), help="监听端口", ) parser.add_argument( diff --git a/海报Fabric.js前端对接文档.md b/海报Fabric.js前端对接文档.md new file mode 100644 index 0000000..797bc94 --- /dev/null +++ b/海报Fabric.js前端对接文档.md @@ -0,0 +1,711 @@ +# 海报Fabric.js前端对接文档 + +## 📋 概述 + +本文档详细说明如何在前端使用新的海报生成API返回的Fabric.js JSON数据,包括图层加载、图片替换、图层管理等功能。 + +## 🔄 核心变化 + +### **从PSD到Fabric.js JSON** +- **原先**: 返回PSD文件的base64编码 +- **现在**: 返回Fabric.js JSON文件的base64编码 + 原始JSON数据 +- **优势**: 可以直接在前端使用,支持实时编辑和图片替换 + +## 🎯 API接口说明 + +### 1. **生成海报接口** + +#### **请求示例** +```javascript +const generatePosterRequest = { + posterNumber: 1, + posters: [{ + templateId: "vibrant", + imagesBase64: "图片ID列表", + contentId: "内容ID", + productId: "产品ID", + generatePsd: true, // 现在实际生成JSON + numVariations: 1 + }] +}; + +const response = await fetch('/poster/generate', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(generatePosterRequest) +}); + +const result = await response.json(); +``` + +#### **响应结构** +```javascript +{ + "code": 0, + "message": "操作成功", + "data": [{ + "requestId": "poster-20250104-123456", + "templateId": "vibrant", + "resultImagesBase64": [{ + "id": "vibrant_v1", + "image": "base64编码的PNG图片", + "format": "PNG", + "size": [1350, 1800] + }], + "psdFiles": [{ // 现在是JSON文件 + "id": "vibrant_v1_json", + "filename": "template_fabric_v1_20250104.json", + "data": "base64编码的JSON文件", + "size": 15672, + "format": "JSON", + "jsonData": { // 原始JSON数据,可直接使用 + "version": "5.3.0", + "objects": [...], + "background": "white", + "width": 1350, + "height": 1800 + } + }] + }] +} +``` + +### 2. **获取Fabric.js JSON数据** +```javascript +// 获取指定海报的JSON数据 +const fabricJson = await fetch(`/poster/fabric-json/${posterId}`) + .then(res => res.json()) + .then(data => data.data); +``` + +### 3. **替换底层图片** +```javascript +// 单个图片替换 +const replaceImage = async (posterId, newImageBase64) => { + const formData = new FormData(); + formData.append('posterId', posterId); + formData.append('newImageBase64', newImageBase64); + + const response = await fetch('/poster/replace-image', { + method: 'POST', + body: formData + }); + + return response.json(); +}; +``` + +### 4. **获取图层信息** +```javascript +// 获取图层管理信息 +const layers = await fetch(`/poster/layers/${posterId}`) + .then(res => res.json()) + .then(data => data.data); + +// 图层信息结构 +// [{ +// "name": "图片层", +// "type": "image", +// "level": 0, +// "visible": true, +// "selectable": true, +// "replaceable": true +// }] +``` + +## 🛠️ 前端集成实现 + +### 1. **基础环境准备** + +#### **HTML结构** +```html + + + + + 海报编辑器 + + + +

+ + +``` + +#### **CSS样式** +```css +#poster-editor { + display: flex; + gap: 20px; + padding: 20px; +} + +.canvas-container { + flex: 1; + border: 1px solid #ddd; + border-radius: 8px; + padding: 10px; + background: #f5f5f5; +} + +.layer-panel, .image-replace-panel { + width: 250px; + border: 1px solid #ddd; + border-radius: 8px; + padding: 15px; + background: white; +} + +.layer-item { + padding: 8px; + margin: 5px 0; + border: 1px solid #eee; + border-radius: 4px; + cursor: pointer; +} + +.layer-item:hover { + background: #f0f0f0; +} + +.layer-item.active { + background: #e3f2fd; + border-color: #2196f3; +} +``` + +### 2. **JavaScript核心实现** + +#### **初始化画布** +```javascript +class PosterEditor { + constructor(canvasId) { + this.canvas = new fabric.Canvas(canvasId, { + preserveObjectStacking: true, + selection: true + }); + this.currentPosterId = null; + this.layers = []; + + this.initEventListeners(); + } + + // 加载海报JSON数据 + async loadPosterFromJson(fabricJsonData, posterId) { + try { + console.log('开始加载Fabric.js JSON数据...'); + + // 清空画布 + this.canvas.clear(); + this.currentPosterId = posterId; + + // 加载JSON数据到画布 + await new Promise((resolve, reject) => { + this.canvas.loadFromJSON(fabricJsonData, () => { + console.log('Fabric.js JSON数据加载成功'); + this.canvas.renderAll(); + resolve(); + }, (error) => { + console.error('加载JSON数据失败:', error); + reject(error); + }); + }); + + // 加载图层信息 + await this.loadLayerInfo(posterId); + + console.log('海报加载完成'); + + } catch (error) { + console.error('加载海报失败:', error); + throw error; + } + } + + // 从API获取并加载海报 + async loadPosterFromApi(posterId) { + try { + const response = await fetch(`/poster/fabric-json/${posterId}`); + const result = await response.json(); + + if (result.code === 0) { + await this.loadPosterFromJson(result.data, posterId); + } else { + throw new Error(result.message || '获取海报数据失败'); + } + } catch (error) { + console.error('从API加载海报失败:', error); + throw error; + } + } + + // 加载图层信息 + async loadLayerInfo(posterId) { + try { + const response = await fetch(`/poster/layers/${posterId}`); + const result = await response.json(); + + if (result.code === 0) { + this.layers = result.data; + this.renderLayerPanel(); + } + } catch (error) { + console.error('加载图层信息失败:', error); + } + } + + // 渲染图层管理面板 + renderLayerPanel() { + const layerList = document.getElementById('layer-list'); + layerList.innerHTML = ''; + + this.layers.forEach((layer, index) => { + const layerItem = document.createElement('div'); + layerItem.className = 'layer-item'; + layerItem.innerHTML = ` +
+ ${layer.name} + (${layer.type}) +
+
+ + ${layer.replaceable ? ` + + ` : ''} +
+ `; + + layerList.appendChild(layerItem); + }); + } + + // 替换底层图片 + async replaceBackgroundImage(imageFile) { + try { + if (!this.currentPosterId) { + throw new Error('没有加载的海报'); + } + + // 转换图片为base64 + const imageBase64 = await this.fileToBase64(imageFile); + + // 调用后端API替换图片 + const formData = new FormData(); + formData.append('posterId', this.currentPosterId); + formData.append('newImageBase64', imageBase64); + + const response = await fetch('/poster/replace-image', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (result.code === 0) { + // 重新加载画布 + await this.loadPosterFromJson(result.data, this.currentPosterId); + console.log('图片替换成功'); + } else { + throw new Error(result.message || '图片替换失败'); + } + + } catch (error) { + console.error('替换图片失败:', error); + alert('替换图片失败: ' + error.message); + } + } + + // 文件转base64 + fileToBase64(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + // 移除data:image/xxx;base64,前缀 + const base64 = reader.result.split(',')[1]; + resolve(base64); + }; + reader.onerror = reject; + reader.readAsDataURL(file); + }); + } + + // 切换图层可见性 + toggleLayerVisibility(layerIndex) { + const layer = this.layers[layerIndex]; + if (layer) { + layer.visible = !layer.visible; + // 这里可以实现图层显示/隐藏的逻辑 + // 需要根据具体的图层结构来实现 + } + } + + // 初始化事件监听 + initEventListeners() { + // 图片上传事件 + document.getElementById('image-upload').addEventListener('change', (e) => { + const file = e.target.files[0]; + if (file) { + this.replaceBackgroundImage(file); + } + }); + + // 替换按钮事件 + document.getElementById('replace-btn').addEventListener('click', () => { + document.getElementById('image-upload').click(); + }); + } + + // 导出当前画布为图片 + exportAsImage(format = 'png', quality = 1.0) { + return this.canvas.toDataURL({ + format: format, + quality: quality, + multiplier: 1 + }); + } + + // 获取当前的Fabric.js JSON数据 + getCurrentJson() { + return this.canvas.toJSON(); + } +} + +// 初始化编辑器 +const editor = new PosterEditor('poster-canvas'); +``` + +### 3. **完整使用示例** + +#### **生成海报并加载到编辑器** +```javascript +// 完整的海报生成和加载流程 +async function generateAndLoadPoster() { + try { + // 1. 生成海报 + const generateRequest = { + posterNumber: 1, + posters: [{ + templateId: "vibrant", + imagesBase64: "123,456", // 图片ID列表 + generatePsd: true, + numVariations: 1 + }] + }; + + console.log('正在生成海报...'); + const generateResponse = await fetch('/poster/generate', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(generateRequest) + }); + + const generateResult = await generateResponse.json(); + + if (generateResult.code !== 0) { + throw new Error('海报生成失败: ' + generateResult.message); + } + + const posterData = generateResult.data[0]; + const fabricJsonData = posterData.psdFiles[0].jsonData; + const posterId = posterData.psdFiles[0].id; + + console.log('海报生成成功,开始加载到编辑器...'); + + // 2. 加载到编辑器 + await editor.loadPosterFromJson(fabricJsonData, posterId); + + console.log('海报加载到编辑器成功!'); + + // 3. 显示预览图 + const previewImage = posterData.resultImagesBase64[0].image; + showPreviewImage(previewImage); + + } catch (error) { + console.error('生成和加载海报失败:', error); + alert('操作失败: ' + error.message); + } +} + +// 显示预览图 +function showPreviewImage(base64Image) { + const previewDiv = document.createElement('div'); + previewDiv.innerHTML = ` +

生成的海报预览

+ + `; + document.body.appendChild(previewDiv); +} +``` + +#### **图片替换功能示例** +```javascript +// 高级图片替换功能 +class ImageReplacer { + constructor(editor) { + this.editor = editor; + this.setupUI(); + } + + setupUI() { + const replacePanel = document.querySelector('.image-replace-panel'); + replacePanel.innerHTML = ` +

图片替换

+
+

拖拽图片到这里或点击选择

+ +
+ + `; + + this.bindEvents(); + } + + bindEvents() { + const uploadArea = document.getElementById('upload-area'); + const fileInput = document.getElementById('image-upload'); + const previewDiv = document.getElementById('image-preview'); + const previewImg = document.getElementById('preview-img'); + + // 点击上传 + uploadArea.addEventListener('click', () => { + fileInput.click(); + }); + + // 拖拽上传 + uploadArea.addEventListener('dragover', (e) => { + e.preventDefault(); + uploadArea.style.backgroundColor = '#f0f0f0'; + }); + + uploadArea.addEventListener('dragleave', () => { + uploadArea.style.backgroundColor = ''; + }); + + uploadArea.addEventListener('drop', (e) => { + e.preventDefault(); + uploadArea.style.backgroundColor = ''; + + const files = e.dataTransfer.files; + if (files.length > 0) { + this.handleFileSelect(files[0]); + } + }); + + // 文件选择 + fileInput.addEventListener('change', (e) => { + const file = e.target.files[0]; + if (file) { + this.handleFileSelect(file); + } + }); + + // 确认替换 + document.getElementById('confirm-replace').addEventListener('click', () => { + this.confirmReplace(); + }); + + // 取消 + document.getElementById('cancel-replace').addEventListener('click', () => { + this.cancelReplace(); + }); + } + + handleFileSelect(file) { + const reader = new FileReader(); + reader.onload = (e) => { + const previewImg = document.getElementById('preview-img'); + const previewDiv = document.getElementById('image-preview'); + const uploadArea = document.getElementById('upload-area'); + + previewImg.src = e.target.result; + previewDiv.style.display = 'block'; + uploadArea.style.display = 'none'; + + this.selectedFile = file; + }; + reader.readAsDataURL(file); + } + + async confirmReplace() { + try { + await this.editor.replaceBackgroundImage(this.selectedFile); + this.cancelReplace(); + alert('图片替换成功!'); + } catch (error) { + alert('图片替换失败: ' + error.message); + } + } + + cancelReplace() { + document.getElementById('image-preview').style.display = 'none'; + document.getElementById('upload-area').style.display = 'block'; + this.selectedFile = null; + } +} + +// 初始化图片替换器 +const imageReplacer = new ImageReplacer(editor); +``` + +## 🚀 快速开始 + +### **1. 基础集成** +```html + + + + + + + + + + + + +``` + +### **2. 图片替换示例** +```javascript +// 简单的图片替换 +async function replaceImage() { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/*'; + + input.onchange = async (e) => { + const file = e.target.files[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = async (event) => { + const base64 = event.target.result.split(',')[1]; + + try { + const response = await fetch('/poster/replace-image', { + method: 'POST', + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + body: `posterId=your-poster-id&newImageBase64=${encodeURIComponent(base64)}` + }); + + const result = await response.json(); + if (result.code === 0) { + // 重新加载画布 + canvas.loadFromJSON(result.data, () => { + canvas.renderAll(); + }); + } + } catch (error) { + console.error('替换失败:', error); + } + }; + reader.readAsDataURL(file); + }; + + input.click(); +} +``` + +## ⚠️ 注意事项 + +### **1. 浏览器兼容性** +- 需要支持Canvas API +- 需要支持File API +- 建议使用现代浏览器(Chrome 80+, Firefox 75+, Safari 13+) + +### **2. 性能优化** +```javascript +// 优化Canvas性能 +canvas.set({ + renderOnAddRemove: false, // 添加/删除对象时不自动渲染 + skipTargetFind: true // 跳过目标查找(如果不需要交互) +}); + +// 批量操作后手动渲染 +canvas.renderAll(); +``` + +### **3. 错误处理** +```javascript +// 完善的错误处理 +try { + await editor.loadPosterFromApi(posterId); +} catch (error) { + if (error.message.includes('404')) { + console.error('海报不存在'); + } else if (error.message.includes('网络')) { + console.error('网络错误,请重试'); + } else { + console.error('未知错误:', error); + } +} +``` + +### **4. 内存管理** +```javascript +// 清理资源 +function cleanup() { + canvas.dispose(); // 销毁画布 + canvas = null; +} + +// 页面卸载时清理 +window.addEventListener('beforeunload', cleanup); +``` + +## 📞 技术支持 + +如在集成过程中遇到问题: +- **API问题**: 检查请求格式和响应状态码 +- **Canvas问题**: 查看浏览器控制台错误信息 +- **性能问题**: 检查图片大小和Canvas尺寸 +- **兼容性问题**: 确认Fabric.js版本和浏览器支持 + +## 🔗 相关链接 + +- [Fabric.js官方文档](http://fabricjs.com/docs/) +- [Canvas API文档](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) +- [File API文档](https://developer.mozilla.org/en-US/docs/Web/API/File) \ No newline at end of file