refactory

main
Matthew Butterick 5 years ago
parent a33da903c6
commit f71a4fe1d2

@ -2385,35 +2385,38 @@ endobj
endobj
3 0 obj
<<
/Length 6310
/Length 6311
/Filter /FlateDecode
>>
stream
xœÕ]ÝŽd7n¾ï§è°,QÔøb7ɹ<08>ØwA.ìžöb<C3B6>8@b yý<79>RUwu¯xzë€b) Ú°gg†?G¢ÈOä§ðìéÿ¿ ô<>Òàùå·§Ðeü“ÿÓWçŸk üö”<^ÿã?ž¼KÏÿûôýß¿þÏ_^^ÿõOxþã<C3BE>O¾ÿ±ÿøÏOßÿé÷ðüçߟ~¼ý/¿_~Çï/ÿÉ¿øwü×­øéMRà97ÿüÓoOßÿ#<ýÛ¯OÿöwÞû@?@?~<7E>~ýdú)ôS ?øÌ¿+ýðìÿýù§zú‡ŸžþEE™œuâs€wuT…äZ<E28093>®°êJFQ؃U…Æêbeæ·xùœŠºÁÈ·©¸>$~Z5©y]Éa‰³:Q‰rU¢ÒO£ŸŸµð.#Šd]aÑaͽ_.Öò¿£ª)“»å ¥ºÂ 5çåÕ|ýœÝXU¹Õ»`ecÚ²r´x¹l^¿QSpô$Ð*Äáë§T<C2A7>HX“]«k!<21>…M^¬ßèçõŒ^nLþuƾu¹þ†WúEÕS76ÞJFk}pUvÅÏ?Ðz`3|8¾,óWU]è»È‹à)Éý!ª ÏÍ<20>ü2NÃák^\—£ òÝå_õJl3]^Öû’™Ó¯T²$\9 ÂàbÑÒøn­î†h<>‰.Ö=õ¯)<29>¢°×ëúú°ÆtÝ蜴ú¨-¹Da!ßa<E28093> ÀÞK³¢<C2B3>ôµØ<"hÐÍ9“wå<>¯œòÅžz ¦²‡ÓóÛÆR•œ««òNV6³—³ )<29>-okj¬®@<40>2ü¢¡<>ÂÈD<C388>Ý*FÕ2¦]ò'øÁ²ô©,-Ï©—}KƯéFMô´oAÔD÷ÔÃèdÁ%:äËÉ«!B)$[v¹®¡Tœy> aåZìkŸ>^–Ù.ruhmv p<>&†60¨VŸ(3β@]ãHHʲ'ߪÏK¢¦û±:D+KS à'Z¾½<C2BE>&—ó3„þ_¯Úg T*;oE:MåïÞ¬þõòsµ~üªj0Œp|Øá{yÿž,¾gÌ­ðª*ѪHÞ(’Ä„.Úä1UÊÓ<C38A>òŠ˜É‰û¸<C3BB>(¼Þ–Šâ­NX³+Ï×ϵžîQ@Âå$ T†¦âÑîìÁá-0ŒZê«ÐÝ•ëÀDMìÆð)<29>ù Qò¦}èTS*,ÉU«E<C2AB>¥ºƒÓH¿¬òi*èC©ÞO!uÌ«ŽÄw€sÂû‡}Ñ*B¢ÒK­}ýX<C3BD>ÑÕݹº—« ð~5±®·²>Üó½\îö¸èøö@W
ý8ªãwÕ°ÅË;ªf‰Öå|Э.#|”ÓJBuý)BȲ^/ ëÛšD<C5A1>×t,ÙÈTJhBIØJ¬-”ìš·ú¤$¤ˆ¢®18i'là©îy¯\Îz@QxÌ.[9˜7¢þ
y*#yÀçËõŸ&jKÉãÇ“><3E>ìáí]|~íBQ­D QÒ,ï%e·æRþzeéÖ!;Y¤nȾ&GÁèonÊ/ûZ<>Tª‰—Í>˜¢«Q\AÀÐÀ[QÂ.ÝnÊƃÕ!KqM”\Áõà<C3B5>ĦÒÏÇœb4oB ÿ«o­œ“ÿÉ´±3!^ov€ŽÚ·S5hGÀÈ™ ÉS.m¨~b Bör¹aøvY¦å<C3A5>¾Þàò×~Ý=þýS¾ (•A«Ã0¨(
[ò<03>ë ÌØXˆ6„]Q[廤œ\9X:ÉêÉÒ]´”4pç¨<C3A7>)i8<69>UÞ3Ñéâäbä>ÝI°§[>\]%1XíÝY(Ûm ËånIwasÒ|5+8¾N¡Ê%=CGþûõ󟣣ΜŒÅ§Z<C2A7>ògú¥ ›§íKÅHš}ø›%é_¾,úÆ<C3BA>̨Æt â^£¨Ø|3*›ÚÔ%K6ÝÉêÍÇ ÕÔ<C395>zôqn&Sî2ˆ>èƒ>Ì<>¬õaø¢þÍ[CŽVØmƒüËe}ˆá}b}Ô‡ê’Õ>TÂw;¢­!]´´Í—4ß( ùG…‡!Z2V ÐÑZJ™Z„…÷û±—÷Žõ|†ÓLi†€ºõ»ˆŽó'A¦rþ”\£2RvÛ@}½é¸&åº%]
®Èzhß_¡K¾mJRoÏéË ƒÕ¬ç\Ô¡;]©>6)× ý•ÎsÈ÷Öè×Òûoñ­R.|b:t®Þµ$Š_-3Ucêó;»z2žø™j÷õM¬‰†”I0˜±«)ô0ü1÷à·¿1€Ç/ÉGñ¹©GGñ5õ篦&ÏMUÛÑÖyoGÒíÀõ£yn.X·4€(å»±À÷‰—óT¹Á7ƽL­kïØ<C3AF>W|åháÔTú…ãÁieW-·øV<E ™$)01^å´7g¤ì;ŠJĵ!brñ\®q¿¨Ú«Œ©¤·‰Â÷¦Ý;È÷j<E280B9>•c.êfÀÕÅÞŠba*Žb¡rë0PÁ¶o
@%ž Üõ²åæÓÚ×5­‰Þ‰[s·õjª½åç06ÂacÔq‰ §ø´÷HÑåŠ:ʨ­ Ôs <73> <0B>T8å.BO'•äÃ¥d|OìÅÕ¤|K\¯sÙÊ>)p‰åºE€Ë| ¯·y*J¹3ºSÏ"Dq—hh˷¿ q{È©Õa£^r<>µ™¢H=ElÔ«}hîÁ-0¾ö*ç"<>Í=ºÆabH^ZìÅ@qÝéöC#Wš¤a1KÂ^_ƒq³t3¿e(X,à@LX”ËøôUtZ‰€¡'KI~„>9_¤üÈCô}y®Äb 3t( gçä”:¬ÅÀz‰êËc@©¹R”PêcrD©£RÜ•Ùùû-³¹j„<6A>æOÕÛ
5º†§Œ<EFBFBD>Ù6À÷ ‚øýîEf"òYò¢9vÖ¯Ùέ¹ûE—Äõ£<C2A3>HWl§ÂÔÝ¢J $[râZú¥VF.c`ek<65>óH¶rxÆ÷…ÅÂÆ+§ò\”.eqD‡b xxœŠ±P´Û
;ãÑÒ´m:Sv1ŽÜ:‹¹¸°±s“Ó¢=¡³ØÛ6‰ ûüùÜŸ›g¼<1C>ÈP:Ïê\°êé†Ñ»j“ƒaŒ}&s*j r†|µ¤è² Ï–÷ÊrJoƒž1Ô¯o-=Îb~Þ((±Îâ9gn˜ WfªÜ0—´Í¢LŸ#¡<><?È<>}ÌE€gScè¯)ÌEéó#ôPqÂ2pJ<A»<41>¡3 ,FŸÇ»µ´Î´s7@ÎZuéT€±¹B .Šê™g‹ó&GNÄ檸zô¯™rÌæ0<C3A6>9ʇéRØ,ÖÖÐ-ŒlàÂÁ³5CO<14>qåÆh>°¥O©Š!ä~w¹i˜Bhý¾s®ÞVÀbê$2»zG¥ºfä ³mwð5¿”í œaEçÏ…,õ²«R˜û <0C>ù/^œ³êÒ<C3AA>%ÚBgLNÅ£—sÁÊŒ}µ<>^NEiwY„>z9µ:ãN ½œ[· :ãYnð°pj‰½Yc¾TlF/½ï£—ÖgôU<C3B4>dÔ1q•X ÔA}ôÒÂ×yôr.i)PÇ<50>É<ziadK}ôr.ʨ£<C2A8>É£—¦F¾<46>9Š…º@_óèå ËLÀ~CPngœ.À£—»zµ`½<Œ<><C592>ÇébgêÚÕ‡-»Ö$šÃtü>¶”_h?A”ŠGÿ~“ûJlŽR®rÅ£tíh(—/I¢[œqªÏdõsÑÊUvší¹(]¶2Ú
Ü÷qÂ*«Ôû5çêmœñÓ29<32>Z6êasªÞÀY(©<>,ìêÁR<C381>Θÿ¼<C3BF> Y6]ïàŠævë9ƒP嘼´%¹ºl±º&<26>:º%*WŠpê,b,˾È™ÃƲÜ©ˆv¿ ì¼_„§¥¯²µ˜dH1`oä²",c$VLNVQÑÜÎ¥÷‹ã@sQKa3~ºÙj ¬DŸ{£ËTa³ÝŠ^Vnø-Ýb¡2cV÷MCèo¥JVrüÎÀY¯„cÛâgb#S5þíù<C3AD>U^™Síì9˸gÜŸrÖý¢ª“<16>þÝam.£$N™Ì FóÅTÔÚ©Kª˜˜ÍÀJ€Ð‰Ùd+×Q!RÞM¬Ä2Z¦¢t)ËRrEôð(©º$Ÿo…çŠåÔò°Q<C2B0>Ž#éCï<43>õ×lăýñl¡“Í=¸% Ctþ\ȲQ/¹*…¹Ý`°ƒ“×tS ×<>òÈàM<13>¦sA(ËJt9KÑee<19>Ÿ+îUĔވµ Æ,µ<>±<<3C>ÜïǦ¢¬XËrtALOV<4F>yòQ `>xólóT´:“WïÈŸŠ²`-cÒKKp¾INÕç,«Á%¾o¼ß2j-ì?Sív†¥è<C2A5>ºrjkX]ä¥$©· pÆ—qbh{¼Ñ»*~c{²:°ûõ!
xFP¥Ÿ7ñн?¢î%ÅøµC—08°-,¤Ú%K[j-UYJýÝK #ÖbàÐ%+˹³ón£˜"ŠYðæêm…šÅšÆ Ù¦ž¬uÜ<75>ÍÔÛ5­º·ÍåÐ1%Ú4Cˆ Y6êå<C3AA>N=ºhÆýæbL^ÊW˜WV<57>«K+<2B>Áe“ Œ¹ÃQÊÀnA³K
z8n{¯ðŽ6ød:xÉ Û;«>ÍÚ[˜g¢Œ8Ë(}i 9Y™µ+ v²©(+Š4&ðBÉÞÕ“—¡<E28094> ºÞÙüÒU/d¦¢ÖŽ^¦à@XÔÚdÖ8@Á©( Š´
ãÄÀVNYåX¨Ì<EFBFBD>ÖÒ`’¹ß.3Ž¬*¹}g¨ŽŸçéióžŽå'}Š´Ÿ¶<C5B8>êø¾ÄÏÿx'¦äÄ o?{éýèè]¢¸yXLœôg/Z¦ÊÁaº®CŒýE= +™‰JÜVkIÒrsE<È•ya@ƒSQº$iÕ;1Õ|x˜Âš8Uo+¸Ž«@ M°)BíÜ<C3AD>Sÿ¡P•Aðà`Y#…¢]=Š™Á¡aa!ƒ GÛgUC©}v.X—)€«0I”2†ÁU˜ôé RzìMsëÖ Röy7§”>ïöE€ZÊ?Æ9;œ
*®J~µâãg{Q2wõ$eH41ðuäAÐR Œ‰©ùÁ! “w!H Ê‚~Œ"½<>¥5¸x •ÙÇøý }“nñÍBÐÜã«D¿m¶Œ—ëŸÃÐøx ¬ÏøJ_ÿñNäß"9Ñ~ΓëÇLF/¹DIœn.ÊuâSQk)Á<>§Œm¬L¾S¬ÉV®c +è+KÙöT”.Y¥Ä^’ôðÈjéHà\½­P)&nåÔò°z™ ¤§êíœA(ýq<C3BD>]=Þ¡BÏžÀ?mçB<42>zTfKan7䌬Ę¼´ Úã¹`]v.©$Q ³æ‚<16>^zèOe˙ÚÑK<18>0¸”ʨ Y"cIŠÄo8'1q±¢ k0p<30>©<>3¾ñ†s Çý¢ÆösQk)È0÷o +qÌÂÌEYp<59><51>¢um­Þ…£hx ž™]ó&ü¸p”xÆÅ|Ó‰™ª· œÕ{ƒ·õ!S97ɇö×Á÷Ýû<C39D>u¿$®¼)uœ9XF™SAÒZÖ²4Z†-ŒLÜ2,Û¸Ž³¬4¢ä^åª"ôù…¹(]βÚ\ÃÀãPjšäô­<C3B4>³è먬÷ô$ó*÷~„™z{ g wÀïêAHbN´'pÆöp.dÙô”Óß)…¹Ý€³˜““—vÔþÎÐ\®.Á@€ë8~$ÛK9Øà ý «3‡%Àß¼ŸªîƒœÏQ¥:.g ¬¥Ò*I»â7[1Ë ±Øãf¡] ²•z¡?>µ– <20>+h^QVR=ÜWÔT”AZ£¶¶A:¥?xé³xÆ0+åÖU<>«·3HÇüYÌ輫c1º¤U¶ JƠ˦^¤¼³ºLÕ3Ç鸌ŒçÖÜý¢pôËS÷Þ<11>T~pX>P×¥¥þN¸);·ÕZ¢´êû»NVV:´E‡ê¥µà¼§bcœ[rúVH?.c÷ô$¸3õö@ê°·ƒlë@î ‘Î¨=‘ºÞù~.d™µ¾Kan7¤scòÒV: ª¸ît»¸v elžÚQ% "²aq™ïN§bÕ;òæŒHË
Ë ¨<>±ÌáÐkÍÆ5Cv${W“Å::HÖ;«ôV$g¯%-Ë͵*m[]+ Pl—¶ªÅÀ¦D7+“\#0ÑPyd“ê`æš?a™ÍÅrq1IŽß×€J<µdl®GØG;
<EFBFBD><EFBFBD>ÇÏø a/~ÿÇ{±Ä1»8UÏž¹,´Aë±>L!À õ<C2A0>7¦îd´:Rù•cùH]K]ؤ6°2c'Ò•­\G]ÆÏŠ§¹®•ü ´H4³îmwG´ðKö¦Áü|s¿Ùœ©· Å/qU®ˆ6õ Õ¹
KqODnëÐÌoeHKr3@‹ŸQÙ•=&­Œúq*X·»Úó3jR°Õ-U=?‰&,ÍEtg@¹QÂË:º3¤BU<Á”QŽìxY¡Ò<E28093>tFÆÜ.›‹²šÚ à$V“<56>EðtŒ¥¿”6µ–í,ç1Æh`encŒq*Ê<£@Ÿm¾(SwãQ(ÔÏø®·m†`ˆ®H'ûÖØójós½»:)s Ò*Û;ãò2Ô¼0¸3ˆ²¢¹:Êwm™)Äç’ô/sé0˜…aÅ;*ÅQkÉËZî©ž…•­öTO¶rÝ &PÎCñÒÀJ€ä0 Jw3FwjãÙ<!œ-.A½­P0žBæGÝvõdJŽTœ«· yДìêÁ<¨Mæ܃æ{†·«G™3[Šr»á`Ñ79$/­w<C2AD>b ¸ît{—¡õ>
ƒŽßÅJÂ^_D^bæ·Ž¼¬Œg-|ZÒWÑi馇N¦e`)úÔɴ梌00Dß™ÚæJ¬f.Ëc.ÒÂÙ9¹$¥G0¹¨ÈíA3+EIå)×ÖßßRÇ¥¶>ÙzÂ2` }²äø<C3A4><C3B8>©P£kxjÉب—]Ai™mLñÈ.ˆßÿá^ä™]nñŸ«gO^°së„)~h'‰ëGÿ*.ú˜&–•@I¶äĵôemƒZXÙZçï•­\7ÉC[6r¹ÄT$sQºCŠ<>àáqŠ¯/ArÄVاmÓ&æ©ó©z{@g1—ÞⱫyD:¢ö„ÎúSUÛú³¡KRŒÛ 8ãKp1 /-<>⟸êt[–£ï}þçwºg)[Ä^ÆWAŠ.ëØËJ<[Ü/*Ë)½Ñ&C ÁüÊÂ\‰Õ„bL4S%áÊ\[µ7L%­E³(ÓïT{:àÒi*Ê<¦”¼¬kj .É«ú|b#TÜo™ ïR‰'h·3tÆm­bôy¼[Ks>J~Ý9k•{c7u"OIGQ={61,Λ9[gy;Ø•º½Þê0<C3AA>9ʇéZ2±Ú\<‡ïÕÀ…ƒfá(dH®‰Ç¸òûD|`KŸR7cŠ¤²o˜Bhã¾sªÞVÀ7B×pj}XKe©®Ù9ÃÔâ¶y_óKYÑžÀVtþ\ȲQ/»*…¹ÏЙÿ¯Û¬K?&ãÙK@ŠÐ Ö%£B1J¢´», (¸swe~<7E>s‡©uë¸ËŠ N-q4kL—ŠÍì¥÷ýukù…ê,I²â.—ÌwªÄjî²úãñ¾Îè´×R—µØYÙ-Œl”áGÉJéK
õh)˜yŽb¡2sYo>Ÿ°Ì†ÿþ¼ ÜÎ83nU!ÜoàÕ.i<>mƒÓñî©=g£\v­I>´'-Ãþ•A€âjÅÅ£¿É}%6G)W¹âQºv4”Ë—P­<50>3Nõ+«SÑÊU<1C>¬TÎøéáÞ÷q¿U6DQiôkNÕÛ
8ã·<EFBFBD>˜ÞnWOòûÈ(¬©=€³PÒYØÔƒ¥:)ôì œ…ÖF%°§CÁƒ+R˜Û­ç B•còZjïþLë\®.A¬®I§Ž2»¿ƒ'œ:(˲ƒbæ°†²¬w*¢Ý/¯ÁqxZ šñ[ZXL2$æï<>|SQVŒeŒÄŠÉÉjÎ2*šÛ¹tã~Q—q ©¨¥°úèJ6Ù@èsot™J2@͘Ú‹^Vnøo±P™³ŒÚ7 é<>‰ÕÖß œý icu
y*#yÀçËõŸ&jKÉãÇ“><3E>ìáí]|~íBQ­D QÒ,ï%e·æRþzeéÖ!;Y¤nȾ&GÁèonÊ/ûZ<>Tª‰—Í>˜¢«Q\AÀÐÀ[QÂ.ÝnÊƃÕ!KqM”\Áõà<C3B5>ĦÒÏÇœb4oB ÿ«o­œ“ÿÉ´±3!^ov€ŽÚ<C5BD>Sõxï€C<E282AC> P·¶t…·„ ìårÅðí²NË%#}½æ¯ /ºI"»œ<qÿ)ß P˜Ê ÕMbU…-‰ù<E280B0>vFfl,Ä<>
®°­òeRN®,<2C>ä?5eé.ZʸuÔÆ¿”5È*ï©hGƒt<C692>r1r£î$ÚÓŠ-A/åHUIŒ V{wJ·EGÆr¹\Ò]AØ\¤<_ÍÊŽïS¨tIÏÐ!‡ÿ~ýüçèìTÁañ©V Äå™~©@ÂæiEûR1fþæ_Iú—µ/¾1$3¬1<C2AC>ˆ¸×(ª6ߌʦ6uÉMwG²zóqB55¤}œ›Ñ”» ¢ú 3$k}¾©³ÁÖ<C381>£vÛ!ÿrÙAßdxŸ<78>Xõ¡ºdµ•ðÝŽhkH-rEmó%Íð'ªhþQáaˆŒƒ0tôŸÆJ<%aáýìå½¥@=Ÿáô"Sš!ÀîEý2¢ãüI<C3BC>©œ?%רŒ„ÝvP_¯:®I¹nI—ÚXèÒ<C3A8>‡o»ÔûsúrÂ`5ì9uèNWª<57> BÊ5CÁD¥ó²à=„5úµôþ[|«” Ÿ<0F>«w-‰âWËÌDÕ˜úÏ®žŒ…G~¦Ú}}k¢!e fìê@
= Ì=øí¯g àñK²ÄQ|nêÑQ|Mýù뇱‰ÃsÓDÕv´uÞût[pýèž Öí à¢$Jùr,ðE†pâÀå<Uîð<C3AE>qd/SëÚ;ö£ÛèÃß9Z85•~ãøEpZÙVË=¾OûEQB&IÊ×n9íÍ)ûŽ¢qmdˆ˜\<—kÜ/ªö*c*ém¤ð½ëF÷2ÄSùÀý¢Z§å˜º™ð_õA±÷¢X˜JG¢ƒ£X¨Ü; T°í P‰'(w½l¹ùôß…öuMk¢wâÞÜm½šjïù9Œ<39>p˜À˜$u\bÃ)íýRt¹¢ Ž2ªÁSk(äB'•$N¹<4E>ÐÓI%ùpéß{q5)ßWÇë\¶²<C2B6>
\b¹nà2Èë­Hù@žŠRîÄŒîÔƳQÜ&šäò­ð/HÜrjuب—$aEí€A¦(ROEõjŸš{pKŒ¯½Ê¹ˆe£^ì“@s<>n<06>1C˜’—»@1P\wº ÅÐÈ•&iXŒà°×× `Ü-ÅÌo
 8å2>}<15>V"`èÉÒb¡OÎ)?2À}Dž+±à <0C>ÊÂÙ9¹$¥k1°^¢úòP*D®%”§ú˜ÝQê¨wevdþ~Ël®a ùSõvÆ¥B<C2A5>®á©%c£^v¥e¶ 0Å=ƒ ~ÿ‡{‘©ˆ|¼hŽ<68>õk¶skî~QÅ%qýè_ RÅÛ©0u·¨(É–œ¸–©•‘ËXÙZ'=­\žñ}a±°ñJª<¥ËYÑ¡§b,Fí¶ÂÎx¶4mŽÄ”] #÷€Îb..lìÁÜä´hOè,¶à¶Í@bÃ>€>÷çfÀ/ä¥%2”N´:¬zºaô®Úä`cŸÉœŠZƒœ!_m)º,CΰijeÀý¢²œÒÛ g uÄëcK<63>€³˜ ·
J,†³xΙæ•i…*7Ì%­E³(ÓçHh`$ÏòksQà@¥ÃÅÄÔús
sQúÄÀ=Tœ°Ìœ@¤OÐngè (Ñçñn-­Ç <0A>³V]:`l®Pƒ¢zæÀYÄâ¼É±¹*®ý+Gæ³9LcŽòaº6µõt #¸ppÀ¬CÍ<43>ÇÅc\¹1šléSªâf¹ß]n¦Z¿ïœ«·p†˜:‰Ì®žäF©®Ù9ÃĬCÛæ|Í/eE{gXÑùs!ËF½ìªæ>Cgþ'笺ôc‰¶ÐY§Ñã8¬ÌØWûèåT”v—E裗SQk ´àÑ˹uË à §Ø5æKÅfôÒû>zi`-xF_IF@ÓGQ‰Å@äÐG/-|<7C>G/ç’–˜Ì£—F¶ÔG/ç¢ €:ú˜<zi`jäû˜£X¨ Ôñõ7<C3B5>^ž°Ì ág0åvÆéb <z¹«W öÑËÃØøxœ.v¦®]}زkMò¡9LÇdKù…öDÉ¡¸xôï7¹¯Äæ(å*W<J׎†rù$ú°eÀ§úÌV?­\U`§ÙžÒe+£­À}'¬²a°J½_s®ÞVÀ¿-“Ó©õa£ö79§ê휅’úÈ®,ÕI¡gOàŒùÏÛ¹<C39B>eÓõ®Han·ž3UŽÉKËãH!P«ËF«kÒ©£[¢bp¥§Î"Ʋìû <C3BB>œ9¬a,«Á<C2AB>Šh÷ ÂÎkðExZú p [I†öF¾¹(+Â2FbÅäd5aÍí\ºq¿¨14µ6ã·k™­ÖÀJô¹7ºL%Y­èeå†ß2Ðm)*3auqß4„þVªd%Çï œõJ8¶<38>a)~&&1"1UóáßžZå•9ÕΞ³Œ{Æý)gÝ/ª:iñèßÖæ2Jâ”ɼ`4_LE­<45>º¤
<EFBFBD>‰Ù ¬<08>˜M¶reÙÄJ,£e`*J—²,%W$I<0F>R<EFBFBD>ªKAòùV(p®XN-õè8(XÍF<ØïÁ:ùÐ܃[Â`1DçÏ…,õ«R˜Û 1È1y-A7Å@qÝ)? .ؤa1Ñi:´ˆ²¬D—³]ÖQñxñ¹2à~QELé<4C>XË`ÌRËÉý~l*ÊŠµ,GÄôdõ˜'惗±8Ï6OE«3yõŽü©( Ö2&Í1±´çäT}β\âûÆû-³¡ÖÂ~ñ3ÕngXŠþ¨+§¶†ÕE^Jg|'†¶Ç;½«â7¶',«»_¢€gEQúyÝûS!ê~QRŒ_;t ƒÛÂBª]²´¥ÖR•¥Ôß½´02Ña-]²²œ;;ï¦1Š)¢˜o®ÞV¨Y¬iÜ<69>mêÉZÇ ÙL½=P³ØªkyÛ\}S¢=A3„èò¹<C3B2>e£^(éÔ£<C2A3>fÜo.Æä¥|e‰ye¹º´\6ÉÀ˜;¥ ì4»¤ ‡ã¶÷
ïhƒO¦ƒ—œ ±½S±êÓ¬½…y&ʈ³ŒÒ—“•Y»Â`'Š²¢Hc/”ì]=yÚ¢ë<C2A2>Í/]õBf*jíèe
„E­Mf<4D>œŠ² H«0.I lå”UŽ…Ê i- &™ûí2ãȪÛw†êøyžž6ïéX~Ò§Hûi¨Žï Aüü<C3BC>wbJN òö³—Þ<E28094>ŽÞõ!Š‡ÅÄIö2 ÕaŠ¡¦Ká:ÄØ_Ô³°™¨Ämµ$-7Wă\™'48¥KS͇‡)¬q ‰Sõ¶ë¸
Úba!ÔÎ 9Uññ:U™n †15R(ÚÕ£˜)º2¸p´}V51”Úgaçu™¸
“D)c\…IŸ~Ñ ¥ÇÞ¤1·nÝ %`Ÿw3p*@éón_¨¥ücœ³Ã©€s¿¨âªäW+þ1~¶%sWOR†ÔA_G-ÅÀ˜˜š²°1y´ ,èÇ(ÒÛXZƒGP™}ŒßÚ7 áß,Í­10¾JôÛfËx¹þ9 <0C><>ÇÀúŒ¯ôõïDñ-íç<¹~,ÁdôkA”Äéæ¢\×ñ!>µ– yÊØÆÊä;Åšlå:²‚.p(0°²ämOEé2<C3A9>UJì%I<0F>¡–ŽÎÕÛ
•b"áVN-«'˜™@zªÞÀ„ÒØÕƒàz!ôì œñÓVp.dÙ¨Ge¶ævCΘÁJŒÉKÛDÊ  ÖeàJµ€0k.hÑ襇þT¶œ9¬½„ñ£<>Kù¡lq<6C>ÚP<C39A>%2¶™¤Hü†s+
²×™*±o8—pÜ/j¼h?µ–‚ s¿ñ¶°Ç,Ì\”Eú(ºY×Öê]8Š†Çà™Ù5oÂÿgp<16> GIñ‡g\Ì7ݘ©zÛÀY½7x[2•s“|h?||¿Ñ½ßY÷KQáÊ0ºÊ™“<E284A2>e”9e$­e-K£eØÂÈÄ-ò<C383>ë8ËJs!JîU®*BŸ_˜Òå,«ÍU1 <<J¥¦¹INß
9¾ŽÊzOO2¯rïG˜©·rzü®„$æD{g|açBMO9ý<39>R˜Û 89É1yigIíï Íåê ÔѸþ€ãG²½”ƒ­Îкj9sXœñÍû©ànA1Èù¼eYªãrÆÀZ*­´+Žp³³¼=núЕ [y ú³èsQk Ò¸‚æe`%ÕÃ}EMEY¤µA0jakd¡sQúƒ—>»ˆg ³¹RnýQÕ¹z;ƒt̟Ōλ:£AZeÛ t<C2A0>a ºlêEÊ;û ËT=sœŽËÈxnÍÝ/
G°¼1uïÑè@å‡åu-QZêï„[™²Cq[­%J«¾¿ëdae¥C[t¨.QZ Î<C38E>àáq*6ƹ%§o…Ôaðã2vOOb€q<E282AC>;So¤{;ȶäéŒÚ©ë<C2A9>ïçBYë»ævCê09&/me¡Ã ŠëN·aZÆFàY U² r!k—ùît*V½#a.Ɉ´¬<C2B4>±œ€ËM°Öl\3dA²w5iY¬£ƒd½³¹JoEröZÒ²Ü\«Ò¶Õµ²Åvi«Z lúà@t³2É5Òs •G6©f®ù–Ù\,“äø<C3A4>ñ3~ ¨ÄSKÆæz4Œy´£ùxüŒßöâ÷¼K³Sõì™ËB´ëà Zycê^@F«#•_9<39>ÔµÔe9ŒIj+3v"]ÙÊuÔeü| xšëZÉOJô@3ëÞöqwD+¿d/húðÌÏ7÷Í™z{WåŠhSR ‘«°÷D´˜¢+á¶ÍüV†´$7´ø‰1•]ÙcÒʨ§u»«=?£&[ÝRÕó“hÂÒ\Dw%)¼¬£;C*TÅLåÈŽ—õj)ÝYAgdlÉýá²¹(«©ÍNRa5ÙYä^) OÇXúKisQkÙÎrVæ6Ƨ¢,À3
ôÙæ2u7…B]ðŒïzÛ¶i†èŠt²o<C2B2><6F>1¯6?×»«c2— ­²m°3./CÍ;S<>;ƒ(+š«£|×™B|.Iÿ1—ƒYV¼£R\µ¼¬åžêYXÙjOõd+×Í`å</ ¬H#¢tg0ct§6žÍÂÙÑâÔÛ
ã)d~ÔmWO¦äHŹz{À`<60>MÉ®̃ÚdîÁ-a0h¾gx»z”9³¥(·}“CòÒz(ŠëN·wZï£08áø]¬$ìõEäe)¸(f~ëÈËÊx6Ч%}<15>N`zèdZ¢O<C2A2>Lk.Ê<43>©m®Äjæ²<æ"-œ<>“KRê°x“‹ŠÜ4¹R”Pžrmýý½¹(u\*`ë“­',3&˜Ð'KŽß˜
5º†§Œ<EFBFBD>Ù6À<14>ìøýîEžÙåÿ¹zöäe ;·ŽA˜â‡v¸~ôo©â¢<C3A2>ibY ”dKN\K_Ö1¨…•­uþ^ÙÊuc<´ea#—KLE2¥;Ñ¡§øú$Gl…<6C>ñ`pÚ6áabž:Ÿª·tsé-»z<C2BB>GA¤#jOè¬?Uµ­?º$Ÿ݀3¾òÒ(þ‰«N·e9úÞçop¾q§{r°Eìe|µ¤è²Ž½¬Ä³eÀý¢²œÒM`2ÔA!üap¿̯,Ì•XM(ÆD3U®ÌµU{ÀTÒZ4 ×Iµ§.<2E>¦¢ À3`êAÉ˺¦ÆàR¼ªÏ'†0BÅýÙð^!•xv;CgÜÖ*FŸÇ»µ4ç£ä×}<7D>³V¹7vS'ò”tÕ³gÃâ¼É‘±u·ƒ]©ÛÑë­Ó˜£|˜®%«ÍÅsqønQ \88`ŽB†äšxŒ+¿OĶô)Uq3¦H*û†)„6î;§êmœq#t §Ö‡±Têš=<3D>3LÍ!nwð5¿”í œaEçÏ…,õ²«R˜û <0C>ù/øºÍºôc2ž½¤ `]21*£$J»Ë"¸€‚;q—Qæ×8w˜Z·Ž»¬øÑàaàÔG³Æt©ØÌ^zß_W0°_¨Î$+î²xÉ|§J¬æ.Ë¡?oáëŒ.Hkx-uY<59>•ÝÂÈF~”¬´˜¾¤P<C2A4>&‘‚™§á(*3—Åñæó Ëlø¿èÏ ÊíŒÓ1ãVÂý^-èrÖØ68_àžÚs6ÊeךäC{Ò2ìOP(®¦Q\<ú÷ÜWbs”r•+¥kGC¹| Õ8ãT¿ò±:­\UPÈÉJUàŒŸî}÷[eC•F¿æT½­€3~Û‰éívõ$¿<>ŒÂšÚ8 %<25>…M=Xª“BÏžÀYhmT{:<¸"…¹ÝzÎ T9&¯¥öîÏ´Îåê²Äêštê(³kñ;x©³ˆ²,û1h fk(Ëjp§"Úý‡§• ¿¥…Å$CbñÞÈ7eÅXÆH¬˜œ¬æ,£¢¹<C2A2>K7îušŠZ
¡<EFBFBD>®d“ „>÷F—©$ÔŒI ½èeå†ßñv° •9Ëø1 <>þ<18>XmýÍÀÙÿð‰c©
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
­˜w\TG×Ç甽KPÁ5FX@,Ø[b
*¢ˆŠ`<17>5{,`CÅX±±D5¶XclOì½;¯šw^¥>I>ï{þ¸;;÷Ì™3çܽŸßwA0 È×Ðo¨w;ï€aåûõë”:UKwÕÝfعèNBwÝ‚º µŒçβõ›Ò"„<>]¾ÔkþËìÜ­ƒÏöÛ•HýH¶+.lòxù[©ÒW*WªÖÀ¯Ÿ_<C5B8>ß(_7ï o·>ƒGMÝÇÍ«T¯ÒnU*U®RÞz©Zέ}ƒGù èíÖpðð«¿ßà·ö¾ðëãíÖ®g…ÔŒÝRSvû<76>ò‡§ÏàÞ¾M||üFR) P<>°¶Â^äùEá EAa(.JˆÂC”¥EÑWôý…Ÿ üÅ@1HˆÁ"P CÅ0$†‹`1ILSÄT1ML?ˆb¦øQ̳Å1WÌóűP,E˜b‰X*–‰å"RD‰b¥X%V5"Z¬ëÄz#ÄÏbŸØ/ˆdqDÇįâ¸8!NŠS⼸ .ŠßÅ%ñ‡¸,®ˆ«âš¸%n;⮸'î‹â¡x$'â¥x%^ %tñ^ü©<>C `<60>`<>ò‚&°ƒÏÀòA~(àÁ îP ŠC ( P*Ae¨_ÂWPªAu¨žPêCøB#h M )4ƒæÐZB+h m -´ƒöÐ:B'è ] +t= ~0†A ‡`<08>0FÁhcaŒ‡P˜a!,‚ÅáK`),ƒåVÂ*Xë!6ÀO Û!~†}°ÀAøA"†$H†#pŽÁ¯pNÀI8§á78œ‡ pîÃx/à%¼‚× @‡x¼…wðþ¼¨¡ íð3´Ç|˜  :bA4c!ü ãX<04>Ðaq,<2C>KaMükamü¿Å:XÖÇø6ÄFØ;cìŠÝÐ »cì‰>è‹}±öG?€þ8aÆ@Cqᜈßã$œŒSp*NÃéøÎÀ™ø#ÎÂÙ8çâ<œ<> p!.Âņá<18>Kp).Ãå‰Q¸Wâ*\<5C>k0×â:\<5C>1¸Â<7F>¸ÜŠÛpîÆ=xÁC˜ˆ‡1 “ñÅcø+ÇxOáiü ÏàY<‡çñ^Äßñ^ÃëøüoàM¼…·ñÞÅ{xàC|„<>ñ >Ågø_àK|…¾Åwøÿ"A@HDLòP^ÒÈDô!'r& ¹<>+¹QQr§bTœJPIò RTšÊPY*Gå©U¤JT™ªÐ—ôU¥jT<6A>jPMªKžT<C5BE>SjJͨ9µ ÔŠZSOêEÞÔú<E280BA>ùR_êGýÉ<C3BD><06>? ¤A@ƒ)<29>†ÐPFA4œFÑhCci<1C>§Pš@é{šD“i
M¥i4<EFBFBD>fÑlšCsiͧ´<>Ñb
£pŠ %´ÑrŠ¤(ZKëh=ÅÐÚJÛ(–¶ÓÚI»h7í¡½Gñ”@?Ó>ÚOè ýB‡(S:M¿Ñ:K—èúýI7è&Ý¢Ût‡îÒ=ºOè!=¢Çô„žÒ3zN/è%½¢×¤H§÷ô F&flöœ‡ó²;rA6s!þœ ó\„<>Ø™-ì®ìÆEÙ<45>q).Íe¸,—ãò\<5C>+r%®ÌUøKþŠ«r5®Î5¸&͵¸6×ãúÜ€¿ã†ÜˆsnÊ͸9·àÜŠ[snËí¸=wàŽÜ‰;sîÊÝØ»sîɽ؛{söa_îËý¸?ûñöç<C3B6><ˆx0ñHÅ£y <0C>åq<žCyOäïyOæ)<•§ñtþ<74>gðLþgñlžÃsy/æ0ç^ÂKy/çHŽâ¼Wñj^ÃѼ×ñzŽá üoäM¼™·ðVÞƱ¼<C2B1>wðNÞÅ»yïå8Žçþ™÷ñ~>Àù8Ÿà“|ŠOóo|†Ïò9>Ïø"ÿΗø¾l2¹iö¦ÒZW® ™Òͬœµè¥ËWê»,®¦<C2AE>“)5µz)nZó"Í:ö«Ó¬{䦞.þ}¥ïîC×<>^\W¾ò«¬ØWäÀÞ<C380>§öoí¿Ç%z«ÜÚµsdc'SVKñl•RB{癤n¨ËÙîælª î£Nª°MIÒ(ð¥×<C2A5>´i—k/Õ7½&[Œg¶ŸéAZŒ Ð¸´¯Ë4½©%×­Æ[wO¹§9^\h±OÓS;¦neˆ­Så&‡•ûߦ=xÑ襉EL&Ùåmd¢žÏ&›ƒ>Ú¢lª*šãåÇ<C3A5>Ë»ØgvˆR[Ó2U}-¯M¦dRãô˜ª¹¦oµÀàà!<21>QÁÑëGE¯ð¡<C3B0>ª<EFBFBD>Ç3gª*ZTÜ!ʱ Ç-j_HÚ8bƒ¦ZÆëíãBCôBšcp׃ovØfجw5ë£÷††| QHË~bs<62>6½[{»šTw}<7D>íîßYpŠgjx%4ˆÐä˜xšŸºñs.·Ôíib¶YëcõÎÙÖtAy¨2É9¬ÊãO sú² <C2B2>Ó<EFBFBD><>¦žÙÌ\8sÉ"¹¬½¦OÈÖ¼§jKNµP75EI†;6/ŽÚ<C5BD>k>f϶>©E5¥>jïZ«îpûÏúÙO™n/¢ô¼™ö«ïuIïmqÉîºrÔòÀc8é…ÿeÿoñ?h²þ lsw‰Ûõ[öÆ/º—ªñLf½c<15>ù…CªfžiÕ°+Ä.«6=jÕ¡×Å+«ztW«b¬lU~^à ÓžÏ÷©ÇgÔæ8}×>•ïà³Ó­8pÀ1.T<>}³Éü¶†óiz β<C38E>ÙùÍS㘳Ìo«J¾Ýeöé0>pÜ ³gÏüqŽëü¥á³¢œ òé8"¤þT×ê¶cO [¶ Âe·í‰…k·ž.4ô`UÇw¡<13><6B>1cB#FGFF,‰Šâj¯‡[ô©…’ZlìÕvÞY<C39E>¬®Ù«Žu^<5E>1ëó4ÇMS.èÖWTð@M<>ÒÔ-=æ™EÕ¶èCÔÆšý²(5ÍúæJ<C3A6>ÞhQþš^_åq·ÃCô&{áU"©sGÍ£-ú½Q>*Ô8ëÔî”òæ.6)ž·<E28093>mí #ô>|@ϯ™UY«[¬½j¬•MýùCR§ò['7…&¤´P7-Ž[T‡ÐZJQ[ÇSê•õ«ÞT³7â£õÈšõ<C5A1>gcVSÛô˜âûÉ3ô!ªX¨Xë¦<C3AB>Ì)ùºØ¦îzÒ¨ÕE7%ØÚ“µ[VsHÕýyRñë¬Ã¸0=2lM˜Â°­a6Fß0Û±ázr¸¢ðÍáyŒ<79>áy]4o—w†<77>)^K4ÅÛÙ%Ú}¦5[׊ڎä‘Úÿö¢ƒè(:‰ÎÁÄŠíb‡Øi}:v=b¯ˆ7ÄÍ\Øä©x&ž‹åߊ\¬O<C2AC>Mg•Ü‰#†ü-idgŒhX<58>/6Â&Ø»aì…8ˆÏÂÉ9ÅEøp®À-¸ wàîGÚx<04>á <Í…8ÚbžH®è†EÑ=”Æ2XËau¬‰E>1HclØ[ak쀱S:™ôÄ^è<>½³0Èp Æ<1C>#qŽ¶ÉX‡ã³0È'îȉ5bq;îÀ<C3AE>餱ãp?È<>8²3Æx¯àÕ¬ñw„ñê˜oа2FF¶(`e G*Hf*DŸSáÿ3g|CßR<C39F>tÖ¨O è;jH<6A>r Ž6ÔÚQ{ê@©u¦.Ô•ºW.ÔL!4Ff <66>ïÿm¬¤U´šÖPtâø‰6Ò&ÚL[ÒÙ#wÚ8A'é<1C>§ t~ÿÀ—é
]¥ktý_“G
½!ƒÞÒ»<C392> F8?øGR<¤q‡gÖÈÌÙ©";KòÊ#²EnðB^ôÿN¿ð!NäÃœÄÉ|$KX ¯ðU¾Æ×ù?ü'ßà›|¾Ë÷ø>?à‡üˆó~ÊÏø9¿à—üŠ_³b<C2B3>ü–ßñ{þK
e>™_<16>ÒQ”fYH~. Ë/dé$<24>¥EºHWé&JwYL—%dIé!KÉÒ²Œ,+ËÉò²‚¬(+ÉÚòù­¬#ëÊƲ‰l*Éæ²…l)[ÉÖ²<C396>l+ÛÉö²ƒì(;Éβ‹ì*»É^Ò[ö–}¤<>ô•}e?Ù_úÉÒ_”ƒd€,å9T“Ar¸ 6«9¶¯;6Lí;<3B>ÑS.ß·h‡ÓŸûÖpíìak©ÑÚÀ
ÉÍŸž>¸.q«ëüÑA{&„m/|Õbô¬enØ;öÌÕÃÛ_ºÞ6ÜmZúw<1F>ÎGfUS3
~m Q˨}ÍC¹œÚ¿*6ÞE/fi;ËlÄÙ¨¢óŽ$_vz¼§žaóAæ™ K5w£¶QûJ1å:Ô¢êÔ|b¸¦ñÚÓÃu £L˺ÊKË€p-mŒŠoƒŒ*z<>ln£š¾<C5A1>6*éí>dn<64>ªàê½G·)<9ʬQ#ŽÈM„ùu÷M°Lú¸“þ^S…N)T5õÏ-®!o'™[{ÅÄû»ªÛ„Í»bO8ÝŽ«g”6ÊtªSÖ5턨¯õ¯-ꎶ@s¼»Þen“}/óŠsç/q½SN ?}œSÀ¸¥?<3F>QI™\Žµ¼ÅD]ˬ7ÕãA“Ñ<x]VŒ1íU¡;T<>øŒSsTžÌ"7IÓOç,{o^Ï"ëKÍÕp«úD¡jbµ­¬œ6Ò\[“ú©+oÝܯš«)éE,ÊMËt(U¡ÑcÃ9mØÚ¢T† Œ-z?íc…ãUhÆ=Z«×s<E28093>³l0Ç¢ªq0ÅëíÒë£ì-*<WÂÈj<C388>—Ë.2Û²¨T|Ô§kÖ¸Ž9þ1 òZÀ´©Uke…ãÓ¬·áa)ó¢o5u9)3*ZIZm;b2u¶>9‡2ÿ}¬–K/YíØÎsZ¥mçíMå}¥¦ê¤xšU@zqéΣ‡ ý^ÿõSc~9¢ægªªê¡ŸÿÔFíý:úfËXïnÉ…s9DZ#êlrÚ*+¿úÇ<C3BA>Lœ/Õ"[ïŸÿ¤¶»·¢WƒÖ¥Q²±Ï¸¬öµÊɵTݶµr‰ò?œ´
­—wXUGÓÀwÊžK°à5F8€½×ÄM¬±÷ÞQŠ"ØEc<45>Æ^ Ø°ÅÞ)±D5¶Xcloì½»<07>šï"|J}“<ß7œ»gvvvvæÜ}æY@®†ÞC<Úyø-×Ü»ÿ€ÀdUMÃÍpŸîàj8 à  W6kšÏ]dë7;¥.„ƒC®ägî<K
ÛŸE9KþÙäPTØäèîã?lLÅŠ_•¯T±jïþÞ<C3BE>Þ#½<Ý===Üûúù<C3BA>¼<E28099>{÷’}»—r¯\±Rår¶G•²îíx¹·ðé=h<>‡{C¿a¾6{o?_w_O÷ö^ƒ¼úùùz÷õpoW¾nùäˆÝ“Cvÿò‡G]O¿>^M<½|½GPI P<>°öÂQä¹EW8‰|Â*
"¢¨(&Š‹¢¤(%J~¢¿ ¼Å@á#‰ÁÂWø  †ˆ¡"P Ab¢˜$&)bª˜&~ÓÅ ñ£˜)f‰ÙbŽ˜+æ‰ùb<C3B9>X(‰P&ÂÅb±D,ËD„ˆËÅ
±R¬«E”X#ÖŠub½ˆ?}b¿8 ÅqT¿Šãâ„8)N‰óâ¸(~—ÄⲸ"®Škâ¸-➸/ˆ‡âx,žˆ—â•x-”0Ä{ñ$<0E>€AØCÈ >¹!äV( E (ƒâP*@E¨•áKø
ª@U¨Õ¡.Ôƒúоƒ†ÐCh
Í 9´Ð
ZCh í =t€ŽÐ :Cè
Ý ;ô€žÐ  oC!†AÃp#aŒ†10ÆAŒ‡ °Â"…0‡Å°Â2ˆ€HX+`%¬…u°6ÀO°v@ü û`?€ƒð x8 <09>Gà(ƒ_á8œ€“p
NÃopÎÂ98à܇ð^ÀKx¯A<C2AF>Lx ïà=ü…9QC :àg船07æÁ¼è„ùЊùñs,€_`AtF,ÇX×Xká7ø-ÖÆ:Xëa}l€ßaCl„m°3vÁ®Ø »c쉽н°öÇè<>Ñá`ôE?ôÇC1ü'â$œŒSp*NÃp:ÎÀq&ÎÂÙ8çâ<œ<> p!.ÂP Ãp\ŒKp).ÃŒÄå¸Wâ*\<5C>Q¸×â:\<5C>ð'܈p3nÁ­¸Ü‹ñ<„ñx0<11>àQ<†¿âq<<3C>žÆßð žÅsx/àEü/á5¼ŽÿÁ?ñÞÄ[xïà]¼‡÷ñ>ÄGøŸàS|†Ïñ¾ÄWhâ[|‡ïñ/„DÄ$ÉŽì)å$<24>,”—¾ äL.¤“+¹‘;¢ÂT„ŠR1*N%¨$•¢ÒT†ÊR9*O¨"U¢Êô%}EU¨*U£êTƒêP]ªG<C2AA>© 5¥fÔœZPKjE­©õ&êC}É“¼¨õ§äMɇÑ`ò%?ò§BC)<29>†ÑHE£i <0C>¥qBãi}OiM¦)4•¦ÑLšE³iÍ¥y4ŸÐBZD¡´˜ÐRZFIkh-­£õ´<C3B5>¶Ò6ÚN;h'í¢Ý´‡öR4ÅP,ÅÑÏ´<C38F>öÓ:H¿Ð!Š§Ã”@‰t„NÓot†ÎÒ%úƒþCÒ ºI·è6Ý¡»t<C2BB>îÓzH<7A>è1=¡§ôŒžÓ zI¯è5)2è=ýÅ<C385>‰™%Û±=çàœœ—<C593>8[9?Îø .ÈÎìÂ:»²»s!.ÌE¸$—âÒ\†Ër9.ϸ"WâÊü%ÅU¸*Wãê\ƒ¿æš\ëq}nÀßqCnÄ<6E>¹ 7åfÜœ[pKnÅ­¹ ·åvÜž;pGîÄ<C3AE>¹ wånÜ<6E>{pOîŽكûp_öd/îÇýy{ó@öáA<˜<>y<04>äQ<šÇðXÇ!<ž'ð÷<'ñdžÂSyÿÀÓyÿÈ3yÏæ9<—q(‡q8/æ%¼”—qGòr^Á+y¯æ(^Ãky¯ç üoäM¼™·ðVÞÆÛyïä]¼›÷ð^ŽæŽå8þ™÷ñ~>Àù8Ÿà“|ŠOóo|†Ïò9>Ïø"ÿΗø¾l±¸RZW.¯YRŪ\´¨%ËV»u7ËGeR ­^’»Ö¼`³Žýk7뱩—«O?éµç<C2B5>ïuçו—^nÕÀåû
ˆÞxjÿ¶Q>{]£¶Ê­];G4v¶d”¤º­Šiïê&¨êr¦Ù¬Eå3<5ÓY°k3Qšy¾ì~#Eízí¥úæ£Õ$Ý|fÿi¨­×Ò8“òºT3šêÙn5NwÚ3ùžætq<74>î˜â§—vLÝJãiD[çJM«Â¶ßÂQKâ Z,²ËÛˆx#—]&c”®ì<>ªÊšÓåÇ<C3A5>˹:¦7ˆT[S"Uýô¼¯â-DRc<52>õU² ß&þAAþAQk—EF­éû¡Œª<C592>ÇÓG¢*è*æe™†ãºÚœ2ß ©±Fû˜<C3BB>`#¿æ”ÁôàÛ¶™vëÜ,Aƨè<C2A8>à<EFBFBD>.òk™Ol­Ý¦Ok7êa Ö2Íþ<C38D>%ÕMv¯„áF“,O±S7~ÎfJ<66>מÆgÒÚ>«w.ö– ª„*<2A>˜Åª >¾iùÔ´¦. ü¨öß"=5õÌnÆÌfí5c|¦â=U[²Ê…º©)Jø0ܹyQä†lã±Öm뙜TKò§ön<C3B6>-ëyoÿY?ó)SåE¤3Ý~c»_2úè®™MWŒ\查_g£À¿¬_ã->-¶¿<C2B6>}ö&1»Ë|aü1»™¹¢z«êÏdƘKäIî™gØzØåb·­7=jëC¯WPÀÖ#†rPËÖãõÿ'æó}êñµ9Æؽ/¯ÊuðÙéVq;bl³Éú¶ºË©Fu»<>ÕåÍSó˜Ìm¯Š¿Ýmõì0Îìüñá³fÍøq¶Û¼%a3#<23>ãöì8<¸þ·jö£M]:?Üu<C39C>ý‰k¶ž.˜0ä`W§w!ÌkÖàÑ£ƒƒÂGED„/ŽŒìæh„éÆ”ü u;GµC7:Õ4GÕQWç<57>õVc®æ´ÃvA ÒÔ)MÝ2Ö?ÓU-ÝPiŽK#ÕTÛ½•¬Þ¨+ͨ¯rÖÍa!qF“hxOêÜQë(ÝX­;NšVEAm·éÅY“ru±Oª«¿=iÖìbXâìM3ä>|@ϯYUÝÑÜî¨kÅBïHHPcUnrSH\R uSwÚ¢:„L×
Ù;<3B>R¯l¯FSÍÑŒ<C391> 2"Fh¶ËÏÎñ¬¦¶ëêŽd«‰Mò$w÷9!+:ïØP#"tu¨ÂЭ¡vf¿Pû1aFb˜¢°Ía9ÌAa9]5ç<>sºƒ%V·Ä:8Ä;|fµÚÖŠZNT"¹ÊíEÑQt<12>?2Í'NÙ.vˆ<76>b—íØ#öŠh#nˆÙÈSñL</>’È¿å\Á Ü¡P*þð·<‘™$¢`MŠØ`7ì<37>½ 1<>(³ä‡‹ð;\?à2\<5C>[pîÀÝ<C380>Ãxš W´Çÿ<>/ÜÐ aát”Q
Kc,Õ°z:âøD<1A>± 6ÅfØ[`Kl…­±vÄN©üÑ öÉ@Ã0ƒq8ŽÀ8ÊÆcp,ŽË@Ÿè"+¢ØŽ;p'îJå‰hŒÁýx ®ÈLàe¼W?ÅßqÄkTh`¾AÓFi "<22><> œ(Y)?}NþÏ4ñ }KµS‰¢>5 ï¨!5Ê-ÚP[jGí©u¤NÔ™ºPWêFݳa
¦á4" c|ÿ<>˜b­¤U´š¢ÒpÅO´6ÑfÚJÙ3Å :Içè<] ‹ôûª¸LWè*]£ëÿš/’è ™ô–Þ}$<24>¾Ðع9Ï?⌢i(#….ê¦!Šô$‘™2ƒ?𞉲c…y<ŸðÂÿwVø…q<æNä#iˆÁÆ |…¯ò5¾Îÿá?ùßä[|›ïð]¾Ç÷ù?äGü˜ŸðS~ÆÏù¿äWüšœÄoØä·üŽßó_RH<52>(sÉÜ2<C39C>Ì+<2B>d>i•ùåç²€üB”ÎÒEêÒUºIwYHEdQYL—%dIYJedYYNdEYK~#¿•µeÙX6Me3Ù\¶<>-e+ÙZ¶me;Ù^v<>e'ÙYv]e7Ù[zÈ>²¯ô”^²Ÿì/Ho9PúÈAr°ô•~Ò_È!r¨ ”Ãd<C383>UͶÝé°iißi\`×ð^rÙ¾˜…;<3B>ÿÜدº[çöfÁê­M,ŸØüééƒkã·ºÍë¸w|èŽWu³WMkÃ>ÛÏ\=¼ã¥Ûm³°]KŸS)Ȫjhf¾¯M4kšµ®•P®§ö¯ÜëjÑÛδš1vªÐÜ#‰—<E280B0>ï­gÚ}h欦^µ°Yˬu¥ˆr¢«Ú5ž˜n)Töôp³„Yºe<C2BA>ª»–ÔZÚ™Þš•<C5A1>@ÙÜN5}!#ìVèS8`NÏ*ߪè£Û”]Ž,›©‘à ϮÕòîá§Oü¸“ñ^SùO)T5ŒÏu·à·­­»¯<C2BB>õqSëíã6ïÞ~ÂùvL=³”YºSí2né˜&X}m|­«;Ú|Íéî:s¤µM潬˗ϙ·ØmÔ.99dÜ´±Î¾c—lütFU0iRÙO=ôvý-ÆZú®R=¬[ÌæAk3Š%Z…ìTµcÓªf«é[ÙÍ8<C38D>us{óz†æ½”Ù,H ³µ9±'DMÈ°¶• vSF͵ÕÉ¿†©Û¨òèæþUÝ,™Ä(¨+w-Ý¡TùF<C3B9>M—”ak]©4˜[ŒþÚÇ Çª<C387>´h­^ëçgØ`¶n|—Mïœ×k´<4B>rÔUX¶Q:.B¤—¥É<E28098>hLÓl~<7E>²Ä•S˦Ul™qNO3NÃÿ"QúEßjêrBz ´ñ²ÚvÄbé8t]biþ/Æ-Å—ñ&+*²É±]IÖ”LÛÏ<C39B>c…¦j'Õµ*ßÔìQ=cR<63>G ùÞøõSa~9¢æ¥ËªêiœÿTFí½;zeŠØè¡g¼ÙœãØu61e•<65>{_ýãJÆÏ“j¡½ÇOqƒ<71>ÿÛÓG¯ÁkSXØÜg^VûZeeZ²NÛšÙ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œ]QMkÃ0 ½ûWèØ´Ë6<08>Ñ^rØËv;$¶ <0C>mç<>?ÛJÛ1<C39B>ýГžOÙ±95ZyÈÞ<C388>á-z<>J ‡“™GèqPšåÅýš¥Ÿ<C2A5><C5B8>eY·Ëäql´4PU ûåÉ»6ÏÂôx¹7'Ð)=ÀæëØ&¦<>­ˆÚÃŽÕ5”aÜKg_»!KÒm#B]ùeT·ŽÏÅ")Ïi%nN¶ãè:= «v!êJ†¨jñ¯¼Šzù·äy ß·ôð˜àቀȒÈòž rj9$ØSVP­X‡­Sö$àDò¢û‰ë^G—¯®ðÙ¹`H:Er"z 4^¯e<C2AF><65>ªô~i<><EFBFBD>
xœ]QMkÃ0 ½ûWèØ´Ë6<08>Ñ]rØËv=$¶ <0C>mç<>?ÛÊÚ1<C39A>ýxOzB³SóÜhå!{w†·èA*-Nfv¡ÇAi ÷+K7;˲`n—ÉãØhi ªÒ“w lž„éqµ7'Ð)=ÀæëÔ&¥<>­½àˆÚÞÕ5”¡ÝKg_»!KÖ]#B^ùe\·ŠÏÅ"‰ç47'Ûqt<71><1E>u%CÔ µø—^M½ü[ òß7z|HpÿH@bIbyG€9•ˆ”+Öfk—8‰¥¬ÏqÎ߉âÈq½×uðÙ¹°‰ôiññJãõ¬±Ñ•ÎðŠ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

@ -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
))
footer-display))

@ -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"))

@ -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)

@ -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)])))

@ -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)

@ -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"

@ -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 ""))

Loading…
Cancel
Save