From f71a4fe1d2d6595f7a42e4cee1fe735b9164a0a9 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Tue, 28 May 2019 11:01:04 -0700 Subject: [PATCH] refactory --- quad/qtest/test-docs-tester.pdf | 159 ++++++++++++------------ quad/quadwriter/attrs.rkt | 84 +++++++------ quad/quadwriter/core.rkt | 15 ++- quad/quadwriter/font.rkt | 31 ++--- quad/quadwriter/layout.rkt | 210 +++++++++++++------------------- quad/quadwriter/markdown.rkt | 6 +- quad/quadwriter/render.rkt | 41 +++++-- quad/quadwriter/tags.rkt | 115 ++++++++++++----- 8 files changed, 351 insertions(+), 310 deletions(-) diff --git a/quad/qtest/test-docs-tester.pdf b/quad/qtest/test-docs-tester.pdf index 014b23aa..9ae45d58 100644 --- a/quad/qtest/test-docs-tester.pdf +++ b/quad/qtest/test-docs-tester.pdf @@ -2385,35 +2385,38 @@ endobj endobj 3 0 obj << -/Length 6310 +/Length 6311 /Filter /FlateDecode >> stream x]ݎd7n,Qb7wA.b8@b yRUwuxzb) ڰgg?GO 巧eWk<^?K߿_^^OxOOߟ~/_~/ɿwEMR97oO#<ۯOw@?@?~~d)S?̿+zEEuswuTZJFQ؛Ubexȷ>$~Z5y]a:QrUO.#d]aaͽ_.򿣪) 5|XUջ`ecڲrxl^QSp$*GTHX]k!M^^nLuƾuWES76JFk}pUv?z`3|8,WU]ȋ)! +p 2Nk^\_Jl3]^ӯT$\9 bnh.=)t蜴-Da!ߖa Ku<"h9w可Şz RNV6 )-okj@2D*F2]',-}KƯFMoADd%:ɫ!B)$[vTy> aZk>^.ruhmv p&60V(3β@]HHʲ'ߪK:D+KS'Z&3_g T*;oE:Mެs~j0p|{y,g̭*ѪH(Ą.1UӍɉ(ޖNX+ϵQ@$ T-0ZݕDM) Q}TS*,UEHi*CO!ȗws}*BK}Xݹ ~5>\@W 8wհ;f|Э.#|JBu)BȲ^/ ۚDt,TJhBIJ-욷$18i'ly\Λz@Qx.[9"7 -y*#y&jK>]|~BQDQ,%eRze!;YnȾ&Gon/ZT>E㘢Q\A[Q.nƃ!KqM\Ħǜb4oB .oɴ3!^ovڷS5hGș S.m~b BravY咐~=S(A0( -[ X6]Q[廤\9X:]4p稍)i8U3b>I[>\]%1XY(m nIwas|5+8N%=CG󟣣ΜŧZg KH}%_,Ɛ̨t ^|3*%K6 Ԑzqn&S2>>̐a[CVme}}b}ԇ>Tw;!]d͗4ß(G!Z2VZ+JZ|ӋLi'Ar\2Rv@}&%] -zh_KmJRo լ\ԡ;]>6) sȂoR.|b:t޵$_-3Uc;z2jMI0)01෿1/GGG5&MUyoGyn.X4(廱‰T7ƑLk؏nW|hTieW-V<lE $)01^7g;Jĵ!br\qګ;Ojc.fފba*br0Po -@%5މ[sj06acq H劂:ʨ s T8.BO'åd|Oդ|K\s>)pE|  9y*J3S"Dqhh˷¿ q{ȩa^rH=Elԫ}h-0*"z=abH^Z@qC#Wa1K^_qt3e(X,@LXUtZ'KI~>9_C}yb 3t( g䒔:zc@RPcrDRܕّ-jO -5z6 Ef"Y9v֯έEHWlݢJ$[rZVF.c`ekHrx+\.eqDb xxP -;Ҵm:Sv1:sӢ=؂6 ܟgP:\ѻja}&s*j r| 9ϖrJo1o-=b~((9gn Wf0͢L#O!~wiBhsVb$2zGf mw5 aEυ,R /^ҏ%BgLNţsʌ}^NEiwY>z9:N [ :YnpjYcTlF/ւgUd1qX A}yr.i)PǍ_h?AG~JlRrţth(/I[qdsUv(]2 -q*5m29Z6asY(,RzΘ Y6]v9P嘼<%l&:%*Wp,b,˾șƲܩv _dH1`o䛋",c$VLNVQΥ@sQKa3~j D{Ta݊^Vn-ݖb2cVMCoJVrYcgb#S5U^S9˸gܟram.$N FTکKJЉd+Q!RޑM2Zt)RrE($oQ#Clăl=% Ct\ȲQ/*`tS םMsA(Jt9Kee+UĔވ ,<ǦXrtALOVyQ `>xlT:Wȟ`-cKKpIN,%o2j-?Sv菺rjkX]$ pƗqbh{ѻ*~c{²:! -xFP7н?%C08-,%K[j-UYJK #b%+˹n"YmŚ ٦uܐ51%4C. Y6偒N=hbL^WWVK+e QnAK -z8n{6d:x ;>[g8(}i 9Y+ v(+4&BՓ U/d֎^@Xd8@( -VNYX̐`.3*}gi'}x' o?{]yXLg/ZaCE= +JVkIrsE<ȕya@SQ$i;1|x8Uo+@M)BܐSPA`Y#]=aa! GgUC}v.X)0I2U RzMs Ry7>EZ?9; -8*J~g{Q2w$eH41uAR ! w!H ʂ~"5x }nBDmx J_N"9~ΓLF/DIn.uSQk)mLSVc ++KT.Y^jH\P)&nz A(q]=ޡBϞ?mBzTfKan7ĘM `]v.$Q ^zOe˙ÚK0 Y"cIo8'1q k0p3s ƋsQk)0o +qEYpQumޅhx ]&gpx| {!S97ɇ×u$)u9XFSAZֲ4Z-L2,۸4^"(]β\ãPj먬$*~z{ gwAHbN'pp.d)݀v\.@8~$K9 3%߼Q:.g *I7[1 f] z?> +h^QVR=WTAZA:?x鳋x0+U3HY輫c1U JƠ˦^L3鸌pST~pX>PN);ZNVV:Ebc[rVH?.c$3@갷l@ Ψ=~.dKan7scV: tvelQ% "aqNb;撌H - k5Cv${W::H;V$g%-͵*m[]+ PlD7+\#0Pyd`?arq1I?׀JL!7d:RcH]K]Ø62c'ҕ\G]H4mwGK|sٜ/qU6  -KqOD)noeHKr3@Qٕ=&q*X3jR-U=?&,Etg@Q:3BU -J^_D^b淎g-|ZWiNe`)ɴ梌00DߙJf.c.9$G0A3+EI)ߛRǥ>z2` }Pkxjب]AimL.^]ngO^s)~h'G*.&@IĵemZXZ\7C[6rT$sQCq/ArVm&z{@g1ⱫyD:SUKR 8Kp1 /-⟸t[}wg)[^WA.J<[/*)&Cg \ՄbL4S%\[7L%E(pT{:i*<kj .ɫ|b#To R'h3tmby[Ks>J~9k{c7u"OIGQ={61,Λ9[gy;ؕ09ʇZ2\<f(dHǸD|`KR7coBhsV7Bpj}XKe9y_KYўVt\ȲQ/*Й۬K?&K@ %B1J, (swe~suˊ N-q4kLuk,I.wj肴RY-lGJK -h)yb2sYo>̆83nU!oՂ.im=g\vI>'-Ajţ}%6G)WQv4˗P3N+SUTqU6DQikN -8㷝nWO(=PYԃ:) F%C+Rۭ BcZjL\.AI2':(˲b氆w*/qxZ [ZXL2$|SQVeĊj2*۹t~QqJ6@sotJ2@͘ڋ^VnoP7   icu +y*#y&jK>]|~BQDQ,%eRze!;YnȾ&Gon/ZT>E㘢Q\A[Q.nƃ!KqM\Ħǜb4oB .oɴ3!^ovڏSxC Pt rN%#} /I">f/%2N:za`cɜZ!_m),Cΰije۠g ucK +J,xΙ•i*7%E(Hh`$ksQ@s +sQ=T@Ong (n-S V]:`lPzYɑ*+G9Lca6t #ppC͐c\1lSf]nZp:̮F9ĬC|/eE{gXs!F>Cg'笺cY8WTvE裗SQk3˹uˠ3 ؛5Kf>zi`-xF_IF@GQ@G/-|G/璖uܘ̣FG/ :(XF<:܃[`1Dυ,R 11y-A7@q)? .ؤa1i:D]Qx2~QELX`R~l*ʊ,Gd'惗86OE3y( 2&1T}β\-~3ngX+E^Jzg|';7',_gEQyS!~QR_;t B]R߽02a-];;1)oVYiܐmZ L=Pتky\}S=A3e^(ԣfo.|eye\6; 4 +hO SӬy&ʈҗY`'Hc/]=y/]Bf*je +EMfH0.I lU i- &2Ȫwy6X~ҧHi AwbJN ޏ!I2aK:_Գm$-7Wă\'48KVS͇)qS +ba! 9U:Un 15R(գ)2p}V51gau +D)c\I~ ޤ1n %`w3p*@n_césW+1~%sWORA_G-1y,(XZGP}7 ,ͭ10Jfx9 D-<~,dkA\!> y;Śl:.p(0mOE2UJ%I +b"VN-'@zՃz! Vp.d٨GevCΘJKDʠ= eJ0k.h襇T9KlqP%2Hs+ +י*8o8p/jh? s,\E(Y]85ogp GIg\7ݑzY7x[2s|h?||ѽYKQʛR0ʙe9e$e-Ke-ò8Js!JU*B_,U1 <g RnQչz;t̟Ōλ:AZe۠ta lE;T=sxn/ +G1u@u-QZ[Cq[%JdaeC[t.QZ ΋q*6ƹ%oa2vOObq;So{;ȶBYvC09&/meàNaZFYU r!kt*V#a.ɈMl\3dAw5iYdJoErZҲ\ҶյviZ l@t25s G6f\,3~ SKz4yxKSB Zyc^@F#_9Եe9Ij+3v"]ue|xZOJ@3qwD+d/h7͙z{ZWhSR D+V$71]cʨu=?&[Rh\Dw%);C*TLȎj)YAgdlᲹ(NRa5Y^) OXKisQkrcV6,3 +2u7B]z۶ito16?׻c2 m3./C;S;(+|זB|.I1YVR\YXjOd+`</ H#tg0ct6 +)d~mWOHŹz{`Mɮ̃d-a0hgxz9(}Cz(NwZ08]$Ee)(f~x6§%}N`zdZOLk.Cmj<"-KRx4RPrm(u\*`듭',3&'K +5z6Eze ;Av~o⢏ibY dKN\K_1u^ucF$ԌIev 91}ӐXmc endstream endobj 247 0 obj << /Producer (PITFALL) -/CreationDate 1559002569 +/CreationDate 1559063482 /Creator (PITFALL) >> @@ -2445,7 +2448,7 @@ endstream endobj 249 0 obj << -/FontName /FHQFYO+SourceSerifPro-It +/FontName /ZOWQBQ+SourceSerifPro-It /XHeight 475 /StemV 0 /FontFile3 248 0 R @@ -2462,7 +2465,7 @@ endobj 250 0 obj << /Type /Font -/BaseFont /FHQFYO+SourceSerifPro-It +/BaseFont /ZOWQBQ+SourceSerifPro-It /CIDSystemInfo << /Supplement 0 /Registry (Adobe) @@ -2488,7 +2491,7 @@ endobj << /Type /Font /DescendantFonts [250 0 R] -/BaseFont /FHQFYO+SourceSerifPro-It +/BaseFont /ZOWQBQ+SourceSerifPro-It /Encoding /Identity-H /Subtype /Type0 /ToUnicode 251 0 R @@ -2525,7 +2528,7 @@ endstream endobj 253 0 obj << -/FontName /EMSDTO+SourceSerifPro-Regular +/FontName /TQAFII+SourceSerifPro-Regular /XHeight 475 /StemV 0 /FontFile3 252 0 R @@ -2542,7 +2545,7 @@ endobj 254 0 obj << /Type /Font -/BaseFont /EMSDTO+SourceSerifPro-Regular +/BaseFont /TQAFII+SourceSerifPro-Regular /CIDSystemInfo << /Supplement 0 /Registry (Adobe) @@ -2568,7 +2571,7 @@ endobj << /Type /Font /DescendantFonts [254 0 R] -/BaseFont /EMSDTO+SourceSerifPro-Regular +/BaseFont /TQAFII+SourceSerifPro-Regular /Encoding /Identity-H /Subtype /Type0 /ToUnicode 255 0 R @@ -2604,7 +2607,7 @@ endstream endobj 257 0 obj << -/FontName /JZGYUV+SourceSerifPro-Bold +/FontName /BNIYZQ+SourceSerifPro-Bold /XHeight 475 /StemV 0 /FontFile3 256 0 R @@ -2621,7 +2624,7 @@ endobj 258 0 obj << /Type /Font -/BaseFont /JZGYUV+SourceSerifPro-Bold +/BaseFont /BNIYZQ+SourceSerifPro-Bold /CIDSystemInfo << /Supplement 0 /Registry (Adobe) @@ -2647,7 +2650,7 @@ endobj << /Type /Font /DescendantFonts [258 0 R] -/BaseFont /JZGYUV+SourceSerifPro-Bold +/BaseFont /BNIYZQ+SourceSerifPro-Bold /Encoding /Identity-H /Subtype /Type0 /ToUnicode 259 0 R @@ -2681,7 +2684,7 @@ endstream endobj 261 0 obj << -/FontName /HMIJGP+FiraMono-Regular +/FontName /FKJGTP+FiraMono-Regular /XHeight 527 /StemV 0 /FontFile3 260 0 R @@ -2698,7 +2701,7 @@ endobj 262 0 obj << /Type /Font -/BaseFont /HMIJGP+FiraMono-Regular +/BaseFont /FKJGTP+FiraMono-Regular /CIDSystemInfo << /Supplement 0 /Registry (Adobe) @@ -2723,7 +2726,7 @@ endobj << /Type /Font /DescendantFonts [262 0 R] -/BaseFont /HMIJGP+FiraMono-Regular +/BaseFont /FKJGTP+FiraMono-Regular /Encoding /Identity-H /Subtype /Type0 /ToUnicode 263 0 R @@ -2733,24 +2736,28 @@ endobj 264 0 obj << /Filter /FlateDecode -/Length 3935 +/Length 3907 /Subtype /CIDFontType0C >> stream -xw\TGKP5FX@,[b&{ -*`5{,`CXD5XclO;w^>I>{;;̙3ܽwA0 ow;a:UKwfعNBw݂ β"]kܭەHH+.lx[W*W_(_7 o>GMͫTnU*URzZέ}_G p7}֮gԌRSv򇋧޾M||FR) P^E EAa(.JCEW @1H"P C0$`1ILST1ML?bQ1WP,EbX*"RDbX%V5"Zz#b/dqDį8!NS⼸ .%,⚸%n;⮸'x$'x%^ %t^C ``&A~( P C ( P*Ae_WPAuPCB#h M)4ZB+h m-:B' ]+t/='o~0A `0FhcaPa!,K`), QV*X!6O !~}AA"$H#ppNI878g px/%נ@xw¼ 3| :bA4c! Xaq,%KaMkam:X=6F;c c>}G?8a@Cq$Sp*N#8< p!.Kp).QW*\k0:\1 7܊p=xC1 c+xOi Y<^^oM{xC| >g_K|w"A@HDLlȖP^D!'r& +QQrbTJPIRTPY*GUJTЗUjTjPMKTSjJͨ9ԊZSOEԛR_Gɏ? A@)PFA4FhCciP@{Di -Mi4flCsiͧb -p%r(ZKh=J(Ih7G@?>O B(S%:M:KI7&ݢt=O!=3zN/%פH F&flö;rA6s! \ؙ-®Eٝq).e,\+r%UKr55&͵6܀܈sn͸9܊[sn=w܉;s؋sɽ؛{sa_?<x0Hţy q8|Oo|9>"Ηl2iZWͬW,)5z)nZ":Ӭ{䦞.}Cם^\WWލo%zڵsdc'SVKlRB{癤nlNMI(׍ik/7&[gAZ!Ƞи4%׭[wO9^\hOS;ne4S&ߦ=x襉EL&md&>ڢl*ǝ˻gvR[2U}-MdRo!QGE𡍪ǒ3g*ZT!ʱ -j_H8bZBCBcp׃ovfجw5| QH~bs6[{Tw}Ypgjx%4Лxs.ibYctAy29㛖O sӁ[\8sɒ"OּjKNP75EI;6/ڐk>f϶>E5>jZpOn/uImqrc8eo?h lsw[1/LfcCfiհ+.6=jա+ztWblU~^ Ӟg8}>ӭ8p1.T}iz βS㘳oJe0>p܂ gq᳢ 8"TcO [ e퉅k.4`Uwk1cB#FGFF,j[ZlvY٫u^14MS.WT@M-=EնC(5JhQ^_qC&{U"sGͣ-Q>*8.6)m #>|@ϯUY[jMCR['7&P7-[TZJQ[ST7⣂ȑgcVS3!XX릏)ئzҨE7%ړ[VsHyRø0=2lM°a6F0۱zryy]4ow)^K4%}5[׊ڎ(:iĊbi}:v=b7\x&\OMg܉#-idghX/6&a89E.p- wGx <ͅ8bHE=2XauE>1HclM6[akS:^荽0p #qX0'ȉ5bq;餱p?ȁ83xwꘂoа2FF(`e G*Hf*DS3g|CRt֨O ;jHr6ԖQ{@u.ԕW.L!4FfmUPt6&L[#w8A' t~ -]kt_G -!һ F8?GR<qg٩";K#En1B^N!NÜ|$KX U?'|o>?~9_bS {K - e>_QfYH~. /d$EHW&JwYL%dI!KҲ,+򲂬(+#Ʋl*沅l)[ֲl+(;β*^[}}e?__d,9TAr 69;6L;jS.hӟpak -͟>.qA{&m/|ben;_6mZwGfUS3 -~mQ˨}Cڿ*6E/fi;l٨$_vzaA K5wQJ1:Ԣ|bu L˺Kˀp-mo*zln6*>dnªG)<9ʬQ#MuML^SN)T5-!o'[{ۄͻbO8ݎg6tS5턨-ꎶ@sen}/s/qSN ?}S?QI\D]ˬ7A92}K/YsZmM}xU@zvqΣ ^Sc~9gꡟԑF:fXns9DZ#lr*+ǝL/"[W֥QϸɵTݶr? +xwXUGwʂK5F8MQ"Ec^ ذ)D5Xclo콻"|J}<7gvvvv}Y@C^M<|GPI PQEW8|* +"(&(%J~ @#W  "P Ab$&)b&~ )fb+bX(P&bD,D +REX#֊ub?}b8 qT8)N₸(~Ⲹ"k▸-➸/x,x-0{$AC X>G!'V( E(P*@EK +@Uա.ԃCh +͠9 +ZCh =t :C +ݠ; z oC!Ap#a10A "0Ű2HX+`%u6Ov@ `? x8 G(_8p +Nop98܇^KxAILx =9QC :g船07Њs,_`AtF,EXkXk7-:Xa}laClm3v c쉽`E?C1G'$Sp*Np:q&8< p!.P p\Kp).W*\Q:\'܈p3n w܋GS|Wh[|/D$Ɏ)$,L.+;TR1*N%$TR9*O"U%}EU*UTP]G 5fԜZPKjE&C}ɓMɇ`%?BC)HEi qBi}OiM)4LEiͥy4BZDFᴘRZFIkh-6N;h'ݴR4P,ϴ:H!Ô@tNot%C I6ݡtzH1= zI5)2=ł%۱=8[9? .:s!.E$\r9."W%U*W\\q}nqCnč 7fܜ[pKnŭ vܞ;pGĝ wnܝ{pOŽكp_d/y{@A<}ُyQ8|Oo|9>"ΗlkRZW.YRŪ\%Vu7GeR ^ּ`k7O?uו^n +xjQ>{]ʭ];G4vdi&r٬E3<5Yk3Qy~#Ez$|fi8T3n5Nw3tq⧗vLJiD[JMQK Z,ۈx#]&cʚǝ˹:7T[S"U-DRcU &AAAQkEFG**e2ߠF`#Ûv,Aƨ.kOlݦOk7a 2%MvF,OS7~fJמg>w. *Ū >iԴ. "=5nƂf5c|=U[ʅ)J0ܹyQlm뙜TKn-yoY?)SE3~c_2讙MW\查_g_->-}&1|a1zd[KIgzb7=jCWP#rP'}9ؽ/uVq;8bl˛Fu.Smm0fqۼ%a3#8<jM]:?uk.0`Ww!kѣGED/hƔ u;GC7:4GQWVcivA )M2?U-PiK#T۽ި+ͨra!qFhxOQ(X;NgVEAmYruO=ibXM3>|@ϯYUkBHHPcUnrSH\R uSwڢ:Lג +;RlFSь 2"Fh񬦶dM$w9!+:P#"tuЭvfP1aFba9Aa9]5s%V:8;|f֊ZNT"EQt?2'N.vb#h#nSL</>ȿ\ ܡP*d<$`M`7쁽 1(䇋;\?2\[pݏLx W/ atQ +Kc,հz:D 6f[`KlvN {@0q88cp,@"+؎;p'Jhx LeW?qkTh`AFi " (Y)?}N4 }KS>5!5ʂ-P[jGuNԙPWFݳa +4" c|bUpO6fڒJ3 :I<]LW*]/ }$¹9?⌢i(#.!$2?cy<wVq<N#i |5?[|]?GS~Wo_RH(s2+d>i粀BEUIwYHEdQYL%dIYJedYYNdEYK~#eX6Me3\-e+Zme;^ve'Yv]e7[z>^/Ho9PAr~_!r dUͶiii\`^rپ;د[fM,k㷺w|WuWMk>\=m]KS)ȪjhfM4kPjδ1v#g}h欦^Yˬur5n)TpYeZڙ@N5}!#VS8`N*ߪ۔], ϮO^SO)T5uqS6~vL=YS2n&X}m|;|:sM潬˗ϙm.99dܴξcltFU0iRO=v-ZR=[Ak3Š%ZTcӪf[8us{z潔,H 9'DMȰ vSF͵ɿۨU,(+w-ݡTFMak]4[ Ǫh^g`n|MkK͏rUXQ:.BɐhLl~S˦UlqNO3N"QEjrBz vb8t]bi/-ŗ&+*ɱ]I֔LύNcj'յ*Q=cRG Sa~9˪iTF;zegٜu61e{_JϓjOqGkSXg^VZeeZNۚx endstream endobj 265 0 obj << -/FontName /WQTCFS+FiraSans-Light +/FontName /AZHSOY+FiraSans-Light /XHeight 523 /StemV 0 /FontFile3 264 0 R @@ -2767,13 +2774,13 @@ endobj 266 0 obj << /Type /Font -/BaseFont /WQTCFS+FiraSans-Light +/BaseFont /AZHSOY+FiraSans-Light /CIDSystemInfo << /Supplement 0 /Registry (Adobe) /Ordering (Identity) >> -/W [0 [666 670 468 581 584 538 584 536 341 413 207 279 282 464 284 270 571 560]] +/W [0 [666 670 468 581 584 538 584 536 341 560 207 279 282 464 284 270 571]] /Subtype /CIDFontType0 /FontDescriptor 265 0 R >> @@ -2781,18 +2788,18 @@ endobj endobj 267 0 obj << -/Length 268 +/Length 266 /Filter /FlateDecode >> stream -x]QMk0 WJ6^rv;$ m?J1ГOٱ95Zyޝ-zJ GqPeYql4PU ɻ6x7')=&=Î5aKg_!Km#B]eT")i%nN:= v!Jj񯼊zy ߷ቀȒ rj9$SVPXS$D^Gٹ`H:Er"z4^e~i +x]QMk0 WJ6]rv=$ m?1xOzBSh!{wA*-NfvAi +K7;˲`nhi@ғw lq7')=&Þ5Kg_!K]#B^e\"47'qtUu%C ^M[ 7z|HpH@bIbyG9+fk8q߉quٹiJѕ0 endstream endobj 8 0 obj << /Type /Font /DescendantFonts [266 0 R] -/BaseFont /WQTCFS+FiraSans-Light +/BaseFont /AZHSOY+FiraSans-Light /Encoding /Identity-H /Subtype /Type0 /ToUnicode 267 0 R @@ -2817,18 +2824,18 @@ endobj xref 0 268 0000000000 65535 f -0000062968 00000 n -0000063034 00000 n +0000062935 00000 n +0000063001 00000 n 0000030086 00000 n 0000029914 00000 n 0000028284 00000 n 0000000015 00000 n 0000000060 00000 n -0000062817 00000 n -0000048949 00000 n -0000040397 00000 n -0000053775 00000 n -0000057799 00000 n +0000062784 00000 n +0000048950 00000 n +0000040398 00000 n +0000053776 00000 n +0000057800 00000 n 0000000105 00000 n 0000000184 00000 n 0000000300 00000 n @@ -3063,27 +3070,27 @@ xref 0000026469 00000 n 0000026585 00000 n 0000026701 00000 n -0000036469 00000 n -0000036557 00000 n -0000039565 00000 n -0000039788 00000 n -0000040058 00000 n -0000040552 00000 n -0000047936 00000 n -0000048162 00000 n -0000048545 00000 n -0000049108 00000 n -0000052966 00000 n -0000053189 00000 n -0000053445 00000 n -0000053932 00000 n -0000056973 00000 n -0000057192 00000 n -0000057461 00000 n -0000057953 00000 n -0000061987 00000 n -0000062204 00000 n -0000062475 00000 n +0000036470 00000 n +0000036558 00000 n +0000039566 00000 n +0000039789 00000 n +0000040059 00000 n +0000040553 00000 n +0000047937 00000 n +0000048163 00000 n +0000048546 00000 n +0000049109 00000 n +0000052967 00000 n +0000053190 00000 n +0000053446 00000 n +0000053933 00000 n +0000056974 00000 n +0000057193 00000 n +0000057462 00000 n +0000057954 00000 n +0000061960 00000 n +0000062177 00000 n +0000062444 00000 n trailer << /Root 2 0 R @@ -3091,5 +3098,5 @@ trailer /Size 268 >> startxref -63084 +63051 %%EOF diff --git a/quad/quadwriter/attrs.rkt b/quad/quadwriter/attrs.rkt index df0324dc..22a44b7a 100644 --- a/quad/quadwriter/attrs.rkt +++ b/quad/quadwriter/attrs.rkt @@ -1,18 +1,13 @@ #lang debug racket/base -(require (for-syntax racket/base racket/syntax) +(require (for-syntax racket/base racket/syntax syntax/strip-context) racket/match + racket/sequence racket/string) (provide (all-defined-out)) -#| -Naming guidelines -+ shorter is better -+ general to specific: border-color-left, not border-left-color or left-border-color -+ don't refer to specific output format, e.g. PDF or HTML -+ consistency with CSS style property names is OK if the concept is mostly the same, but usually it's not -+ default value for any missing attr is #false -+ measurement units are points by default -|# +(define (list->attrs . kvs) + (for/list ([kv (in-slice 2 kvs)]) + kv)) (define (cm->in x) (/ x 2.54)) (define (in->pts x) (* 72 x)) @@ -42,36 +37,52 @@ Naming guidelines (hash-set! new-hash k v)) new-hash) -(define :font-family 'font-family) -(define :font-size 'font-size) -(define :font-size-adjust 'font-size-adjust) -(define :font-color 'font-color) -(define :font-italic 'font-italic) -(define :font-bold 'font-bold) -(define :character-tracking 'character-tracking) -(define :bg 'bg) -(define :link 'link) -(define :href 'href) -(define :line-height 'line-height) -(define :hyphenate 'hyphenate) -(define :list-index 'list-index) -(define :no-colbr 'no-colbr) -(define :no-pbr 'no-pbr) -(define :page-number 'page-number) -(define :doc-title 'doc-title) - (define-syntax (define-attrs stx) (syntax-case stx () - [(_ ID (ATTR-NAME ...)) + [(_ (ATTR-NAME ...)) (with-syntax ([(ATTR-ID ...) (for/list ([attr-id (in-list (syntax->list #'(ATTR-NAME ...)))]) - (format-id #'ID ":~a" (syntax-e attr-id)))]) - #'(begin - (define ATTR-ID 'ATTR-NAME) ... - (define ID (list ATTR-ID ...))))])) + (format-id stx ":~a" (syntax-e attr-id)))]) + #'(begin + (define ATTR-ID 'ATTR-NAME) ...))] + [(_ ID (ATTR-NAME ...)) + (replace-context stx + #'(begin + (define ID (list 'ATTR-NAME ...)) + (define-attrs (ATTR-NAME ...))))])) + + +#| +Naming guidelines ++ shorter is better ++ general to specific: border-color-left, not border-left-color or left-border-color ++ don't refer to specific output format, e.g. PDF or HTML ++ consistency with CSS style property names is OK if the concept is mostly the same, but usually it's not ++ default value for any missing attr is #false ++ measurement units are points by default + +|# + +(define-attrs (font-family + font-size + font-size-adjust + font-color + font-features + font-italic + font-bold + character-tracking + bg + link + href + line-height + hyphenate + list-index + no-colbr + no-pbr + page-number + doc-title)) -(define-attrs block-attrs ( - display +(define-attrs block-attrs (display ;; inset values increase the layout size of the quad. ;; they are relative to the natural layout box. inset-top @@ -130,5 +141,4 @@ Naming guidelines page-margin-left page-margin-right - footer-display - )) \ No newline at end of file + footer-display)) \ No newline at end of file diff --git a/quad/quadwriter/core.rkt b/quad/quadwriter/core.rkt index 47ae36e7..e7d66bae 100644 --- a/quad/quadwriter/core.rkt +++ b/quad/quadwriter/core.rkt @@ -3,13 +3,12 @@ "render.rkt" "param.rkt") (provide render-pdf - para-break - line-break - page-break - column-break - bullet-quad - hrbr - lbr - pbr + q:para-break + q:line-break + q:page-break + q:column-break + q:hr-break + q:line-break + q:page-break (all-from-out "param.rkt")) diff --git a/quad/quadwriter/font.rkt b/quad/quadwriter/font.rkt index f998be51..dd76c73e 100644 --- a/quad/quadwriter/font.rkt +++ b/quad/quadwriter/font.rkt @@ -11,23 +11,14 @@ (define default-font-size 12) (define default-line-height 16) (define default-font-color "black") +(define default-font-features (list (cons #"tnum" 1))) (define font-paths (make-hash)) -(define (setup-font-path-table! base-path) - ;; populate `font-paths` table with font paths - ;; search "fonts" subdirectory in project for other subdirectories - ;; which are presumed to contain fonts. - ;; and link them to their family names & styles. - ;; this allows a flexible mapping from internal to external names, like @font-face - ;; note that all the semantics are derived from the file system - ;; not any metadata fields within the font. - ;; this is faster and easier, because you can just muck with the directory and filenames - ;; to change the font mapping. - ;; though it also creates the potential for mischief, - ;; if a font is named something that doesn't reflect its visual reality. - ;; but we are not the font police. +(define top-font-directory "fonts") +(define font-file-extensions '(#".otf" #".ttf" #".woff")) +(define (setup-font-path-table! base-path) ;; rules for font naming ;; "fonts" subdirectory on top ;; family directories inside: each named with font family name @@ -35,19 +26,19 @@ ;; and change the font files without disturbing anything else. (hash-clear! font-paths) (define-values (dir path _) (split-path base-path)) - (define doc-fonts-dir (build-path dir "fonts")) + (define doc-fonts-dir (build-path dir top-font-directory)) ;; run doc-fonts-dir first because earlier fonts take precedence (using hash-ref! below) (for* ([fonts-dir (in-list (list doc-fonts-dir quadwriter-fonts-dir))] #:when (directory-exists? fonts-dir) [font-family-subdir (in-directory fonts-dir)] #:when (directory-exists? font-family-subdir) [font-path (in-directory font-family-subdir)] - #:when (member (path-get-extension font-path) '(#".otf" #".ttf" #".woff"))) + #:when (member (path-get-extension font-path) font-file-extensions)) (match-define (list font-path-string family-name) - (map (λ (x) (path->string (find-relative-path fonts-dir x))) (list font-path font-family-subdir))) - ;; search for subdir in path matching style name - ;; note that this will work if fonts are contained in another subdirectory (e.g., real font name) - (define path-parts (map string-downcase (map path->string (explode-path (string->path (string-downcase font-path-string)))))) + (for/list ([x (list font-path font-family-subdir)]) + (path->string (find-relative-path fonts-dir x)))) + (define path-parts (for/list ([part (in-list (explode-path (string->path (string-downcase font-path-string))))]) + (path->string part))) (define key (cons (string-downcase family-name) (cond @@ -70,9 +61,7 @@ (define regular-key (cons font-family 'r)) (cond [(hash-ref font-paths key #false)] - ;; if there isn't one, try the regular style. [(hash-ref font-paths regular-key #false)] - ;; If there isn't one, use the default. [else default-font-face])) (define (resolve-font-path attrs) diff --git a/quad/quadwriter/layout.rkt b/quad/quadwriter/layout.rkt index 51302baf..45b5c7fb 100644 --- a/quad/quadwriter/layout.rkt +++ b/quad/quadwriter/layout.rkt @@ -8,7 +8,6 @@ pitfall quad racket/unsafe/ops - hyphenate "attrs.rkt" "param.rkt" "font.rkt") @@ -18,7 +17,6 @@ (define-quad string-quad quad ()) (define (q:string-draw q doc) - ;; draw with pdf text routine (when (pair? (quad-elems q)) (font doc (path->string (quad-ref q font-path-key default-font-face))) (font-size doc (quad-ref q :font-size default-font-size)) @@ -28,7 +26,7 @@ (text doc str x y #:tracking (quad-ref q :character-tracking 0) #:bg (quad-ref q :bg) - #:features '((#"tnum" . 1)) + #:features (quad-ref q :font-features default-font-features) #:link (quad-ref q :link)))) (define (q:string-draw-end q doc) @@ -66,18 +64,15 @@ (font-size pdf (quad-ref q :font-size default-font-size)) (font pdf (path->string (quad-ref q font-path-key default-font-face))) (+ (string-width pdf str - #:tracking (quad-ref q :character-tracking 0)) - ;; add one more dose because `string-width` only adds it intercharacter, - ;; and this quad will be adjacent to another - ;; (so we need to account for the "inter-quad" space - (quad-ref q :character-tracking 0))] + #:tracking (quad-ref q :character-tracking 0) + #:features (quad-ref q :font-features default-font-features)))] [else 0])) (list string-size (quad-ref q :line-height (current-line-height pdf))))) (define (->string-quad q) - (cond - [(q:line-break? q) q] - [else + (match q + [(? line-break-quad?) q] + [_ (struct-copy quad q:string [attrs (let ([attrs (quad-attrs q)]) @@ -88,42 +83,38 @@ (define (draw-debug q doc [fill-color "#f99"] [stroke-color "#fcc"] [stroke-width 0.5]) - ;; ostensibly it would be possible to control draw-debug with a quad attribute - ;; but that would potentially mess up unit tests (because something has to be inserted in the data) - ;; therefore controlling debug state with a parameter is cleaner. (when (draw-debug?) (save doc) ;; draw layout box (line-width doc stroke-width) ; subtracting stroke-width keeps adjacent boxes from overlapping (save doc) - (apply rect doc (append (pt+ (quad-origin q)) (map (λ (x) (- x 0.5)) (size q)))) + (apply rect doc (append (pt+ (quad-origin q)) (map (λ (x) (- x stroke-width)) (size q)))) (clip doc) (define pt (to-point q)) (circle doc (pt-x pt) (pt-y pt) (+ 3 stroke-width)) (fill doc fill-color) (restore doc) - (apply rect doc (append (pt+ (quad-origin q)) (map (λ (x) (- x 0.5)) (size q)))) + (apply rect doc (append (pt+ (quad-origin q)) (map (λ (x) (- x stroke-width)) (size q)))) (stroke doc stroke-color) (restore doc))) -(define-quad q:line-break quad ()) -(define lbr (make-q:line-break #:printable #f - #:id 'lbr)) -;; treat paragraph break as special kind of line break -(define-quad q:para-break q:line-break ()) -(define pbr (make-q:para-break #:printable #f - #:id 'pbr)) -(define-quad q:hr-break q:line-break ()) -(define hrbr (make-q:hr-break #:printable #t - #:id 'hrbr)) - -(define-quad q:col-break q:line-break ()) -(define colbr (make-q:col-break #:printable #f #:id 'colbr)) - -(define-quad q:page-break q:line-break ()) -(define pgbr (make-q:page-break #:printable #f #:id 'pgbr)) +(define-quad line-break-quad quad ()) +(define q:line-break (make-line-break-quad #:printable #f + #:id 'line-break)) +(define-quad para-break-quad line-break-quad ()) +(define q:para-break (make-para-break-quad #:printable #f + #:id 'para-break)) +(define-quad hr-break-quad line-break-quad ()) +(define q:hr-break (make-hr-break-quad #:printable #t + #:id 'hr-break)) +(define-quad column-break-quad line-break-quad ()) +(define q:column-break (make-column-break-quad #:printable #f + #:id 'column-break)) +(define-quad page-break-quad line-break-quad ()) +(define q:page-break (make-page-break-quad #:printable #f + #:id 'page-break)) (define q:line (q #:size (pt 0 default-line-height) #:from 'sw @@ -132,8 +123,8 @@ #:id 'line #:draw-start (if draw-debug-line? draw-debug void))) -(struct line-spacer q:line-break () #:transparent) -(define q:line-spacer (q #:type line-spacer +(define-quad line-spacer-quad line-break-quad ()) +(define q:line-spacer (q #:type line-spacer-quad #:size (pt 20 (* default-line-height 0.6)) #:from 'sw #:to 'nw @@ -182,25 +173,10 @@ (module+ test (require rackunit) - (check-true (q:line-break? (second (quad-elems (q "foo" pbr "bar"))))) - (check-true (q:line-break? (second (atomize (q "foo" pbr "bar")))))) - -(define (handle-hyphenate qs) - ;; find quads that want hyphenation and split them into smaller pieces - ;; do this before ->string-quad so that it can handle the sizing promises - (apply append - (for/list ([q (in-list qs)]) - (match (quad-ref q :hyphenate) - [(or #false "false") (list q)] - [_ (for*/list ([str (in-list (quad-elems q))] - [hyphen-char (in-value #\u00AD)] - [hstr (in-value (hyphenate str hyphen-char - #:min-left-length 3 - #:min-right-length 3))] - [substr (in-list (regexp-match* (regexp (string hyphen-char)) hstr #:gap-select? #t))]) - (struct-copy quad q [elems (list substr)]))])))) - -(define-quad filler quad ()) + (check-true (line-break-quad? (second (quad-elems (q "foo" q:page-break "bar"))))) + (check-true (line-break-quad? (second (atomize (q "foo" q:page-break "bar")))))) + +(define-quad filler-quad quad ()) (define (sum-of-widths qss) (for*/sum ([qs (in-list qss)] @@ -265,7 +241,7 @@ (define end-hspace (- empty-hspace word-space-width)) ; make filler a leading quad, not a parent / grouping quad, ;; so that elements can still be reached by consolidate-runs - (list* (make-quad #:type filler + (list* (make-quad #:type filler-quad #:from-parent (quad-from-parent (car qs)) #:from 'bo #:to 'bi @@ -274,7 +250,7 @@ (struct-copy quad (car qs) [from-parent #f]) (cdr qs))])])])) -(define-quad offsetter quad ()) +(define-quad offsetter-quad quad ()) (define (hr-draw dq doc) (match-define (list left top) (quad-origin dq)) @@ -290,8 +266,6 @@ (define (make-hr-quad line-q) (struct-copy quad line-q [draw-start hr-draw])) -(define bullet-quad '(q ((special "bullet")))) - (define ((finish-line-wrap line-q) pcs-in opening-q ending-q idx) ;; we curry line-q so that the wrap size can be communicated to this operation ;; remove unused soft hyphens so they don't affect final shaping @@ -301,7 +275,7 @@ (define new-lines (cond [(empty? pcs-printing) null] - [(q:hr-break? ending-q) (list (make-hr-quad line-q))] + [(hr-break-quad? ending-q) (list (make-hr-quad line-q))] [else ;; render hyphen first so that all printable characters are available for size-dependent ops. (define pcs-with-hyphen (render-hyphen pcs-printing ending-q)) @@ -355,60 +329,59 @@ #:draw-end q:string-draw-end #:to 'sw #:size (pt inset-val 5) - #:type offsetter) + #:type offsetter-quad) elems)]) 'sw))]))] [_ null])])) - (append new-lines (cond - [(q:page-break? ending-q) (list ending-q)] ; hard page break - [ending-q null] ; hard line break - [else (list (struct-copy quad q:line-spacer - [attrs (hash-copy (quad-attrs q:line-spacer))]))]))) ; paragraph break + (append new-lines (match ending-q + [(? page-break-quad?) (list ending-q)] ; hard page break + [any #:when any null] ; hard line break + [_ (list (struct-copy quad q:line-spacer + [attrs (hash-copy (quad-attrs q:line-spacer))]))]))) ; paragraph break (define (line-wrap qs wrap-size) (match qs - [(? pair?) + [(? null?) null] + [_ (unless (positive? wrap-size) (raise-argument-error 'line-wrap "positive number" wrap-size)) - (define line-q (struct-copy - quad q:line - [size (pt wrap-size (pt-y (size q:line)))])) - (define justify-factor (match (quad-ref (car qs) :line-align #f) - ;; allow justified lines to go wider, - ;; and then fill-wrap will tighten the word spaces - ;; this makes justified paragraphs more even, becuase - ;; some lines are a little tight, as opposed to all of them being loose - ["justify" 1.04] - [_ 1])) + (define line-q (struct-copy quad q:line + [size (pt wrap-size (pt-y (size q:line)))])) + (define permitted-justify-overfill + (match (quad-ref (car qs) :line-align) + ;; allow justified lines to go wider, + ;; and then fill-wrap will tighten thes word spaces + ;; this makes justified paragraphs more even, becuase + ;; some lines are a little tight, as opposed to all of them being loose + ["justify" 1.04] + [_ 1])) (apply append ;; next line removes all para-break? quads as a consequence - (for/list ([qs (in-list (filter-split qs q:para-break?))]) + (for/list ([qs (in-list (filter-split qs para-break-quad?))]) (wrap qs (λ (q idx) (* (- wrap-size (quad-ref (car qs) :inset-left 0) (quad-ref (car qs) :inset-right 0)) - justify-factor)) + permitted-justify-overfill)) #:nicely (match (or (current-line-wrap) (quad-ref (car qs) 'line-wrap)) [(or "best" "kp") #true] [_ #false]) - #:hard-break q:line-break? + #:hard-break line-break-quad? #:soft-break soft-break-for-line? - #:finish-wrap (finish-line-wrap line-q))))] - [_ null])) + #:finish-wrap (finish-line-wrap line-q))))])) (define (make-nobreak! q) (quad-set! q :no-colbr "true")) ; cooperates with col-wrap (define (do-keep-with-next! reversed-lines) ;; paints nobreak onto spacers that follow keep-with-next lines ;; (we are iterating backward, so the geometrically previous ln follows the spacer) - (cond - [(null? reversed-lines) null] - [else - (for ([this-ln (in-list reversed-lines)] - [prev-ln (in-list (cdr reversed-lines))] - #:when (and (line-spacer? this-ln) - (quad-ref prev-ln :keep-with-next))) - (make-nobreak! this-ln) - (make-nobreak! prev-ln))])) + (match reversed-lines + [(? null?) null] + [_ (for ([this-ln (in-list reversed-lines)] + [prev-ln (in-list (cdr reversed-lines))] + #:when (and (line-spacer-quad? this-ln) + (quad-ref prev-ln :keep-with-next))) + (make-nobreak! this-ln) + (make-nobreak! prev-ln))])) (define (apply-keeps lines) (define groups-of-lines (contiguous-group-by (λ (x) (quad-ref x :display)) lines)) @@ -436,9 +409,6 @@ (make-nobreak! ln)])) (cons ln reversed-lines))) -(define zoom-mode? #f) -(define zoom-scale 2) - (define (page-draw-start q doc) (add-page doc) (scale doc (zoom-factor) (zoom-factor)) @@ -470,8 +440,8 @@ #:from 'ne #:to 'nw)) -(struct column-spacer quad () #:transparent) -(define q:column-spacer (q #:type column-spacer +(struct column-spacer-quad quad () #:transparent) +(define q:column-spacer (q #:type column-spacer-quad #:from 'ne #:to 'nw #:printable (λ (q sig) (not (memq sig '(start end)))))) @@ -534,21 +504,21 @@ (when (draw-debug-block?) (draw-debug q doc "#6c6" "#9c9"))) -(define (block-wrap lines) - (define first-line (car lines)) - (q #:from 'sw - #:to 'nw - #:elems (from-parent lines 'nw) - #:id 'block - #:attrs (quad-attrs first-line) - #:size (delay (pt (pt-x (size first-line)) ; - (+ (for/sum ([line (in-list lines)]) - (pt-y (size line))) - (quad-ref first-line :inset-top 0) - (quad-ref first-line :inset-bottom 0)))) - #:shift-elems (pt 0 (+ (quad-ref first-line :inset-top 0))) - #:draw-start (block-draw-start first-line) - #:draw-end (block-draw-end first-line))) +(define/match (lines->block lines) + [((cons ln0 _)) + (q #:from 'sw + #:to 'nw + #:elems (from-parent lines 'nw) + #:id 'block + #:attrs (quad-attrs ln0) + #:size (delay (pt (pt-x (size ln0)) ; + (+ (for/sum ([line (in-list lines)]) + (pt-y (size line))) + (quad-ref ln0 :inset-top 0) + (quad-ref ln0 :inset-bottom 0)))) + #:shift-elems (pt 0 (+ (quad-ref ln0 :inset-top 0))) + #:draw-start (block-draw-start ln0) + #:draw-end (block-draw-end ln0))]) (define/match (from-parent qs [where #f]) ;; doesn't change any positioning. doesn't depend on state. can happen anytime. @@ -580,7 +550,7 @@ (add-between (wrap qs vertical-height #:soft-break (λ (q) #true) - #:hard-break q:col-break? + #:hard-break column-break-quad? #:no-break (λ (q) (quad-ref q :no-colbr)) ; cooperates with make-nobreak #:distance (λ (q dist-so-far wrap-qs) ;; do trial block insertions @@ -608,27 +578,21 @@ (raise-argument-error 'page-wrap "positive number" width)) (wrap qs width #:soft-break (λ (q) #true) - #:hard-break q:page-break? + #:hard-break page-break-quad? #:no-break (λ (q) (quad-ref q :no-pbr)) #:distance (λ (q dist-so-far wrap-qs) (for/sum ([x (in-list wrap-qs)]) (pt-x (size x)))) #:finish-wrap (page-finish-wrap page-quad (pdf-output-path (current-pdf))))) - - (define (insert-blocks lines) (define groups-of-lines (contiguous-group-by (λ (x) (quad-ref x :display)) lines)) (append* (for/list ([line-group (in-list groups-of-lines)]) (if (quad-ref (car line-group) :display) - (list (block-wrap line-group)) + (list (lines->block line-group)) line-group)))) -(define (handle-cascading-attrs attrs) - (resolve-font-path attrs) - (resolve-font-size attrs)) - -(define-quad first-line-indent quad ()) +(define-quad first-line-indent-quad quad ()) (define (insert-first-line-indents qs-in) ;; first line indents are quads inserted at the beginning of a paragraph @@ -639,17 +603,17 @@ ;; stick a pbr on the front if there isn't one already ;; because of the "lookahead" style of iteration (define qs (match qs-in - [(list (? q:para-break?) _ ...) qs-in] - [_ (cons pbr qs-in)])) + [(list (? para-break-quad?) _ ...) qs-in] + [_ (cons q:page-break qs-in)])) (for/fold ([qs-out null] #:result (reverse qs-out)) ([q (in-list qs)] [next-q (in-list (cdr qs))]) - (match (and (q:para-break? q) (quad-ref next-q :first-line-indent 0)) + (match (and (para-break-quad? q) (quad-ref next-q :first-line-indent 0)) [(or #false 0) (cons next-q qs-out)] [indent-val (list* next-q (make-quad #:from 'bo #:to 'bi #:draw-end q:string-draw-end - #:type first-line-indent + #:type first-line-indent-quad #:attrs (quad-attrs next-q) #:size (pt indent-val 10)) qs-out)]))) \ No newline at end of file diff --git a/quad/quadwriter/markdown.rkt b/quad/quadwriter/markdown.rkt index c610aff6..9530d4be 100644 --- a/quad/quadwriter/markdown.rkt +++ b/quad/quadwriter/markdown.rkt @@ -23,9 +23,9 @@ ;; markdown parser returns list of paragraphs (root null (match strs [(list str) strs] - [_ (add-between strs (list para-break) - #:before-first (list para-break) - #:after-last (list para-break) + [_ (add-between strs (list q:para-break) + #:before-first (list q:para-break) + #:after-last (list q:para-break) #:splice? #true)]))) (make-module-begin doc-proc) diff --git a/quad/quadwriter/render.rkt b/quad/quadwriter/render.rkt index 36c426db..5b2ecda7 100644 --- a/quad/quadwriter/render.rkt +++ b/quad/quadwriter/render.rkt @@ -5,6 +5,7 @@ racket/file pitfall quad + hyphenate sugar/coerce sugar/debug "attrs.rkt" @@ -13,9 +14,6 @@ "layout.rkt") (provide (all-defined-out)) - - - (define default-page-size "letter") (define default-page-orientation "tall") @@ -31,22 +29,43 @@ (define (replace-breaks x) (map-elements (λ (el) (match el - [(== para-break) pbr] - [(== line-break) lbr] - [(== column-break) colbr] - [(== page-break) pgbr] + [(== para-break) q:para-break] + [(== line-break) q:line-break] + [(== column-break) q:column-break] + [(== page-break) q:page-break] [_ el])) x)) +(define (handle-hyphenate qs) + ;; find quads that want hyphenation and split them into smaller pieces + ;; do this before ->string-quad so that it can handle the sizing promises + (apply append + (for/list ([q (in-list qs)]) + (match (quad-ref q :hyphenate) + [(or #false "false") (list q)] + [_ (for*/list ([str (in-list (quad-elems q))] + [hyphen-char (in-value #\u00AD)] + [hstr (in-value (hyphenate str hyphen-char + #:min-left-length 3 + #:min-right-length 3))] + [substr (in-list (regexp-match* (regexp (string hyphen-char)) hstr #:gap-select? #t))]) + (struct-copy quad q [elems (list substr)]))])))) + +(define (handle-cascading-attrs attrs) + (resolve-font-path attrs) + (resolve-font-size attrs)) + (define default-line-height-multiplier 1.42) (define (setup-qs qx-arg pdf-path) [define qexpr (replace-breaks qx-arg)] [define the-quad - (qexpr->quad `(q ((font-family ,default-font-family) - (font-size ,(number->string default-font-size)) - (line-height ,(number->string (floor (* default-line-height-multiplier default-font-size))))) ,qexpr))] + (qexpr->quad (list 'q (list->attrs + :font-family default-font-family + :font-size (number->string default-font-size) + :line-height (number->string (floor (* default-line-height-multiplier default-font-size)))) qexpr))] (setup-font-path-table! pdf-path) [define atomized-qs - (time-name atomize (atomize the-quad #:attrs-proc handle-cascading-attrs + (time-name atomize (atomize the-quad + #:attrs-proc handle-cascading-attrs #:missing-glyph-action 'fallback #:fallback "fallback" #:emoji "emoji" diff --git a/quad/quadwriter/tags.rkt b/quad/quadwriter/tags.rkt index adbf774b..b422d98e 100644 --- a/quad/quadwriter/tags.rkt +++ b/quad/quadwriter/tags.rkt @@ -17,43 +17,83 @@ (define-tag-function (p attrs exprs) ;; no font-family so that it adopts whatever the surrounding family is - (qexpr (append `((,:keep-first-lines "2")(,:keep-last-lines "3") (,:font-size-adjust "100%") (,:character-tracking "0") (,:hyphenate "true") (,:display ,(symbol->string (gensym)))) attrs) exprs)) + (qexpr (append (list->attrs + :keep-first-lines "2" + :keep-last-lines "3" + :font-size-adjust "100%" + :character-tracking "0" + :hyphenate "true" + :display (symbol->string (gensym))) + attrs) exprs)) (define-tag-function (hr attrs exprs) - hrbr) + q:hr-break) (define-tag-function (blockquote attrs exprs) - (qexpr (append `((,:display "block") - (,:first-line-indent "0") - (,:background-color "#eee") - (,:block-clip "true") - (,:font-family "blockquote") (,:font-size "10") (,:line-height "14") - (,:border-width-top "0.5") (,:border-color-top "gray") (,:border-inset-top "8") - (,:border-width-left "3") (,:border-color-left "gray") (,:border-inset-left "20") - (,:border-width-bottom "0.5") (,:border-color-bottom "gray") (,:border-inset-bottom "-2") - (,:border-width-right "0.5") (,:border-color-right "gray") (,:border-inset-right "20") - (,:inset-top "10") (,:inset-bottom "8") (,:inset-left "30") (,:inset-right "30") - (,:keep-all-lines "yes")) + (qexpr (append (list->attrs + :display "block" + :first-line-indent "0" + :background-color "#eee" + :block-clip "true" + :font-family "blockquote" + :font-size "10" + :line-height "14" + :border-width-top "0.5" + :border-color-top "gray" + :border-inset-top "8" + :border-width-left "3" + :border-color-left "gray" + :border-inset-left "20" + :border-width-bottom "0.5" + :border-color-bottom "gray" + :border-inset-bottom "-2" + :border-width-right "0.5" + :border-color-right "gray" + :border-inset-right "20" + :inset-top "10" + :inset-bottom "8" + :inset-left "30" + :inset-right "30" + :keep-all-lines "yes") attrs) exprs)) (define id (default-tag-function 'id)) (define class (default-tag-function 'class)) (define-tag-function (strong attrs exprs) - (qexpr (list* `(,:font-bold "true") `(,:font-size-adjust "100%") attrs) exprs)) + (qexpr (append (list->attrs + :font-bold "true" + :font-size-adjust "100%") + attrs) exprs)) + (define b strong) (define-tag-function (a attrs exprs) - (qexpr `((link ,(cadr (assoc :href attrs)))(font-color "MediumVioletRed")) exprs)) + (qexpr (list->attrs + :link (second (assoc :href attrs)) + :font-color "MediumVioletRed") exprs)) (define-tag-function (em attrs exprs) - (qexpr (list* `(font-italic "true") `(font-size-adjust "100%") attrs) exprs)) + (qexpr (append + (list->attrs + :font-italic "true" + :font-size-adjust "100%") attrs) exprs)) (define i em) (define-syntax-rule (attr-list . attrs) 'attrs) (define (heading-base font-size attrs exprs) - (qexpr (append `((,:font-family "heading") (,:first-line-indent "0") (,:display "block") (,:font-size ,(number->string font-size))(,:line-height ,(number->string (* 1.2 font-size))) (,:border-width-top "0.5")(,:border-inset-top "9") (,:inset-bottom "-3") (,:inset-top "6") (,:keep-with-next "true")) attrs) exprs)) + (qexpr (append (list->attrs + :font-family "heading" + :first-line-indent "0" + :display "block" + :font-size (number->string font-size) + :line-height (number->string (* 1.2 font-size)) + :border-width-top "0.5" + :border-inset-top "9" + :inset-bottom "-3" + :inset-top "6" + :keep-with-next "true") attrs) exprs)) (define-tag-function (h1 attrs exprs) (heading-base 20 attrs exprs)) @@ -66,35 +106,48 @@ (define h6 h3) (define-tag-function (code attrs exprs) - (qexpr (append `((,:font-family "code")#;(,:line-align "right")(,:font-size "10")(,:bg "aliceblue")) attrs) exprs)) + (qexpr (append (list->attrs + :font-family "code" + :font-size "10" + :bg "aliceblue") attrs) exprs)) (define-tag-function (pre attrs exprs) ;; pre needs to convert white space to equivalent layout elements (define new-exprs (add-between (for*/list ([expr (in-list exprs)] [str (in-list (string-split (string-join (get-elements expr) "") "\n"))]) - `(,(get-tag expr) ,(get-attrs expr) ,(string-replace str " " " "))) - line-break)) - (qexpr (list* `(,:display "block") `(,:background-color "aliceblue") - `(,:first-line-indent "0") - `(,:block-clip "true") - `(,:font-family "code") `(,:font-size "11") `(,:line-height "14") - `(,:border-inset-top "10") - `(,:border-width-left "2") `(,:border-color-left "#669") `(,:border-inset-left "0") - `(,:border-inset-bottom "-4") - `(,:inset-left "12") `(,:inset-top "12") `(,:inset-bottom "8") - attrs) new-exprs)) + (list (get-tag expr) (get-attrs expr) (string-replace str " " " "))) + q:line-break)) + (qexpr (append (list->attrs + :display "block" + :background-color "aliceblue" + :first-line-indent "0" + :block-clip "true" + :font-family "code" + :font-size "11" + :line-height "14" + :border-inset-top "10" + :border-width-left "2" + :border-color-left "#669" + :border-inset-left "0" + :border-inset-bottom "-4" + :inset-left "12" + :inset-top "12" + :inset-bottom "8") + attrs) new-exprs)) (define (list-base attrs exprs [bullet-val #f]) (define bullet-space-factor 2.5) (define em (dict-ref attrs :font-size default-font-size)) (define bullet-indent (* bullet-space-factor em)) - (qexpr (list* `(,:inset-left ,(number->string bullet-indent)) attrs) + (qexpr (cons (list :inset-left (number->string bullet-indent)) attrs) (add-between (for/list ([(expr idx) (in-indexed exprs)] #:when (txexpr? expr)) (list* (get-tag expr) (cons (list :list-index (or bullet-val (format "~a" (add1 idx)))) (get-attrs expr)) (get-elements expr))) - para-break))) + q:para-break))) + +(define bullet-quad '(q ((special "bullet")))) (define-tag-function (ol attrs exprs) (list-base attrs exprs)) (define-tag-function (ul attrs exprs) (list-base attrs exprs "•"))