From 57a19bcbf7360c017d408dc38fd01bea3049b9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Th=C3=A0nh=20=C4=90=E1=BA=A1t?= <91755733+lds217@users.noreply.github.com> Date: Tue, 1 Jul 2025 02:10:23 +0700 Subject: [PATCH] [update] Enhance CheckUpPage by integrating ultrasound result printing functionality. Implement image selection for printing, including validation for selected images, and improve user feedback with warning messages. Update medical invoice format with new patient and service details. --- .vscode/launch.json | 14 + medical_invoice.pdf | Bin 24164 -> 26121 bytes pom.xml | 21 + .../ui/component/CheckUpPage/CheckUpPage.java | 82 +- .../PrintDialog/UltrasoundResult.java | 568 ++++++++++++ .../PrintDialog/print_forms/InvoiceItem.java | 86 ++ .../print_forms/medserinvoice.jrxml | 854 ++++++++++++++++++ .../PrintDialog/print_forms/test_print.java | 94 ++ src/main/resources/database/BSK.db | Bin 1175552 -> 1175552 bytes ultrasound_result.pdf | Bin 0 -> 432112 bytes 10 files changed, 1713 insertions(+), 6 deletions(-) create mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java create mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java create mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml create mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/test_print.java create mode 100644 ultrasound_result.pdf diff --git a/.vscode/launch.json b/.vscode/launch.json index 55e4357..3d256b9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,20 @@ { "version": "0.2.0", "configurations": [ + { + "type": "java", + "name": "test_print", + "request": "launch", + "mainClass": "BsK.client.ui.component.CheckUpPage.PrintDialog.print_forms.test_print", + "projectName": "ldS_BsK" + }, + { + "type": "java", + "name": "test_print", + "request": "launch", + "mainClass": "test_print", + "projectName": "ldS_BsK" + }, { "type": "java", "name": "Client", diff --git a/medical_invoice.pdf b/medical_invoice.pdf index 1a1915e3c49009505e59e056d26741c3250be460..a0ea4cbc1f90a0bc7b7743ed681449a81063c8a1 100644 GIT binary patch delta 23285 zcma&MV~}OrwzgYoqbe)yO50|oQEA&&rJW;fXI0v^ZQHhO8@JZl-?@A3`<-**#_aL- zr$4Rz84+{F95F`QZ3PJ#1&L+>*ftSjP$HvT~h1RM#^_s;nxnRULlHw+I+IZ zvZEYVUchW|(8;mBGNrtGiHB4Zlf~Olq4{w!pvG`#KYh8`yM7zkOUanc)qPf_qcqnm zAjpFjqf4;Ko0XQZPhiBNpm|RcmI$#>&T}MT;XP@EB%C2NR^Kt%edD|U5>#@_Ue0%r zzR=F}MByjs(CRRLNz{kf_WL@`gdEEeMBTe43q6hYks@9-VZph}0+r+hZ5-lCH|2do zdF>qfBq&$`9lbm58sR`&d~M+Y&0s)o$Lbkuz?!i`lIEY`5}T^Qn%s7kthef%y<~dM z%9riNwPZBQobaH zHPN`8MaU~D$?8kb5!`)}jh1BHNgDwP-Pb0ktN4U4xe%0oTo+spBovLLP-$nCT#b#N z=;D4-QdU*)w>$mJZ#M%TyK1)A*qQ<2*xjg2+O~IFKa!&I+G@;MmIT6IB8li2y>#Yql7IU=s8+Do+b@WO&Aup|v=HcF&|$BmFen8Wb)Z zNQG(g@-A#lwUMN?FCSq8wPaEoy4OjM)q3fKp4g(U-xLpzkV7Lu-$vjk&>bPuOO{$8 zrQJV~X{T;!0bRDIkebOGA@A?eW}A+iJpg?<3HM{Xo|&*1a3+)(ha!VsiT+m5Qj9Lw zB|W*Ea>%e<=zrJ3{nL=HGLA+4F}BBN?ai&sRNpCORVg)Jn=JdtD4!V)vyG<|1RA_j zyN}g=&0>C=UGX;c{hWnWGCY^{klIJrb*5^;5fZsXUm$U~C?p;yJ2|WvwNz@T=U{I4 zV1_Yx+z12#sQ-9<<~r`_?_#Iz85W}WrL6ncGFtbU)pH6&Y8BmXLz$b9&`$pSn@0MW0% z;hpspK6oKyCvUeD?I=ZlDA$R`xKgc3`GpT-KTM%D;2W(AJ?cUNe~J@;LqsBa4$T@w zVKd$e#^{*Z@g8n^cKbl$BT;v0TU8hl!bjK9wi;m_I5g}(Tg=PqHtH`ub!})jn0@uw zzD02x4P&Lr@r5C9Zcv77qIb)Bijkf}QCI5}QSLB3#9sS+KF+B0>o6-#{Y}%mm2=c@ zI*x@^KwGuFU{_K)L&CFp1)28NzE$r>s4wVi{+_J^m0MTR))9e#9x;++v?fpi|KLU= z;VfOF<@WTXrdRJBuh@a0t+6`qKe!+1c(GEba?-p4WQtLdd7PpaxuZtMUdHyAm2k+k zfxYQ*ba}TTT^T=-KDhZ#2EJ3BjXJvymfL(#L;z1zSW@FD^UC~zn zdZ1%G(d7ufh7Y-e*e)JzA|h~!eF(>8F%+eMf~|m=6NyAXf>ve?Tz%YOL7OwhbibrB z_GtG&=)K-pyksmRbdwO7d?H0;;zyR2;(V};ioj!>D(On;=~p}?L|OX5k__t!TEUpx zW2m1vo?H`b9&XE7B9VBWl+FoV#z0*U3?Y(`DM>Q7(Ysqn)bCMwHmBnLHfsF=(p*um z*dkHKT^z_5Z2FLLq8NUBd;iKjPj2*O1^$7~$k2d%Wt6E=3AS9?9rPzv-gPN(kkYcu*Lvqge9AOJw}i5=#2QQWp_eoONbIO!1$Ti zL$VWH-Hi|l`;1r9;t?_F`DfV{mtpqJE&@(FS&MK=Ya&kd?Us7PJky;~xBM|=D`TPz zE73rJYfI(JMaAFPBq%LAlu11xJlsEYeasbkE;&tj zI-&C4jkKQY7%Q@r`N}pIkv2<-)<46%7@1s^`B|kcylOWoxdHy5&dz{vQ zc*76Um5g8YNkwK}($lF5#_u%VH{&9&!H$?!^6{`< z3>)Dq!gr9Abn!%Cvr&9}tN}c<9`GEb+ZTV!@{j&0FQiCp)f;X`JDtJYo77{v{5q5a zcMfDdsh{GL38k^Qb^AS;ut?>jWZQn8_F7Do>k-xFaj8#^rWI}^wTG8Z2 zH-OC_E5(SLfgNZRIZLKD>%ARU^$5$)9zsA!Erv%1tS|r4!&s9C{Cma*%CnO8kaDOK(eAy{nVmIfNw#5^{Ad8}p zjPeEnZ4{d+EGSXjJ4r}M(@>w~$g1@`Xa#RrnajHs>sXy8N<&$7b#0W;;8M7)8R#Fj zpVu2-4qTj)%-P+Iua_Ywaqa7yjv3HWSJX_Eh3DAav9sGHBLm2vjzaFQVF z&18GMmVAri@=3dFMuR1f-C4g&ICcl zhmHR#9w_$ zUE~Gl461g}oy#;Bl1rI+%Zr^h4S#$jW8VK6Tw$hkv=qJ!F=bBdC@AWq?jf`12JeUU zj(A>?{=MEaQN;7UKxRH=h09r1f^flMvzc{|#X&wHByifB1M^^zO;b4oMuTKUz3gW? zGC7}fCcqVZk6!CM)z1GyroP)o&p_0u6TuNUWFLNytT1DUUmuR45k5x_zCSfP+sitH zG{(hU_@Ks+qMeJ6%iCD*GI_${1Gf>p5#|=TVSNSG?5(J;41%d{VDS({}=1i<~DvCphja*Fuz2qH@@V^AR0Bbmg za>VB;Af=dt?8l}*Oe(z(3hB>oaOuQO7!-Cr(0|I@zdb!}c<@x}T&}OG7;$DXfqDLj z;-(F=iE%DhA?Bn7PsmWaslg8sxBQ{Q8{*?yu>OuJv;KB7+<~^Lr!goM{8c5@MvH~i zZmtW|^-S|+P{YzhfJhb9>se1@74X^E;24`U8i}9&G%nA~SCIUTE^e(KI!0+LB zCB7Li2znCYQA2AvpAZRmGpSWPg|Blge$9;j$<>H9x4^c+2lHKNeZcrZOB&X2U}tjK zyxtF}ByEshc+HFYlsa8+OGsfjNYk!QG#Hr>V70p5Xz-K1AnNo0|37WvoNw3`iIhec zOYq~8=@y8BMhIaj0= z<|#SF(WF~%495W92>!=m;Pzq7g_l}v%gbebUQfy<9++Q7i2;EMrE7(6W3XBk2a_QK z6;Pc>7>4=3g(SpOmNSU2=mO}PP)9B1irqrA)uYuBm=eTfdf?9)~GY6Ii0HNvM64q;`>M{=N}k^ygRWqRAY_ zvUl-&lI~~-;gpRSiZj$N(z3FfIa%v_tf3$R7>yAxr+0~9*Bpo@Zs&g=z+aUv&mWY} z^ydchB=~Wm3<|#)6c~&t`+xK$7GW@<8szJbDuyTh@&A^;Pl~l!>^qdtIoVTUJM)Pv zDPSxmK>sKF%F2)wM~{g{p&K=!0HI2>JEbQx7ds+hvMRO(oHu|nRBG&t8LXq+!g%zB zMn7~W^s7QMKEcYKG((RIX;{0kDUn+oHV&ci0(XN6J8orPvKe-9ahW8K#qmkJCNh1&(5ZqNd*s&}cC@^@qAoy4Is^ z*R+{|2Wbv~HDt#^U+@}j=F_~JIGnU;SCMHK3=Ml01X=B-KU(UfFN*q4fxrAeWNbl^ zG2)cCKFL{iAvo=nzq?D`VJ}>nOBO0!?RJ22>trap`jr5cwrSr#WTBk z=5^eB3`AWeh3FJG)UQM8or;;ojv>`xnpKkTEh_s)9ViFfU~W{-!k56F=^7d3QC3u6 zKSvHrgrmbFXAjL(3Uk1o#?IrQMkyzWOSF^wx`K_{|EVz;f#BIF{^Y%{xpqPt%qrksBPVS$h~u9CwK9Sf#W9# zFBoUyWL*k=7K(QLLL-W=cbLPWk4fq*hVz-krI0HP$|9l&>V)kc!VD_vzZjy|_?|ycQXIk29+e4j%JgMyBw5&wLF75e)-vwD! zgAnHR{2Ui`asP!1e-(6#!1dlmX>Pu$tLIp%*O4*6_$J0NPS3FdxVz%Bu*zXcYTnJK zs@PX`6Xn4d+;s^~oXS$zb#5#>i#aX4n3|VQ+hzE00&yiQqDQ?8d^up+jV{lTQWo{W zDk}J2I>>oPcgekRpa_E!xFgAtdVlDH2RPqWt-?0bSK2Q|^?TLogl3<{l2r;Sa~anC zGl2g%Q&le$mZZ6Z8p{m*n6Xy&5i>d}I3sPz9&61{$&K=P^d?U%(TiEfLwc4&giXauJ6IeVu6yU22Mnt6#8Z9Qq%{(PW-8+y0j}M<6=$8L z!;^toT$q~)-D%5h(#|#zCEQa}7;@r-ZOJOvv#P7}^E0Ex>ld=TR4|`!m!B&u^wu-g zkMG#$s|gy_>+RBXhUr+m_7A!nnW0IgteKH`8*ov|akbclT)k@fJbo*l4F}on#V&KSJ2W0+UWVjK>0kvQkap~ zVYmU$w9BW2?B;vk<3Oza5zEj5qF598KX+75>J0NIms|i(sX}G2V6S~7T4{cKfclC)gaomHmJk}Y%@v;|8kx6@fFl3s1y}e8O3ew z#DejFGO+r9y-yU;rXrJaIUXf}^%K$y71E0)hyN1rJ)2$(_zD@)O(2Is{8!=swO{*5 z%)hy-1SV)y>#|WVBEUKI_^ws{joOWRUDm3V*l4GL5G{YBM%7;hEe$hMSsGD!EIX(H z;y|br4_x%hNCiY;dB(7`++LY`J>@VO5v!ea>>%;>E;?bvx0_S(7W-!a!J0&R5Rc1k z{2|Kf?Lc2jxGh#IFGYj7;9?(*r?zqbC~kXWe#SW*MP+@>*8G4rho^q4NKHdjij&js zG|5$NL}jd{z(%D1@s~2)c%qlnaV;Ij!c1ohY&#;?5|o@dN_Y?`8@*3KkoJYI| zn3H2wVrB?6MbkYSDW zkf_>jpWQSLTZDk*?v>qF@m&iOdk$BlJbGnECjE`Ua{VNtd#!8W39>rlS`z-!55$YU z6AALM>k}WwCKTAKt~dTaWk>g4{s3Or*|e)RV_Y(*l$BslojO$FpwK1B&GW~TT9T!p zCRqXq_;?@&S6%NSH?JY5obT7gt#3*vF;D|%JgZjb55_xm;rW)hQ_RkHol~yp-1cj{Z%BZ>%+i$N2|%pmNRSm zlhs%@>)6o?#a#qN+uK^u+DyY3m?qSlZ-SDFw-vX>K$-mb_|Ga(bgKU)C>vFu`WJvJ zRaac3gC_`$fxOjf3aYNY+2l`g%5%>UFLMWU!5@j&ebuq%2kVVZe(~q)^*>iBn+NZw z)yxY5c@z^Hob9_GVJh0L?yH_s(X|*JkoFoc)QUtfN6^wJLsF@erPX|@jU@DF zWZe@b)Ad=8*{dcSDg1|hUp`K|>iBwLv z?e3%Y1gP5qP*}ba;loCTnpQr@YGB|ICJc*&2V?hXakED7PEW}0`wz6Vlme^S zF)C2?x#&=l4V+hjRtcIpn<;X)w`#qO?9Wu7=>Dg{`(O#E=hyITzBlgTlBsrAYC?QcJBjo#yI)CMchox0*YXBuW z!?J0yk*Sk0D4cczqSA+hFt{99L}U)T{{NIkf4pfbn~7HPvMFc4Lr5?)iO07_CUJ{x zf26o@dbN=mx_ml~8hDh4nf6ITHo(Tkt-!1%puPnl2gwTmZarV6U9-c} zX=^+-$oPCTVgk6&kHYz-)SO!H1>ojU&7*4JKkO_eO%!3Jb6p=NEl9VL_7O>L{-(1~ zWlba+!jCjY2%(|2v-~`M@-7dpw^`UnE|>6qyo_0M;561vNC6x7YPtb+3q8f{>wWyM zF%HI^r*xy4fd`Extnbqg7UygAg!GDwt%l0u#Y;Ydno)ox1Bb_uefRFlzP~)pRlWzZ zv!=STa{l}A=@M4SvWorpSbGrO>PMF;wmz-t8fexD2qWr=IEJa`PC}%nAXMc%2l7bt z8?{5dUjf9uJ%SWyq`kI67|yM*l;P&KtaJQV{gT-51VbDF0(Bgo zFO8To><2*Va-(~P$4r##4FXzA(nwR<}&pfb=e#^a|C zyT_d^q1t>w@C>;Pw_i!7@zr}wmg5YAx0s@st6Py{gn!>IlKZX%Q;UNyl&OVCmnBU< zf9BjH(a7)Mv$W^S$tvA?JXdfo^prV0V{CGMKJ5e)^{#q-5iQ2S7sFsCEleIG4M(ZI zc&CdP4qybL0ru2pAh~EH*VP37Ki1{oNH=#Cj--gv2gAf0F;aTx=~6F|Y`oFpt_ioA zd{;tCgRH?>tJPA*dJaQPyZU>U%O0@VP7?NTrc-N}fh-lLGKS0ttLVj&!D+giG-ERU zc63V-IrYW1bQvLh^in^nhBNM~J(pmuNqn7&u_G5kR3yIuL=`&$8dUGLw<->D7?{2- zKQ&@-AILxDPH>+s+yRRXMfdbDFIAvQaN?+3Ds8m#K5f`Hz}1y}DHry$>rj2XML%^J z7Ta5onjfCEIbj5^ zFoRa#UX63-#}|*)xC(wgIk3}(uT~PJL}1ne4b-=oh^<88Y9Lgx&$f>}INLN4 zHEFcyDS+Bk1245bSnr_c3fLDPwiA}g{-sQ`A&#__;}G*rot)^Z2cRdKx@mBw_CEiV z@_+lfQj5ag7xO0hVdV{MM=rc|61M*8n54U!pZM64v5%b7);euQHLxbEH@t58o*Rf` z*yZC%Z`TA0X1%jj0XzEouP|_cj;H?SB1+9fyX49&KA(}|0c*Odja!tfXERUY_K!1O z3x`uJzl-7P*#=SUSVqpThj(e_Uq7HuDnOes}YV8$bdMI^{ltf82$Kpkh+CyOluDKUMZ&7EeWE(OS5_U z1W{X-i2Ff$q_-`qzwQ}U`AfyXa6k`oEKC>6ot(4A$)M1p;kfg$OeH;wC$VP<%Byd6 z=5HTr_5aHCzq&g;{?++>bOUPwy<_p-+F+oK*kS@p?}73V{;f!;_*|k-72zPU-zA6t zi=2j`!3C{sSx{a&pqP-fRi6*NlD{A7sYC=OO2)$;=~X9^w@~g%KyRqLsBnshU+4_y zX<;u)$wc5|T*#|>>&POB+c!dt@ykwhjG;^PSr?CUIz^MHfs-%9V;}>6=)kpnDZW)f zdU=g;#qz;sn3YP*{!`Lm$n+O?ul9v}C+*Ysvm~q9>kqhVKrRhR^Y*pB_7?x)RLg8tVO&&D9t9?(Vu&!Q1)P|wfD6F-9N5fS!r1`1X_x()7 z*^X4*;Ls|Y^Z6*T-iiq~JzpW8Stc~%PLR(-Yx}uLW*TYE7XNDICT@_#EB!^$i&uvi z@FkyUhBU)?Xf4I-N+%_L%p-#t&Mq$B#**?DLHQa1kn-oiOqzl+Cx43gAMj@c>7b)p zzFa(db4l(fp_R*v+^Rr3r}9+?=Z zyRV_6&1!3s_6m=w^m=VkiS+2nedQylRh6-ZdIE17+yW0klb#h7g5qC*1}md>)^?6E zf!9Ia-r8?QIrQVi&UGNY11&#fKR~`rgh#DMo{esaPOrytVga)np;bKe6_0C+f?K`Bep6Hb*9tiCMrfkb{Qx?DA!T{^LRFZ z#YzB{Q@>q)RBk@quKnJD5TGE_vT3?^cxzDRYza{2w++N!#}RCRNq=HZUV!;?h%5BRn9SLNKAv_=Z>P=`EsE zzpump`Rh>BYs`BN!VNynZVHu`;1$`&3iNNnXKbE;M~6KdN2CS18fT5!*p7MEUCk^a zAZ(L!ZQ8*3IcgM*QtV~9E%MrNI+Zi%`Ozu2r-L|bQ1)&7NA`qsg7U|>rGQJc(btCQ zwcj7CYjq`as!*#4ey zxa=yxMsNom({i^gT7Hs6rY}+y^wS4SC349I<5}AG5n6;D?aYd=`?>AG4K%65-Lh~ zl}GT;vO94bI2vA2WHm45P&F?q2ge_(w2L7wjydi9R`br+QLWfDd}&9l1>o0|T_l|M ztR~J(=qn#-+&l5Xgfx0))seGJ?4DdJMz05pStD>*ITBqrB0P5APJR-c`c9+<>i7+eu&ZO7;Dj1Y=#z6B5(7dQY*3?(_#wc zJ^6%J_Ud9VVQC#X&EpJ!QlkI@oV^G_8r2xSY;7sNho@-l;xdj>5v@0xLs1-=@+ zmON7lo3bMn2|xiK**)oRHUpy(ABu&3d1UL^O-zn+~pJdg28`H24+=vBZE-)Q;o%{F zcGM-u5#w7C-8U5S5pvbhh=D{lY&lm5JJ)Ywj|8kCD^10n6XoF}B*0g2|>&D6T~s~(aWq3WRCt=AyH zHj}W=l&z1@@xk9sSQ|ql(bH%(iD$qV#7bL2T%jCK1k~OsVtJi>cUMcdH`O4RSSaJb zgr|R6YFoMpKJRH%vBiXTdX&yydv^Uf|ZMmLTy`L`%Z^@!GK3+=aUMs-!FH z&kEu(1bjb%Lri6 z2>G232f1Bkt$?*K#oQyAjb%ZQ&C1}+NQ=(ZT?4oTw1)IkL~KOkpZG_3+%?!sG+{+^B3kURVgN?};0 ze!ipGk?oF+Yu5^KBXkLEjm%Bf!Rz)bTvh3x4`Nrn!m@s#9v6=NB5R~o)!I(1myrf) z1LZnu2kuJ2RR(EeC9bZWo^#$C6`Pq>mK!4?p_#=y z;7oIx;YZVP%Wm7PZ|>lAZi2r3`s_WI$bD^h6L z)s5?i!5iOUk?gIr$k2|W(){_vy|UT8ij>Si&4wRq8!rO6o>inXStL2yA=W22A9MSZ zdKxFoR&pA)#!_%O)+v|gh@Uf7I+y7P@Wp7wK8mb-lAeyc{npmC)}D($MCu%aGo1m? z@8QKZBrL`gyebG8=RSnY_zRw>XUNkK98+9`&w10EAM0 z+j0*UhCQUzb2&;RuHARW@r*8 z3@z-shrh~%Q%^#h=TbPpuL(Wq?@C2lMaD%^4z!e;s+5*^9S(L;O`7996B+7WA~!KD z@~C31qIl3Y5;Y=JP3Lb0ZYp$<`Sx!bbg^`a_UcxWvlA|=zRJBy{CGM9IB|ASYe zv|kUmgBG6*Y-X$7ftqCF?Oy{=I&-7uG30z3q@NmP9H z8$NVWJ_>4KR7$*JdAr}a@gb9~kC}dAN#>=$r+tNf!ul?2p@bEI_C_j{oP~0-w6O;Y zr+F^KVZ$!^fWl!Rd%%PafgAe)6`Y908~2-%^uK1=x+w1;)udE~6$Ij1seS<*OiaxG z9&4j#VPX9LjI}8WH{YuiELf(U=O#FCt{q96$Yc@{!n{bUe)~eu3vRd(BW0_D{M86b z?z^I*!%rchi0m(*GBp-dXxP!%=%AtrNWHX4poP*6LXi40*Qd=E-CF*)>1$D{$gs$9_MYBSB}a)y|NHH>2~Wspdc` z-DU2OB2UQgvUN9=>FW5YS-_BUQ@oMn32AqsCTCEeM@}5n_K3`V>)uh{QT+-^FB4u&ex%A5OnjkfPi)R+-32Og zVpy22Y?P!j%bm*#^0?~i>#6#uZftM$Q&Ht z02!@|_qufcPQFZQ9V4l=_V;vtE1Gl0`q8gv2uupI94YiB2E3fcM!#ZM9NTyt64IHEQ((Od#g7yvhqa3p8~dj)8#nV+dK_sKOB}*$FhI&y_?6FaAo#TKL@46T_6U(eQypLeAya171-X?zfg!I22W0JOd#B^YC@ z6H!G#te`W?avxD-Wkv-_OwovziKGR(ksOy5V#@+i&p5|ag@DK#=pBZ6_aZMOW9AP3 zZ`y!s#-Pkj0bI^%-FiiRDOwt;NFzk|*bxdT&#UvXxRQiZ4c<0UaalaC#J5WlIQpYW z?I+x)Y@3Ebjf=Fvr@Nk4z%A@aYHr_T^M*;5nzN{IIxo=@-2pOZK6_lK4bqY0c&#uh z`&_^i;qKFMLSe_HT;;Y4mEorDEO(syu>_}hv%X1ek9DjAn8^J_GON`SoHNgKgMJJ5 z>(PPC>shGHQV#WP)+yK3xbwA)U7WxG)vng5-hN;m4CT#kK3Uc$U>U%ZKON@D|ERyX<1#rWNdk+;u)QaHo%kEYBEKKfBT0;*K9_cu;Bv?Q z$3D*JfhpWC(%*W#Bx%)tiMq;W#IEF7p1WNKvycebxmXN5wn_5<>Az)`n9V?n7%y4(O7#DjV^&>~Z6Q;tl^e{TdMxAb#)tuL>{ z)3y;4uncv)eu?eiyF5*P!?#z9KKxe7fPd>O7tJfHY}Ory;(zBdoTS|%7;$!>E6MA+pgey$7=E_~Aiurm)JGYzTs#>a_wavbWf!oTd_vhL8A zTAC|LHvzI$gl-uFHhA8jRb(;}bD3!zlK|ru9LAXR61s+qB^K*0CL>NW_(p~ zfjGnErQ*7P`eQ}({fpPntxGtQ(F!bH9lmK5r6vxKw%3`L4-#O)e96uUDg~%+c^cU} zsnBY=kH`K#ve3z#aa^<2l}w&HDPLfY*6nc@t%<8{K(ucqdr(q{M_>uJ*f~`mJVI%GR~>HGSceQCci zSRuBin((@yqbInB8yL8c8Vm4w1k(n@+!8X_9?J27NIV%SIZpWtooO!W;fi4MZDnGeEB2PLjl{;g(sdf1dbgBz{L zMBm{b{|||3?VVofSM4JL1TZbzk3$z*j#hF82!hh!Y$Ky=Qk(S=DNjyzEup-9y?r}; znS8LtMj^iz*Gzj%qz4lb^j-=L8+K#-DJ|_JYXV1(-)V@QODMx#?{M3C)v(Tb-$fNP zm@xuF?fkLxgs!Q`kb9YCMQvvPeIur=!gXzOv+eQxYNi2w_q53t7+#k|8I65MeXZJ@!52mq^`rC-X-hfBaH2ro}v2Ze(TilQa!0WmE>S|ra1d9koqo^ z{*oR+eK~|GTBB?#im9pIgkqaNo`38@@cQY8zxwC&KOxOySHx#2DAb~wEEe1KO>Q8j z)Eq8Qjl8~XaL-`Xlm7aPV%2Z<)K3r6LoN2*+s`m(!}$npuG8_C?$i%)CnJy%7wew) zgXt4kSku)HBS9axr(-E(D!Tmw;BQmv*g^4I@@%O>!XheKGqj1AmFh$(e7+ zo?Da8?J3Oi**&9%Vh0L%cUwBej(j5k*oLJ1xLAI2{^W^6I^8kN6$o-Eok5m2RQis` zi(vXK%VrbnI&(VtYAKl2=f&`q6onE6$-V@WNLd!XtmroawaW2?-Ck6;J3PV`dhVZR z{gPolZ2E{L^f4yznuG-zrAZtC@E#udZofTRm8cgcFD*FITd?f`@zJ)FYt7VWKr{9C zgo>v${`7tU{HT9%n4J4?A~iBt#**Qmwo+q@ZhY~*Ykk-8hr~nF>EX+fv9P@6j6D#S z1JBD4JXUTH0-h;*)Qf4}!V{;}peaPmbWRnAHvNVJGPn>kVB8Z58M$FCVSH)M+*pG<8&})NQ`?Y`zH< zzBK$!%GHx4GhZIMKKF0sVsUjASWk;~Cfi+u`5QlV{cHb+`qR*Dqq4hJJQ#^uZ4hv( zO30=BH$sr9{}=wt0)vrCakx~51C)kA3Zo^a5#VzLKdIDnP?&iWP=%$w)R5J=e5rd2 z#mi{-fI6A5jzJ%G-_yDln7;}uNPj+!OXETkzrbH+|2Xn3zt()hd*XG)`?VLYvnxhC z?U4<`tbw#DgMkG;cK))FiTNPLzB*3sndBnN5K3xv_xc+l5l9ie6GLHMziD9(Q|(-p zw~aAIUa0Ay9|;avAzgFaDqYa_I2@vSiNIE@UYgv5_qg7jeokT)eH482aus+H_oOr- z?uAXq{qiL60p^k?8-FDR?nv~Y`<63lxZMD+nXJI8%iTg{a(r35t?-K7!FT_@B)fR!ySf}FV z8)eVQWcwY=XF&@nv}u~LQ+W!?fa!8-8x=ZUt3Si{1%b;M+AStXmI>`SB4R_0FQGrS zVJoWyMS~s0J3)Bv*jx*}>O8W%K0;8Wc%Imu`Qw=wB2%6z9&-bKVKB2u6jb_^z4kih zKxFVFuSX(XC6JY{E*Lx8UV9s4ik6~LBzBcq4dGI=(Yl`9!J+3>uOf!=0dCuCU~p&J zf&w*qP5M$LI+dBw#C>OYOml#!I;LYX3omxs2$DbRHE0os)ep3{ygu;FF zK376ma-LwlPfbqV#T}WiY|fy327p3n9v)D5#6$6pfMx9YOk8)GxNg>PU#j6fQX~DR#`?Ps zo?&)NQ*GC_#?RWyF49=>8rmRd#+7-7ET6qEok|4fnyb^v!ITMU??m}Ec{TsOvKi;6 z(dS256X#~oR5==5nXb~{LR8QGMt?4sLSn$zl9fgN@_fbpn93oY`l*DyM4ZiZWRb?b zJ?zTghzlufrx%Q36d7gHIVxgjW&$pJbp{e!t$S6leCD-U_ zCJ^b6v?E3FSL{rihNGO5+PprQR&hw-@0JwS^~gQZqyy|ON4upf}VF;ShA zo}@ZYw7RdP_l^?qE}er5YvUpa%QyMGTu4g@l%wK!db(yYw%u^GT zhOj&dUjNK?vY_?13#{0sAZU3nj2AFyKthS2JE2*5DXUOMcrDaaG@+2=++DHqaVs^7 zD-C%GE+dk#+)C&N6)0%=UHHAmELjY+k|AZ^t1`)OM3nD4V;jUa5-<%|HjO# zkR@);=iqKqW_uyWF!(_MiBeBJU*Pu-SrQb*A#f$e@Be|oKXe3TQl>b8o zw^jt)N(gHZb#bmLxu|S^fDMi>Bdy;|UMN43F}yLqhAppcmutNs@I=ToBzNR*p^9I_ z*5GEXrfk>U?FqE&eKDYGZ4FLx8{cSCe&3qePa*rEQ)Agbn=)e0$>jlJGO*d`-T?5m z+=HPF$m8p5aQ~F)DLrqQSIteqZ@cJv!C&{lU+8MM@ua(qSGYN0571Y7xAoRXj6qSn z6)r8N!ayrH4E-vFGH|)#?=<@@OK6&6xl>tv)IjUd5O36272~3MApR_^(JWDg_Y{9U z$HThH1LOVp00}<1!_vwnZT2c=iUfE|*udg7-^6i*B5l@sTqLFBRd^RPGk8pOIp3be z8N`)96#BrK85xrjyj_yQhjXvTVaA~IxZ*Xr?E9EPVtWk~ZBK1l(q&cGM#?zDeL6_d zvyZHz+B@mlStJ|TJKJ6>v*1d31fi1M2?`MEkJ$XcQP1Q~2%sfvg_7`XPzIDJ#2eE| zNX@$0@O+ZzhG}3Z%EO>oxMm4j`Sw7_`a)OqA#9)gmQf4D0)tms_#K-Y)1jc?EVpus z7VEoPqmPu&Q|hw>p9Il$u4HLD@Au&^SaaGu78gotC&Hbs^*ryJMN32?im?FOkBBXG zv)8w?79{ni@A0|U?xN`NbU+i=YIt4w4tHoBrPb zYS?&YZ2DtQesLc_AMU%;H-M`qG-$M8yohUuU$UP~204xG)b{*Yz`wSgT0fYxKZXB$ zb??-QKFy#B+pA?ZQBP{3nbN{BCqN{RfQ}jY>{s*uhZdgEL>sKk>)W~gIcB7A)8l4J zPJ$;cU)|-;&dWt5@i{{E^ZKpxytvod;o0>S>S%}FIk0+naxlI|U{tE#k9u9Q@uuZv zCT@80rqN)%Uh7CzFeBkw|4utO^c=-HioGYQ;m=bO3vmfu+{Dk4Ai{!PxDPs~nVAfy zXzycAD?0}9m;Z;X+wB?4Ob#%yy2MK{{~UlI3C|MDL)fQ2&^1>` zXpOJ*Q|mvo6gsmr@O5 zZ>tdmiP+a}l#qapIT*NK9_am`j;b*@}RLAjKn#0jf zPN)fXo-PX}5P#NNkUjNQVl03HCgA;2%1=U5=FP||x65AHK^nMWw27bppF*w!D2i>{ zil`__!666|1WA%-7>0~UmYg#qIY-Hk8#+ zn6=&Th@v>yk>EZhb~ii>*uqb&QyAr=lEE)DhPUT!Kn__0*v#+$^gniHY@DZue2R}b zo$(``qZPFjQIMvfR|KU5-s4giE&X6ab1ac!a4*p(2h}$b#s=c;$U2SXm@8wX-+Spi z>s$dp2veZC=jKxWV~@wgol!W^;%f7QBNr~g0R*Czt${6Cq$M$MG&Cttwzcwba&VB! zd#eZ8h~9Sr{7xJNnLK(!dOx4tXtWH*71bZTO$ZrTcW_xBb_vEJ7TXDiP*imf1$EZg z=BkkGf@3sowgyBY3JJy4^v7;ZbCVP$DL4{u4xguNK=UfAV@Sq0d#H1lDz0t@?iLv3 zC;p*HlH7R|)&b{|x616_^ul&~sh*4B1XHog zcAsE5U+Epq2>4{})=3cvzL+adKgB<6$HL8OFW?FnK}cxZCgfUwuWCoyGqO{=0MZjn|; zwv-V?IRq}cme{g?Z%8kH(DXqi{ zdw_na4M(dJ7$qr$?pJ};G-0#5Z-&Tf$XuCcsh8}kj-+=(qKf2!YLH@%31#&&+;_XDu1~yv6Q&EJC_3^5ss$!>5y_(#G z6u&_yH@=ZSM&4!^x3t7_Bb5rL$+y1KOB=$Q%EvC{Wzf=$=HWyYus5xj2UFB>E%B_3 zZMh#Eq{K{l&JS4O-#V{b-8uqFDkpN_tSr+eMmjqFTdg!W^yk2hQyr_oaxboc3>RNa z&$x5F)o31cE52lSy>ymjB%Ld{L|xoKTzzKQMj@-G_IBF92qS6UaVVD?8)F!%dvcD> zvheKn&VsWW#_7rBSZl&?KpQVyMQ; zx8WAgD<{)YVd>Y?EmYbYh+>A3T8sEE*sa6OnIjV)7Dvxx{jr-HZMSI!_0xyryg75* z;tv+@5Y*sNMGlruGWDk?GmFA&h0;xacNJn412d5(m~y9OPIT+dU1PA*E^}9)>S*+IOfw*gL!?f#+fMgl{ADr;W7WIe8yZT3XEX(n&m3y5%5UXu#0b<~ z`n`Vm^`zpJ=JWW}fKaQ^)x2YknE)woORu6FTp>u6<0Jk@%))JCd^+v>ogz*T={puv zIR4d!Id^MQZ?mc#m&EDC)O*NAburdz*rS>9x_wMn)Z&6(mn%Tg4RxQW75g5h>AEFF zpo%P@#~uJuS*l@9dTaf!op@uMV4fm0c};1PLtoDIwy|jy)b=YRBJyt7=F>%%#pYPn zk5a*EE^BHkk;bIzi|)-$COip2Zp_n{wiyydVuI>TNE;!)#s0Was^2qCYERBo_B)Sf z`07k9g>N822>?Wz78fpwi@rXM=zSS_o}qRTDL?lyrAR)i04Zr4l`%Ua{HRDx-)pM@ zeT$Sn0ADGg7C&S#QiwS5iJLxY^yP*^2_EUW+LX{lz%Pw9u!dB~;Z!K;>}y1JM~#fdj#}KTP(AHY;qpz-A>^c1 z4`bP!xuX)H&d#W((-^JL`f!z*SWSiV?MgGE$A&DuTD`4_m0CRcOB)B7L)Y4=BO@ye z6hzSltz=habq`M7$KkcL2b77_;WxAmdjH}$$-r!ps)KB{9ma$?rqpI=SmeYD1J-(^ zu$#dF3k!5NJXt3`KE!Lpx_>wIlTLakOLA0iAIERMwFB>&Oh?s&V~;QiTBt&e2QRpp z1M-yl^7~fHzgL)*jw#=UiK7OTQ40zg--Nnx@g2ZKX_;8>iq-k2_RKw*I}xeSwoz5q zex>COx_+X{{nu+>$An`GO8nmzS&7*z6u-#0p)=JAG0P|f>1Si4_>~7AleWxNMi4sK z50>+B(+D8kn?7Mq`i@x&$qpE-i&2e(^JxJf<2Wm3mY!!K^ga%aJ6o9-HXyr_?zW zczJf3=q=_Mc!3SJ9?N;EXHrTz$t3t9{v2r8UqRTVQz9;D*|4CatY|I!b(YP|)TJCl z#N4)GB~4IRIRmmN_07`f!nyXGqPG*7Z=aWbvk>!5p=_W{jpp$t%QkiBUDbpJk9t0j=mJiAVg1P z`^j~6C@hOJYus&Daq5>i*Va`&V$H)2_k=(=;M|KQ^bFO! zeA9O8eVxpYnkJfqM_6-gX2EEIg*vb1Zw0J`&k_{tL~S9)AYb{Zp9^Dz#ouDWk`&z6 z*b>mj_yu@>EF)5x8{YY>L(Lq1FY*L~c zMBaFe@`P+dp1K&g-n)D9IHOwH3g=rnxt;cX+jpVHBdUDc_xjdv-iUx~&LtliOXC!) z3010Ms9vs{!_CRc$SUYdnwKK>2^jUh+^Rz|k5%#$@*^JY;m?DE0m?z2W_6@~KbiP? z*Yhhi!8Gmk&%_zg`K?#owEah%8>714X%pKDos1~3bnp zTv}`uxK}gs_>7IN)4IE+*iZmF$ZW*Mo?@;Y%j4VKGfsY_9SkMOC9Z~5r-Bw@yXU+I}9C;>gHTz@aDn? zdZ!n1bxMJUhRI5Mws<(vpny~Ry4Th}rE72?AnDtFj+$P&92k}L`#~%*ReyJ-rg4&D zg{??;M&b6gMdaC1c8~DlXvz#NkKDI6$cSDgyg|ZBbPMlUio9 z2wXU8aXI>SZVJ4W9G@O+5`X_l)#P;bk>9hZ+LXh7hE)|ms?^;(bXm+Ephk>aO@bL~ znUA$qt>dlnuJm7tDS<`-s8gCi0$j%h2r%4F{J_iPB4?T(*^d4|#eBx(f4s^wm+!ny zHCy}W!lGfM{np8Hdu03Khk@H2l5dBD0K%6zxMX$^P;lDUCrtsI?f2P)Mcy^ReSMVg zr#pxG+Afh0^6siOlmj~icad7HzCaTBp>lLGj_h3rXk7c9W->iJ*p98m>kq#*L#O+7 zPw77&PTSG4?@*sBj7zPytvaZ$vy0zJ9`3*T5Kdf0^yzmSsh?^4_XqJKOt!aZ4WE9S z^tit7|DdM+nachXXvmM*%h)yGagoS*$^d<%jV*NnfKV{at6aB9W>$eH#7z}IsH1FQ zdzWGCYpATwEduk4r*CPr>VJB@r_or+-w_aL%)P`@2ZEIl(P~#kPyecPs#P1p<43$# z*Wjo5ak6Av#Qi}9ETds>8p4SrAf~!U?eCg$SBCqXCesu15IE;Mo_4ri4dO= zl#h?`3jVne4VvO4e~snm1OL^5xYVRVkZV1>`Iidc+i z=YGP=y~|&%PSQTx1!2#g=fv1`5(Tb)M4dwiMV0*}T5#u!I~S?TINF`PKt<1x-nZ7R zM#@LI44AQARH4Y`o>I)GJ*6Unw#!Cm(Hs&hylU($qnqKxt20^bh&czLt5oomBDfMbDcQ&>1>nA%tZRY7MRF^!}ysL z507c(EDsNtqdz8%olZrc_qqK2(RTwR@gf=`luiU1butkYA?dQB67>55vr+8goF2OI z>4CM6JDa0|csrY`#Psm+hj&MAgIz`I?*mpZ9Ald;UpO+zJQ!(^e*xyJ-jaR5#NzSv zmf)=0MSHW>iUH9``NP-^CL?FV7b0J*T3^VFf-U}qWJk+?%v?3S{%ZC`mM@+VX0y{8 ziZ_!b3Eqim|3xQqg{W6v*?fpVGxV_uJ7sTkxtnL;Co{1biq8nV#{kuooy=^`@&t$s z{>riMk8O!dy=(~u{3$1A1zuU7g{;0H7dRIrJ0nguGooiDQW=vw;L}KCxwU4N#iuk8 zgEw%^Zs8YRBVom_2z;+Ed`jao`v!?DhIHHh++E~A6~3yS8B)LJS}}e}>qZ1SiccwE z?lzn`K$b@`EAV^4j{g1D4xImq$S3#Er$q0VC@Gqv3$> zGnzlUH8na^P6LW9*ErZ+DB)A$}OY_;-85a`n=+{d_gYW z+R<#fp+WaR-y(Lcz1?)fP0(^7Mtt{Hg5MkYsSHi!thU1pzxr7GYJ$OLgFm)iA0OAH zJsU{r1#~G7FjG2qD3ez(e6g^O^LfZ@+^aon))OAeOex~k6P|w6^)vR15JYhwJuBO* zOfK4%X#+<5di=25yM5}VJi*>oBinPE6QUTkI4hG!80<9Xb}vcMJjLhD(QO85&fOY5 z2TP0A3pZoot6i=Ues}rS$ZgIays=br8*cX?{)ca}EEm(xgk})u7fwC!t$?=pl69Qez0?Q6U@Z6)%m)!-??HnFg!rL|?e#lOy!)Jc-+T9|ulD;@&H9aJ zK4bm6P;-sYage7Vs8PZLoauQaF z@?BXz^DE$$>oTWj4G&L;Rhm|3w?%i@KWl%s!Xu5*)>~h*jk%2hyZ4wT_&y%5=?SN^ zPy6S0ug6S8z7%Xf7%U3&zYt_5_e=IhNXkDvZ3e=KUDSuZeyzbl7-zXvj{|v?NwO6^ zD3;Yl_;K86hrisB#=2=pWHKm!U-5u2G~jJbyG;DXTzr(eY~ho{OV(mb96r~}?V>qN znLcUZ15rVn5wS-F0M&5Jo4kY?{=u+16o661^rM)Tksrs|1EMc%pkw!P4 ze#6VeImVb1L*`x52v!goTrGmiU3!JQeY@AzjsQe z`4cyEH{6WTo|s!qT9=eeyOazRCp-V2VkEHDW$s~c0Mc(`lQ{En5T<>ByXnV+uznTWfyTM zhO&uve%G1aE-oVSR|J6>P~_8kLWLaAmMPXa6~e8C?>-nj_if$$f?NIcd4@jU_U!>H z|5Ecu6rdgq!5q05J8xKPr4vg$3ou?-*y4^TK^MRX4+0bEZNks}q6K9G0UB_^lupYf zF>SbBi*B$N$Lt3>=7FxENzfO+4K8iy^Jn$Qf-uZBi>c^PGZS*EWWp-E6Z-E#4 zQXI}rwbZd)zG6On+1{TQhc)aOhV}xr=}P{bAXu-oVt39xZY?&|&CFCsWoxtTpm(m*EBjvJi&I5QU^o`Kd7IwjRPLwx0!`~)n7 zuqsVWOFEGmo6pZ24}E{NNhkEsz1lCyNQPh!>FVumS07y$VV zN(>JtTFz@Pj3c`&DjlA4UO*%AEQsA=n#rpUnO*-Z8sZ5H`j>dm1 zk2}_D(Uh3e_sSwkx@|A6pj~%Oa|ClnHcD#O>E4Z%o%<#u(5Hl{_VYS?g{Co zq_XnJ!A5nQR3H)0$}BZk5U8rCxl%`@1+q+|ucg4BLtQvbE4e(T3Kce5H2j4yU71q3 zNLX1I+%yJm;3rD>&Jp>=SVWZ2$iyq(;YLCZebVFp>%^;fcl21iqa%>)ICTqr9BNF; zK#~Lq$A}0EFb+MMb{u^-8p>$Bav&zq zDMBQ~kQy4#EHW?e{SLGx?s^w;- z8%Za~3D@9aAQyWvUasnPb3tu~&PkPl`aY<_ew%?j6j83~;Rb{w?@~c|Pg=(5(L-kU zL1`~Eqpzg7456dB2=hrUM#nuWDTLsWj`2tz@Z|V^p;at=bXBG55P}LqQ>HsHDevl3 z%bnPFc&OrKEy#IMr%NxdK~O7A7Y%dyATH-Ng&nN0iNvj(m+}#~KX0;BR97JIc5E+0 zS5f{$nG^ihnFQdx4uDAdxNo^Ue~HoOq2%tgI?f|_n)2Vo0>`?m0}a6CamLa0#SOc`5f7RrkR zGfp8PEH6DoyL${`jNvfOu){F;Fzv9xu*5J|j2r=9k~)dIncm440i6mHIu?1EnIXca4BIe=$@mm~6PR~+ zdkDp!E-Zk{uX~%lO9zN<3oET7>Lxzt$JFa_c@<7X2O@T#yK)J%hMM0D2g7B+mZ zjV_YT`2DgWadXPQUWcs{MUG0>ldr#qs3TOroYX-Y1j4}k6fu`Xfy4{nc+k}D3PSfs zk;QqqAd3xu4WK3npyvNq{l~x|fcCJS`Gq%w1z@q0!Tj|U&!GQ>3~tx^MHb^XBq+PV zm%mGdoqzT9*^dxAIj};qQ^ZknZjedpO|Y((upR3d&*#4%EnF0!*jDh;QZWtXJhsI% zpKO!Z+N3+$sYZ|3+5o3L&SDQqiRbI>IcHIx0c@plF3Wr;F&Nx*VZe@+O1h6+HWHi9 zD{iJZ8Ng)G8gL%`r3x|SSUfBnU%sHW=EP|65mZS~6C(om4WpzXd1i5+Rai-ebv%)T zdC>`Q{4?17VoGu7OykJCba+R;C}P&{1^dzvzwGm)XxF7#+X@WVX)drL#~;ygWNFNNh0{EM4Bc)=;)SaynYB6cJPtn=Xj;N-Zuz?6+Z?0q99Vd;z{!C1x zYV{kqy+hu-Smw4JLGvJfICQlAn0I}=#w9veQCa7T=4smM-wcz&TUA8#eCVVrgL=SS zZ*`E?;x-b#QZm80lO(!)4UNYTa-96G6G5N@L`)C-1^ZwHmCOOYQ4H>FDd=rE z{jd6u!Op8of5#dW9oMk))bMRV9h)ef82pQ953xc`1NM+%i?@;EIX_jfQ1M74^!X_H z3P6!waTeba-$7)Fb1H>~_lJSc>emPcL%qxMo}=uU z1XWpb!cVrJs0Wky?FAic&%LFP5JQnGvx6VAD@Hrf*?wk_TM@eZWO}6)xIral z$)=jZo#WCZgV;Skqx{)(2fR-u=T1Ijk@;lnJLRZB92@T<&DJ zEGdYoLjS|r9>?RY*1v1?8*R?2@3-34q$}9$Hh5bHklP0!|Em8Gz}CTb%khjBXCDnD zQyT1IP;f14Si#M+3`>Z}53&Qs{DN2+YgZ(OBS&8i9F{!k6Ni&_* z7aTtOy*#f_R?Rk?fZeZK6BswF;+4o+^*3QDP4N#!Xi-I3jX#X5CZeWAi!F{w<`)

yC`>x`7SkxcW&AY>qT1V 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 z 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 diff --git a/pom.xml b/pom.xml index e93e953..57fbe1a 100644 --- a/pom.xml +++ b/pom.xml @@ -246,5 +246,26 @@ opencv-platform 4.9.0-1.5.10 + + + net.sf.jasperreports + jasperreports + 7.0.3 + + + + + net.sf.barcode4j + barcode4j + 2.1 + + + + + net.sf.jasperreports + jasperreports-barcode4j + 7.0.3 + + diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java index 6cb0169..31047bc 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -7,6 +7,7 @@ import BsK.client.ui.component.CheckUpPage.AddDialog.AddDialog; import BsK.client.ui.component.CheckUpPage.HistoryViewDialog.HistoryViewDialog; import BsK.client.ui.component.CheckUpPage.MedicineDialog.MedicineDialog; import BsK.client.ui.component.CheckUpPage.PrintDialog.MedicineInvoice; +import BsK.client.ui.component.CheckUpPage.PrintDialog.UltrasoundResult; import BsK.client.ui.component.CheckUpPage.ServiceDialog.ServiceDialog; import BsK.client.ui.component.CheckUpPage.TemplateDialog.TemplateDialog; import BsK.client.ui.component.MainFrame; @@ -87,6 +88,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import javax.swing.JFrame; import javax.swing.JTextPane; import javax.swing.JToolBar; @@ -167,6 +169,8 @@ public class CheckUpPage extends JPanel { private JLabel overallTotalCostLabel; private static final DecimalFormat df = new DecimalFormat("#,##0"); + private List selectedImagesForPrint = new ArrayList<>(); + // New member variables for Supersonic View private JPanel supersonicViewPanelGlobal; private JPanel imageGalleryPanel; // Displays thumbnails @@ -1328,7 +1332,35 @@ public class CheckUpPage extends JPanel { medicineInvoice.createDialog(mainFrame); break; case "ultrasound": - // No dialog needed for now + if (selectedImagesForPrint.isEmpty()) { + JOptionPane.showMessageDialog(this, + "Vui lòng chọn ít nhất một hình ảnh để in kết quả.", + "Chưa chọn ảnh", JOptionPane.WARNING_MESSAGE); + return; + } + if (photoNum != 0 && selectedImagesForPrint.size() > photoNum) { + JOptionPane.showMessageDialog(this, + "Số lượng ảnh đã chọn (" + selectedImagesForPrint.size() + ") nhiều hơn số lượng cho phép trong mẫu (" + photoNum + ").", + "Quá nhiều ảnh", JOptionPane.WARNING_MESSAGE); + return; + } + + UltrasoundResult ultrasoundDialog = new UltrasoundResult( + checkupIdField.getText(), + customerLastNameField.getText() + " " + customerFirstNameField.getText(), + dobPicker.getJFormattedTextField().getText(), + (String) genderComboBox.getSelectedItem(), + customerAddressField.getText() + ", " + (wardComboBox.getSelectedItem() != null ? wardComboBox.getSelectedItem().toString() : "") + ", " + (districtComboBox.getSelectedItem() != null ? districtComboBox.getSelectedItem().toString() : "") + ", " + (provinceComboBox.getSelectedItem() != null ? provinceComboBox.getSelectedItem().toString() : ""), + (String) doctorComboBox.getSelectedItem(), + datePicker.getJFormattedTextField().getText(), + getRtfContentAsString(), + conclusionField.getText(), + suggestionField.getText(), + selectedImagesForPrint, + printType, + templateTitle + ); + ultrasoundDialog.createDialog(mainFrame); break; } } @@ -1420,6 +1452,8 @@ public class CheckUpPage extends JPanel { imageRefreshTimer.stop(); } + selectedImagesForPrint.clear(); + // Only stop recording if it's active, don't cleanup webcam if (isRecording) { stopRecording(); @@ -2469,14 +2503,50 @@ public class CheckUpPage extends JPanel { Image scaledImage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); JLabel imageLabel = new JLabel(new ImageIcon(scaledImage)); - imageLabel.setToolTipText(file.getName() + " (" + originalWidth + "x" + originalHeight + ")"); - imageLabel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY)); - // Set fixed size for the label to ensure FlowLayout behaves - imageLabel.setPreferredSize(new Dimension(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)); + imageLabel.setToolTipText(file.getName()); + // These are important to center the image if it's smaller than the thumbnail bounds imageLabel.setHorizontalAlignment(SwingConstants.CENTER); imageLabel.setVerticalAlignment(SwingConstants.CENTER); - imageGalleryPanel.add(imageLabel); + JCheckBox selectCheckBox = new JCheckBox(""); // "box only" + selectCheckBox.setToolTipText("Chọn để in"); + selectCheckBox.setOpaque(false); // Make it transparent to see the image behind + + // Check if the file is already in the list to maintain state on refresh + if (selectedImagesForPrint.contains(file)) { + selectCheckBox.setSelected(true); + } + + final File imageFile = file; + selectCheckBox.addActionListener(e -> { + if (selectCheckBox.isSelected()) { + if (!selectedImagesForPrint.contains(imageFile)) { + selectedImagesForPrint.add(imageFile); + } + } else { + selectedImagesForPrint.remove(imageFile); + } + if (log.isInfoEnabled()) { + log.info("Selected images for printing: {}", selectedImagesForPrint.stream().map(File::getName).collect(Collectors.toList())); + } + }); + + // Use a JLayeredPane to overlay checkbox on the image + JLayeredPane layeredPane = new JLayeredPane(); + layeredPane.setPreferredSize(new Dimension(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)); + layeredPane.setBorder(BorderFactory.createLineBorder(Color.GRAY)); + + // The label will fill the entire pane + imageLabel.setBounds(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT); + + // Position the checkbox at the top-right corner + int checkboxSize = 20; + selectCheckBox.setBounds(THUMBNAIL_WIDTH - checkboxSize - 2, 2, checkboxSize, checkboxSize); // 2px padding + + layeredPane.add(imageLabel, JLayeredPane.DEFAULT_LAYER); + layeredPane.add(selectCheckBox, JLayeredPane.PALETTE_LAYER); // PALETTE_LAYER is on top of DEFAULT_LAYER + + imageGalleryPanel.add(layeredPane); } catch (Exception ex) { log.error("Error loading image thumbnail: {}", file.getAbsolutePath(), ex); JLabel errorLabel = new JLabel("

Lỗi ảnh
" + file.getName().substring(0, Math.min(file.getName().length(),10)) + "...
"); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java new file mode 100644 index 0000000..aaeda0d --- /dev/null +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java @@ -0,0 +1,568 @@ +package BsK.client.ui.component.CheckUpPage.PrintDialog; + +import BsK.client.LocalStorage; +import com.itextpdf.io.font.constants.StandardFonts; +import com.itextpdf.io.image.ImageDataFactory; +import com.itextpdf.kernel.colors.ColorConstants; +import com.itextpdf.kernel.font.PdfFont; +import com.itextpdf.kernel.font.PdfFontFactory; +import com.itextpdf.kernel.geom.PageSize; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.layout.Document; +import com.itextpdf.layout.borders.Border; +import com.itextpdf.layout.borders.SolidBorder; +import com.itextpdf.layout.element.Cell; +import com.itextpdf.layout.element.Image; +import com.itextpdf.layout.element.Paragraph; +import com.itextpdf.layout.element.Table; +import com.itextpdf.layout.element.Text; +import com.itextpdf.layout.property.HorizontalAlignment; +import com.itextpdf.layout.property.TextAlignment; +import com.itextpdf.layout.property.UnitValue; +import com.itextpdf.layout.property.VerticalAlignment; +import com.itextpdf.layout.element.AreaBreak; +import lombok.extern.slf4j.Slf4j; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.rendering.PDFRenderer; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.text.rtf.RTFEditorKit; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.*; +import java.util.List; + +@Slf4j +public class UltrasoundResult { + + private JDialog dialog; + private final String checkupId; + private final String patientName; + private final String patientDOB; + private final String patientGender; + private final String patientAddress; + private final String doctorName; + private final String checkupDate; + private final String rtfContent; + private final String conclusion; + private final String suggestion; + private final List selectedImages; + private final String printType; // "ngang" or "dọc" + private final String templateTitle; + + private static final String PDF_PATH = "ultrasound_result.pdf"; + + public UltrasoundResult(String checkupId, String patientName, String patientDOB, String patientGender, + String patientAddress, String doctorName, String checkupDate, + String rtfContent, String conclusion, String suggestion, + List selectedImages, String printType, String templateTitle) { + this.checkupId = checkupId; + this.patientName = patientName; + this.patientDOB = patientDOB; + this.patientGender = patientGender; + this.patientAddress = patientAddress; + this.doctorName = doctorName; + this.checkupDate = checkupDate; + this.rtfContent = rtfContent; + this.conclusion = conclusion; + this.suggestion = suggestion; + this.selectedImages = selectedImages; + this.printType = printType; + this.templateTitle = templateTitle; + } + + public void createDialog(JFrame parent) { + dialog = new JDialog(parent, "Kết quả siêu âm", true); + dialog.setSize(800, 700); + dialog.setResizable(true); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setLayout(new BorderLayout()); + + dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) parent.setState(Frame.ICONIFIED); + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) parent.setState(Frame.NORMAL); + } + }); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton saveButton = new JButton("Lưu PDF"); + JButton printButton = new JButton("In PDF"); + buttonPanel.add(saveButton); + buttonPanel.add(printButton); + + JPanel pdfViewer = new JPanel(); + JScrollPane scrollPane = new JScrollPane(pdfViewer); + dialog.add(scrollPane, BorderLayout.CENTER); + dialog.add(buttonPanel, BorderLayout.SOUTH); + + try { + generatePdf(PDF_PATH); + displayPdfInLabel(PDF_PATH, pdfViewer); + } catch (Exception e) { + log.error("Error generating ultrasound PDF", e); + JOptionPane.showMessageDialog(dialog, "Lỗi khi tạo PDF: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); + } + + saveButton.addActionListener(e -> savePdf()); + printButton.addActionListener(e -> printPdf()); + + dialog.setLocationRelativeTo(parent); + dialog.setVisible(true); + } + + private void generatePdf(String pdfPath) throws Exception { + PdfWriter writer = new PdfWriter(pdfPath); + PdfDocument pdfDoc = new PdfDocument(writer); + Document document; + + boolean isLandscape = "ngang".equalsIgnoreCase(this.printType); + if (isLandscape) { + document = new Document(pdfDoc, PageSize.A4.rotate()); + } else { + document = new Document(pdfDoc, PageSize.A4); + } + document.setMargins(20, 20, 20, 20); + + String boldFontPath = "src/main/java/BsK/client/ui/assets/font/SVN-Arial Bold.ttf"; + PdfFont boldFont = PdfFontFactory.createFont(boldFontPath, "Identity-H", true); + String fontPath = "src/main/java/BsK/client/ui/assets/font/SVN-Arial Regular.ttf"; + PdfFont font = PdfFontFactory.createFont(fontPath, "Identity-H", true); + + String title = templateTitle != null ? templateTitle.toUpperCase() : "KẾT QUẢ SIÊU ÂM"; + + if (isLandscape) { + generateLandscapeLayout(document, boldFont, font, title); + } else { + addHeader(document, boldFont, font); + document.add(new Paragraph(title) + .setFont(boldFont).setFontSize(14) + .setTextAlignment(TextAlignment.CENTER) + .setMarginBottom(5)); + generatePortraitLayout(document, boldFont, font); + } + + document.close(); + } + + private Cell createHeaderBlock(PdfFont boldFont, PdfFont font) { + Cell headerContainer = new Cell().setBorder(Border.NO_BORDER).setPadding(0); + Table headerTable = new Table(UnitValue.createPercentArray(new float[]{0.3f, 0.7f})) + .setWidth(UnitValue.createPercentValue(100)) + .setBorder(Border.NO_BORDER) + .setMarginBottom(2); + try { + String logoPath = "src/main/java/BsK/client/ui/assets/icon/clinic_logo.png"; + Image logo = new Image(ImageDataFactory.create(logoPath)).scaleToFit(50, 50); + headerTable.addCell(new Cell().add(logo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); + } catch (Exception e) { + headerTable.addCell(new Cell().setBorder(Border.NO_BORDER)); + log.warn("Clinic logo not found", e); + } + + Paragraph clinicInfo = new Paragraph() + .add(new Text(LocalStorage.ClinicName + "\n").setFont(boldFont).setFontSize(10)) + .add(new Text("Địa chỉ: " + LocalStorage.ClinicAddress + "\n").setFontSize(8)) + .add(new Text("Điện thoại: " + LocalStorage.ClinicPhone).setFontSize(8)) + .setFont(font); + headerTable.addCell(new Cell().add(clinicInfo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); + + headerContainer.add(headerTable); + return headerContainer; + } + + private void generateLandscapeLayout(Document document, PdfFont boldFont, PdfFont font, String title) throws Exception { + Table titleTable = new Table(UnitValue.createPercentArray(new float[]{1, 1})) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginBottom(2); + + Paragraph titleParagraph = new Paragraph(title) + .setFont(boldFont).setFontSize(14) + .setTextAlignment(TextAlignment.CENTER) + .setMarginBottom(2); + + Table mainTable = new Table(UnitValue.createPercentArray(new float[]{1.2f, 0.8f})) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginTop(0); + + Cell leftCell = new Cell().setBorder(Border.NO_BORDER).setPadding(1).setVerticalAlignment(VerticalAlignment.TOP); + + leftCell.add(createHeaderBlock(boldFont, font)); + + leftCell.add(new Paragraph(title) + .setFont(boldFont).setFontSize(14) + .setTextAlignment(TextAlignment.CENTER) + .setMarginBottom(2)); + + addPatientInfo(leftCell, boldFont, font, 8); + leftCell.add(new Paragraph("NỘI DUNG").setFont(boldFont).setFontSize(10).setMarginTop(2) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + + Image rtfImage = createRtfImage(this.rtfContent, PageSize.A4.rotate().getWidth() * 0.58f); + if (rtfImage != null) { + // Set maximum height to prevent page overflow - ensure single page layout + rtfImage.setMaxHeight(320); // Fixed max height to ensure single page + rtfImage.setAutoScale(false); + leftCell.add(rtfImage); + } + + // Add conclusion and suggestion to left column + leftCell.add(new Paragraph("KẾT LUẬN").setFont(boldFont).setFontSize(10).setMarginTop(3) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + leftCell.add(new Paragraph(this.conclusion).setFont(font).setFontSize(8)); + + leftCell.add(new Paragraph("ĐỀ NGHỊ").setFont(boldFont).setFontSize(10).setMarginTop(2) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + leftCell.add(new Paragraph(this.suggestion).setFont(font).setFontSize(8)); + + Cell rightCell = new Cell().setBorder(Border.NO_BORDER).setPadding(1).setVerticalAlignment(VerticalAlignment.TOP); + rightCell.add(new Paragraph("HÌNH ẢNH SIÊU ÂM").setFont(boldFont).setFontSize(10) + .setTextAlignment(TextAlignment.LEFT).setUnderline().setMarginBottom(1)); + Table imageTable = createImageTable(selectedImages); + rightCell.add(imageTable); + + rightCell.add(createSignatureBlock(font, boldFont)); + + mainTable.addCell(leftCell); + mainTable.addCell(rightCell); + document.add(mainTable); + } + + private void generatePortraitLayout(Document document, PdfFont boldFont, PdfFont font) throws Exception { + boolean multiPage = selectedImages.size() > 4; + addPatientInfo(document, boldFont, font, 8); + + if (multiPage) { + Table contentTable = new Table(1).setWidth(UnitValue.createPercentValue(100)); + Cell contentCell = new Cell().setBorder(Border.NO_BORDER).setPadding(0); + + contentCell.add(new Paragraph("NỘI DUNG").setFont(boldFont).setFontSize(10).setMarginTop(5) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + Image rtfImage = createRtfImage(this.rtfContent, PageSize.A4.getWidth() - 60); + if (rtfImage != null) { + contentCell.add(rtfImage); + } + + contentCell.add(new Paragraph("KẾT LUẬN").setFont(boldFont).setFontSize(10).setMarginTop(5) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + contentCell.add(new Paragraph(this.conclusion).setFont(font).setFontSize(8)); + contentCell.add(new Paragraph("ĐỀ NGHỊ").setFont(boldFont).setFontSize(10).setMarginTop(5) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + contentCell.add(new Paragraph(this.suggestion).setFont(font).setFontSize(8)); + + contentTable.addCell(contentCell); + document.add(contentTable); + addSignature(document, font, boldFont); + + document.add(new AreaBreak()); + addHeader(document, boldFont, font); + document.add(new Paragraph("HÌNH ẢNH SIÊU ÂM (tt)").setFont(boldFont).setFontSize(10) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + Table imageTable = createImageTable(selectedImages); + document.add(imageTable); + } else { + Table mainTable = new Table(UnitValue.createPercentArray(new float[]{1.2f, 0.8f})) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginTop(5); + + Cell leftCell = new Cell().setBorder(Border.NO_BORDER).setPaddingRight(5); + leftCell.add(new Paragraph("NỘI DUNG").setFont(boldFont).setFontSize(10) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + Image rtfImage = createRtfImage(this.rtfContent, PageSize.A4.getWidth() * 0.55f - 40); + if (rtfImage != null) { + leftCell.add(rtfImage); + } + + Cell rightCell = new Cell().setBorder(Border.NO_BORDER); + rightCell.add(new Paragraph("HÌNH ẢNH").setFont(boldFont).setFontSize(10) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + Table imageTable = createImageTable(selectedImages); + rightCell.add(imageTable); + + mainTable.addCell(leftCell); + mainTable.addCell(rightCell); + document.add(mainTable); + + document.add(new Paragraph("KẾT LUẬN").setFont(boldFont).setFontSize(10).setMarginTop(5) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + document.add(new Paragraph(this.conclusion).setFont(font).setFontSize(8)); + document.add(new Paragraph("ĐỀ NGHỊ").setFont(boldFont).setFontSize(10).setMarginTop(5) + .setTextAlignment(TextAlignment.LEFT).setUnderline()); + document.add(new Paragraph(this.suggestion).setFont(font).setFontSize(8)); + + addSignature(document, font, boldFont); + } + } + + private Table createImageTable(List images) throws IOException { + int numImages = images.size(); + int numCols = numImages == 1 ? 1 : 2; + + Table imageTable = new Table(numCols) + .useAllAvailableWidth() + .setMarginTop(1).setMarginBottom(1); + + for(File imgFile : images) { + Image img = new Image(ImageDataFactory.create(imgFile.getAbsolutePath())); + + img.setAutoScale(true); + img.setMaxWidth(110f); + img.setMaxHeight(85f); + + Cell imgCell = new Cell() + .add(img) + .setBorder(new SolidBorder(ColorConstants.GRAY, 0.5f)) + .setPadding(1) + .setHorizontalAlignment(HorizontalAlignment.CENTER) + .setVerticalAlignment(VerticalAlignment.MIDDLE); + imageTable.addCell(imgCell); + } + + int remainder = numImages % numCols; + if (remainder != 0 && numImages > 1) { + imageTable.addCell(new Cell().setBorder(Border.NO_BORDER)); + } + return imageTable; + } + + private Image createRtfImage(String rtf, float availableWidth) { + if (rtf == null || rtf.trim().isEmpty()) { + return null; + } + try { + JTextPane pane = new JTextPane(); + pane.setEditorKit(new RTFEditorKit()); + pane.setBackground(Color.WHITE); + pane.setOpaque(true); + + pane.read(new ByteArrayInputStream(rtf.getBytes("ISO-8859-1")), null); + + pane.setSize(new Dimension((int) availableWidth, Integer.MAX_VALUE)); + Dimension prefSize = pane.getPreferredSize(); + pane.setSize(prefSize); + + int renderDPI = 240; // Further reduced DPI to make text appear even smaller + float scale = renderDPI / 72f; + + int imgWidth = (int) (prefSize.width * scale); + int imgHeight = (int) (prefSize.height * scale); + + if (imgWidth <= 0 || imgHeight <= 0) { + log.warn("Invalid image dimensions for RTF rendering: {}x{}", imgWidth, imgHeight); + return null; + } + + BufferedImage img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = img.createGraphics(); + + g2d.scale(scale, scale); + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + pane.paint(g2d); + g2d.dispose(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(img, "png", baos); + Image itextImage = new Image(ImageDataFactory.create(baos.toByteArray())); + + // Set width directly. The reduced DPI has already made the content smaller. + itextImage.setWidth(UnitValue.createPointValue(prefSize.width)); + + return itextImage; + + } catch (Exception e) { + log.error("Failed to convert RTF to Image", e); + return null; + } + } + + private void addHeader(Document document, PdfFont boldFont, PdfFont font) { + Table headerTable = new Table(UnitValue.createPercentArray(new float[]{1.2f, 3.8f, 2f})) + .setWidth(UnitValue.createPercentValue(100)) + .setBorder(Border.NO_BORDER) + .setMarginBottom(5); + try { + String logoPath = "src/main/java/BsK/client/ui/assets/icon/clinic_logo.png"; + Image logo = new Image(ImageDataFactory.create(logoPath)).scaleToFit(50, 50); + headerTable.addCell(new Cell().add(logo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); + } catch (Exception e) { + headerTable.addCell(new Cell().setBorder(Border.NO_BORDER)); + log.warn("Clinic logo not found", e); + } + + Paragraph clinicInfo = new Paragraph() + .add(new Text(LocalStorage.ClinicName + "\n").setFont(boldFont).setFontSize(10)) + .add(new Text("Địa chỉ: " + LocalStorage.ClinicAddress + "\n").setFontSize(8)) + .add(new Text("Điện thoại: " + LocalStorage.ClinicPhone).setFontSize(8)) + .setFont(font); + headerTable.addCell(new Cell().add(clinicInfo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); + + headerTable.addCell(new Cell().setBorder(Border.NO_BORDER)); + document.add(headerTable); + } + + private void addPatientInfo(Object container, PdfFont boldFont, PdfFont font, int fontSize) { + Table table = new Table(UnitValue.createPercentArray(new float[]{1,1})) + .setWidth(UnitValue.createPercentValue(100)) + .setFontSize(fontSize).setFont(font) + .setBorder(new SolidBorder(ColorConstants.GRAY, 0.5f)) + .setMarginBottom(1); // Reduced margin for compactness + + Cell left = new Cell().setBorder(Border.NO_BORDER).setPadding(1); + left.add(new Paragraph().add(new Text("Họ tên: ").setFont(boldFont)).add(patientName)); + left.add(new Paragraph().add(new Text("Năm sinh: ").setFont(boldFont)).add(patientDOB)); + left.add(new Paragraph().add(new Text("Giới tính: ").setFont(boldFont)).add(patientGender)); + + Cell right = new Cell().setBorder(Border.NO_BORDER).setPadding(1); + right.add(new Paragraph().add(new Text("Địa chỉ: ").setFont(boldFont)).add(patientAddress)); + right.add(new Paragraph().add(new Text("Ngày khám: ").setFont(boldFont)).add(checkupDate)); + right.add(new Paragraph().add(new Text("Mã khám: ").setFont(boldFont)).add(checkupId)); + + table.addCell(left); + table.addCell(right); + + if (container instanceof Document) { + ((Document) container).add(table); + } else if (container instanceof Cell) { + ((Cell) container).add(table); + } + } + + private void addSignature(Document document, PdfFont font, PdfFont boldFont) { + Table signatureTable = new Table(UnitValue.createPercentArray(new float[]{1, 1})) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginTop(5); + + signatureTable.addCell(new Cell().setBorder(Border.NO_BORDER)); // empty cell for alignment + + Paragraph signParagraph = new Paragraph() + .add("Ngày " + checkupDate.substring(0,2) + " tháng " + checkupDate.substring(3,5) + " năm " + checkupDate.substring(6) + "\n") + .add(new Text("BÁC SĨ ĐIỀU TRỊ\n").setFont(boldFont)) + .add(new Text("(Ký và ghi rõ họ tên)\n\n").setFontSize(9).setItalic()) // Reduced newlines for signature space + .add(doctorName) + .setTextAlignment(TextAlignment.CENTER) + .setFont(font).setFontSize(10); + + signatureTable.addCell(new Cell().add(signParagraph).setBorder(Border.NO_BORDER)); + document.add(signatureTable); + } + + private void displayPdfInLabel(String pdfPath, JPanel pdfPanel) { + pdfPanel.removeAll(); + pdfPanel.setLayout(new BoxLayout(pdfPanel, BoxLayout.Y_AXIS)); + + try (PDDocument document = PDDocument.load(new File(pdfPath))) { + PDFRenderer pdfRenderer = new PDFRenderer(document); + float scale = 0.6f; + for (int i = 0; i < document.getNumberOfPages(); i++) { + BufferedImage image = pdfRenderer.renderImageWithDPI(i, 120); + int newWidth = (int) (image.getWidth() * scale); + int newHeight = (int) (image.getHeight() * scale); + ImageIcon icon = new ImageIcon(image.getScaledInstance(newWidth, newHeight, java.awt.Image.SCALE_SMOOTH)); + JLabel pageLabel = new JLabel(icon); + pageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + pageLabel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); + pdfPanel.add(pageLabel); + } + } catch (IOException e) { + log.error("Error displaying PDF", e); + pdfPanel.add(new JLabel("Lỗi khi hiển thị PDF: " + e.getMessage())); + } + pdfPanel.revalidate(); + pdfPanel.repaint(); + } + + private void savePdf() { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle("Lưu file PDF"); + fileChooser.setSelectedFile(new File("ultrasound_" + checkupId + ".pdf")); + int userSelection = fileChooser.showSaveDialog(dialog); + if (userSelection == JFileChooser.APPROVE_OPTION) { + File fileToSave = fileChooser.getSelectedFile(); + try { + generatePdf(fileToSave.getAbsolutePath()); + JOptionPane.showMessageDialog(dialog, "PDF đã được lưu: " + fileToSave.getAbsolutePath()); + } catch (Exception ex) { + log.error("Error saving PDF", ex); + JOptionPane.showMessageDialog(dialog, "Lỗi khi lưu PDF: " + ex.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); + } + } + } + + private void printPdf() { + File pdfFile = new File(PDF_PATH); + try (PDDocument document = PDDocument.load(pdfFile)) { + PrinterJob printerJob = PrinterJob.getPrinterJob(); + printerJob.setJobName("In kết quả siêu âm - " + patientName); + + if (printerJob.printDialog()) { + printerJob.setPrintable(new Printable() { + @Override + public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { + if (pageIndex >= document.getNumberOfPages()) { + return NO_SUCH_PAGE; + } + try { + PDFRenderer pdfRenderer = new PDFRenderer(document); + BufferedImage pageImage = pdfRenderer.renderImageWithDPI(pageIndex, 300); + + Graphics2D g2d = (Graphics2D) graphics; + g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); + + double scale = Math.min(pageFormat.getImageableWidth() / pageImage.getWidth(), pageFormat.getImageableHeight() / pageImage.getHeight()); + g2d.drawImage(pageImage, 0, 0, (int) (pageImage.getWidth() * scale), (int) (pageImage.getHeight() * scale), null); + + return PAGE_EXISTS; + } catch (IOException e) { + log.error("Error rendering page for printing", e); + throw new PrinterException(e.getMessage()); + } + } + }); + printerJob.print(); + } + } catch (Exception e) { + log.error("Error printing PDF", e); + JOptionPane.showMessageDialog(dialog, "Lỗi khi in PDF: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); + } + } + + private Table createSignatureBlock(PdfFont font, PdfFont boldFont) { + Table signatureTable = new Table(UnitValue.createPercentArray(new float[]{1, 1})) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginTop(2); // Reduced margin for compactness + + signatureTable.addCell(new Cell().setBorder(Border.NO_BORDER)); // empty cell for alignment + + Paragraph signParagraph = new Paragraph() + .add("Ngày " + checkupDate.substring(0,2) + " tháng " + checkupDate.substring(3,5) + " năm " + checkupDate.substring(6) + "\n") + .add(new Text("BÁC SĨ ĐIỀU TRỊ\n").setFont(boldFont)) + .add(new Text("(Ký và ghi rõ họ tên)\n\n").setFontSize(9).setItalic()) // Reduced newlines for signature space + .add(doctorName) + .setTextAlignment(TextAlignment.CENTER) + .setFont(font).setFontSize(10); + + signatureTable.addCell(new Cell().add(signParagraph).setBorder(Border.NO_BORDER)); + return signatureTable; + } + + +} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java new file mode 100644 index 0000000..aa0898f --- /dev/null +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java @@ -0,0 +1,86 @@ +package BsK.client.ui.component.CheckUpPage.PrintDialog.print_forms; + +/** + * Represents a single item on the invoice, which can be a medicine, a service, or a supplement. + * This class matches the fields defined in the 'med_pre' dataset in the JRXML. + */ +public class InvoiceItem { + // --- Common Fields --- + private String type; // "MED", "SER", "SUP" + + // --- Medicine Fields --- + private String medName; + private String dosage; + private Integer amount; + private Double unitPrice; + private String medNote; + + // --- Service Fields --- + private String serName; + private String serNote; + private Double serUnitPrice; + private Integer serAmount; + + // --- Supplement Fields --- + private String supName; + private String supNote; + private String supDosage; + private Integer supAmount; + private Double supUnitPrice; + + // Private constructor to be used by factory methods + private InvoiceItem() {} + + // --- Factory Methods --- + + public static InvoiceItem createMedicine(String medName, String dosage, Integer amount, Double unitPrice, String medNote) { + InvoiceItem item = new InvoiceItem(); + item.type = "MED"; + item.medName = medName; + item.dosage = dosage; + item.amount = amount; + item.unitPrice = unitPrice; + item.medNote = medNote; + return item; + } + + public static InvoiceItem createService(String serName, String serNote, Integer serAmount, Double serUnitPrice) { + InvoiceItem item = new InvoiceItem(); + item.type = "SER"; + item.serName = serName; + item.serNote = serNote; + item.serAmount = serAmount; + item.serUnitPrice = serUnitPrice; + return item; + } + + public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, Integer supAmount, Double supUnitPrice) { + InvoiceItem item = new InvoiceItem(); + item.type = "SUP"; + item.supName = supName; + item.supNote = supNote; + item.supDosage = supDosage; + item.supAmount = supAmount; + item.supUnitPrice = supUnitPrice; + return item; + } + + + // --- Getters --- + + public String getType() { return type; } + public String getMedName() { return medName; } + public String getDosage() { return dosage; } + public Integer getAmount() { return amount; } + public Double getUnitPrice() { return unitPrice; } + public String getMedNote() { return medNote; } + public String getSerName() { return serName; } + public String getSerNote() { return serNote; } + public Double getSerUnitPrice() { return serUnitPrice; } + public Integer getSerAmount() { return serAmount; } + public String getSupName() { return supName; } + public String getSupNote() { return supNote; } + public String getSupDosage() { return supDosage; } + public Integer getSupAmount() { return supAmount; } + public Double getSupUnitPrice() { return supUnitPrice; } +} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml new file mode 100644 index 0000000..669c1f0 --- /dev/null +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml @@ -0,0 +1,854 @@ + + + + +