From 31abeec36298c63e238fd6b1c988b8082fb21b9c Mon Sep 17 00:00:00 2001 From: Consult AS Technology Date: Mon, 21 Sep 2026 19:06:57 +0000 Subject: [PATCH] docs: generador reutilizable de PDF con portada institucional Script que convierte cualquier documento de docs/ a PDF (portada con logotipo y tabla de datos, encabezados de tabla repetidos, pie numerado y control de saltos de pagina). Genera el logotipo con transparencia real: el PNG del repo trae fondo gris y sobre la portada blanca dibujaba un rectangulo. Con esto el PDF del documento de alcance se puede regenerar tras cualquier cambio del markdown: /root/.venv-tools/bin/python scripts/generar-pdf-documento.py docs/ALCANCE-VS-ENTREGADO.md --- docs/CRM360-FRASA_Alcance-vs-Entregado.pdf | Bin 495810 -> 495740 bytes scripts/generar-pdf-documento.py | 233 +++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 scripts/generar-pdf-documento.py diff --git a/docs/CRM360-FRASA_Alcance-vs-Entregado.pdf b/docs/CRM360-FRASA_Alcance-vs-Entregado.pdf index 8c551e721ea1fc069a43dd38470a43564c174d43..418b80fca9bfe892e3b4b2cc785767176d6519e0 100644 GIT binary patch delta 24556 zcmY&=Q*@w#uI|)sr?$ zO+__pJNGcVay^=8GcWCEt7#Y2?{AuvEilC~5SLm9;=5JQEgDB526K_MwQv@Pp8orN zZ6NIP<^|S7!XFwpV%P~93+HM1C#<+t>+756BVhQLRqfLx2sV`_ph^llWT9GQirZ<$ z){Y}&)9uHly~}mJzSPyNM9G&Bfd*|H#Ds&eP_BuI5Jh?&xefdNqf$<)_MqgwR}p`v zi%X(UNeq)4S{>Y>?QDW9x70tIW!W3D#|3tJ(I??IF+{}TJT<5C zBVdX$quk4$i4_l#CfApd`3r@rm?%Mu8^Zi=w~P_IAikHg1CAu_kqew-!V+By#1x=pT$wQ`aY3)Z?}V!STH`OG$kkJXJ%QQ| zpNn-e0hH;)@UyZ3UYWU{Mi6NZf9QpO@>BbdRbJjIz0Zf;%V)xERtp3cFsJ+3YJk5B zvbM83>O*S>jh6%w3!DsEEXPG{MLgS3DgB9$nC(Y0P%2I1d-*;)q)X zVMxthVT!mTCx=N}emo~lGcoHCe}j5#3kR?x@H=5b3~R{1JfT6*59B<@F{#Q!+rvlV z4&iheF6-(23j8FJ0vzAr9x!Rk0slfUFTCiTSO$H)RD#}GOzUR`;N$k|>$79Hzi(wb z-TqzLe&IPNc+EgYmjH6?4=wcr=6GZ9Ll#o`W`6YBi)E{5IyP*%>o|TfTj-b7ZJ8_l zANq8h$87G-G(w6`v;KW!0w4G6I9-BdC*>7sN#zP3Yi^|Kfuh6=I^;4K0PXf<;mLyM zpEi>Tapb}SbHZ@}jZJpu<`P9yuWfqz#kh-7{W)$@;$IAg>eA$1F9=H16Ks+V7DRX9 z<%?WW8n8YMVb|>9G*~IfHo?+~A8W3czL?nEJT1c~iLYBxH~79&58RqX_>X!Jb@^x% z!&qgoOphIjc7H)caewtM0>zU<`o7GIolTDl)nMj7_o)(_*O)n@=pC@4v0H^QeM=x` zHh3?X|Anw|XSo!EOm9pk)Bo@|(QWKNlnRz2DpKv+5FEQ(+_YoY@UWwFe&Vi;Pqd!` zkz=dZSZ52Xykb5$@P(JEx^nw4f>avCg(0&!CN1gaSu#-Bdic9#2>9uPaH!j|$))>- zUF_S8S#=F&YUN6c23xV;jx&?7)ASn_A|}8dDsC2uKebS+(4|OJ&ZIvua<1S1S}%$= zO`6E;t6oH9R&w6+851ISf03=G102Wy1^fVuTKa0hoXQ{P=9vtVyo z*$8V!86!jPG(wGa2Cj2+qpQzxg@$?RG>*Konro~&B-gh7(A!Kyoi(qSY4e+<=y3NX z$H8_q9)&eC66oIfDR8gF%2FHSXiKR-g|gdnu6J^2VrSeO+my^`<<&BOyswJ}PAl!N7$W?e{Ny-yZB zK~tcKOy6M~^f2uXmbp#OSCI8=oIbloI_#0WcsD;Aap>D7}834#?( z$oY?t%29l*1~v*dwXu}U0#>FGR|;3`e!Ow$k%Oc%*?lI`zl-nct{J32mZ_ZAZl32X z@16#XcityG>+7Z!y*OXLX?Fnnr_ftciU`Sqk+C^%`{&cs42P3>tsuN(@em6J4GW|$b%=pV9z!5?EC^g;WLCK`qevg zZrbKNuSF;6sd5EwzOEx`Skj_=mi&z91OQ)}hxo4CA?~zt_F`9x1eeNcN65Co($@rX~mJ{(> z;dV%NeihZ4SYqHIXsQ&Qbsd?}T4>3lqkbT{bR-e|0n~25JN4fema+X%ev8k6L z$DS{JdFf@b-mItr-#ph=FMFlBBY{+spIT7eB_p*mY+a2~%613hD5p|CMrR2DgR;_897^zSNUug_r6$KbF{e|Hdg+xj#|7aPX`fdfh0vx# zp)jIm0TiVa9F<$Y^C`8$HRww{P=+Y$gAlCxy=NnLt0U*UN8P*1Z)Rl=>b>`o=v#i* z&KJwT9|RK1ptlEiRAfgSCkg+JFp2&R2DlmcCnGVvW35gRpY*oTxF>7+wOqS)dsUY) zy2mF8?1iZ_6I79rU4a17c}iYOpZ5}S~DhVurh zjJYc!G;dr^0U-$PH%j*(WyFgyg#6#rqMH30Q#A zmv(sXnkSNlNZuDymLSX89o!2s$XB~ax)d*K z`12W5^cCYO)N}f?k2nvx`HodCAF3Brj|HderYEv1as;l5o?x!-Me?HgXP(yPjwSLM zzFGBN({?YnPYO9_4|<8i`??tK005m5$09%Ad!TjjHy8rz?EfY=JBaGrAvNt6q{%>$ z5P}-wPr^NEXbFsG=&?OR@>iMS@hk zLsVPk%+M{##O?0~rl~fO$Kkk=+a<+?fHNebFA(zCS{|_#maTe$(^cZ!F5V^@`$rH% zo&$-+^{-aMk3Q{jKgH?lroMzf6fY;^&li0x=BtBQAKv(-FpASu^)DLXFyzt0($ z4$iQV2_UKm_rzuPs4l1J7^cT*^b$hac}KeqfYLQVEF{67(EFCO@K%88Idvd#D!Kg} zyF>?1X@mW$szXRv+vbKt2$d7koceOmgc$E+amx$Uqorj}^TYO9fkB|cll7D6=_UHa z44FWS@4K`7;x8o}&501d&$X77n!4s%2P|~lj%J6`%y?ufRq9v+)%97(5650Z;BVO4 z9Bn$N=nSVxE0z*RxP$z9VyJ<%bWq8CMd=crOHM6Y0Yv8-vRf2eov;FQ(md|T>k?xA zh{kz%Jkr@f|LWs{`Wlx8Tt+SU_5pzX3TyRtkZ;N+6r@fz!OLKy`Mi$SkbABD>w9)+T^`}Aq;y2~XI?$^=l~7l=l~-X@|tc7I4A-u;Rym7zlR#8 zrp)y>kpwbwl3Bf&G_ukCY&ZE01jLc6$=7gM zM6Pj{hUog&{R97E%&jthXGRjT%tTPNYS8S(3tWdq3hUPQff`s&pDjtARkebwaTkmTZBKrH>!;8;i;wlv+i+`c8h&#RAKzF1tqr1J6gNH z(-S5Jpr)du`hbg})f?zG$+NCF0S|}#m8HT?|1~$V7N+GVW#z<7Te$s&F!TK?FJ=6B zd*RdwfqTY+yt0}V|3k$%t<4jL=Rh&zTwQYVtv$HPT)6-dR=Rr{(0U2`*lB70;{Qg1 zD;19mxQvHnU*)IB#Lqz)u+#1AwHuPz)#}YCGai!hC}!b;2UD>5#b;N+3CC@xdw!{{ z-gs@uGU+d#X1Z>ZAz#lIuIXfZyeEA;tp{%HB#6`Tc%*${rX|SSVsN3!AIJCH)qse1 zei*Qhl3AUy@HdtPI00lYP*QU$%cG8|?jBCZ7N+{7I!>C*BZPllmZcHv{BlauJJ|Nx zB^I>=5`5o673BDMyI^EQ$vL8Wx_z5YzaIWgxTx9LR4OP?*zCh*;!Rxa=o#AN)53%O zl`h$Q!DR_hb%B>JS|8D2zI1n?(~?MFz`dJw^m21tvE-5kyu*uRbvN-p=T2_quNlDk zXZy+Q*;O{^+j+(5*ITFAxm7F93lvMF5vNHSSCMzB1Sq>RKRF>zBKm3Gx`W|3P`eF( z)t#MJW~NZb`@*~id<1xgBU6lX5goTM+VBv%jrT z)*7e>M7;rVXQ6iK4GZSpwCP?qnRcZPLs-G`f)W;}VfsxL_Ra$95xstHSY(Hb=EC|R zn2q04AVgGfaiyXJ_s?e~kvg}iFpSvs<3w+6vK_``z}A4BD$E7VXrWx*5PV}a^O+T| zY=HWu7eiw(WWdtq!sHiuJ>TExg;uK-^W7UN5?-sDoY8->9YP>tM!z=O3nJ#7MKdx) zQ!10?hGc`@Loo?yv4Oy~?HD~?c`paKc3TYXAv=o`L~(xLv=_Q%UeyNU;8^yURy8zL z0Mr~681!YIMHcDm#Dm!+WXZ3jrJ|CM2i>jraTzL!b`{9d)o^`}yMX6;1%00gMVM`Q zDK|%1ws||_eChK?C!*z94VyFmBi8}!5lO$8KXUX$MFvYM6XqZj^0=0fKg2a1S3}<1 z&N-#fA@g+b^@~axQK*gUw8gm%PBirwDohUm66Po_UfY63x{nrJqShx2gEj3+F+U1ST=sc7BAyin$Sh6BkONL7ZjsZ(Ic$HyG>(m$#NBXoQMb;qSkeSY! zNbi_~9#~LO0ib>OSbffj6$VTfJZ?EdMAwF%c)u(b+wgH=ZmvtIj|bn7$96L*SULlM zFBUL2iylOIHi&u3Y`s zmidR%N3{NDa*hEH(`0mJlQh9dH$0N>`;z(&B9gJWk1x}lCz06uT@K+6;Xfjvx?i~U z7FzU{fwZ5mBsXcT&;0^v1%tqxqxaCGk0-^4K{_(B*o*tz&Dzw8L8>) zZbigVaBXUHqiP^d{J?+enH|PM7zB4w@E*7nJb{l+%mc+!IwWrKMS3-$@+3B2O>oZCLaY8;bY|Y&m!hlKnhi*CNE~%TPSw1o> zw;q7QHC!#+V;s_iR21q~_cn>7e7r<{QEoLw@5d}q)B5sTibTfd+CL0HQFeF9TxDCn zFbAnA8?91J)N1lPw^q$!SHD%Q7PNLB;0nL6k6<+%on6tI4BkjA-5YLxaDNQP&1gx~ zp4z`YV)YSnw_%Yg^r0sPI+AR8)B>RYe0X{dRsF!g6jn8fDS@R4-sJT#3II5T@| zq&5{+SDSFZiZs>xCE10JokuCiP?gG$HmY<`d6?Xn3#J@?Vcb9cggtT6iL!KQ(c=~A z==^o`*(sd2he|#;yx~N>!R+VdMX+rqvznr(KzlhlYG=J{Hs1<-1zl@4*S@h`!r`>o z?`qdrl^z!VeU0YxC?IgF>3t&330d3X|xV@#R)Yj?$A=$RlKa|EMB!SXqxvR7qJl+DqY0O z_tahBIxcyASS$&9x1SV~9r)pXsQX$&kp0+6TRBMkeg6tHFN!A;fxjp7+uNL!eN79~?vzzwk6)sa zW~h|dpw5YLqFYh+2N#Lm`5PA^*QVQg5Z&^*UC4V|Tc_Ao_Q`4d_G!$y_(aX_8+`t8 zT>?rkGIR`(MxR}AIbH&jrAVg@0qsagmrDC_!Xdw{d&Pn zXl)?Ng>Y3?cAy6J(Lz0h``($9=R1em)r&g6X3{JpcV?5e7zJdP^*f!M#MgeIg00p+R%C2eWk@6 zlk@I|>|0DfsEIGWXEE$NhyS{x+`Hnzczx&09aYpcpiY*vXkzVNNE)^lKP+~$t;lIq z#>E5}we6B-G-hpt|}RSPz?K_p@c>Ew(m1;*Wbb?uRy?c+U` zit@GS*eibLd3E_}s`CwB*J*rn)|76HHeLAzf`7V*3zUj6N+&Ux(SNB96J%w zylVH&G(+N2DkCU%p@LWiB7YlT&!3!DfXu|Qslhwg?!--FC5@aVXb)7@Ad=)VvtPcS& z92HZQpos?1JTo+g1ZVvble_jX6Rp!fX|{2XR(|smFFMLbunclnlkY*@##*Td!iyIcsKse6tfO6?zDMPjaT++gBbk;bOP@`y^# zT!{(CdALlFHTCiK6x1S-D#fxGT_rHFCQPIP7vxZJo)SrcpJpUEgm=e#Cu4<|SAMxa zyAFy(+9Z1ZL1}%qZuIADJ{`5GV5>pdN&MqkPMAn76Y z?i!u16@2(THhhVjCfT?xTQA(sflz6Hyf)rz$p1Jttsirm*Q@~~>cP?^JBJo9Ap^ia z=TdXtsrgasaM!}Sh_FklsP2%@bzk5M%Mm5B*#iF&DbZB-i-~IE28#;dLdXZU$hV8l zJdkPg#Ib6+?h)l4-M1hlt!vVbp++{Y)z*G$-URw;+M=c#PsMxM-ynfCe6WQ?jF8A=SZ2oi@Vq$?JGl<53<;*L&5F#vDQxaGh6auluI zRJxjq_wP(GVx=2=7rMouD>g46tdZs}D;D0R!nd;g>8@pYzK&CNaJh#b(w(>fV|>f? za}OL0+*m)t16s^N$q;rs?7rP1xctf3S-amJp5$lJRy%tyfc%2?!f2R`rx z+vEUKJ)nNW<^xx??R8DC&F4E$kMXak{ur){_-C%rdbobRtMGalS5?U=|3^N{wuKVo zAYbwqO9gaNGoLZmeP2B);zY(!MC;5l) zi{izv(D0<8wmohpX*yJSNh0jxjCp|K)c7<}Y?e14yUoqGLo zUC6c}sAxbKF$ljOPtR@jK;i7B#8`aFthq6pJcjzEBPvie=uf5JpXj~ssD)zRG0ZQy7cY<4=FSV; z34C=-k`PR}fbuhjA7fWIJg(!D+)sh$A2;PpM14ZiNzHMKpws_gAfbPdo-Icq+2Dx~8N+R@T}LEyq}06y|7H zOFY(H0?YLHsoBG9+j;piqXs5x_z|hHncDU`n>A~@_C*97=2~r*`l$HS)B)JEIua3^ z1c??{ctu=CTt(j!<>-hiRe|r}x=>r{Q;JAibGUk>h5mGc^;`mtfrTVAdpjItOX~W^ z5~>Chk5KNxr{VzsDqC#*(i7nz9H96>{0gg^6vv0KsvDRc*l%f3R%IIP?0;yfMYDA4 z7O3_IQS`2hr2J~c^EdI@JlUT$HMo_C_V$x#FxP4ZRs*8ecK-s*p83V>%Z*$6_b2wf z(W~aBx{g6bSmV+)YPwRzvIKaNVO}$%a}8#e1OyWAK?l4I7}q<;l)#VZ zP1xvYQ|+;fB$G<69tqZv~TT`ux71m7Em?X=1pn9<`47qxhO& zWFZ7>VSAjz;m13A97S$xkv;NVVh=Oji$T8XDk_c1HaP6|6VB2Nxqe5B{sAle>1wt4 zlpPUw&JN80Me+{r@i<4w$%6D6iy9^?R-g^~`);semRlXm#rnh9s^rKqeswh!6<2&l z$N5x57-ojt$1Tn2oq6BP`9REZ&+UB$4kIJ&Wz6K3G-f)A&*wyE*Gy{l=D;_EG}<&R zVbyQ+8B+ehIdn{kTL{4cDnr4_>Z@R0-8&Wzn9?a=SSVH8Oii<^Kr&irI(K==?NBR# z=ju%^vZ(twgurRrSeek8y!+z%V*254`VFb`b1rY2`-gT?aXU%sb{9{F@4od@SBv|U z{}+^#6XMHT5x*ssfV3vCGyR@yaKQLbDD>_RX@3R755{Nl^@h*0Y)l+muptz;;zp>) z{fq)6XiS(=G;EMX^P;uOV(GQrR|P@stZPc9rM0LX7mN8*4b9_~D_zysded8@F`1F0(!3F!Mcq=FEVI@9j~0#EdPr^#1TlJO z9B{FU6tCB$r#ZzX=>jg`HxB}>?<0vdDMMPsZ=JP{3n>dkY|DlcOsxnP@^m0j4wEG`FG-|TG0V& zt5r+<42Vvjc7*lvn48Ln&jsdSm`OY^*U@ahGlZDXJC((5Yc{Q7W2f81+~D8TIJR;E z^XlVtmbW}_!OmBo8p6u(1Wlrba^{6N8IW?5~co>qS|hPI@5Gab(7eM zaJ1CM+N{%4%4N$mCwiLjui-EsHDI%>ja<;ZC;MJNsdW|aHhUV(y0P6kgk*w-P zKMM^V;NdLcj2@d5QDQIC1wBZFp;ZN}T8tUZNyVuqMh#<_%V+a$J$#e_uJ;LaLB998 zipx;VipEv1pq6u#xANGmY2kOm{XrK{YolRu1=5E8Fb^!O|RFZZN(eO_fQNE~w1bHB{H+f+0pq43m)H_l`KcbNGG7%DEJfg%axxWbfzvDW zGy&sdP3MaoRhSlxOTOgn9EVr3+Yi5KOr}t)4V)ppXc}L zUVO99qq)Z4H^&4!k=D-m$DxW^wfJwWd-$(zF+RA@&el1=;4?&WVd;Y)jrx#v6*k=G zgw^$YX5wEYDXUco)?;)x5Iz=yAdjectK4CnHLg3VU6>BiP-Do$TiovxjaLo&a!P$4 z8ucQ3ugH}YUFaf^#tvKW-^2&IxjzR^lv~||vtABar43v22^kL-B3eBAy8g|NFCa$O z#zDaf98**P=MfQuD=hY~k2W~(`~~I|eS5N5--Pb)DQsB)j5*d&Vd%Q-(sW8_y+MB`k z_I0_1x#+aV+Qn#(ws{SEjjRuK;1B-YMxbJ-0`Ja%utIGGv4pYr9OK(+-V5)^F5FPX zezRI~S{!#{cdM8k>BA*G3zAHxFXzVwhE=p&y6N9v=cqI_43V*%?f0Lg?SqRWguh^+ z8J+X02XF6gb0#%!PoF;Ggi#o>2rC>Xf8-{I(At-l_Dn^692;I!WY=rRk$3V`76v|5 zSlU$seL|EXqP4;o1bYSHWkP6?Zhh{w1=z<61KUM0%{r?Ssc<1jjvE6{ggngxHt(&+ zaC)h8nr|pm2RTLxjs^w`rwg(H0ixaACS<2#E+**#=P^!04tV{db6H_tL%ho^AV@~; z=_qcgX-MAgrZGcG4R^Y2IFt$+;YmLcXHNxyZJOvvqM`_}y%XpoyDW*050OP}BqVC< zgSmpn7WzGllr@VX5ogP^1t6v>7v0m0;XPJ0iT`LqL|YDS*<9VFXkh?J9SKL}?!?#x z+Oc2c37+SBH?=-d#}1bld)}K9iU47&&2+Ph2Pp|b-_*-q|KPrukVm`DUVrQz=W`jT zO&?`-7TC2}Zw(s9_9f0D2`!499^VMqTK8;M=&&%f$C@E3s%;>0b{6bAnhbOr)*_7@ ziJQr}(Oa9HPa({jBCMgq*OC**v$x^Z+gkNV(luN0=CA**6W{sXkWOfD=z$R-8!!{4 z!G?`f72t_L<;T0Z&fLl^5JS*x_5Dy^=2!C?8+a< z9*Ed1J#^bGFhI50Xlky#SBH+np1CVC6gX=ioWaIy^Mnu(~x6sjxp6K=K#3eM_ z4&Em=f7%~92+wZjYaU`SIwWa*vg_T#%)Iv)t#;vO zv14A3O;RyTTM%=*bsSo|g|z~(n3?j~7Ts>0Z(fKz({^<=vngSPBWGhj2`Fb*?i8!* zcRsjP>LJ4A0tQqR=a30c;-B&rsnRU6BQHq6+t#|bF8DI8%{r!}x!)NGkl&i@4Xn;q zmj=cscnafnj9N;HBK00Pp6qI zOo3PiRPmIDD?ZElq?uP2Zx?H+X^@~@2ZdR}k#FM3nhtw2CA(WvPAf*cc>7|VieERq z{$_%4Bk;Xo!L*uED67D_K>9Y&V`uJEpFF`Yk#w&#Y~8ycHot@fWC&4yIz#4@klW`geApY&l-h5 zQS?vJsj7`u1?K{B4U3sD?~lP zo5o8V%*lOx3l0d;;7U%-&@VE*vvD*lXd^CCxY^$sk*HcEf>)5slR|;JIbKbOoU?(vuLH(riwbXe1C1q8k`JmT!}38sz(uR2w+4#g4g6!+=m7HV9=5eLi@c} zr^_X4QCNpRqr`xt!3a9uM2;OUf;qO|ee?BUQt}jT^zIqEVaFmvKJs z_<2_tJXiEb{3bCd3!%8E<>Lp-0Ye5ik4RB%%agNL{7@ z`39s03sBM5&c|0(zGCsHaj$@sSN+xfz=rdg_?_gJlUsaa(6!-=n#e-%WLZO{Pgn!T z<>EQAB5oM|%BkBuw_enAJ%`w4eol>!^*Z_HCYD@-EOJ0dTHMaDrpPR{rrqyqm@CxR zlY|qv868-xGiD_xS^uiq+MO0{B>qgZlIKjF2z*xF?NTz7%e`(ZZr*;f63AKh4-#(* zMDV~s4`ZJC8^~g5w_kW|K-ZnUF~7-i0#xnY(EwBGlu$bR)}OSftm!FeP*?Ile>=yw zJSXvTsj}>$wMR|Y^PulqY;t@yK2lQR(i^ES*X;FGQ`iroP^otEAE}r`6HcZfaHwG6 z0g^w1(V3krxc80J9Fq_iqJPXA#u^UTA2A`f{oh$(oRF!Q&Nf}DuUv$UI9ZM_t0(s( zTNUv&=T1#)}R`sEkRU9`R@)*S2(P*$*71|4E?DsP|GowYS zCoS6oPFDr%V>U?uWWD;eSxuk^A?I-uuo0khm$|YL#(pNUPVlR}671^@r2VQ>eGAxX z_=1ha=zsn79}#_FOhrrM>j07)nawtnqb2C-thl?BgLs>hokP?wJg?zr=W{6U>E7-^?=vT3wo9K=3LGN0@abb z9L<|2n5pF(ZuxONQ~$BSQs?*$l?CyL&?w22DdO$v*c=vR#KrjH&SalwmL?xgtLCdQ-4YWK-AG@3Nb>IULi`N0?C!yT5|E%9gq2RxjA{`GrvKT zF+5?sY%n|Bz;ZWynMzR>A1n{)pG`Z(h)^1DojT1(P*y`E43J0y+<(Gch?CtAkOT;a z51vAmc6hMyWZYWqY8Oh+Fz^MqZ0$7Rg-?K|!AOj`It4~KAoTx>V94hKYN*nJnS3eC zCb%o=JMEAzXUK-t@;+(+yhs<}Hfg`18i^2XOLXm)l}i&BE2I{0y$9tSL=X$~)^*WFMOfXP{fF4wAG#CqH5_qty3hA5R3mghc-rp} z+k4z*@n5j46(soEeNCDRWx#wG= z0jHrXj*;8;O!3cH8w|u{De)&rIjj&oQk8v4KPXA7zmaDAjRLen0*yjca%dP930z_8 zD#^K0akX{TQa-&^3XKM4T}-pWUxqEUTGf?3FxgN(XH?e4tMiRX4i`e9{q<;KlKx?Ci9t6L3y`#ad4)z;9;V9I&opG{zRSz<=n{m`#H1?^ZfK-Q9ox|ggBAM?cc#Ab4B za)Oo98yd(v&Pc7<4n^FPl1V_Q97ZIig_Kq^)aBu#9y*;$DXXGV{P>fm^=GDe@H*a7qKnD>3T2jil1Ls z%Ls&IkD*ixpFqo64-zdPy;Q20keqAL|CeAq$~J4*CI%=0p+c$w4rX#{$jiH#1h= zF8yE7jtJ%>UalnKzghMRTh5hHQ;1Vg!c*LG@oR7|>=W*Q`3*_*?a`49ZKeTH;E1eI z$s=eFkIIf5ZIABApZJ`UphDqI-6oblEy0Do8ZJ(&;9-e_oxR!MnTv}j)s9BH_J&2i zW;@PUzB=z4U*Z=Cna^+TK|33ypAJ6)D*Cteu7+lamD2_2D2=OVtwyK3CDW2n1ax(Q zli6wA4k_z#ICWM^a{zBP0)o5}C^}!e1iDU{%T<|*r0&gJePd}@-0u#IQ6yKw+PwRI zp%}`Z`h$R#!rBxjr$imNF(=57sWPfZ;C7Q-@_08dX^^E`w2tX9jy@;iRPqYW%?5Ta z_@nMs)8`Wxauf3~;Z(C*Y)n7k&d5Y$dx6Cf^C7V}23K-pJYR*4?C3TFaJ}|qhsL>- zEpL8A?qrE@dbv>NR!Cet%5nJh(RCImGUms7LhgBTM#hw8CMdPwWRLYDazZ?Hp3F~Y zoAv*a^5pDg-tR`f?2KBYOf~Q{B-`(4Y472pq9DTJxH=Lm4!liaJ#Q9toe0f$ow9@2 zVuu%vy`KrZzh|IM1jR@JT>Ba!KT{$Rt??_4C*NreTy#+1!6KhNwAxM3*5$Lv2jACnRT$Y5ic~l5|w+bai#=avBfQcDhy_TX=iU@P)cRQ{lT&JGpqw zo%QUV%>I~;q|z_(G^bwZFz{u&mlf?RY#Ww8Z1ULdJT)#i?=9g1J08Zy(?>qPX^|Rl zkDCfC5M)7d>OS;V!-M}RMH_|2=okqnI6E1p9;>VTpc7r8wxTCMYs%~H+ti(xwS!b) zRuzuNlK#O?H?*Hj?P2mQa06W*Qk+DRcp8qCwBlUtAC@w9Eo!#IfyJplFZe+O%J@;< znb*R6!!{}P%1C4in99@>a3nro?=Hu(?sqLB2276DK@rIIStU+fgIA8Ds<9#RB4Q8; z{*iTO)CP+|+~&_VchF^Rlat$T^DuJJbucg%naE(FS9}LWv$Jc3HUGz|b9uDP?@a(6 z0(Vezu-Capgv)wd_`4L@?7c*DdEyVQIup#WKz8Y$w&J-H0E~N}e8d!_gsmQ;MC6Fx zB~=OYz~mpD#zu>)drTG^G-AZk*KshX}pp2K9omm zP*rq@=}q@A&pqnc~xHPE5XY4NI&0j)YhRXPcu{L;OQe7w)wT^kOs*t z;9P#k+0gF+oKN+Cps;8K_@|!7_so8}JG_`OI=YrXa>KwUU+OQR6;-L*>U*D(d0J@P zi%K1Z9GneT-YrHE?&|BtSYRybravaWExnkmY0O9yp4G@^xse+vO4LH64HL zT0N;?r@=KJL$XYS;`NE|k|t7@ZQX7YskibX=so2F=nK5wGlb4c?gYKEvJL#JAKT_} zfR{fUt&yAu;X#cVnTe0zB>iZ*e$B^LdHa5z4Bg~YZh^Ba0^I%Ie8oMq zNqlm&flag};-auMCPJDQ;ejD^lFaNX8Z}J2HKj5J_9vN7VWW+h?fY>n_ng?<*3Gq& z@4a(C&dgW?4G*VfOm-dxyBegx|I{-68s6Ib4+J88vNPf)z8HKl9FMWxT6MgCr23c7 z#mm=f&i&^fnZ9lf__61q^3tQ3PPe+xw@R*jcZSiX0P5izFXhtR1m?9yzGi5&3#pvx zE-laQmKu6POwVqODh7Jhil{Y2v)x{#%o_=S7jDB4`6+^|h{4_OGo-r$HRDh`VB4+J zho@XqxubyX2fNyz&wqvRe3XbHIVx3L__72^a&&FZuK}^NztpY?2L>zA2>6cA-O*Ba z!)$LYc;kE)lv=J05aJLt8fhy7yh89r2V$RzIk}5n2DnyYUktM4?_5OUN-y4jQ&sB$ zsc1E99<})EKes^iq+6#pcu{L+W8Wy5O%k_ z?LO_5^QfA?y-gRZd7fbPJkhGJZ}gmzZ9y8PGk%4?FSZ9QXBnyOW~{<$?`I&>PJ}%W z+Ak9>vdhkhGj5Bt-O5_)PyJdt<;7(Nb~Z-vm>`+O-IDQrXEeyraG!^V^-F2}H)vX* zO``wJ$n!^1mUV0{q@6#*90EN30;0Bn z-jFEU{948jT{D=T8pHv4u?cUyq)yk3=BiBF4c0ECtLMkV>ep&r(ev}{GTh~ z0n)M3Vx-PaHk2I}Z~mq9V8_@^NN2n{2LayAC!$p-^ztQgaw}Kl`*X`Bn5r(0b)r~S zHYVjVRtJIo)Ejh%Lo`EgUKVB@9)~==%4N_$5cyruI1B<{ivhIhDX2j0H?)Osp~0N> zwoUVUl)j(YM|RZZdi*gfjGG9O2W-GQc7m=2_8MP$K^By^p{4E1pG{S9Flkl+9aw>S z_S2$6u8`+S%%1!A>E@Kj>>We*XVcvtm}I`^^Gw;t5z~6J5E8#3Gx;N~k;qOyAVCR^ z{zpy~gnib9fBO?73p&6Q(Hc8AXwsx_Z(mHn;F0B1$hO?w-Cp>`i&`<{9J+Vx!4NuL z3o*hB@=mtdmc3kJ(%Dv0KIXhr@s8^N#ztW!CG)i39?E&>N&~X(mK_;&Xm9K!V~;qF z>cR9RAh`Sq&Cry+_1&5}4*`;cb0S8}8kk_%+6+Q}WF*18dYTL{5&p=3(&!yJr;5{!Xg9G_rIJvlipKRQ~sJ(=ut?^ z?-PC8S!QRu2J&WOqtw$L!DAYgnVb^hmSy1?y%SUP%F6T(JU)C)69)g$ux!UsiZXpa zM!1k(<2H{qy9BxeJioL7+?>?zyB6)N7FD;M?K-`V>N7d_5cc|u-K%=X{Rt}qE7H)J zT?34Z{W}+mq<@qwV_h~GJ{ z+R$I3|3q^Tudi(y5%o4CZ;bSj7Lyctvq4b~LjDLR4y;(^$LB-*ZXhrf(hEcRhfpn~ zT)^?81f+|{XDUqHlV;IS-o*5-3cL)}|Co&`mUoA_Wckmvt15sI&+TtMuL+RGs_R0$gJ} zX`bPS@TPF*pYPJye`)@_UPs?BXpUwM@udeNHFZI^sozPrZ1~up!NzDJQxUTqFmo1r zpfF&|b_(OfhQ+_P6&OGX`z@%oKulS+AWY%OqK#mP13CfFMvjKWv^LWJ)P!ivAY{P* z`Ur5T>#d`rO7NZIeLeXiVwN7DBRVAblgShta!@?AW_D3xOpDpnBZ8tZ)&e?@QA?Dc z!J4}N4U|kl*vPj8j5;FK!s6Ua<&UpVZRr20$%7x+q%`lg;8`FNkyY>U=_*{FJXR0hF}Xx;s3(z&_j{Kf z8ylP_-Yhe#%SOuH1I&jX(cW>N{)R~-9Pk4uhdkkLXJQn2*$CpxFhVVjzZUR}*qEVy zWQgfZx^eaTQ#sg>-B^iBQ$kWSF&QbKeS$M%TKW}xl|}%L+=P4mQlk*K*o}_|?aG1p zs;?&8&@aC%JeQHolg!_%y(@-$*PY3bV`jSif`c*e%DH#m-j_~t5a_H!*ymE4Izz;i z4PBm*W>|SkxIu{5&9Z&Ck3LCh4orB)di1gK7$~pkjcz&snj`A0=DO%*R60@W35UJI zD$4tq60p;~xMAa_y+T;P2#E*hJS(#>s!ISYVK;^VlU(#2QQ!uT?XoWb6& zc7@_FC0c?S&4i2)RjPIr%k$IBKMYAKI`a;$Xjt0qR&XJj$t$ zMd3X;DxBxJK^7KM7Q&TfW9gFF8$S4&ws1X=z)#9SI43?ts5DF%n|_Ya5i$0`@FT-h zNUAGZcVuOh?SQbt(jdE)TlVuHbZBW%>w;nqJ5@3++00vO!>yuS&|_yX79XGn72t!z zf{OCNp+FuvUs@AAyYGQ~faxxvE_fI66 zLFGD5EY7NKuvxlF3Ay6hh)rtiHy!S1!-1S5r4(V}4nQ%m8xEN!my1LyJR(t2?D178 zLyk*e8RrA6MJWSMAyWgCpWuU0!7QvdCH8c5Ib+r(*RpGVq<9X^%w(r`!&INl{l@A$ zxvbrb-Q@$e8q|DxE%Z+@(>csd>(ew`HoAHss-R~Xd%yHsq@68%{nd;rt-@H(!P0>gK z?4BbR8rT$$pO$y!eJJ0evB|7AOFx>9DCoSSo2G6xJ1tK?TXtPqab-EeRLVUfcy*!B zn(ZAa*MV{gz!5__4Q>PGhSB0}uZtIKTifjDPDbN}aB%erA5KmCaSF*mV(pu5O{X_= ze%ju%+w?iteA1uNA0LgfyOmn>Yk%$3{_s}~&wDSK774S9#{@kdT{}hid5?5@3oqL~ zUHuiUt~|Nbt`7X9uY)S>Oit!>fmbnqNW3hdoZ(UASUN5C;(l7+^3UX zrypqfRo2OR&hIaRw~Z^+nEijti35`G_pfAA8z(n=U$0>SkT)$iw@XA1S~6~vnP<6f zEruNTj2AufJebD`?pc}bX_08-s1<1spl;E%L3P#oOaD8cvq73cgn?RWa^K;cQGvPl zj(M?1d0~*2Lv{Jy)}QhIXZssREh1O$j5T`>I$E9HZte3Z2MaPIdHzuwm3)IU0Z~<~ zQe#J|gzN*gqcu52MVFEhW1MqQ-%=W{kFl?Ap$8NEhZxLwAaGRJsn_}T$Cm7P5W*EF z*^jGC-DzJK z9Dzdlx>cU#z?~N(L-(W9TIu(w&Q4Im5JqR?Gi(9t%7C+K-;i<0Xl{-_T~{aJH`FB9 zOQJVFLwc$1o?N!8R?oJyd~OJEQ9sxWN+$KDhSsF}etvRyj`!Iu=&qk**6hc)aax^z^{fhuF$JZxHUw z<>&j|iUjeY(*VyUbM|1oUUM>2F?S1@!8H7C6J*fjil;d{L1r4_X13fGVKIx$Ad z1r}aS6eCBn{w*E|o-ONKs^u5D zvg$<&4>6@e;sJ#Yfqt%I@5d|qnK=9pfyB1WaoaWC0%@U#iS@2zK!lj^97t9Yp`I&8 z+2SZT_vTZUd_5|hOiQq`{%==fH^IFa!;FQdI3H@^it$G%>E4~K(XT8<=U-e=0dRz5 zv4X+DpzUlRx~?Ixmhl>?rrg6uq=W4~CJa~<0g%(zq(M&1m8-*Vny0UBbq}B((m*EP(+vh2^dH6GvcW#06C7jEd>q|H&R$xq(e) z&$nIaNN)mi73uxugJSd7v?LjXO_{vqI^I2Wk+R^skK~lHN&vX~4T)A;ltJTYehDhx znHfcl7o0??7PMu3R$1s?G;o=t2eVdv*o9#@p*DS5yq?z)T%*m1kV!wbPXe5hbQr^n zv0vXkL|ed^vyy&cSF|2+PqYdI4Hr*pb&b&3L*>7ctvT_*v$4qe7HJh z{rqE2Bs~{yY!o-5i^12&8&6;+Z7 zKr+tO<+)@U%YmP5`&XKBF|H;eJ-9nq?X}r_dDB2WS$#S6$<`J-_f>2e8C5@UgBw*} zoUhXzKuJkXmh(HUA2o`lUm;@J#VhNh?3TYD?m15i z>K`#Ag}dN&HUm5U&OXL($lIJvGD7Ka)5S@J>94>}my7^*y>ALM*?Y0dc zJE~}V3&cDg=ylaiK%_;2&no#tYL3p8siBr#F_oKc2bhKDSI+>m-EVOfLR7NU^NWYh zS-nxt1l|>*g^zV&wMhqwSLcHbBNS2N26PpbL~W)%o+x680{_3PA|Oe$U+zsa3Oka!l0D%RsQG01aXH0yFc{2H<5UCmPgnnS z{U=+0Efkkh>S=l8?NV!X$H>^)9Qq6U3oXN~!{sX3gkU>|R@rA0V85OZ(yh|1w5IZ% zpTLwbBS^&)H|3s0CfHOFn$F9ts5QsPNa0UGs@56AT_*Z|K_L}&4-ZDFL6p5TpiEW} zt-oQ&(4T4Y*CIZ;0A?ubh3Km1hk`89(dl+~9;9pZ_`%IfOsvJt+~cYW6e6{SZ1WJSyQ0$Rm9$G28k zQdwLn+X@+TE1HoG8I(p6ju4JJ zEv%Fg!q3h3zl)fr8B;Z@oH+lTP;SJ~!_D`R5*_O{v(!1aa4v@9e7tQQd9oWlZDJni zm<;Y&f;~pC?cG$4ulNVqN zomq=g=D9FquXrn{W5a=K;@!lR_x|(5{Y_rUPg0h_$j11)>h~V5pPXwyd%olia@7@T z6Gor)-b+e3Yx}wVVKZIjGgbPcM&86sPc1=ZBViN3oewW-6_`AskON6*VbI1`E)ptT z38zrdj>_U_MBu`fQcfh2^)>AT{yj?I&CXOwq>rbklt96$VXML9W8=d#qAh&g*jCmFDK_Fg>5g-~7l zBxH}M9X5&n3xO*yg2i=q$Jmbsf%}_xU?+-DIzGMKIO0XY_@qX~#FS;h=d3@2tbhub zJq(atIemrUPv0t(%$!}+=5XP_^}xm2uyM=k+Hg0LOHOswCD#5BJ9B<=J8&!gd{3M; z>}U7vm1 zN0}^fCX~u_jVtOU;Y!6aZa$2;4@pH2Sz3~FK*lKnL5DPBq1i-r3T*NyVCG_5DEDtVCwV z+pLGnSw;KKKAT>MWCW78XE4cADT!EgM3LN11AP}z=cwk(@owGBgZcd5U==oLz7M(`1ws0R&OVm?uRNSNA@C5G!Klum4g+e6gbjSk zdl@F2?0<&ldl^O?LdlA>v`3$gup$adZ@;{~+#pSJ#-J>kzOmKSsS=}-_nw+hPeJ}e z1sTT&jyrU{L7Jh~#3bKr4)6aOw=!=o5EAun8HR?2E*N~gnnQK1NaJDEXo@F>uPtk+ zQ%U+<;V3wIxD4_9?Ns=}2uZLN_DwDmb`OF21+bEGpr|_k0I$x$-@znOR?1MSAzS zy{k&1X<49}_~z)DsW5|8^U6Ch!atws3=J`)R>?{eF$khfr2E?RimxNFikPNokI^#k zoZ?TNw=g)rkF34OYQ-(cvUGFmguF3?$#ky2a1*TDWs+{|f&KFhU;EC66H^^oo3;R} z<_K2w2u@cLp{&DGm#^EK*JODB|56g8Y)$}=0KF`y+I-10~}1H++?jW6#L@T8TGBByG$>o;XKo!ScTVn98&>2S8Av2jh)nnXUfYvhnYmy zBOAN@9Z#`512X~E-y)JMy1|PFGcwDSqqA+E4w^s^bT|$V<-k4q`3nZ2RxfNp5T9&rjODO89s9kHAZgD2lQ&RwuYT)ZgTqv1qAb|NprgAxg7 zuf-G1q=5?^cU@HquU?Sk;!pSbxL_qpNjzy(;YgK3RWk1r+*#t2e{KV%3Mg zXT^RP7m^6QEj{*Fn)`qc`ZMHpjT*4jUGERa_JaYyz>B0qD@sg~h&Z`I&1x6x%$VSF z9oVnb_}`;OA&?|153H^DkY~F6T?5y_!~TU}ly=OCC&`=@Onu}ZiE@Z-*4+{=$8yrd zB1Y>`LQj^{{6T1<`d_dRra*@bIXBglPxU@o?!z${_~qgl&j}Rec|A#<^_GtG%LJ*e zjmJo#zYcQTSNMpUqmH!)E(#WODEIFo`auGs=Y|%1H4vEy0ys8q<=%2(u&-RA)*!|* z=ZxCr@kAWAQf9;Jw>tiP7cX?kqJLK%?R-+YQ*g}K^clM|f0A7gW)<3N>nh25Ut}I@ z5KHYdph)k>cD<4eD)iyp`@lTfqgl#UD_TN7=1Ffcu~QSsB+vsDmDrs4i4Xh9sX-AJ zLhiVc4Uu#yU1ZyhUaus|=25Epp)iqPw`|8tjn<9NhqcxgQtB6q3nLFNX-+prC(ZG1 z4y$c0C)zZ9s_#CV=|8I|Koyx+A_daa?q1>yS1tYdZfhTI>_)CW%agmRQdVBSS~@3Y z;#OCOn|*WCRyx+W$3$rk4u^BD zN8+W__RyQ70deRKIeca3nc7qM>d_Y;$K_MHwDZ56JypLl4)-{mHu-1etceB0lZy z+4ivS#wW~{!aYCZo66*|AMkaf3$irS+}K5>Q4!xTUO%7Ke9*t%+WqY)88-N59^!T4 zGjCGOsn#H&X5QV?k}mZX9Wy%AO=$*FMeL7WIOpca zLD1db1m3rxzLqSi0KWv|7P1easO9(%k$M*0`$qx_%=`rf%e$yU4Jaw$6uB|`WGqva z%6G5V(C`_r;NvYdt#;GXPX-hdA#wJ^i?gRqjH~3z$0G9|Zv<7ejfSwt+*y$bSg;Jv z$!G%D<$Ax<7~VNz%=$=_*|x@dBNoxb>Us#%eJ&!y#rYml7Uu+K@IL&C_b9ex0~kEo zN3qU;7ga`_Wh3bmG!HAdpLcE)Mv8m9M__;jzsxt~EW`jom!jqzvvmExA#sE8ZsgS* zIDvCc&PC*G%h!^R4->xPuYMtmn~z>P_%aVtHI8D+d?;!+`>k3MSv0gni0?lEreuDO z|GnYW-inw_EH6R%bSqgH$H8K%rFKlaRKU9^PucA5NdQP?7YbM)Re5F|a6zEG!3nH{ zLl18JTGCkNA?p}zaV<<~1mU^w)A~qP73pKeBMnczpi?FAgJfeeQfTgna?N~%h67bF zvXhK$Xq$E~Asa>uJ_~sk4xtSNPZ(=NlI=5t4`zKUNQ)Otn4`n<(MHFz*ocs*Lenxk zT@VKa{AEBfJ9r+2Pz}Y(m@jh*S+NweSV}c=f{xasXiZI2tpmlT5i;)#pe|i{+ztqS zTp#NV5&F%=>~>@;TCtXZY>WwvCUfn0mO^tCSMDGAd}rHgl6>YdCbsKL0MrZ8o-L`) z0?C4a4&XCd7Wr%l#z)EPw)dUCMR-xEl)3`*29vld0k^kYR_E00w+G@qPuoxP`E{FS z>^0)96HjY7H_=+^5TcU_h3+TT>fT*2Br$N++mTlA`=8u7p1ODg#~>a0qcKHQSlnsJ zg+M8mtr7=}BY=ug@h((2d(QYMUK9dP`$;mI9`^7V!Q*zvVX^b4_*y!;zNd_TH4 z^gIlO0SZh)!3J=ea_G|-bJ6h7aGP>yI|kU&2y$}#d;JH~aR0j#X>lmJ+PTpP0HL5H z6uSS*WagXcV3+Zr7`pI6zO%||G<)KYXiREMG?mHnk7OQ1_|~eogG2yM$BWXVFOVqJ zQvPKoA-I-S`mmRq2DzM|es^?3pngnEE{FDCzG~Fq delta 24702 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 CW>uO1 diff --git a/scripts/generar-pdf-documento.py b/scripts/generar-pdf-documento.py new file mode 100644 index 0000000..4bbace3 --- /dev/null +++ b/scripts/generar-pdf-documento.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +"""Genera el PDF de un documento de `docs/` con portada institucional. + +Toma un archivo markdown cuyo encabezado tenga esta forma: + + # Título del documento + **Subtítulo o bajada** + | | | + |---|---| + | **Etiqueta** | Valor | + +y produce un PDF A4 con portada (logo + tabla de datos), tablas con encabezado +verde, pie con numeración y saltos de página que no parten filas ni dejan +títulos huérfanos. + +Uso: + /root/.venv-tools/bin/python scripts/generar-pdf-documento.py docs/ALCANCE-VS-ENTREGADO.md + +Requiere weasyprint, markdown y Pillow, que en esta máquina viven en el +entorno aislado /root/.venv-tools (no en el python del sistema). +""" +from __future__ import annotations + +import argparse +import pathlib +import re +import tempfile +from collections import deque + +import markdown +from PIL import Image +from weasyprint import CSS, HTML + +RAIZ = pathlib.Path(__file__).resolve().parent.parent +LOGO_FUENTE = RAIZ / 'apps' / 'landing' / 'public' / 'logo.png' +PROVEEDOR = 'Consult (AS) Technology' +COLOR_FONDO_LOGO = (247, 247, 247) +TOLERANCIA_FONDO = 8 + + +def logo_con_transparencia() -> pathlib.Path: + """El logo del repo trae fondo gris; se aplana a alfa 0 desde los bordes. + + Se rellena solo desde el exterior para no borrar los grises interiores del + arte (contrafuertes de letras). Sin transparencia, la portada blanca dibuja + un rectángulo gris alrededor del logotipo. + """ + destino = pathlib.Path(tempfile.gettempdir()) / 'frasa-logo-transparente.png' + if destino.exists(): + return destino + + imagen = Image.open(LOGO_FUENTE).convert('RGBA') + ancho, alto = imagen.size + pixeles = imagen.load() + + def es_fondo(p: tuple[int, ...]) -> bool: + return all(abs(p[i] - COLOR_FONDO_LOGO[i]) <= TOLERANCIA_FONDO for i in range(3)) + + visto = bytearray(ancho * alto) + cola: deque[tuple[int, int]] = deque() + for x in range(ancho): + cola.append((x, 0)) + cola.append((x, alto - 1)) + for y in range(alto): + cola.append((0, y)) + cola.append((ancho - 1, y)) + + while cola: + x, y = cola.popleft() + indice = y * ancho + x + if visto[indice]: + continue + visto[indice] = 1 + if not es_fondo(pixeles[x, y]): + continue + pixeles[x, y] = (*COLOR_FONDO_LOGO, 0) + if x > 0: + cola.append((x - 1, y)) + if x < ancho - 1: + cola.append((x + 1, y)) + if y > 0: + cola.append((x, y - 1)) + if y < alto - 1: + cola.append((x, y + 1)) + + imagen.save(destino) + return destino + + +def partir_encabezado(texto: str) -> tuple[str, str, list[tuple[str, str]], str]: + """Separa título, subtítulo, tabla de datos y cuerpo del markdown.""" + corte = re.search(r'^## ', texto, flags=re.MULTILINE) + if corte is None: + raise SystemExit('El markdown necesita al menos una sección "## ..."') + encabezado, cuerpo = texto[: corte.start()], texto[corte.start() :] + + titulo = re.search(r'^# (.+)$', encabezado, flags=re.MULTILINE) + if titulo is None: + raise SystemExit('El markdown necesita un título "# ..."') + subtitulo = re.search(r'^\*\*(.+)\*\*$', encabezado, flags=re.MULTILINE) + + datos: list[tuple[str, str]] = [] + for linea in encabezado.splitlines(): + celdas = [c.strip() for c in linea.strip().strip('|').split('|')] + if len(celdas) != 2: + continue + etiqueta = celdas[0].replace('**', '').strip() + valor = celdas[1].replace('**', '').strip() + if not etiqueta or not valor or set(etiqueta) <= {'-'}: + continue + datos.append((etiqueta, valor)) + + return titulo.group(1).strip(), (subtitulo.group(1).strip() if subtitulo else ''), datos, cuerpo + + +CSS_TXT = """ +@page { + size: A4; + margin: 20mm 15mm 18mm 15mm; + @bottom-left { + content: "CRM360 FRASA · Consult (AS) Technology"; + font-family: 'DejaVu Sans', sans-serif; font-size: 8pt; color: #6B7280; + } + @bottom-right { + content: "Página " counter(page) " de " counter(pages); + font-family: 'DejaVu Sans', sans-serif; font-size: 8pt; color: #6B7280; + } +} +@page :first { margin: 0; @bottom-left { content: none; } @bottom-right { content: none; } } + +* { box-sizing: border-box; } +body { + font-family: 'DejaVu Sans', 'Helvetica', sans-serif; + font-size: 9.8pt; line-height: 1.52; color: #1F2937; margin: 0; +} + +.portada { height: 297mm; padding: 26mm 20mm 18mm 20mm; page-break-after: always; } +.portada .barra { height: 6px; background: #1B4332; margin-bottom: 14mm; } +.portada img.logo { height: 24mm; margin-bottom: 10mm; } +.portada .eyebrow { + font-size: 9.5pt; letter-spacing: 2.2px; text-transform: uppercase; + color: #2D6A4F; font-weight: bold; margin-bottom: 4mm; +} +.portada h1 { + font-size: 29pt; line-height: 1.1; color: #10261C; margin: 0 0 2mm 0; + border: none; padding: 0; +} +.portada h1 .kicker { + display: block; font-size: 15pt; font-weight: normal; color: #2D6A4F; + margin-top: 3.5mm; line-height: 1.25; +} +.portada h2.sub { + font-size: 11.5pt; font-weight: normal; color: #4B5563; + margin: 6mm 0 15mm 0; border: none; padding: 0; +} +.portada table { width: 100%; border: none; font-size: 9pt; border-collapse: collapse; } +.portada table td { + border: none; border-bottom: 0.6pt solid #E5E7EB; padding: 2.6mm 0; vertical-align: top; +} +.portada table td:first-child { + width: 50mm; color: #6B7280; text-transform: uppercase; font-size: 7.8pt; + letter-spacing: 0.6px; padding-right: 4mm; +} +.portada table td:last-child { font-weight: bold; color: #10261C; } +.portada .pie { position: absolute; bottom: 18mm; font-size: 8.5pt; color: #6B7280; } + +h2 { + font-size: 14.5pt; color: #10261C; margin: 9mm 0 3mm 0; padding-bottom: 1.6mm; + border-bottom: 1.4pt solid #2D6A4F; page-break-after: avoid; +} +h2:first-of-type { margin-top: 0; } +h3 { font-size: 11.4pt; color: #2D6A4F; margin: 6mm 0 2.5mm 0; page-break-after: avoid; } +p { margin: 0 0 3mm 0; text-align: justify; } +ul, ol { margin: 0 0 3.5mm 0; padding-left: 6mm; } +li { margin-bottom: 1.2mm; } +strong { color: #10261C; } +hr { border: none; border-top: 0.6pt solid #E5E7EB; margin: 6mm 0; break-after: avoid; break-inside: avoid; } + +table { width: 100%; border-collapse: collapse; margin: 3mm 0 5mm 0; font-size: 8.8pt; page-break-inside: auto; } +thead { display: table-header-group; } +tr { page-break-inside: avoid; } +th { + background: #1B4332; color: #FFFFFF; text-align: left; padding: 2mm 2.2mm; + font-size: 8.6pt; border: 0.5pt solid #1B4332; +} +td { border: 0.5pt solid #DCE1E8; padding: 1.8mm 2.2mm; vertical-align: top; } +tbody tr:nth-child(even) td { background: #F6F8F7; } +td:first-child { white-space: nowrap; } +table td:empty { background: #FBFCFB; } +em { color: #4B5563; } +""" + + +def main() -> None: + parser = argparse.ArgumentParser(description='Genera el PDF de un documento de docs/') + parser.add_argument('markdown', type=pathlib.Path, help='ruta del .md de entrada') + parser.add_argument('--salida', type=pathlib.Path, help='ruta del .pdf (por defecto, junto al .md)') + args = parser.parse_args() + + origen = args.markdown.resolve() + destino = args.salida or origen.with_suffix('.pdf') + titulo, subtitulo, datos, cuerpo_md = partir_encabezado(origen.read_text(encoding='utf-8')) + # Títulos largos con guión largo se parten en dos niveles para que la portada + # no deje una palabra sola en una tercera línea. + principal, _, secundario = titulo.partition(' — ') + kicker = secundario.strip() + + filas = '\n'.join(f' {k}{v}' for k, v in datos) + cubierta = f""" +

+
+ +
{PROVEEDOR}
+

{principal}{f'{kicker}' if kicker else ''}

+
Documento confidencial · Preparado para el cliente
+ +{filas} +
+
+""" + + cuerpo = markdown.markdown(cuerpo_md, extensions=['tables', 'sane_lists', 'attr_list']) + html = ( + '' + f'{titulo}{cubierta}{cuerpo}' + ) + + HTML(string=html, base_url=str(RAIZ)).write_pdf(destino, stylesheets=[CSS(string=CSS_TXT)]) + print(f'PDF generado: {destino} ({destino.stat().st_size} bytes)') + + +if __name__ == '__main__': + main()