From a8b5ca6e9dccbf8fcf1e4de6ac0aea059f68a961 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 01:31:04 -0500 Subject: [PATCH 01/24] Remove ubuntu font --- fonts/UbuntuMono.ttf | Bin 172232 -> 0 bytes ubuntu-font-license-1.0.txt | 96 ------------------------------------ 2 files changed, 96 deletions(-) delete mode 100644 fonts/UbuntuMono.ttf delete mode 100644 ubuntu-font-license-1.0.txt diff --git a/fonts/UbuntuMono.ttf b/fonts/UbuntuMono.ttf deleted file mode 100644 index 23f03502415896e13665d19569f4106ae20c3912..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172232 zcmb5W2Vh*q@jw3dy2>?OKV9!dC!KVc6gjUGZx0s%q- zPB6wms0k#57CP7vLqfiEFroSMpLy?2It!Eh{)W%(yS;t8JF_#hv%9mR2t`qH_~1}g z^)FOdx{+X3q@Jm>brYm3$FweF z#I>yydAtm2@wayu6zv^->;EW<{0Y9#>F%lS`Kal+>G1n9_&$GN!@wFVTQLQ`KM&uv z^Og^+Qsv*UhoY2sK?j~&2*2-saQSig{Ui82wrF7ODo7>JA84NvELyf{q4&xcLlkBI z6Ge(2SOn!a96ENIB2V}!>aD8=7Yxid_T0A|+HZmKp+U&tmUG^L@4Mi;ZE*RzjbhcO zcT*J01w`*!wsPJ;@l}@>QB*Gco>{YeVB;$8MshQh{}q%str%Fopz+P&Ur?lPJ4JE- zuxjPnb>9?!{w+o3L@3I4c-5K(s|I-ocfc7v{Y4 zWHZu1oq4Z*G;#M>DeFV_ZSa*xu`n0@!F%l6#^TT~f8y>$BkMz^Cgw-{ z8#RyOPWqh<-#IKlxe|V%So>Mi;FV@3CH+)8^&RA9u`}2_7MsO6OR>f}DASBo?KRb{ z)zqUDHFkmN1N#T+ZTRiyQ=ml_k97i`O%$8ury>C8;W(a$=NkcLKpda~H~^mjMgV&O zm>&ZC2Jj2Ovw$7|)-?-I3upj%0lk0nQ=O1Ox$@fHDA&a7OMAvXVLq0{6w7Y|v;puv>?gLZ23P?;0P+g?bRK|x$Om`;_W-g0 z*tQLTzfA#L1wg(aO}7J(jynLq0${tvfbD=T0FLD@fE<8*xDSBihjim_F92|y@Ev>? z^RXY;*L?tYGR5)2zT)^|-YDP*0LKfT4+EY6AT1UEjw$y4AYd_IDL?}d0dfH=0Z7B8 zfVATHVwrTTg74`-no?tpJV-}p@;mY_9b))C9ncTJvAqni~KREn~T{la6hd~b4!I-^a?$FWA*C#R_EsBe=~94nOn z$tl*+0hpX3|FQ4?uW3rB#qbXD5N%4&CDK!nm+C{RP2|@<0I1iafNKG-1AY&99Iyq@ z3P`mvIZd^xgED)P<&Q#o9Po1h>M=g!Z>W#h#$f=~d#Q3L9|i!QNyq*JDJuyq?nKWfqaSwfHGrWL8?jS;rl4>xq$rSb3LTx0GwMw0Hg!$hck&; zkYZW<4do1hyt7Qg|0{j}ujM|2`agr2@i9nW0E__k0)7F&vBiGe3Frl2o!C!&Mw^^& zfA53u&jZkgq8(fbKw43rQI}EPk(X#2ivh@goC7fr%OG!%-U5Iffc)_T6aXwM1$Y4{ zN2>ts0IU=1urle`-;#N#FDab~!?O{9ZD4uS8I(}$_(1dRRFXjDH^a1 zr1yWNNNcK&REqPA1AufEq@`G29w605Dn;6Y0OWIepHuI9Ab&cb8h~_8ZYNbwDnFI3 zgtC4>DqR525x`_xQ|}{x`T;3EO{N{o;rl6jj;RrV{6?Khjn(A#QC4t_Q)#Nb)ORub zhPDm)kNoKXV7&;e3;BS*WFR(5Aj=y^VY5^ z-U&e3k=NKR2Y_-|p9IRA2!Ox$0I)sGLty==dpI@*0FEc-BkgEY5!gPaQvhv%RJ+&~ z0)NN$O91#g@&bW<$N^LUung9VeZscz8R@}%1k#QDk^zuU$TuvT`W<)R41$l#gLjH&W*soNI{Y74- z>csIvo?%<~jI?3aMB#dfd^_6^5pa-ULlV!JqY_%6yE@;{Ygf6-2@1R(8b z|4}|tX{s&!4f&F?Qz`zYQltlIotzd!9*!x>Cyv|XG}TAs9gYjiDauMdfC6COTmYmE z>rUwmmd9u8H_8Y8ZUG>T?Ew7E1Hk@y0r)PKK{>#FBE*1{&S0C!XOuydQ#+slfORzi zQnG;U;xmpNjz97i=U%K2^AV_9_`L%#6<`2hd88k08}x1L8NhD9Q}7-7JoY$X3p_)g z#vXwb`+;p^eb^=f>A-ri&J?gdv|V2UIsy3Je*?5l5oiNsa;y(vHhrhxQ&NM{4m$05bj6rWObruv@hPpYl-cQ5fgb^_|Z8QS_c;0JgHJs!&d^aD^1 zQtkajO3U63c=tuXyMTWHo`rX14E`I<_}kbu0OS?OZQ><(hA~Ne0%|YXQT6^u8nC;QQE>fI|SFaSZd&&JF>97JBYZ=WFU2Q_wM5 z&yK-+(0A%`z&gNnfE4djDb|ZfeHX(sU~E317;qna2R_rd7t++2rKMxrpxiwGkiju5 zqXJ-x^rh(Uf!`Ja_5zjx9s&FouoLiKbmo7`9M+Gv4%@6|+8e8f^#78eAe{{SEtQx4 zoXW%WNLraxK9zOLHYtuolNEnsJ}#q1omi#@{L z&pyb$l6^D#4)(q5KeM0X%;wDFtmk}d*lqaIs4(h{wZ>NCRO58x4C5^0WybrBPZ(b^ zzG?i__?0nXQkb+RgUMoYnEa*^Q;lhrX_IN6=`Pbfru$7NO{dK=v(juaJIwj!ehbUO zvxqG@7KKG?F<88o2Fo1F0y|7x_H4VvF1M@gI=juDXD_yw*_YY(+K<|wIXm|0h0j>$ zrQhhkXN_GL8v|-#SDdETMBPM+NQ5+!F49Bhkhx?d*-nm-qbxN`2lPg0dY=M%Ut?2j z9y=50m9ib|UF^L;?;-YK_O0wY*}r8!!=X5HIcqrk4ciT07%8LLXfie!JCgLyHf}fm z-uRU97315+FN|NCWG1yqXEM?BMrnH2nYJhCec&Q`yOZ=vfnLoxz4L#9-VUJmaFSl? zJnx&#?+HyW8T)qZ{Mfm%e~i63_SV?jW3aP7_7>m`;6q>HMQZw3HPt)jpn9mB#4Qw+ zxSHk=e6S?Rr&6jEpOANB-$04`6Za)z?wNCcKKJywr_Mcb4keQQced(m2}Ql@eV00W;p~6T{QkYO?|}rr_tM!F@2x-6 zb9U3&9@y!>_RLjh4ncbG%)T=tXEvYdKU06E=FEp@{&nW%GcTU`>zQZazbDR|JoCFV zaMEyQn4;c1Ykf!lE=6VU%eWnO`mK|8_?f1GRw@N1XX5iUczlg|kNP+00_xir)YtTn zYpHeYgJ3OI!dc2H zYBjZhT0{*}Yp7+^M)oLKNQTKSvXAU12gpHk1vx}^lM%9q>?Jpnn>askKI3w@T+TO~ zPdFcQKIgKzEY5#8Uva+Ve8D--`I_@R=l?h#aX#fzT*CQ|li-YTzU2zIJkABKhO6f) zIA_SO$=&2G0@m;WGPe6(&K1Z6dGfy!Y5?rtYs3!i-$WiFud%$WN>)E>32QrRl=U3z z-)t*;kbQvt80e{%GsL-%^9biz&Kb@ZTrM}py_x$APsMBC4e<`~ZsGl%_Yt4)t^6kb zbp8SUo&2{196_~Ui{Meg3xe|*+KkSOEg3gtoXYr1#+w-*WsGHJWoj~KWsYV(lKFX- zGpjA@%B=gd{+X@HuFM|DzBc>8><@%0VS{jy@Yli@gt)<`>~1JY}x4@h6lk><4I4CbuQ*^zTk&U-l@=UkBW%a*`@ zSIVB2eIu`yuapnVKT{|bD;2*{{8{n3;$3B_vO&2{c}RI$c|nz>@~b*j(^Y#^x2T>| z{Y!0952~Ng@HH}xQRCHwHF3>B&3#&qR-)BuUD{%8m9|AYO*>!vGwmJP6WTv&|Ehgm z`@S}jTbJ9F+n>86cSG*Z+`DxwolvLNS#|lksIEqLpPs9i=yUZBeWAWgU$5`d&(hzc zKdFC0|2O>``uFvp8FYsEhLwiRFcVy9xZd!*;Z4Io4PO`%#tfJd?lL}LeBAgq;~T~g zjGr4Xm;@%dsnyhL8Z}*S`i1FtrqiZBn_f2a&C|^b%&W{>&3nv8%{Q9wG@md(YJS%I zD$E6LOSfgVWr<~jWryVo%Qco~tP-ovT4}AfuCU%~eaQN>^+oI3Hm$AN)@s{e+hIFs zyV`cE?KielwpZ+IyV$O^+wFdsE9&h-_F?<|_J7*Hv?m;y4u!+)$a6#-aYvhDx?_Q3 zm1C=8kK?H0MkmL)*mb^XQls_P%FPuxED688r8 z4)+!AYuvZFf8##oe#-r#`)&6R9=<2XqxZNx4W41*(H`ucqHe9L_I_`b+* z$bX zguV@%!+qgJ;g#Wy;p5@=BbgCJBp7LrERNg|c{Oq-s*D<Jc5-=ptEzc0xt(Uw?BJS9aXB_*+vrjo9bzLL2m zOG?(1Y%bYZvcKeL$@L|-mfThHL1{(l($b%ozEYN5R#LXD?2)pk%QMQu?umL{eO3KX{oVB+HsmyD8|)47 zhUSJF8a{4}HO_6ky7AQ}PE$kEv8J1ve%IhpfZU;pqr=RCly=OzW7}v9e=x$4JNFj-Pe> zs^k8SM>?MEc%|dLj*mOO?c{VyI&(Ycbl%o^Z|6H*RM+gTKXh}t!`+L!*LQF4eyB&; zv#e*R=kGmddp_#b(nYYaR+sq$k<<07! zwQSa9vkuKVHS1sf>V8|lzkgBx+WtHHf7kzD|5N?X&6dq>oxOJU<+BgWzIyiEvro)E zJ^RVougzi2$(++L=gK+1nR9B+<8%H!=ZiVt4X_5}1G)j*K;FRof$Ilu9e8lyoq@0B zO6Jzi-8%QzbI;Dx&zm-H&b;5u`*yxye%}1z`K#yuZvON0-&{ZzI2X)Wux`QD1&0<~ zwcy4Dzgh5y1)neYcA;cpU}59J1q-iU_}n70sA$ouMRzVbvFOo7KMe8*O9o?uO@qCI zvj(ppd|~kQ!E=M34t}>-vUp(evBiH~Vpy_h$&E{%TPj`JyLA83CzmFcl`LytHfPz^ zWk;4hyDYK1YWdFPAFgOwF|^{Q70<6otn{wzTe)N99V?$)`OeC(S7on?uiCWg`PGiq z`Kv>#TUO6qy=3*l)xTT)`WpV4qBX5+2G<;3b8^i;)@H5EUmIGxeeIL$a@NgQH*ej_ zb(`1ye%&YQE$e&MU$*|X_0O#TXoGx1*@mqfp4jl04XoZ9J2vNU4sEX3T(`Mx^Y+bq zH{ZMYfx}Cq+`MaH` zcm8STOFQ4$`Qgs5c8(2a4l9Q(!}-G{!*#=5!~MfchSv{oA3iXA)$q;3cMbnx__5*V zhTj^pGmz|RlddEoa49y{>dfj16}9kdoGJovkV4Rp6`26F`u6A6Ve|6~Uife4w%=3O$>noup{|!OW%GIDV{==p(O+n6X){keVQpsk5JS&$DI-u2wgwpl_<=A$VWnXW zTlr!5q2AWiYu~)r+H4uIG+P>boLk`8I$|r>W8ZzM^0A6j@UP;rO8ETlF<4E-$BvRu z*aE7MqEtbL!x>g814W@wuo#}9v&Eq>utBL*@f{AUg{P3iqk%>9Me(27K7~x*QB_){ zEoyZ;oBe(5wVr0BL=lfvG}tO#Mbjd#_LAk>i8iZNtkHOKoyAs>*jW{5nv%JGEs+S~ zf*gN=$)gv`d<~^}bzVMfX>cjk*yk(_>j@YyF;MKJ0zfkf5N_HqIE%1@On=xvk#8e6 zHYVnfP^79Vg3ucbdY!>=K3Y{3rN8U-dRU~hPF!dfPw(xWjwos>Dr#&jDrzFN=~+#X z-30w{z`4Gd^(5t{f>aFnWpSWngy~Nd5R?$Ufu#zC!YU9PV3ifP$m2nGp%6R(`(!k4 zldOC-*pU}0GuR56z0O)sO_M#}Aj}eRVi{_w#;=@~sTNvixD4@JM~}^7skD_A6zUBT zfhNP>UJzXAcXxQB%{F6sela{3TcpM|pCGotWogY*xO4pO8kajb(`)XvhRexJN4v%A zE%d8gJ{a>)$ZMpEmQG4GA&?1>LzHdunq@|x(c?8vpJBlmypgdw@=Lae62agw1Osuj zAdpR)HgDeOU9{NS-~8af*q)IK{S`fb-`H_zq#oYe3-8^*yr*&|B?_bqcyz)yxZFFd z#kYCW)(0!5yfV~zaHOtc;KIlVY(IcR5)=6dq&-AYGH1Ag1*+(DhC%976Mco;u!=8Y zA-7e05L72TKT@j_n=De5&!=>2-AcDx&duOW;bm}@d3j2Gu2JnT(8%l-k+dO4sFG)8 zDOD0;3g~4PvxLLT;AMA81TtAxZgDVICeJM{&dpZHGO{`ayi7hvYPBjfMY#%(SE0=F zDxjAks)$@*<${J&;UH)vXbEVd;_F8rt$FCUp6y(JT-KIIn z7!%&Z;@m=Krd3y@b2;<-Zmi*njCyvyEYDZ!bj_&2w|_zX2iDtFAbZeCK=F%n|7mQ* z{Aa;dEu@ZP>*;zyG2zu>+_li*oLm()ONuaAp zDkG0CVk^vwE>hg-wv^hNiu0=MJSV@fJEtkuySUAmXsjhWZM+`%nY6;_CoLwZH6KKM!W5vP{M1@soFSIB zL}zzI`-*aMt&N4YQgczI%AlAPZJ6#U&$m@NgDb@%DW{H~+f)^<%B_?Ki#&x2ov2)D z&F=`5w8=9HnuDeNpb{(`B^DoeCW5^Y${aKYE7&y0tO|QTDq@kviN|tcJ#z}%dwLR2 ze(syzcA_|M!}iVoM0NFvugQY^p`FOVUZCe=sKt5_Jz!R0ykQ8;3bTj}77Pja)>0eD z6pLFT<&7p?_3ECC3@(c_B*qAb!)0B+r)Vj3om+ zw@3UDO}xfgWhI(Q@y~bfIXRbALkbp6%a)Z7gzar{cdV$v+}I)(WvR0&7FQsp5c5pN zXns)vU=?so0i^w&S5VvOO595BvvxEU^Se*neJHQ4*0uYNhx$P)%Avnsz-XH(IJd?g z(^Da1>MK}*qU02a{2A|g(!eJzJkCzEl_cugDfv0(VqIaOG1TlWZ7Yx)eClXa7wHTZ zRp@-}!NQQLyfZJV3j6(Gl`$r^mBbA0nxItMTjH!K(%T{xT6e5aqM25e-&mxL*&_P< zN@s?qwL}qdlX`V+MNYBH5mXr~;0&EA9ZNvZpP-a5bArWDhQd6am7b`u-}HFOU^)sF z)1u4taSnU$jY?$k441m7BeG`g)Zckr)fSezva}$oiL+v4`G`r%D5^I#Hp9L#ZIM_!mIEyvcv|pDRzvXr9DJlM=p@x!I*&eCWEo#0z%~JM3g*D zR@BtITw4pZl|Y0FVQLFI?e;KN(V-+O6Ni=LxACg$uc`b4=rD^~30CuQ7*zx10ecz( zyHUYrj0U5%II3VU(jC6P6PBsKZ;D1tReGhVy&zQYmRJG-t2X5T^0Il3(m-Lzl2B%s9t>4`Cmcc+6&zd4%7&JU&~E@~f@bOAP%6OYF~dV^0=i%+a|=cV-3S~l zDeZiLI2l-EFut(Cr*PHJ4#)coEpdHOw?8y9A`tW06&0>nfkqddTAJlC3Ub4eOpP#0 zmT4~3@f7Z>gVR>73$FRa(&DPsO^NNI%>03thM7JV5j1tVdUwyN9bQ_=%c$iG2%pU% zW;WYeU7!pWg2Tz8GU3BPTWK}eTQoNYw#w=>gTI-NGjC>2ex%AaXsrn6=XB>K-u6~~ zC(rXrrCwk4uTDMG+xyUizlxnYb)n&kO`g2X2XNLY8~dJo$$E&=fLwrohn|#EnxB?eai@H>hE;&{~v&_es$*f_93Y zTF{)U82^{n=;XkV?$*m&TDLbVf@Nm4H?E7#EQrpD<~Dd0o?dawP1}ZU?&8bkF_o-! zSNHtw6=GFPEkkZ1?PEZ@h|ut60rAp;f<~pl6X70PPx9d<>?SgP* zu*ss9TDchl*3ShQwb$?1etm6C@UkU?mqpkYo@4sI5}IJcSb=8%YaN*U;6F%LlC6oC zh#>I|sb!sre-KZ6N6%_dntTeSGtx`1B-;~zBf`X&Q1YMg!~+ah$Pd636K%bJk}Fc2 z!byAA*1G1)TZ`f@fiAb!8J-y`m>G9E`EBx9;wiVRS$j)wj*=gjYa4d;b?M5^z&QVU3}@LAMG{B3Ozi*O?G3h`WMhPem(v%= zZfa=h@a_$hrDJTV#H3oi_0stPSqs(4z$RQm{6&VtE^BfTalI-GJq0 zWk6e?e2liSRM;N)y0Ct9!chmb>||#OrCF_VyFDkzYL~Z3a9u@L5yt zz4z{}>h){NO4n?t9(?zcZEa7#GCy&e#OJ;8r#9$8HFN_-KGA# z0t0v^4OQg^)`{lTvuCerW(6-a58k?GsO-WikYO`ui4^2PjD1Yk6Z99x)pmRaY*<-U zxw5fgRdvtrmdz#de1KzD%M=n zooV)}xp!6%&qY$`PiPqhD3P>wDN3M8Y!L?(W&nnyr0VndmCBNG^<`U|BFbo~rdJ&d zsre>uPffVT?`v?I!kILME0$Cb=qz)Vha=0T=Ms|mH<_xatSBp20=2lVNB{0gDxVpj zq`ykH;Kb<%oz!%rrZTXhON8%lj|erg>6HaF4u7-TQl76}IX^O>&?-Cq=2C~P+U=|J zYc`3cDnWIYSzYAxm%1b(YhI~i`h1PF22PF&te#?@knG+TP|H5SOoRAyHrAw$=5w4lOW|ZCV)NfKgbUp|L9Zk~P$-uLf<95Vz*SP` zobu}%_bs)w+M4^61|eLrp0S1n(H9I8;5X3Fd*M;^V>|w6ISo6Y|

;i!{u`ydvc+>wXMt7_p_FTPd+wx`}glo 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 Date: Sun, 22 Mar 2026 01:32:34 -0500 Subject: [PATCH 02/24] Make error implementation much more concise and easier to maintain --- src/error.rs | 415 +++++++-------------------------------------------- 1 file changed, 53 insertions(+), 362 deletions(-) diff --git a/src/error.rs b/src/error.rs index f1f8624..934da4c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,397 +1,88 @@ -use std::{error::Error, fmt::Display}; - -/* -* NOTE: Struct definitions go below. -*/ - -/// This is the type that we want to use for boxed errors to make sure they're thread-safe. -type DynError = dyn Error + Send + Sync; - -/// Boxed error type for converting to [`ImgiiError`]. -/// -/// # Example -/// -/// This is useful for errors from other crates, which we don't know or care about their errors. -/// -/// ``` -/// use image::open; -/// use imgii::error::BoxedDynErr; -/// // open an image, convert it into an ImgiiError -/// let input_file_name = "test.png"; -/// let loaded_img = open(input_file_name).map_err(|err| -> BoxedDynErr { Box::new(err) }); -/// ``` -pub type BoxedDynErr = Box; +use thiserror::Error; /// An error that can be returned by Imgii. Represents errors when converting images. -#[derive(Debug)] +#[derive(Error, Debug)] pub enum ImgiiError { /// Errors related to fonts. - Font(FontError), + #[error("{0}")] + Font(#[from] FontError), /// Error related to parsing ASCII. - Parse(ParseError), - /// Unknown, unspecified internal error. - Internal(InternalError), + #[error("{0}")] + Parse(#[from] ParseError), /// Error related to image. - Image(ImageError), + #[error("{0}")] + Image(#[from] ImageError), /// I/O operation error. - Io(std::io::Error), - Other(OtherError), + #[error("{0}")] + Io(#[from] std::io::Error), + #[error("{0}")] + Other(#[from] anyhow::Error), + /// Invalid argument error. + #[error("invalid argument(s) provided")] + InvalidArgument, + /// Unknown, unspecified internal error. + #[error("an internal error has occurred")] + Internal, } -/// Font error. Use this when something related to the font has gone wrong. -/// -/// Suberror of [`ImgiiError`]. -#[derive(Debug, Clone)] -pub struct FontError { - font_name: String, +#[derive(Debug, Error, Clone)] +pub enum FontError { + #[error("could not load font {font_name}")] + FontLoad { font_name: String }, } /// ASCII text parsing error. Use this when parsing ASCII text and something goes wrong. /// /// Suberror of [`ImgiiError`]. -#[derive(Debug, Clone)] +#[derive(Error, Debug, Clone)] pub enum ParseError { /// Handles regex errors. - Regex(regex::Error), + #[error("imgii regular expression failed ({0})")] + Regex(#[from] regex::Error), /// Handles errors related to parsing values. - ParseValue(ParseIntError), -} - -/// Regular expression compiler error. -/// Doesn't actually implement Error, as it is easier to implement functionality in the super -/// error, [`ParseError`]. -/// -/// Suberror of [`ParseError`]. -#[derive(Debug, Clone)] -pub struct ParseIntError { - /// The name of the value to parse. - value_name: String, - /// The string that parsing was attempted on but failed. - the_str: String, - /// The `std::num::ParseIntError` that was emitted upon failure to parse. - err: std::num::ParseIntError, + #[error("could not parse int value from string due to {0}")] + ParseInt(#[from] std::num::ParseIntError), + /// Handles errors related to parsing colors from colored output + #[error("could not parse value {value_name} from string ({the_str}), parse error ({err})")] + ParseColor { + /// The name of the value to parse. + value_name: String, + /// The string that parsing was attempted on but failed. + the_str: String, + /// The `std::num::ParseIntError` that was emitted upon failure to parse. + err: std::num::ParseIntError, + }, } -/// Some other, unknown internal error. -/// -/// Suberror of [`ImgiiError`]. -#[derive(Debug, Clone)] -pub struct InternalError; - /// Represents an error while creating an image. /// /// Suberror of [`ImgiiError`]. -#[derive(Debug, Clone)] +#[derive(Error, Debug, Clone)] pub enum ImageError { - InvalidParameter(InvalidParameterError), - ParseImage(ParseImageError), - Render(RenderError), -} - -/// Contains other errors. These are errors that can be emitted from other crates for various -/// reasons. -/// -/// Suberror of [`ImgiiError`]. -#[derive(Debug)] -pub struct OtherError { - // we can hold any other Error in here - other_err: BoxedDynErr, -} - -/// Represents an invalid parameter error when creating image. -/// -/// Suberror of [`ImageError`]. -#[derive(Debug, Clone)] -pub struct InvalidParameterError { - parameter_name: String, -} - -/// Represents an error that occurred while parsing an image. -/// -/// Suberror of [`ImageError`]. -#[derive(Debug, Clone)] -pub struct ParseImageError { - /// The row number of the image where this occurred. - image_row_number: usize, -} - -/// Represents an error that occurred while rendering an image. -/// -/// Suberror of [`ImageError`]. -#[derive(Debug, Clone)] -pub struct RenderError { - /// The reason for the render error. Since this error is intended to handle various internals - /// that aren't well represented by errors, this will explain why the error ocurred. - reason: String, -} - -/* - * NOTE: Implement `Display` below for errors that are intended to also implement Error. - */ - -impl Display for ImgiiError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ImgiiError::Font(font_error) => { - write!(f, "{font_error}") - } - ImgiiError::Parse(parse_error) => { - write!(f, "{parse_error}") - } - ImgiiError::Internal(internal_error) => { - write!(f, "{internal_error}") - } - ImgiiError::Io(io_error) => { - write!(f, "{io_error}") - } - ImgiiError::Other(other_error) => { - write!(f, "{other_error}") - } - ImgiiError::Image(image_error) => { - write!(f, "{image_error}") - } - } - } -} - -impl Display for FontError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "could not read font {}", self.font_name) - } -} - -impl Display for ParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Regex(err) => { - // let's just print their error out with ours - write!(f, "imgii regular expression failed ({})", err) - } - Self::ParseValue(err) => { - write!( - f, - "could not parse value {} from string ({}), parse error ({})", - err.value_name, err.the_str, err.err - ) - } - } - } -} - -impl Display for InternalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "an internal error has occurred") - } -} - -impl Display for ImageError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ImageError::InvalidParameter(invalid_parameter_error) => { - write!( - f, - "invalid parameter {} found", - invalid_parameter_error.parameter_name - ) - } - ImageError::ParseImage(parse_image_error) => { - write!( - f, - "parsing error found at row {} of the image", - parse_image_error.image_row_number - ) - } - ImageError::Render(render_error) => { - write!( - f, - "error occurred while rendering image ({})", - render_error.reason - ) - } - } - } -} - -impl Display for OtherError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "an error from another crate/boxed error occurred ({})", - self.other_err - ) - } + #[error("invalid parameter {parameter_name} found")] + InvalidParameter { parameter_name: String }, + #[error("parsing error found at row {image_row_number} of the image")] + ParseImage { + /// The row number of the image where this occurred. + image_row_number: usize, + }, + #[error("rendering failed because {reason}")] + Render { + /// The reason for the render error. Since this error is intended to handle various internals + /// that aren't well represented by errors, this will explain why the error ocurred. + reason: String, + }, } -/* - * NOTE: Implement Error for error types below. - */ - -// we don't need to implement anything since there are default implementations for this trait -impl Error for FontError {} -impl Error for ParseError {} -impl Error for ImgiiError {} -impl Error for OtherError {} - /* * NOTE: Implement any `From` traits here. */ // NOTE: -// ImgiiError should only have to implement From for all of its direct suberrors, but Rust makes me -// do another From impl for the errors that can be converted into a suberror type too. -// -// The suberrors can implement From for anything that can be converted into them specifically, then -// for each of those, a simple From can be implemented for ImgiiError so we can call `.into()` -// to convert a suberror type into the main ImgiiError type. -// -// This makes it easier to maintain, as more errors are added. This pattern should be replicated for -// suberrors which have their own suberrors. -impl From for ImgiiError { - fn from(err: FontError) -> Self { - Self::Font(err) - } -} - -impl From for ImgiiError { - fn from(value: ParseError) -> Self { - Self::Parse(value) - } -} - -impl From for ImgiiError { - fn from(err: InternalError) -> Self { - Self::Internal(err) - } -} - -impl From for ImgiiError { - fn from(value: std::io::Error) -> Self { - Self::Io(value) - } -} - -impl From for ImgiiError { - fn from(value: ImageError) -> Self { - Self::Image(value) - } -} - -impl From for ImgiiError { - fn from(value: InvalidParameterError) -> Self { - Self::Image(ImageError::InvalidParameter(value)) - } -} - -impl From for ImgiiError { - fn from(value: ParseImageError) -> Self { - Self::Image(ImageError::ParseImage(value)) - } -} - -impl From for ImgiiError { - fn from(value: RenderError) -> Self { - Self::Image(ImageError::Render(value)) - } -} - -// for converting from errors boxed at runtime -impl From for ImgiiError { - fn from(value: BoxedDynErr) -> Self { - Self::Other(OtherError::new(value)) - } -} - -// for converting from a regular expression error (not ours) -impl From for ParseError { - fn from(err: regex::Error) -> Self { - Self::Regex(err) - } -} +// ImgiiError should only have to implement From for errors for convenience. This avoids having to +// map_err from one error to another. impl From for ImgiiError { fn from(err: regex::Error) -> Self { Self::Parse(ParseError::Regex(err)) } } - -impl From for ParseError { - fn from(err: ParseIntError) -> Self { - Self::ParseValue(err) - } -} -impl From for ImgiiError { - fn from(err: ParseIntError) -> Self { - Self::Parse(ParseError::ParseValue(err)) - } -} - -/* - * NOTE: Add any custom implementation blocks for errors below. - */ - -impl FontError { - /// Creates a new [`FontError`]. - /// - /// * `font_name`: The font file name which failed to be created. - #[must_use] - pub fn new(font_name: String) -> Self { - Self { font_name } - } -} - -impl ParseIntError { - /// Creates a new [`ParseIntError`]. - /// - /// * `value_name`: The value name to parse. - /// * `the_str`: The string that parsing was attempted on. - /// * `err`: The `std::num::ParseIntError` that was emitted. - #[must_use] - pub fn new(value_name: String, the_str: String, err: std::num::ParseIntError) -> Self { - Self { - value_name, - the_str, - err, - } - } -} - -impl OtherError { - /// Creates a new [`OtherError`] from a boxed error (created at runtime). - /// - /// For use with other kinds of errors that the program can handle. - /// - /// * `other_err`: The other error, boxed. - #[must_use] - pub fn new(other_err: BoxedDynErr) -> Self { - Self { other_err } - } -} - -impl InvalidParameterError { - /// Creates a new [`InvalidParameterError`]. - /// - /// * `parameter_name`: The parameter name(s) that was invalid. - #[must_use] - pub fn new(parameter_name: String) -> Self { - Self { parameter_name } - } -} - -impl ParseImageError { - /// Creates a new [`ParseImageError`]. - /// - /// * `image_row_number`: The image row number. - #[must_use] - pub fn new(image_row_number: usize) -> Self { - Self { image_row_number } - } -} - -impl RenderError { - /// Creates a new [`RenderError`]. - /// - /// * `reason`: The reason for the render error. - #[must_use] - pub fn new(reason: String) -> Self { - Self { reason } - } -} From 7f772111a9bb3dbb002bf78bf0c8b8a1fecdc2e0 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 01:35:38 -0500 Subject: [PATCH 03/24] Remove reading ubuntu font at compile time. - Read font at runtime. - Update code for changes to ImgiiError and its sub-errors. . --- .../converters/generic_converter.rs | 71 +++++++++++-------- src/conversion/converters/gif_converter.rs | 12 ++-- src/conversion/converters/png_converter.rs | 7 +- src/conversion/image_writer.rs | 4 +- src/lib.rs | 13 ++-- 5 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/conversion/converters/generic_converter.rs b/src/conversion/converters/generic_converter.rs index 68d20b3..2211fc2 100644 --- a/src/conversion/converters/generic_converter.rs +++ b/src/conversion/converters/generic_converter.rs @@ -3,19 +3,13 @@ use std::{collections::HashMap, sync::Arc}; use crate::{ ImgiiOptions, conversion::{image_data::ImageData, render_char_to_png::str_to_png}, - error::{FontError, ImgiiError, ParseIntError, RenderError}, + error::{FontError, ImageError, ImgiiError, ParseError}, }; use super::super::render_char_to_png::{ColoredStr, str_to_transparent_png}; use ab_glyph::FontRef; use regex::Regex; -// TODO: Read this font at runtime instead and allow the user to choose - -// read bytes for the font -const FONT_FILE: &str = "../../../fonts/UbuntuMono.ttf"; -const FONT_BYTES: &[u8] = include_bytes!("../../../fonts/UbuntuMono.ttf"); - /// Simple struct for holding a 2d image with its width and height. #[derive(Clone, Debug)] pub(crate) struct Imgii2dImage { @@ -36,9 +30,11 @@ pub(crate) fn render_ascii_generic( ascii_text: String, ) -> Result { // set up font for rendering - let font = FontRef::try_from_slice(FONT_BYTES) + let font = FontRef::try_from_slice(imgii_options.font().as_slice()) // there's nothing useful in this error, convert it! - .map_err(|_| FontError::new(String::from(FONT_FILE)))?; + .map_err(|_| FontError::FontLoad { + font_name: String::from(imgii_options.font_name()), + })?; // 2d Vec of images for each character let mut image_2d_vec = Vec::new(); @@ -70,14 +66,20 @@ pub(crate) fn render_ascii_generic( // create the image for this character for (_full_str, [r, g, b, the_str]) in re.captures_iter(line).map(|c| c.extract()) { - let red = r.parse::().map_err(|err| { - ParseIntError::new(String::from("red"), String::from(the_str), err) + let red = r.parse::().map_err(|err| ParseError::ParseColor { + value_name: String::from("red"), + the_str: String::from(the_str), + err: err, })?; - let green = g.parse::().map_err(|err| { - ParseIntError::new(String::from("green"), String::from(the_str), err) + let green = g.parse::().map_err(|err| ParseError::ParseColor { + value_name: String::from("green"), + the_str: String::from(the_str), + err: err, })?; - let blue = b.parse::().map_err(|err| { - ParseIntError::new(String::from("blue"), String::from(the_str), err) + let blue = b.parse::().map_err(|err| ParseError::ParseColor { + value_name: String::from("blue"), + the_str: String::from(the_str), + err: err, })?; let generated_png = { @@ -102,13 +104,18 @@ pub(crate) fn render_ascii_generic( // we haven't rendered this image before, so render it let image_data = Arc::from(str_to_png(&colored, &font, imgii_options)); let result = rendered_images.insert(colored, image_data.clone()); - if result.is_some() { - return Err(RenderError::new(String::from( - "this image should not exist already in the hash map", - )) - .into()); + match result { + None => image_data, + Some(colored) => { + // the returned image from insert should be the same as the one + // we put in + return Err(ImageError::Render { + reason: format!( + "the image ({colored:?}) should not exist already in the hash map", + ), + }.into()); + } } - image_data } } } @@ -127,10 +134,12 @@ pub(crate) fn render_ascii_generic( } else { // check that this width is always the same now that we have the width if width != line_width { - return Err(RenderError::new(format!( - "width {} is not equal to the current line width {}", - width, line_width - )) + return Err(ImageError::Render { + reason: format!( + "width {} is not equal to the current line width {}", + width, line_width + ), + } .into()); } } @@ -139,11 +148,13 @@ pub(crate) fn render_ascii_generic( // Check that the length of the final vector is what we expect. If not, something has gone // terribly wrong, and we should not continue. if width * height != image_2d_vec.len() { - return Err(RenderError::new(format!( - "expected length of the 2d vector was {} but got {}", - width * height, - image_2d_vec.len() - )) + return Err(ImageError::Render { + reason: format!( + "expected length of the 2d vector was {} but got {}", + width * height, + image_2d_vec.len() + ), + } .into()); } diff --git a/src/conversion/converters/gif_converter.rs b/src/conversion/converters/gif_converter.rs index ff0356a..85d66e8 100644 --- a/src/conversion/converters/gif_converter.rs +++ b/src/conversion/converters/gif_converter.rs @@ -2,7 +2,7 @@ use std::{fs::File, io::BufReader}; use crate::{ conversion::converters::generic_converter::{Imgii2dImage, render_ascii_generic}, - error::{BoxedDynErr, ImgiiError}, + error::ImgiiError, options::{ImgiiOptions, RasciiOptions}, }; @@ -211,9 +211,9 @@ pub(crate) fn read_deconstructed_gif( Err(err) => { // the input data in the gif was wrong - // convert to boxed err then convert to ImgiiError - let err_box: BoxedDynErr = Box::new(err); // have to specify `dyn Error`. ugh. - return Err(err_box.into()); + // convert to anyhow err then convert to ImgiiError + let err = anyhow::Error::new(err); + return Err(err.into()); } }; @@ -222,8 +222,8 @@ pub(crate) fn read_deconstructed_gif( Ok(frames) => frames, Err(err) => { // the data is malformed in this GIF - let err_box: BoxedDynErr = Box::new(err); - return Err(err_box.into()); + let err = anyhow::Error::new(err); + return Err(err.into()); } }; let ret = frames diff --git a/src/conversion/converters/png_converter.rs b/src/conversion/converters/png_converter.rs index 7144589..2ca415f 100644 --- a/src/conversion/converters/png_converter.rs +++ b/src/conversion/converters/png_converter.rs @@ -1,7 +1,7 @@ use super::generic_converter::render_ascii_generic; use crate::{ conversion::converters::generic_converter::Imgii2dImage, - error::{BoxedDynErr, ImgiiError}, + error::ImgiiError, options::{ImgiiOptions, RasciiOptions}, }; @@ -39,9 +39,10 @@ pub(crate) fn read_png_as_ascii( ) -> Result { // render the ascii text with RASCII let mut ascii_text = String::new(); - let loaded_img = open(input_file_name).map_err(|err| -> BoxedDynErr { Box::new(err) })?; + let loaded_img = + open(input_file_name).map_err(|err| -> ImgiiError { anyhow::Error::new(err).into() })?; render_image_to(&loaded_img, &mut ascii_text, rascii_options) - .map_err(|err| -> BoxedDynErr { Box::new(err) })?; + .map_err(|err| -> ImgiiError { anyhow::Error::new(err).into() })?; Ok(ascii_text) } diff --git a/src/conversion/image_writer.rs b/src/conversion/image_writer.rs index 13c5ef5..2fb1859 100644 --- a/src/conversion/image_writer.rs +++ b/src/conversion/image_writer.rs @@ -3,7 +3,7 @@ use crate::{ converters::generic_converter::Imgii2dImage, image_data::{ImageData, InternalImage}, }, - error::{ImgiiError, InvalidParameterError}, + error::ImgiiError, }; use rayon::prelude::*; @@ -37,7 +37,7 @@ impl AsciiImageWriter { pub(crate) fn from_2d_vec(the_image: Imgii2dImage) -> Result { if the_image.image_2d.is_empty() { // no image to build - return Err(InvalidParameterError::new(String::from("parts")).into()); + return Err(ImgiiError::InvalidArgument); } // find out the new canvas size diff --git a/src/lib.rs b/src/lib.rs index c577759..cc21fc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub(crate) mod conversion; pub mod error; +pub mod fonts; pub mod image_types; pub mod options; @@ -19,7 +20,7 @@ use crate::{ }, image_writer::AsciiImageWriter, }, - error::{BoxedDynErr, ImgiiError}, + error::ImgiiError, options::ImgiiOptions, }; @@ -85,7 +86,7 @@ pub fn convert_to_ascii_png( .imagebuf .as_buffer() .save(&output_file_name) - .map_err(|err| -> BoxedDynErr { Box::new(err) })?; + .map_err(|err| -> ImgiiError { anyhow::Error::new(err).into() })?; Ok(()) } @@ -184,8 +185,8 @@ pub fn convert_to_ascii_gif( let err = gif_encoder.set_repeat(image::codecs::gif::Repeat::Infinite); if let Err(err) = err { // repeat couldn't be set properly - let err_box: BoxedDynErr = Box::new(err); - return Err(err_box.into()); + let err = anyhow::Error::new(err); + return Err(err.into()); } // FUTURE: the longest part of the GIF creation process is encoding...is there any way to speed @@ -194,8 +195,8 @@ pub fn convert_to_ascii_gif( // encode the frames match gif_encoder.encode_frames(frames) { Err(err) => { - let err_box: BoxedDynErr = Box::new(err); - Err(err_box.into()) + let err = anyhow::Error::new(err); + Err(err.into()) } _ => Ok(()), } From 7b1feca7c9fbea2b8de69302568ab21c974a254b Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 01:36:51 -0500 Subject: [PATCH 04/24] Read fonts before converting to png/gif. - Update imgii options --- src/fonts.rs | 14 ++++++++++ src/main.rs | 66 +++++++++++++++++++++++++++++++++++++++++++---- src/options.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 src/fonts.rs diff --git a/src/fonts.rs b/src/fonts.rs new file mode 100644 index 0000000..5fc2b4b --- /dev/null +++ b/src/fonts.rs @@ -0,0 +1,14 @@ +use font_loader::system_fonts; + +pub fn list_fonts() -> Vec { + let mut property = system_fonts::FontPropertyBuilder::new().monospace().build(); + system_fonts::query_specific(&mut property) +} + +pub fn load_monospace_font(font_name: &str) -> Option<(Vec, i32)> { + let property = system_fonts::FontPropertyBuilder::new() + .monospace() + .family(font_name) + .build(); + system_fonts::get(&property) +} diff --git a/src/main.rs b/src/main.rs index 0f68a15..9aee644 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,11 @@ use clap::Parser; use clap::builder as clap_builder; use clap::builder::styling as clap_styling; +use imgii::error::FontError; +use imgii::error::ImgiiError; +use imgii::fonts; +use imgii::fonts::list_fonts; +use imgii::fonts::load_monospace_font; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::{sync::Arc, time::Instant}; @@ -44,6 +49,10 @@ struct Args { #[arg(short = 'H', long)] height: Option, + /// The name of the installed monospace font to use. + #[arg(short = 'n', long)] + font_name: Option, + /// The font size of the output image. /// Larger font sizes incur harsher performance penalties. /// @@ -149,6 +158,7 @@ fn setup_threads() { /// * `font_size`: The font size argument. /// * `background`: The background flag. fn create_imgii_options<'a>( + font_name: Option, font_size: Option, background: bool, width: Option, @@ -156,10 +166,53 @@ fn create_imgii_options<'a>( invert: bool, rascii_charset: Charset, char_override: Option, -) -> ImgiiOptions<'a> { - let mut builder = ImgiiOptionsBuilder::new().background(background); +) -> Result, ImgiiError> { + let mut builder: ImgiiOptionsBuilder<'a> = ImgiiOptionsBuilder::new().background(background); // build the complex values first + if let Some(font_name) = font_name { + // read the font that the user wants to use + + // NOTE: if the user inputs an invalid font, it seems to fall back to the first monospace + // font it can find + match load_monospace_font(&font_name) { + Some((font, _)) => { + // successfully loaded font + dbg!("loaded font successfully"); + dbg!(&font_name); + builder = builder.font(font).font_name(font_name); + } + None => { + // could not load the font + return Err(ImgiiError::Font(FontError::FontLoad { + font_name: font_name, + }) + .into()); + } + }; + } else { + // we need to load the first font that we can find :) + let mut fonts = list_fonts(); + assert!( + !fonts.is_empty(), + "list of installed monospace fonts is empty" + ); + match load_monospace_font(&fonts[0]) { + Some((font, _)) => { + // loaded first font successfully + dbg!("loaded font successfully"); + dbg!(&fonts[0]); + builder = builder.font(font).font_name(fonts.remove(0)); + } + None => { + // could not load the font + return Err(ImgiiError::Font(FontError::FontLoad { + font_name: fonts.remove(0), + }) + .into()); + } + } + } if let Some(font_size) = font_size { builder = builder.font_size(font_size); } @@ -171,7 +224,7 @@ fn create_imgii_options<'a>( } if let Some(char_override) = char_override { // converts the string to a string vec if it is Some, otherwise stores as None - builder = builder.char_override(convert_string_to_str_vec(char_override)); + builder = builder.char_override(convert_string_to_str_vec(&char_override)); } builder @@ -216,7 +269,8 @@ fn main() { }; // our options for rendering ASCII in imgii - let imgii_options = create_imgii_options( + let Ok(imgii_options) = create_imgii_options( + args.font_name, args.font_size, args.background, args.width, @@ -224,7 +278,9 @@ fn main() { args.invert, rascii_charset, args.char_override, - ); + ) else { + panic!("could not create imgii options"); + }; log::debug!("imgii options = {:?}", imgii_options); // Now, handle the conversion diff --git a/src/options.rs b/src/options.rs index 427a270..a38a545 100644 --- a/src/options.rs +++ b/src/options.rs @@ -8,6 +8,8 @@ pub use rascii_art_img::{ convert_string_to_str_vec, }; +use crate::error::ImgiiError; + const DEFAULT_CHAR_FONT_SIZE: u32 = 16; // NOTE: we don't want to ever make members of ImgiiOptions public so users can't cause imgii to @@ -16,6 +18,12 @@ const DEFAULT_CHAR_FONT_SIZE: u32 = 16; /// Options for creating the output ASCII PNG. #[derive(Debug, Clone)] pub struct ImgiiOptions<'a> { + /// The loaded bytes containing the font. + font: Vec, + + /// The font name. + font_name: String, + /// The font size of the output image. font_size: u32, @@ -31,14 +39,34 @@ pub struct ImgiiOptions<'a> { impl<'a> ImgiiOptions<'a> { /// Creates a new image options object. #[must_use] - fn new(font_size: u32, background: bool, rascii_options: RasciiOptions<'a>) -> Self { + fn new( + font: Vec, + font_name: String, + font_size: u32, + background: bool, + rascii_options: RasciiOptions<'a>, + ) -> Self { Self { + font, + font_name, font_size, background, rascii_options, } } + /// Gets the font data. + #[must_use] + pub fn font(&self) -> &Vec { + &self.font + } + + /// Gets the font name. + #[must_use] + pub fn font_name(&self) -> &str { + &self.font_name + } + /// Gets the font size to use to generate the image. #[must_use] pub fn font_size(&self) -> u32 { @@ -46,11 +74,13 @@ impl<'a> ImgiiOptions<'a> { } /// Gets the background flag. If true, sets a background behind the output image. + #[must_use] pub fn background(&self) -> bool { self.background } /// Gets the RASCII options. + #[must_use] pub fn rascii_options(&self) -> &RasciiOptions<'a> { &self.rascii_options } @@ -59,6 +89,12 @@ impl<'a> ImgiiOptions<'a> { /// Builder for [`ImgiiOptions`]. Intended way to create options for Imgii. #[derive(Debug, Clone)] pub struct ImgiiOptionsBuilder<'a> { + /// The name of the installed system font to render + font: Option>, + + /// The font name. + font_name: Option, + /// The font size of the output image. font_size: u32, @@ -72,6 +108,8 @@ pub struct ImgiiOptionsBuilder<'a> { impl<'a> Default for ImgiiOptionsBuilder<'a> { fn default() -> Self { Self { + font: None, + font_name: None, font_size: DEFAULT_CHAR_FONT_SIZE, background: false, rascii_options: RasciiOptions::default() @@ -88,6 +126,22 @@ impl<'a> ImgiiOptionsBuilder<'a> { Self::default() } + /// Sets the font of the output [`ImgiiOptions`]. + /// + /// * `font`: The loaded font bytes. + pub fn font(mut self, font: Vec) -> Self { + self.font = Some(font); + self + } + + /// Sets the font name of the output [`ImgiiOptions`]. + /// + /// * `font_name`: The name of the font. + pub fn font_name(mut self, font_name: String) -> Self { + self.font_name = Some(font_name); + self + } + /// Sets the font size of the output [`ImgiiOptions`]. /// /// * `font_size`: The font size. @@ -105,8 +159,18 @@ impl<'a> ImgiiOptionsBuilder<'a> { } /// Builds a new [`ImgiiOptions`] instance from chosen values in this builder. - pub fn build(&self) -> ImgiiOptions<'a> { - ImgiiOptions::new(self.font_size, self.background, self.rascii_options.clone()) + pub fn build(&self) -> Result, ImgiiError> { + let (Some(font), Some(font_name)) = (self.font.clone(), self.font_name.clone()) else { + return Err(ImgiiError::InvalidArgument); + }; + + Ok(ImgiiOptions::new( + font, + font_name, + self.font_size, + self.background, + self.rascii_options.clone(), + )) } /* From 55f08e35633b628577f0b9f8e36c0ce15c64e9f5 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 01:38:08 -0500 Subject: [PATCH 05/24] Update cargo lock & toml --- Cargo.lock | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 3 + 2 files changed, 177 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db3faeb..b00dc98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "approx" @@ -171,6 +171,12 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.9.4" @@ -215,10 +221,11 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "cc" -version = "1.2.7" +version = "1.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -280,6 +287,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -292,6 +308,58 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -367,6 +435,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "expat-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +dependencies = [ + "cmake", + "pkg-config", +] + [[package]] name = "exr" version = "1.73.0" @@ -411,6 +489,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "flate2" version = "1.0.35" @@ -421,6 +505,45 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "font-loader" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c49d6b4c11dca1a1dd931a34a9f397e2da91abe3de4110505f3530a80e560b52" +dependencies = [ + "core-foundation", + "core-text", + "libc", + "servo-fontconfig", + "winapi", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -529,8 +652,10 @@ name = "imgii" version = "0.7.6" dependencies = [ "ab_glyph", + "anyhow", "clap", "env_logger", + "font-loader", "image", "imageproc", "log", @@ -538,6 +663,7 @@ dependencies = [ "rascii_art_img", "rayon", "regex", + "thiserror 2.0.18", ] [[package]] @@ -885,7 +1011,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags", + "bitflags 2.9.4", "crc32fast", "fdeflate", "flate2", @@ -1059,7 +1185,7 @@ dependencies = [ "rand_chacha", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.69", "v_frame", "wasm-bindgen", ] @@ -1178,6 +1304,27 @@ dependencies = [ "serde", ] +[[package]] +name = "servo-fontconfig" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +dependencies = [ + "libc", + "servo-fontconfig-sys", +] + +[[package]] +name = "servo-fontconfig-sys" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +dependencies = [ + "expat-sys", + "freetype-sys", + "pkg-config", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1260,7 +1407,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -1274,6 +1430,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiff" version = "0.10.3" diff --git a/Cargo.toml b/Cargo.toml index 7d3abdc..c1f97af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,10 @@ readme = "README.md" [dependencies] ab_glyph = "0.2.32" +anyhow = "1.0.102" clap = "4.5.48" env_logger = "0.11.8" +font-loader = "0.11.0" image = { version = "0.25.8", features = ["gif"]} imageproc = "0.25.0" log = "0.4.28" @@ -21,6 +23,7 @@ num_cpus = "1.17.0" rascii_art_img = "0.4.7" rayon = "1.11.0" regex = "1.12.1" +thiserror = "2.0.18" [profile.release] opt-level=3 From a88c42017d679941a7a8309979adbf46fcb9178f Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 01:40:32 -0500 Subject: [PATCH 06/24] . --- src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 9aee644..cc17637 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ use clap::builder as clap_builder; use clap::builder::styling as clap_styling; use imgii::error::FontError; use imgii::error::ImgiiError; -use imgii::fonts; use imgii::fonts::list_fonts; use imgii::fonts::load_monospace_font; use rayon::iter::{IntoParallelIterator, ParallelIterator}; From f1189d4fa4ffa2d164ec6c7e09553bf02b5ac664 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 17:35:03 -0500 Subject: [PATCH 07/24] documentation --- src/fonts.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fonts.rs b/src/fonts.rs index 5fc2b4b..ee1b07a 100644 --- a/src/fonts.rs +++ b/src/fonts.rs @@ -1,10 +1,17 @@ use font_loader::system_fonts; +/// Lists all of the fonts that are installed on the system. pub fn list_fonts() -> Vec { let mut property = system_fonts::FontPropertyBuilder::new().monospace().build(); system_fonts::query_specific(&mut property) } +/// Loads the specified monospace font from the system. +/// +/// * `font_name`: The name of the font. +/// +/// # Returns +/// `Option<(Vec, i32)>` containing the bytes of the font, followed by the index of the font. pub fn load_monospace_font(font_name: &str) -> Option<(Vec, i32)> { let property = system_fonts::FontPropertyBuilder::new() .monospace() From 036de55394acc8edbe0ab3db3d52fa50c4834612 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 17:43:40 -0500 Subject: [PATCH 08/24] Add file docs --- src/conversion/converters/generic_converter.rs | 2 ++ src/conversion/converters/gif_converter.rs | 2 ++ src/conversion/converters/png_converter.rs | 2 ++ src/conversion/image_data.rs | 2 ++ src/conversion/image_writer.rs | 2 ++ src/conversion/render_char_to_png.rs | 2 ++ src/error.rs | 2 ++ src/fonts.rs | 2 ++ src/image_types.rs | 2 ++ src/lib.rs | 1 + src/main.rs | 2 ++ 11 files changed, 21 insertions(+) diff --git a/src/conversion/converters/generic_converter.rs b/src/conversion/converters/generic_converter.rs index 2211fc2..2aa8798 100644 --- a/src/conversion/converters/generic_converter.rs +++ b/src/conversion/converters/generic_converter.rs @@ -1,3 +1,5 @@ +//! Generic converter implementation for rendering ASCII as individual images. + use std::{collections::HashMap, sync::Arc}; use crate::{ diff --git a/src/conversion/converters/gif_converter.rs b/src/conversion/converters/gif_converter.rs index 85d66e8..764fbb9 100644 --- a/src/conversion/converters/gif_converter.rs +++ b/src/conversion/converters/gif_converter.rs @@ -1,3 +1,5 @@ +//! Handles rendering for GIF. + use std::{fs::File, io::BufReader}; use crate::{ diff --git a/src/conversion/converters/png_converter.rs b/src/conversion/converters/png_converter.rs index 2ca415f..e9e788f 100644 --- a/src/conversion/converters/png_converter.rs +++ b/src/conversion/converters/png_converter.rs @@ -1,3 +1,5 @@ +//! Handles implementation for rendering PNG. + use super::generic_converter::render_ascii_generic; use crate::{ conversion::converters::generic_converter::Imgii2dImage, diff --git a/src/conversion/image_data.rs b/src/conversion/image_data.rs index 50ef605..548e814 100644 --- a/src/conversion/image_data.rs +++ b/src/conversion/image_data.rs @@ -1,3 +1,5 @@ +//! Implementation for generic image data. + use image::ImageBuffer; // easier to read diff --git a/src/conversion/image_writer.rs b/src/conversion/image_writer.rs index 2fb1859..02bfeea 100644 --- a/src/conversion/image_writer.rs +++ b/src/conversion/image_writer.rs @@ -1,3 +1,5 @@ +//! Implementation for writing ascii as an image. + use crate::{ conversion::{ converters::generic_converter::Imgii2dImage, diff --git a/src/conversion/render_char_to_png.rs b/src/conversion/render_char_to_png.rs index 5b13dbf..c570a2d 100644 --- a/src/conversion/render_char_to_png.rs +++ b/src/conversion/render_char_to_png.rs @@ -1,3 +1,5 @@ +//! Implementation for rendering a character as an image. + use crate::{conversion::image_data::ImageData, options::ImgiiOptions}; use ab_glyph::{FontRef, PxScale}; use image::{ImageBuffer, Rgba}; diff --git a/src/error.rs b/src/error.rs index 934da4c..931190d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +//! Error implementation for `imgii` errors. + use thiserror::Error; /// An error that can be returned by Imgii. Represents errors when converting images. diff --git a/src/fonts.rs b/src/fonts.rs index ee1b07a..c59672c 100644 --- a/src/fonts.rs +++ b/src/fonts.rs @@ -1,3 +1,5 @@ +//! Contains helper functionality for handling fonts. + use font_loader::system_fonts; /// Lists all of the fonts that are installed on the system. diff --git a/src/image_types.rs b/src/image_types.rs index 0af3894..61a2887 100644 --- a/src/image_types.rs +++ b/src/image_types.rs @@ -1,3 +1,5 @@ +//! Contains helpers for handling supported output image types. + /// Holds the image types that imgii can output. /// Each value holds an index into the `IMAGE_STR_TYPES` array. #[derive(Debug, Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index cc21fc3..b0e3860 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,6 +161,7 @@ pub fn convert_to_ascii_gif( .into_par_iter() .filter_map(|(writer, frame_metadata)| match writer { // let's just get rid of errors and try our best with what we've got + // NOTE: this will discard frames with errors. Ok(writer) => Some((writer, frame_metadata)), Err(_) => None, }) diff --git a/src/main.rs b/src/main.rs index cc17637..f286610 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +//! The main file for the `imgii` CLI tool. + use clap::Parser; use clap::builder as clap_builder; use clap::builder::styling as clap_styling; From 9a49f7f616353d43f67e7822cdca0f82a07addc5 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 17:57:29 -0500 Subject: [PATCH 09/24] Install requirements in github workflow --- .github/workflows/rust.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index df30416..f53ca74 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,10 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build - run: cargo build --verbose + run: | + sudo apt-get update + sudo apt-get install libfontconfig libfontconfig1-dev + cargo build --verbose - name: Run tests run: cargo test --verbose From 4a5bc136f678c95a51aba5055233b897673a9a5b Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 18:07:18 -0500 Subject: [PATCH 10/24] Update rascii version --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b00dc98..e088d66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1145,9 +1145,9 @@ dependencies = [ [[package]] name = "rascii_art_img" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12329487092c3301421bf8625241af47e375ebd67fc01b98d0b7527e3a398145" +checksum = "606a1fb24a05ce8558ad454e7df938e2f29da101b7f3daaa70ef78d78c533e91" dependencies = [ "ansi_term", "clap", diff --git a/Cargo.toml b/Cargo.toml index c1f97af..3127032 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ image = { version = "0.25.8", features = ["gif"]} imageproc = "0.25.0" log = "0.4.28" num_cpus = "1.17.0" -rascii_art_img = "0.4.7" +rascii_art_img = "0.4.8" rayon = "1.11.0" regex = "1.12.1" thiserror = "2.0.18" From 06b3924457c27b0b82578b505e6882875d08a114 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 18:49:50 -0500 Subject: [PATCH 11/24] Add better debug logs. --- src/main.rs | 96 +++++++++++++++++++++++++++----------------------- src/options.rs | 21 +++++++++++ 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/src/main.rs b/src/main.rs index f286610..27e9b4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -154,6 +154,53 @@ fn setup_threads() { } } +/// Loads a font based on the input font name. +/// +/// * `font_name`: The optional font name. Uses the first (alphabetically) monospace font installed +/// on the system. +/// * `builder`: The builder to add the font to. +fn imgii_builder_load_font<'a>( + font_name: Option, + mut builder: ImgiiOptionsBuilder<'a>, +) -> Result, ImgiiError> { + // get the font name + let font_name = { + match font_name { + Some(font_name) => font_name, + None => { + // if we didn't get a font name, use the first font (alphabetically) that is + // installed on the system + let mut fonts = list_fonts(); + log::debug!("Found fonts installed on system: {:?}", fonts); + assert!( + !fonts.is_empty(), + "there are no monospace truetype (.ttf) fonts installed that could be found" + ); + fonts.swap_remove(0) + } + } + }; + + // read the font that the user wants to use + log::debug!("Attempting to load font {}", font_name); + // NOTE: if the user inputs an invalid font, it seems to fall back to the first monospace + // font it can find + match load_monospace_font(&font_name) { + Some((font, _)) => { + // successfully loaded font + builder = builder.font(font).font_name(font_name); + } + None => { + // could not load the font + return Err(ImgiiError::Font(FontError::FontLoad { + font_name: font_name, + }) + .into()); + } + }; + Ok(builder) +} + /// Creates an instance of [`ImgiiOptions`] for the CLI for imgii. /// /// * `font_size`: The font size argument. @@ -169,51 +216,10 @@ fn create_imgii_options<'a>( char_override: Option, ) -> Result, ImgiiError> { let mut builder: ImgiiOptionsBuilder<'a> = ImgiiOptionsBuilder::new().background(background); - // build the complex values first - if let Some(font_name) = font_name { - // read the font that the user wants to use - - // NOTE: if the user inputs an invalid font, it seems to fall back to the first monospace - // font it can find - match load_monospace_font(&font_name) { - Some((font, _)) => { - // successfully loaded font - dbg!("loaded font successfully"); - dbg!(&font_name); - builder = builder.font(font).font_name(font_name); - } - None => { - // could not load the font - return Err(ImgiiError::Font(FontError::FontLoad { - font_name: font_name, - }) - .into()); - } - }; - } else { - // we need to load the first font that we can find :) - let mut fonts = list_fonts(); - assert!( - !fonts.is_empty(), - "list of installed monospace fonts is empty" - ); - match load_monospace_font(&fonts[0]) { - Some((font, _)) => { - // loaded first font successfully - dbg!("loaded font successfully"); - dbg!(&fonts[0]); - builder = builder.font(font).font_name(fonts.remove(0)); - } - None => { - // could not load the font - return Err(ImgiiError::Font(FontError::FontLoad { - font_name: fonts.remove(0), - }) - .into()); - } - } - } + + // load the font + builder = imgii_builder_load_font(font_name, builder)?; if let Some(font_size) = font_size { builder = builder.font_size(font_size); } @@ -282,7 +288,7 @@ fn main() { ) else { panic!("could not create imgii options"); }; - log::debug!("imgii options = {:?}", imgii_options); + log::debug!("imgii options = {}", imgii_options); // Now, handle the conversion match image_type { diff --git a/src/options.rs b/src/options.rs index a38a545..5d32a1b 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,5 +1,7 @@ //! The options for using imgii. +use std::fmt::Display; + // We need to re-export these, as they might be necessary for users of this library. Imgii's CLI // uses these. pub use rascii_art_img::RenderOptions as RasciiOptions; @@ -86,6 +88,25 @@ impl<'a> ImgiiOptions<'a> { } } +impl<'a> Display for ImgiiOptions<'a> { + /// Writes an `[ImgiiOptions]` instance. This should be preferred over debug printing, as + /// `[ImgiiOptions]` can hold a lot of binary data. + /// + /// * `f`: The formatter. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // write everything that won't spam a bunch of binary data + write!( + f, + "{{font.len()={}; font_name={}; font_size={}, background={}; rascii_options={:?}}}", + self.font.len(), + self.font_name, + self.font_size, + self.background, + self.rascii_options + ) + } +} + /// Builder for [`ImgiiOptions`]. Intended way to create options for Imgii. #[derive(Debug, Clone)] pub struct ImgiiOptionsBuilder<'a> { From daa58a5ca4b11e5ef9210621a8fff16730dc8156 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 19:07:36 -0500 Subject: [PATCH 12/24] Update readme. --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index da69f66..6c66958 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,22 @@ Supports popular image types as input, such as PNG, JPEG, GIF, WEBP, and more. ## Installing +### Requirements + +Building requires the following dependencies: + +For Ubuntu/Debian: + +```sh +sudo apt-get install libfontconfig libfontconfig1-dev +``` + +For Arch Linux: + +```sh +sudo pacman -S fontconfig +``` + Install `imgii` as a binary: ```bash From fe75b0fe6f659df57a79c8483ce3141494b4872b Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 19:08:22 -0500 Subject: [PATCH 13/24] Update workflow --- .github/workflows/rust.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f53ca74..a03969f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,7 +22,10 @@ jobs: sudo apt-get install libfontconfig libfontconfig1-dev cargo build --verbose - name: Run tests - run: cargo test --verbose + run: | + sudo apt-get update + sudo apt-get install libfontconfig libfontconfig1-dev + cargo test --verbose clippy: From 36834b28f8a1e8d39b99f47a31cea619520fb153 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 20:28:26 -0500 Subject: [PATCH 14/24] Updates from cargo clippy --- .../converters/generic_converter.rs | 6 +-- src/lib.rs | 3 +- src/main.rs | 45 ++++++------------- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/src/conversion/converters/generic_converter.rs b/src/conversion/converters/generic_converter.rs index 2aa8798..65ed59d 100644 --- a/src/conversion/converters/generic_converter.rs +++ b/src/conversion/converters/generic_converter.rs @@ -71,17 +71,17 @@ pub(crate) fn render_ascii_generic( let red = r.parse::().map_err(|err| ParseError::ParseColor { value_name: String::from("red"), the_str: String::from(the_str), - err: err, + err, })?; let green = g.parse::().map_err(|err| ParseError::ParseColor { value_name: String::from("green"), the_str: String::from(the_str), - err: err, + err, })?; let blue = b.parse::().map_err(|err| ParseError::ParseColor { value_name: String::from("blue"), the_str: String::from(the_str), - err: err, + err, })?; let generated_png = { diff --git a/src/lib.rs b/src/lib.rs index b0e3860..f271837 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,8 @@ use crate::{ /// let imgii_options = ImgiiOptionsBuilder::new() /// .charset(from_enum(Charset::Minimal)) /// .background(false) -/// .build(); +/// .build() +/// .unwrap(); /// /// // perform the conversion /// match convert_to_ascii_png( diff --git a/src/main.rs b/src/main.rs index 27e9b4a..b8670f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,7 +157,7 @@ fn setup_threads() { /// Loads a font based on the input font name. /// /// * `font_name`: The optional font name. Uses the first (alphabetically) monospace font installed -/// on the system. +/// on the system. /// * `builder`: The builder to add the font to. fn imgii_builder_load_font<'a>( font_name: Option, @@ -192,10 +192,7 @@ fn imgii_builder_load_font<'a>( } None => { // could not load the font - return Err(ImgiiError::Font(FontError::FontLoad { - font_name: font_name, - }) - .into()); + return Err(ImgiiError::Font(FontError::FontLoad { font_name })); } }; Ok(builder) @@ -203,39 +200,34 @@ fn imgii_builder_load_font<'a>( /// Creates an instance of [`ImgiiOptions`] for the CLI for imgii. /// -/// * `font_size`: The font size argument. -/// * `background`: The background flag. +/// * `args`: The CLI arguments. +/// * `rascii_charset`: The rascii fn create_imgii_options<'a>( - font_name: Option, - font_size: Option, - background: bool, - width: Option, - height: Option, - invert: bool, + args: Args, rascii_charset: Charset, - char_override: Option, ) -> Result, ImgiiError> { - let mut builder: ImgiiOptionsBuilder<'a> = ImgiiOptionsBuilder::new().background(background); + let mut builder: ImgiiOptionsBuilder<'a> = + ImgiiOptionsBuilder::new().background(args.background); // build the complex values first // load the font - builder = imgii_builder_load_font(font_name, builder)?; - if let Some(font_size) = font_size { + builder = imgii_builder_load_font(args.font_name, builder)?; + if let Some(font_size) = args.font_size { builder = builder.font_size(font_size); } - if let Some(width) = width { + if let Some(width) = args.width { builder = builder.width(width); } - if let Some(height) = height { + if let Some(height) = args.height { builder = builder.height(height); } - if let Some(char_override) = char_override { + if let Some(char_override) = args.char_override { // converts the string to a string vec if it is Some, otherwise stores as None builder = builder.char_override(convert_string_to_str_vec(&char_override)); } builder - .invert(invert) + .invert(args.invert) .charset(from_enum(rascii_charset)) .build() } @@ -276,16 +268,7 @@ fn main() { }; // our options for rendering ASCII in imgii - let Ok(imgii_options) = create_imgii_options( - args.font_name, - args.font_size, - args.background, - args.width, - args.height, - args.invert, - rascii_charset, - args.char_override, - ) else { + let Ok(imgii_options) = create_imgii_options(args, rascii_charset) else { panic!("could not create imgii options"); }; log::debug!("imgii options = {}", imgii_options); From de598238b5edeeb152d0f1c7f40da1ee6b390db2 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 20:38:02 -0500 Subject: [PATCH 15/24] Update example --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f271837..ed09e4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,7 +121,8 @@ pub fn convert_to_ascii_png( /// let imgii_options = ImgiiOptionsBuilder::new() /// .background(true) // set black background behind GIF /// .width(50) // keeps the aspect ratio but is 50 pixels wide -/// .build(); +/// .build() +/// .unwrap(); /// /// // perform the conversion /// match convert_to_ascii_gif( From 01b6cf4908e9696fd6dd712a6d92aa1556f85eb2 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 20:45:12 -0500 Subject: [PATCH 16/24] Update packages --- .github/workflows/rust.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a03969f..5a02562 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,16 +16,13 @@ jobs: steps: - uses: actions/checkout@v4 + with: + packages: libfontconfig libfontconfig1-dev + version: 1.0 - name: Build - run: | - sudo apt-get update - sudo apt-get install libfontconfig libfontconfig1-dev - cargo build --verbose + run: cargo build --verbose - name: Run tests - run: | - sudo apt-get update - sudo apt-get install libfontconfig libfontconfig1-dev - cargo test --verbose + run: cargo test --verbose clippy: @@ -34,4 +31,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Clippy - run: cargo clippy -- -Dwarnings + run: | + sudo apt-get update + sudo apt-get install libfontconfig libfontconfig1-dev + cargo clippy -- -Dwarnings From 73fc4fd7fe38bf0737ec261b357bdb1a9e1218ca Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 20:55:38 -0500 Subject: [PATCH 17/24] dependencies try again --- .github/workflows/rust.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5a02562..080ea38 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,6 +19,10 @@ jobs: with: packages: libfontconfig libfontconfig1-dev version: 1.0 + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install libfontconfig libfontconfig1-dev - name: Build run: cargo build --verbose - name: Run tests From 202a56763631eca01005d91ed16431b9b6f33506 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 20:58:06 -0500 Subject: [PATCH 18/24] Install dependencies as another step --- .github/workflows/rust.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 080ea38..9fb1238 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -34,8 +34,9 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Clippy + - name: Install Dependencies run: | sudo apt-get update sudo apt-get install libfontconfig libfontconfig1-dev - cargo clippy -- -Dwarnings + - name: Clippy + run: cargo clippy -- -Dwarnings From cbe6920d2bca893ba3924c8a6d605a24a82d66e2 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 20:59:39 -0500 Subject: [PATCH 19/24] Remove with --- .github/workflows/rust.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9fb1238..dc9d0be 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,9 +16,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - packages: libfontconfig libfontconfig1-dev - version: 1.0 - name: Install Dependencies run: | sudo apt-get update From db043c85b60496515864149cc527762c79930c1a Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 21:08:33 -0500 Subject: [PATCH 20/24] Update versioning. --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e088d66..c38f589 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,7 +649,7 @@ dependencies = [ [[package]] name = "imgii" -version = "0.7.6" +version = "0.8.0" dependencies = [ "ab_glyph", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 3127032..5f41510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "imgii" authors = ["Stattek"] -version = "0.7.6" +version = "0.8.0" edition = "2024" license = "MIT" description = "ASCII Image Generator" From 7dba2cd0825245d9674a3168030f408ec7ed5a0b Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 21:23:05 -0500 Subject: [PATCH 21/24] Update argument descriptions --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index b8670f5..6a8a639 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,7 @@ use imgii::{ #[derive(Debug, Parser)] #[command(author, version, about, styles=set_color_style())] struct Args { - /// Path to the input image + /// Path to the input image. /// /// Can also specify a format for an input, if is also set to the final /// input image index. @@ -46,11 +46,11 @@ struct Args { width: Option, /// Height (in characters) of the output image, if not specified, it will be calculated to keep - /// the aspect ratio + /// the aspect ratio. #[arg(short = 'H', long)] height: Option, - /// The name of the installed monospace font to use. + /// The name of the installed monospace font to use. Must be an installed TrueType font (.ttf). #[arg(short = 'n', long)] font_name: Option, From 131acb93997a0541f91f4bc62a86572cf2bc7498 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 21:29:05 -0500 Subject: [PATCH 22/24] Update documentation. --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6c66958..e35e8a9 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Usage: imgii [OPTIONS] [FINAL_IMAGE_INDEX] Arguments: - Path to the input image + Path to the input image. Can also specify a format for an input, if is also set to the final input image index. @@ -82,6 +82,9 @@ Options: -H, --height Height (in characters) of the output image, if not specified, it will be calculated to keep the aspect ratio + -n, --font-name + The name of the installed monospace font to use. Must be an installed TrueType font (.ttf) + -f, --font-size The font size of the output image. Larger font sizes incur harsher performance penalties. @@ -114,7 +117,7 @@ Options: ```bash # Makes assumptions about how to convert the image. -cargo run --release -- input_img.png output_ascii_img.png +imgii input_img.png output_ascii_img.png ``` ### Examples Using Arguments @@ -122,23 +125,26 @@ cargo run --release -- input_img.png output_ascii_img.png ```bash # renders an image with a width of 150 characters, using the block charset. # NOTE: Just setting --width will maintain the aspect ratio of the input -cargo run --release -- --charset block --width 100 input_img.png output_ascii_img.png +imgii --charset block --width 100 input_img.png output_ascii_img.png # Sets a background behind the result, can often look better -cargo run --release -- --background input.gif output_ascii.gif +imgii --background input.gif output_ascii.gif # Forces the output to have a width of 10 characters and a height of 100 characters. -cargo run --release -- --width 10 --height 100 input.gif output_ascii.gif +imgii --width 10 --height 100 input.gif output_ascii.gif + +# Uses a specific font +imgii input.png output.png --font-name "Adwaita Mono" ``` ### Example With More Verbose Output ```bash # Running with debug logs enabled -RUST_LOG=debug cargo run --release -- --background input_img.png output_ascii_img.png +RUST_LOG=debug imgii --background input_img.png output_ascii_img.png # Running with info logs enabled -RUST_LOG=info cargo run --release -- --background input_img.png output_ascii_img.png +RUST_LOG=info imgii --background input_img.png output_ascii_img.png ``` ## Supported Output Image Types @@ -153,7 +159,7 @@ Specifying an output type can be done simply by changing the filetype in the out ```bash # Takes an input GIF and imgii will convert to an output GIF -cargo run --release -- input.gif output.gif +imgii input.gif output.gif ``` ## Example Output From c8d13bb90e166135dcf970165e09c0d72aefda79 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 21:36:39 -0500 Subject: [PATCH 23/24] Fix return --- src/main.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6a8a639..67d9ec0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -188,14 +188,13 @@ fn imgii_builder_load_font<'a>( match load_monospace_font(&font_name) { Some((font, _)) => { // successfully loaded font - builder = builder.font(font).font_name(font_name); + Ok(builder.font(font).font_name(font_name)) } None => { // could not load the font - return Err(ImgiiError::Font(FontError::FontLoad { font_name })); + Err(ImgiiError::Font(FontError::FontLoad { font_name })) } - }; - Ok(builder) + } } /// Creates an instance of [`ImgiiOptions`] for the CLI for imgii. From 8ff08a9ada6b2d66f10ff8ecf05e3e705a479924 Mon Sep 17 00:00:00 2001 From: Stattek Date: Sun, 22 Mar 2026 21:37:29 -0500 Subject: [PATCH 24/24] remove mut --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 67d9ec0..5a9683f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,7 +161,7 @@ fn setup_threads() { /// * `builder`: The builder to add the font to. fn imgii_builder_load_font<'a>( font_name: Option, - mut builder: ImgiiOptionsBuilder<'a>, + builder: ImgiiOptionsBuilder<'a>, ) -> Result, ImgiiError> { // get the font name let font_name = {