From ea0d3a1046bb08348b11a60a04a3a4495be4026d Mon Sep 17 00:00:00 2001 From: Ranjan Bhagat Date: Thu, 30 Apr 2026 18:37:01 +0530 Subject: [PATCH] feat: add export to PDF functionality using html2pdf.js Add a toolbar button to export the editor content as a PDF file with matching styles, US Letter format, and proper margins. Co-Authored-By: Claude Opus 4.6 (1M context) --- bun.lockb | Bin 137777 -> 146475 bytes package.json | 1 + src/components/Toolbar.tsx | 12 +++++++ src/utils/exportToPdf.ts | 68 +++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 src/utils/exportToPdf.ts diff --git a/bun.lockb b/bun.lockb index 5bc12405d88ee286680a278d47c603854d255216..c2c51a94e65a54c2a767ffcda06d315f3e494de9 100644 GIT binary patch delta 28906 zcmeIbXINCp);7HNmR4E?K}A6k!2l)@kf5Lqn6*(sg~n`yBqd2u!2o8As3mT59CH|R zz=&hcnDdy^m>r#w8TGx_4#Iex^PKa1=X-QM=Fa^RcB~9z67@tcB^3YY({>^Lz!4UWwBU)iWCn;3Yv0 zN^@M+LqwDTy$PxXO^!51q#`*Mxkx^T__Cmf6}|o`N&TXbKBI)3&%=b{ERmj;nj9O6 z_cEB8&-xqLE=@Nsg<4_zX}B(6h)u851B= z#r;6Z(B$awh{U+W^jLIxdyTp4tO8J8(3PC*1!qyr^`J|h>{tS_*J3XDjOjH50{ME0y$EVJAqPx?x0kWts--! z*+jw7r-?nsRY&xAluZqMAC&Ar4@wPZi*(AS0i_-(gfcA2x>=n)6GAgTI?CKB`gX5^9*Q&J;RQn;WxazC{OMa5YO#)QP!ejFE>5}pO5U6Xg??t%PB7H&D?rKa8K6|aFi=vAQRHAyvfo#UuLDXIR#egj zP|E+ZuAKiWDCIv4N_rbXsh;^t`Zy^)6LU<7=nF~(Bqzqjr6qCPFPg13}5cAceYu+C#Qb=o>%TkPDz>K&e)8)820>55d%wxY%%x<1Rv`)#?~1 z&E0pP6+yQt`DXiJaiAv6f;lu+=eLmyGH)w4;S^BvU=S$v{clJoIvJG4da5zW7#)MCT+jpYi$5kVET1*N`rQK%It_2sAb@*I8y zN(S6$&yq_zW+rsuxY`hdK&c_z6?&|*Jmzd5Q}z;|G#1++p6t6HBKO2A$kZS=6nZ#R z_P+vTE#i9{>)eSKgBuS7$3)_K_&;1d(x0hoS&V<7-yDET@CgIDZzz=4u@?yJ8ii;gyjHH}Ud@O!mbOlxaOhHcyu8O$4R+ z-xZW*Lv^H+U*%I}yZ3?8Fkc5+9&|CNWG}`J4e!X52xFYFUt~$hs3$Y)ZJKPrBT!Pz z2PMZvWYmJD8{^_)Q&Ks_G436PGc`zwba{Smf=t6>7i25YbC4}TduPb`4?wnn+&=|X z!@;GB{$0@Wm>yX(!O+Bsj!Z~NPl(|9rz9qLLeUqfhYew5dW@Bb{0lWl1gdC`)$qidR#gv8-8{`k7uO#`^6m8MS|UO7(@8 z7A4=mHaaY_f!C*=f4W^d-l^x1vo$T-T<=ouV8`SMJBNR?Su%got{xUQruH56dHYYh z3Vw^6{dukNy=f~4-@}Xd+Sh-3!T0LLjwNEN#Qt1%&AD?vCi7$74fkj!JiE4`(&rkj z_WyWc>ee;wn>O)z$j;Z>oIb8@(|{km`>p!z3TJJ-@y^E`50lOJ*YVlXcu+{sz4kNi zTlvilN$~7HcINt~H7j`d4D8lp*;(V+x>avHX*>7P?whtYH9H?0u_dO=xzd`~Jjdx+ zv!+d1dL=(ivcPeT*ziie{BriOk{^GEHL2{UZC`@p{LmO28yV!SSpdnM?QPoB1dNV- zs_v_8Wx{dop}@0%2Hu(+Nbc-YJ73KOaDft6UBhuLSu;CdZ3H+U=$2r-y|>vWNbMk@ zUXEh{-Rd*fN~UIoi1j8NM7pszR&uOcNf@WZaxC9kj$MG*AjDDuW>W0ND6J_Xv9)9g zDAL=^vlI-41j1Eg`KTl*RBH;!ACV;`o3}vnWW(F|YM+3^<^f+uR`=H0n{u2BB*|J$ zFG#H=FP4ELt&%L?-dp#E3(SBpWPq!c zyoH$ZEU&6g*jSzwf|RMid~9_>hYBnMWI+X%2XeLoD+Fm)k@-~9X%}O1w2=HnecJnw znm|I6Lu!agMnlG&1=x5Cqpg^aole+o#WL)4+K)(c|EgIZ)2`{)QlcSI+nF%l)>~Lq ziDgvRX|5o$D{EHOSEyf^6;{^?!z(i%d!4qRGKLqFVZWWXP|cd<+3Pfk)*RPLYR0YL zf+bFawX}o8bp|Il_iAvxq_~n;Adp(8sR@`KQq%@;vYH0#rIc0ZQI+}B)M;`NA(#3H zT!f_F&X(i4NZfpI-6ZZkIJt;GtlVM}PcmRDaV#5%D;kQGkMr-4p;$4OQxDGdRO zIxG*7z3RwbV}+^Ytz86(#tbBkuBUZahO~elIylpMMS0`F$$7VcYcIup0wl-Y7OyzUdk~5 zoSeD?oLt@~aB|*OXb8Ey3E%oy;Qxh<DeEO&mCkJ8=2n&^zF4v0$NN!KHzdQ%``CQ)^=q%Bds3VQfRM032ov zI2TNG3@~t0z{zpXz+pD{G&RBME?2t&9HO9RiG5j)O9m%rI14UXO7%itMoQc~aB{(g zU%770)EwKv$wrmKaxUv7fRp2ng2ROK@D=R*SfP(j(+vxdAB{SVeE?NO+<0(CY4eg8 zifV`1)JIBO1+JIGX|S%zTsXKMQrzCJoOMg8lkyG(XCO{%+e+TZjbB^ zECXa!TbAdq(-tB#ZM`K}Ks9fnWjp2*pc8W1v5Wwn_C9u2S``GAU)5V{12tNbv9#9l z7Q)&ypXNGYZhMx|T&FR|G8)TK&O$hU8i)Al_Wwh04 zeM318y-mlLHr~RpP*#Yv>!Hl2oldA_U>P9s29^hM%D@WSVP6cBmltF}wSkamyE2io zj|gLV?RADSD2p6GK@Omw`7)Q)M+}U&={xqMybpvT&KC2N=>xa=&SKb zlMF&qRvIfrQr$t)83bqdap2?umEX-<*f)sfMd$>6Fe?P|CsAQn;2zFwBV|CihOt~{kF}||5 z<}4&ExltG@SvWJJM06RBfBmyqVSk;lK8yJb&_>X(=ap!6b2ijzseizvxC05$b=fL_IENywrU`+Z6k&Q{Wk zQz|cWE&{S-AwU%^0Vu;#g)Rdn#pQ~;LZK@ax=Nv|K}m0|BCk{EdQd8NBS0^rRQ@Ia z`YzlSC1R^Ww<&bHLU$;1C#b-j6Pz;3fIF_lo>1sXPdpXU&N+ZyM5)>WfYL82 z@+DAu6{nb6$$owVZ|FWF<4;KCV22@2BXQiBfmuCLE8UDlicK_7!vh3E-%Ix`2 z)4xL|O;0GNLy>qDr?dihRP=%r+F8*nPN|%(N_r?y zD;{28zEWZh2+D#+DDnUQQN3I-RX{Bft5is|CgjPAT%3{zQt*DQ_`QK{5>J%a#XQG+M`(>&@T-+H zqQtI2KzY_F@_LHI>z`2?7aJA*O^QBI6V@!XvgGM@CH8xiYTK!#7pD}Tr^FK_cDEuE zC3cTO_bTy3$(jR7e7-t*z(_Bm#2!>+qSUF!6#1V~GU$Y&N0iu;3O%LJ(-etU5rzL0 zcLo6!K#!@ATtIbfQ$=14SlEEIlSe^pQdztBc=b zip7g4?MH7xX+nMkrGoxYWB~%Th$%&WL!rWHmGp9;BwHwYL`k+(WTGT%%7YQul(fp3 z@y>{+9&}aG{}oErxGDO@DYcs?;wi~nz&b+y>J&wyR8fE;6D0#$ zD>6|^Z>PvaiPbA|aZ2TOR?@pF=|qVQRpd|sBY`UDfe5m=r$T#yl1?8A6iVrR6`A7x zBWGTkiB$0a;?VmaIrC=z?+?AQ;r}1cyqSMF{8Cr^%ZFa*(qN*qF1?7-*!cI(ylDUb z?Vjz_bh(KkJe{$^Va?dGUejzjfx_Vl%RVka^-= zyR1%sp4h!T`0k=vEpAOow6@GyS$2fs)u&liCoSD&zcJi?_3IDAES7}_mdvYOKF!N@ z*QF1g?c3)aF{GPvCp2grp8H(xGbpXU>dPd;zVeBWkEVDP+}(7p<(}I|lQ!6&8=b4o zUo&R*3HPMHPn$ltp4_?koz2PZ6Fc{E7+vk<*yBTwHuk=@%IVR^@S#hN<+59&tuu$c z_c2L{-BQEh$5Wf#XP#_mQFZ##J;G)_IjU`Idwq7}h>W#YmWGa*?Y6>VQ$nBfjSqQO zsk2fmJ~+`YZ|%-XkHU&sNAJMZmr3@MoX$D-!0>Lgu=^Thz5oPwS-Rt#?m9f6@QPi7jSi{W>L)j+cz>V1OP+sls{MXL zH^1D3=RK-wBgPlAj@~A!FO!TSP6zZ6_tQGTi6>NH{7 zpKZOk`^Bs?Q%wUkslBTEG*{EnVeUQ&w`OT(n0pRcw7Krpx6>c9ww+e};`Y|QL0F|_ zrxR@k?alP>KWl53;MFOgf_uMTV#wLWXGh!+PIoOd`@q* z)p1Afb=8+i#_6s%9#z=klf+R9SNx7d^*lz5Ylx=swMPLiZRnB~Hf5h6I2RoLi`~KL|-J@cj4th0w za`{pb8*c|5t^3x!_J>j2-IMX>bDA%iIPy)2Pj&%+q#m~VZO`&2)+HYOej{}{eYT;R zXQ|o-YBGI_e`NO$t$tizI6F6Hq5a_{-CI2hsXm3PbKN(nc43dlEe}-jyFRX+&6$-i zy}M6Z+r7;FxlNw_biKpCG6%Qbeciq9DAr-Tb>{KUXDqIJ-Q*{XsdBGsHH(QKn$-F{ z`{JNq*17wH?anAOYlAWOVe_56m*hB<%bu4J(Ee=jnPDB9_9$1&EyAgm-@AdutgBGm zI!F8SV@5PNwdC@#=$-v81+S}21=h zk6}NYu^srVvuEL!=wBOh7U7TBCs?=U*zJrBVUsJR4>{UuSL>Zolgc)4MLAG4hQZYdi&Vv z$%5bA*LKrB9P1Y~t$qK88^+nM(xmA;*4HR+usJ-5jci_hT07sHbqt@NC=T_0vyUrWmSIOnGfkI%vT z#}3Mj-*Tai_vn)aKWc5)c&|OWv8BzJ4`&wi%~`$Vq8NGEzkJZ12VbmAd%5YZd}3x3 ztusUV9!%JLJ0v;q{qIY{$MwbE? zVT5B^_j_Sk?{661ow<2#vgPSFYwPx_GwkV`J+YS`?p*!Dlz?`wvo{|~NiV(W&p&77 zrd7^=ajQe~d9ONe$o7&y_>{M@YN~0VCP{C*-rq7;$eK60eZYQS`{j-9&aIOD@Lt6D zw{3Ty?36QiU#Fdu_9h;;;m6jS-SI?YJIhP+$BcLF?oc{t`I=iMy;EMZ_(|5R)o49m zolPHYV09*&vGY!6oucQPvAd9l%raxGrs{b=Hhn7m6X74E02Yt~{~#^T(eo|X8%Wu+ z;omep--<1n2LI;3zv+6u4GW$Q{~&FH)Q<5p;NM*MH$%_s*(OMB=fOXw=Q}VXgMW|? zL+ZpVXTrbv@NcG`4`%t0!WO{4Ts_}~4a|jq3*jH65auuo{y`c$OV1lv0i>8k@K4n9 z-PlMG{w;=oka{q;+3*ii?rc5Zi`|7ZWC{G6qv!ju>2nNxU-lHCkp;{(@ZoF@!U*;T zVL#Sxo`H{KOAtn}j|ih#@O%Rw!&W1VW&8pI-=B3uIDle8p5H> zZJB`|#wH*f&h8={!J7PF;4|5Dgjwt<-7I#PwWk(>_g~@J$n8yTe1f|aTq$mobmfGOO9Zc?9=ln*`|F4{uI;hH}I#K5#brO3*lL2dBDJ*W3dPeSU$q@%qHK! zUtj|fUS!7+USbXhF*icWgAl+bYhcGuFaui;jgICA& z{9_h;3|>Ln2I(o|kHf11cy(OQ|H3vwYI`1ibVAR+V8#=e`;ZPpdc`bHV(wqS+&`)3 z->`g0VHYv?PwDx0Y#<&&G`)nm59vK~I1T?GjXkaBe`5uZVlKnKGkX3b8+iu)U4eg) z{$Orr;UA>jvwHp$y9;T^Rrq&K&wpXl&%wWI@b8>n;Ca@(Ae3E&xV%6wl;GK0h}qZS z;d#BF;n~vjp{(%@xOhP?l;TM+vrP3+aT3t{Lkpi$8hs!X`h1B z_6fYaBkfan(3g-7Lu$w@@1ifC!pFPPJ_RZ48C<+4?Nj&Am%qS6NS@5$KKc^U*!$8x z1u5n^{CgnnQxD+Z3-|}gm$^NJe~@w?O8XR~Aur+IBWa&{1pi*aKS%*A;4%DzwEVHO zPeICl4ga1<`_vQo_XhqwmG-Hp@DI{9NbMN^4F0`^f6t_S3R2s5@b4FCpZW#E_y%0Rmm!tU%E z!XC`+wSn)+CLrv^?jr2Xn!GX4{)7`w@zS@5sY z{)8}=@$aSm3E=>?2_g2U57PdGFk#EC4`+oPeYN~dp1uAQh#wf4nAum9f7WG-V|s;c zOL!*}iY`4UmVR5$aoZf(nNJICZq$)~WJVvjsXvRhtBah-QAWs}#+MaNyKh^_hif!_ zg776LEBI7dv@OZo3kAVDbS3#vL36JMipX7pH6G=Q{#xEt6VMyqkD`Q?yktt1e)4Y$ zYk^uIx`!aCo-4$M{_TZejioviHS)Tu;P9Otbo`>QdWOHS9{ow+ZOzS=5lit zg-LwrAjq9_l& z=urvEM;`_Ks>D&9#L{=om%xyIoDzo*ZK_J&WK&|i64{ng>6I+@@5;~3q~kDsXG#xL z(U-A-KnI{B&rZX8x1=4^)z+hkqkPc)3LxEwya9{*L z4U)xkZMczOMggONF~C@096%qTPzy}}CIa-0c^RNAUeztu_#`|K(>p&1t)F; zcv_322hiv#F8aDN4Cn@Q2Qc2ao<112 z`M^Pu59J$Wwg*d(wgdtlfQ~>X0K=Vg0BQg=0Xx77W>*3#1J*zlpekSsR0HgQ>VQ4q z0Mr1&fNnr{fW8dh40pExTLC-`k;$C}GYeP`YyeIJ^clr*fFA5%06m}a6L1JP3>*QD z0`#EAK43qP1JH-(JAmcD3SbpLkHO3U=*a|ns=)-H(f0&+&SQ*S2XjMA3*&1xw*^}b zumh?C_J9LW1E>kq0%`+}0DTrv2dE3w1L^|}cySaKwoD7~mH<60_W}3~Xal(&&>jc` zoBazQ0q#H}zyt6EyZ~>YF_4Wi=$mT#ws|Tr5{L%|0`zpCD^MS3fbW+hLU}7A zJ#kS1Xa>+@4K0CI06p^H4%7l_1CD?bunmP)1p$dxUqc{wFsT_)@yi_LfG>C{NL&-o#%B>boqpWxa z8et@>BhE`)(w(oGNn=R0v3T87FU1p8>m+IqP$LmlL5)m2#S>5H0N4W*N0d;UZwVQw z@(+n>VfJD{FTO%1Z3HC%0Z_-PJ$NdMWZEqH0QLaI8G*hEipNtK_6n$Om5%~X;DH0c zdLRKv24>Je91JD}7ziW*iGUc;i?3hl3+Yhw7rX|6rU9wq$6kD&fDEwdzz|>^K)Kce zYk<|jDqtnB0$2|G04xKR0!x6!Vz1tO{mfjjw6ri_KR{dE4q!X54cH270X74hfQ`V9 zzy=@>*aPeab^&{Voj~z8(jicq%3lFbt#N{e^Kme#z!4xHI1C&F4go(YAo&<@RFN-( zUIH!x7l89X0dNjD3!DK?1E+wK0IjLC572(l0Jx60Yk@#F??j!I! zK;`}hWa<$72^0c<06a2L=aALJlSd^14Nw9w0mzfmpyq&?0&1!9KxLp3P!X^KtN}8T zjt5l%GM0?50^Xj^FmzUM0Mv5=6-eg-CxFff^#D2_kSDaK(;lA$p7win|EIG6oe8K< zNsrl7f9fw9Ak-(c0n>qkHe+>=(7;g(Rnv+OBGm}eQH`SV zzSW`rrgDqNU#I>LKxA8>CD0u32dHw2ZvnId+5oKqsw5ngP7FqX(kNeFpf?Z(^aQ#C zeSm!E(KGLj-4PpD7>Kn9SZR4K)gF{%Mn zxms~I@I3%3gYxtOR6TWiEAjxzQWB^DDm)H|0iuB@pdXNcf+9gFU8PC^(i@Zhh>ry* zP6f3}0&r(20=yRQ5?vyA#~oo2{8XWG8N35gFDaput^7JC-jC!PRlSKES||*)_AQJZ z-!)3$d%HDpad&a$9*S;JybT{K>ZABpnKepCGy3brck1mW&~N0@$i=Pb>OA%S6DrBY z6$NqX<$CH3DM;~Z z*UR_RD^yI#I9C@pH%`5tPrYPCO7SA&)SLWNhoqE$a43^*bTQ#?p%th()oTFNyIf#` zy9>OTqUU92cx98W8flBd6cT&5L1-$HBY8;iA+(Fbn_rG zsn^HAZuFXqH#S>il+?xQeK5$=$i;(N?%P(7QW}$S>NSMwT{22N$Rl4=h?{nl`(}Pn zii5A}U54t7G)O^{QgPqyS2uSTPfooL@ttz34f2rLC{4XFQN8yDDd@UJ@CbJzW=gjN zsW;n5nWW}cuR~Ptv?)qaFHBT#y+Mj61}d6cy;4!V7e~q?nN6o>TeNA|<&9;(cwB6X z6y$&voUM8%pWoglWhZGumKG(smln@N@z!4IEq?m-1w*>6JaWEBL%rwE`O`Z0@-5Ch zDoPnyTC|Pkt!>qt0mrUcG~>X@+YO5}c9a%_py8$7CD^^p7t^UdyOk-@P;VP-<2r@S zZg=rqQA(*Y;zHyV>XZ?8M)U51e;M&P)uvu#81gCF>5p#y8;WuaEF;>-pf>di#2$?+ z|8n}qo4Z9C>LrP-gL8r`+pGvKN;z6aOh8^+^;*RrRww^(U~A;@B8~TD#HBH?qf}Y( zvO?|CAYUyj>S9r=dQs!H!042whr2XJPB(dNQSXpk)gZ&KQCOZJrC`$Ilwu}MMqXR> z(#OnFuCIJYw{M{2g@(>dJV2$Z*F%16F>%?d1$_wZ5BJ>(^E;q%LV1pn>QN_!2=_6Xa1fy!W1dAgdwkuxpxjZA>RWQ^X}Q9(Txn11YYri zbFvWsL|)r(OShoTcE>78jpQ2lozf+ZMvYt=|6PY+;Zd8w6K#od(AZWQw5r^8l}tQ2 z7jy9x3b$3SF@1h@#Ewl5M$v3Vk5fC#E$8*EeZ{?(-co1%rA@tQ*^{#hiV@ zI^w${SZYj+&vVpOw=fDV(eo?A0%ER|!h>y6gU~31_0-$~Y`055!>Bi_#?E_bSd`=I zBx$03$$hzu?{*oD9>pBNzM{AoMX9FR)96$$WPR~0zGAihqwZ0$@;*JfqWF@ub1I6a ziF}}tU?qkoV&V?55+{aeH zEh(TIlVS0<6ieWpfpWa0r^*VoRqu4oZd-cTO`Et1Fxf-iHNI2wcWc5TryGPCR9eh} zzv!zq7fUCLZEse`+c6A-n8GI;-PZQ41q;w3I1zD$)x|R@=zfj8d{y0+mUd37&Szei znpIlJY>`q8d0D0L=Ue{L|AKUuk@l}9_M&YnU(YK{(fIb*XThr}s9w6suZmS|%aJv^ zoT3zQSSoK_TGdcmLOhqsd)SV5kUo>jG6vt6fA9JTKWUOmU8tzpo`(jF>(!mA70jP; zez#< ztAvw0?bWMCN9;^XIo9}+dqruU(wbV!NxU}%(?ZP(>Y5I@bsh1{Toe^lM;wuk4pi@< zy?uNCg0=2@o0#yfE?70`7*?cugD^e zjdf>bpm6mzT-WAv`|K!LPAF1P@5?o--16zd4TFvrrM#>wcBH)O4ZE?sny&IF8ULtA zL%oCd#^|WlZnLiE7p2sxC(cJ+TlLo7wXI`&CHpzeFVYCEC!V9y)q8&Hm9Nu?pLBmo zk%oE`a9rP|UoKDI6;PBiqn>Ct6nR(Fleg%{vr^wK@%ym5NJG6{xWRzRrQZG2zjaZ{ z>3U)u^4h8w61!Ut551e$IlM^YLp^aZm9Ac4oEN#z#j|?o@gfcNlH>e`(^plf+~&8U z6tDW?OUkQWi)?DCtG|<Xcg3m=Msg1;alvlkYxMb@2esO2sp}*C<8!(_S zo3pmmHtFU1WNd|^l+%sGvcu6b>b=90az4%9QPc8Sk%qa4=noAq^`_!S0kuuvw`h5& zNJG8L*dnLa%b3Xl#p+G;5T{aJ>H1^T@9ng{vHKtA&@-xK>IKQQqsH_eed85IqMB0R zA>Jg*)GL)wdUsj%XkzKmA`Np-(P9KjS1)1qD-|4-oPE4sk%oFr^Qczs3Lfq|*r+JQ z=qdK5yz0fyqx6dg{Ia@qV3CG;^>ev<1#S)gJo#r)%2rQt9rD_$mqmYQR9Mp5dp7m7Zt-N=GSZOO81-7@GfywAAG`GZl%kXiexgez@~RgvH?^mM`B*TCAn_=l5HRSKl8Vxd(%R#lrB~o$$JRzT8cp<`6zXD|8{8>c)Wjbqg+{S#OEW6mSACW8_|9g zyio6>_O~j(`d*WnD`1H`_IqqjzqJvA=Hc`R*1WBl0A(-rlIp(gBFfa55JDR-J}97R zs$OIL(K2$tXOGi!B!}>pzym4b9^@62GK8z`#Mh&EciZpg@T7`wv=?=wQJZ=bwRgAJ zs5eVoTuics<8dbnQt z?k8(_!Iukz2cC^7(ipC!EEvHx@3nMs|Du%HdeM9g@~W3?JI6R_x=qnsDbgrFUOIq` zn6U4`gPp&p7p1(^i#?H7corl!m;ovTi_;+6suvv}J9Xw+m7v}(0^iuhliopazQJP0 znb?r~1dFdJ=g?rW^jQA2P@%KfdlI(wisSeyf^BEfbsS7muYnF45x3{&rY-Bx0Vsq{ zP3pzb3&-tlvFejK^)P)%fn#;k&f*BltKQjdQ_@hYYMntdp@FyjWP^Gs^}IQ?_H|$@ ze?>}Te3Am=`g9ggA+N1^optx|4jE0heK;)V^+Ls#jb@EU;p+X@HpixGZ;jo!778?G zur#YTVp~~k+MSV;FdHdua^bT&iybNNiq7)c$~|t&yAx+)qNVDk^N@NAcuL@akiIQO zjg_jEKH@pnS#+5U!_*7MZ#&qOFWtD~6f)XcyAqK;ID8-38V z)gUH8L)d2!Cr;o4ZU4UPxNf5DRA>(BCYGHDs>=S|MgNI-1M|GQICUaFS4it2)|*t! zhLJtQAtXoj6xU(Q*~+(~b6IO0!j=|}{X&Ox@)s?U-&4E;P1^^(G_n4Ii)$Hsmex~ltG|JT& z4>}xfGu!4ps-gEO(8xB5fm4uKrtU&sxR^Bsh3F#0GZfDv#3xhWlH3ID!o_}KkkT~$ zry~AVKXD;sTlI$Y(BP9DjTT#vq6V5bsG(|pdixYXvvPZcFAr3F)n5fgIIT#|8n8;g+W_^Kx8T(jbac<@Mb(Q_r}I_5H;VC|n`RkC@1f~vc-d&%zh4&A zrBj}kl}y~Z^mwr^EvBl2v*N{ba9CV7gDjM+p;WpDi~cI}mS)v6P@C zT{-n%0FKyJ_`Kk0IaQg%}-UFAa_qC6E99~@rvT_bMQ&s zzu0R^Z~f1`d9QR z!M|vfd|FkHnUzc$D+86Btd}G%oCmk%!6P?TQ4^@{sDA~q^`qVOX;}+*%EnS(t{x`Z z&qrsgf0ZzYpiOeco5ub@@1a+Iwu6ygEJ}CMF=SU^ZOLp3m1S z?Kxa}ZU%qHAikz}xl1aUIJ^F1jN*Dp`cex%=oY{R^=}!<&S=%ua?I5>0*?;{q?h`~ z4b6_2*6Ct;6z?h2l$cC$GVnGfuW_-72`K6x;!vQgxa1J;P*?hf z6Dks=XzbvqC)}74>E+>U#Q&nD%YS?(nOOfHOohunYBai*h;x43BuU)@Ui z>Xi(T|0#}&7Yi=%H9AonM^ge(M5BYHw)U5+#n-thh)*)6#`H6$;&0)M5d*^G(vqF! zzDbV6U!O!K8&eaLou!Ty@2uvlx&A|Rd?c#)PArWlydx0@uHo&SzAYSCzKw!Y-XdQ? zs3V5o=F6L4L2DvPEnVD5%2&*0XPg|VyK9JxukaOv2x3oEPNC?iQ_o$)=B=x~;IC{rvCjVVV9>HqntLWyy96&r5k RYl^Qo@}@hQFXdki{y$SdFX#XO delta 24159 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 + + + + {/* Export to PDF */} + exportToPdf(editor)} + > + + ); diff --git a/src/utils/exportToPdf.ts b/src/utils/exportToPdf.ts new file mode 100644 index 0000000..c7eed8a --- /dev/null +++ b/src/utils/exportToPdf.ts @@ -0,0 +1,68 @@ +import type { Editor } from "@tiptap/react"; +import html2pdf from "html2pdf.js"; + +const EDITOR_STYLES = ` + .pdf-export-container { + font-family: Arial, sans-serif; + font-size: 11pt; + line-height: 1.5; + color: #000; + } + .pdf-export-container ul { + list-style-type: disc; + padding-left: 1.5em; + margin: 0.5em 0; + } + .pdf-export-container ol { + list-style-type: decimal; + padding-left: 1.5em; + margin: 0.5em 0; + } + .pdf-export-container ul ul { + list-style-type: circle; + } + .pdf-export-container ul ul ul { + list-style-type: square; + } + .pdf-export-container li { + margin: 0.2em 0; + } + .pdf-export-container a { + color: #1a73e8; + text-decoration: underline; + } + .pdf-export-container img { + max-width: 100%; + height: auto; + } + .pdf-export-container hr { + border: none; + border-top: 1px solid #dadce0; + margin: 1em 0; + } +`; + +export function exportToPdf(editor: Editor): void { + const editorHtml = editor.getHTML(); + + const wrapper = document.createElement("div"); + + const styleElement = document.createElement("style"); + styleElement.textContent = EDITOR_STYLES; + wrapper.appendChild(styleElement); + + const content = document.createElement("div"); + content.className = "pdf-export-container"; + content.innerHTML = editorHtml; + wrapper.appendChild(content); + + const options = { + margin: [0.5, 1, 0.5, 1] as [number, number, number, number], + filename: "document.pdf", + image: { type: "jpeg" as const, quality: 0.98 }, + html2canvas: { scale: 2, useCORS: true }, + jsPDF: { unit: "in", format: "letter", orientation: "portrait" as const }, + }; + + html2pdf().set(options).from(wrapper).save(); +}