From f048076a913da2614cf970ac9c614d47589dd313 Mon Sep 17 00:00:00 2001 From: Matt Heffron Date: Mon, 17 Nov 2025 10:48:15 -0800 Subject: [PATCH] Next phase of BDF to MEDLEYDISPLAYFONT - in progress. --- lispusers/READ-BDF | 205 +++++++++++++++++++++++++++++++-------- lispusers/READ-BDF.DFASL | Bin 21485 -> 24281 bytes 2 files changed, 165 insertions(+), 40 deletions(-) diff --git a/lispusers/READ-BDF b/lispusers/READ-BDF index a4c28123..3741f553 100644 --- a/lispusers/READ-BDF +++ b/lispusers/READ-BDF @@ -1,19 +1,20 @@ -(DEFINE-FILE-INFO PACKAGE (DEFPACKAGE "BDF" (USE "XCL" "LISP") (EXPORT "READ-BDF" -"WRITE-BDF-TO-DISPLAYFONT-FILES") (IMPORT-FROM "IL" "BITBLT" "BITMAPCREATE" "BITMAPHEIGHT" -"BITMAPWIDTH" "BLACKSHADE" "BLTSHADE" "BOLD" "COMPRESSED" "CHARSETINFO" "DISPLAY" "FONTDESCRIPTOR" -"FONTP" "FONTPROP" "INPUT" "ITALIC" "LIGHT" "LRSH" "MEDIUM" "REGULAR" "TCONC" "UTOMCODE" "UTOMCODE?" -"WRITESTRIKEFONTFILE")) READTABLE "XCL" BASE 10) +(DEFINE-FILE-INFO PACKAGE (DEFPACKAGE "BDF" (USE "XCL" "LISP") (EXPORT "READ-BDF" "BUILD-COMPOSITE" + "WRITE-BDF-TO-DISPLAYFONT-FILES" "WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE") (IMPORT-FROM "IL" "BITBLT" +"BITMAPCREATE" "BITMAPHEIGHT" "BITMAPWIDTH" "BLACKSHADE" "BLTSHADE" "BOLD" "COMPRESSED" "CHARSETINFO" +"DISPLAY" "FONTDESCRIPTOR" "FONTP" "FONTPROP" "INPUT" "ITALIC" "LIGHT" "LRSH" "MEDIUM" "REGULAR" +"TCONC" "UTOMCODE" "UTOMCODE?" "WRITESTRIKEFONTFILE" "MEDLEYFONT.FILENAME" "MEDLEYFONT.WRITE.FONT")) +READTABLE "XCL" BASE 10) -(IL:FILECREATED " 6-Nov-2025 23:10:51" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;13| 49101 +(IL:FILECREATED "16-Nov-2025 22:55:52" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;30| 56989 :EDIT-BY "mth" - :CHANGES-TO (IL:FUNCTIONS BDF-TO-FONTDESCRIPTOR BDF-TO-CHARSETINFO READ-GLYPH - WRITE-BDF-TO-DISPLAYFONT-FILES) - (FILE-ENVIRONMENTS "READ-BDF") + :CHANGES-TO (FILE-ENVIRONMENTS "READ-BDF") + (IL:FUNCTIONS READ-BDF BUILD-COMPOSITE GET-CHARS-PRESENT + WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE) (IL:VARS IL:READ-BDFCOMS) - :PREVIOUS-DATE " 6-Nov-2025 22:43:21" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;9| + :PREVIOUS-DATE "16-Nov-2025 22:37:22" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;29| ) @@ -22,9 +23,10 @@ (IL:RPAQQ IL:READ-BDFCOMS ((IL:STRUCTURES BDF-FONT GLYPH) (IL:VARIABLES MAXCHARSET MAXTHINCHAR NOMAPPINGCHARSET) - (IL:FUNCTIONS BDF-TO-CHARSETINFO BDF-TO-FONTDESCRIPTOR GET-FAMILY-FACE-SIZE-FROM-NAME - GLYPHS-BY-CHARSET PACKFILENAME.STRING READ-BDF READ-DELIMITED-LIST-FROM-STRING - READ-GLYPH SPLIT-FONT-NAME WRITE-BDF-TO-DISPLAYFONT-FILES) + (IL:FUNCTIONS BDF-TO-CHARSETINFO BDF-TO-FONTDESCRIPTOR BUILD-COMPOSITE GET-CHARS-PRESENT + GET-FAMILY-FACE-SIZE-FROM-NAME GLYPHS-BY-CHARSET PACKFILENAME.STRING READ-BDF + READ-DELIMITED-LIST-FROM-STRING READ-GLYPH SPLIT-FONT-NAME + WRITE-BDF-TO-DISPLAYFONT-FILES WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE) (IL:DECLARE\: IL:EVAL@COMPILE IL:DONTCOPY (IL:FILES (IL:SYSLOAD) IL:SYSEDIT) (IL:FILES (IL:LOADCOMP) @@ -59,6 +61,7 @@ (DEFCONSTANT NOMAPPINGCHARSET (1+ MAXCHARSET)) (DEFUN BDF-TO-CHARSETINFO (FONT CSET SLUG-OR-WIDTH &OPTIONAL MAP-UNKNOWN-TO-PRIVATE) + (IL:* IL:\; "Edited 15-Nov-2025 14:26 by mth") (IL:* IL:\; "Edited 6-Nov-2025 17:30 by mth") (IL:* IL:\; "Edited 23-Apr-2025 17:53 by mth") (IL:* IL:\; "Edited 21-Apr-2025 16:23 by mth") @@ -92,6 +95,7 @@ (FIRSTCHAR MOST-POSITIVE-FIXNUM) (LASTCHAR MOST-NEGATIVE-FIXNUM) (CSINFO (IL:|create| CHARSETINFO)) + (IMAGEWIDTHS (IL:\\CREATECSINFOELEMENT)) (DLEFT 0) SLUG SLUGWIDTH GLYPHS-LIMITS BMAP OFFSETS HEIGHT WIDTHS) (COND @@ -140,13 +144,17 @@ (IL:|for| I IL:|from| 0 IL:|to| (+ MAXTHINCHAR 2) IL:|do| (IL:\\FSETOFFSET OFFSETS I TOTAL-WIDTH)) + + (IL:* IL:|;;| "Now WIDTHS is NOT the IMAGEWIDTHS array. BDF provides both, and MEDLEYDISPLAYFONT can persist both.") + (SETQ WIDTHS (IL:|fetch| (CHARSETINFO IL:WIDTHS) IL:|of| CSINFO)) (IL:* IL:|;;| "Initialize the widths to SLUGWIDTH") - (IL:|for| I IL:|from| 0 IL:|to| (+ MAXTHINCHAR 2) IL:|do| (IL:\\FSETWIDTH WIDTHS I + (IL:|for| I IL:|from| 0 IL:|to| (+ MAXTHINCHAR 2) IL:|do| (IL:\\FSETWIDTH + IMAGEWIDTHS I SLUGWIDTH)) - (IL:|replace| (CHARSETINFO IL:IMAGEWIDTHS) IL:|of| CSINFO IL:|with| WIDTHS) + (IL:|replace| (CHARSETINFO IL:IMAGEWIDTHS) IL:|of| CSINFO IL:|with| IMAGEWIDTHS) (IL:* IL:|;;| "JDS 12/4/92: Apparently, these fields can be signed values, if all chars, e.g., ride above the base line. ") @@ -168,7 +176,8 @@ 'INPUT 'IL:REPLACE) (IL:\\FSETOFFSET OFFSETS MCODE DLEFT) - (IL:\\FSETOFFSET WIDTHS MCODE GLW) + (IL:\\FSETOFFSET IMAGEWIDTHS MCODE GLW) + (IL:\\FSETOFFSET WIDTHS MCODE (FIRST (GLYPH-DWIDTH GL))) (INCF DLEFT GLW)) (IL:* IL:|;;| "Now insert the SLUG glyph into the BMAP, or make a slug (block)") @@ -292,6 +301,82 @@ :TEST #'EQL))))))))) +(DEFUN BUILD-COMPOSITE (BASE-FONT &REST FILL-FROM) (IL:* IL:\; "Edited 16-Nov-2025 18:25 by mth") + (IL:* IL:\; "Edited 14-Nov-2025 17:04 by mth") + (LET (UCHAR-PRESENT FONT FAMILY WEIGHT SLANT EXPANSION SIZE UC-PRESENT) + (UNLESS (OR (TYPEP BASE-FONT 'BDF-FONT) + (STRINGP BASE-FONT) + (PATHNAMEP BASE-FONT)) + (ERROR "BASE-FONT is not a BDF-FONT, nor string, nor pathname.")) + (UNLESS (AND FILL-FROM (LISTP FILL-FROM)) + (ERROR "FILL-FROM is not a list.")) + (WHEN (OR (STRINGP BASE-FONT) + (PATHNAMEP BASE-FONT)) + (UNLESS (IL:INFILEP BASE-FONT) + (ERROR "BASE-FONT ~S doesn't exist or is unreadable." BASE-FONT)) + (MULTIPLE-VALUE-SETQ (FONT FAMILY WEIGHT SLANT EXPANSION SIZE UC-PRESENT) + (READ-BDF BASE-FONT :MCCS-ONLY T)) + (SETQ BASE-FONT FONT) + (SETQ UCHAR-PRESENT UC-PRESENT)) + (UNLESS UCHAR-PRESENT + (SETQ UCHAR-PRESENT (GET-CHARS-PRESENT BASE-FONT))) + (LOOP :FOR FF :IN FILL-FROM :WHEN FF :DO (COND + ((TYPEP FF 'BDF-FONT) + (SETQ UC-PRESENT (GET-CHARS-PRESENT FF))) + ((OR (STRINGP FF) + (PATHNAMEP FF)) + (UNLESS (IL:INFILEP FF) + (ERROR + "Element of FILL-FROM (~S) doesn't exist or is unreadable." + FF)) + (MULTIPLE-VALUE-SETQ (FONT FAMILY WEIGHT SLANT + EXPANSION SIZE + UC-PRESENT) + (READ-BDF FF :MCCS-ONLY T)) + (SETQ FF FONT)) + (T (ERROR + "Element of FILL-FROM (~S) is not a BDF-FONT, nor string, nor pathname." + FF))) + (LOOP :FOR GL :IN (BF-GLYPHS FF) + :WITH V :DO (SETQ V (GLYPH-ENCODING GL)) + (WHEN (AND (LISTP V) + (EQ (FIRST V) + -1)) + (SETQ V (OR (SECOND V) + -1))) + (WHEN (AND (UTOMCODE? V) + (ZEROP (BIT (AREF UCHAR-PRESENT (LRSH V 8)) + (LOGAND V 255)))) + (SETF (BIT (AREF UCHAR-PRESENT (LRSH V 8)) + (LOGAND V 255)) + 1) + + (IL:* IL:|;;| + "What other bookkeping of BASE-FONT needs to be done when adding a glyph? Any?") + + (PUSH GL (BF-GLYPHS BASE-FONT))))) + BASE-FONT)) + +(DEFUN GET-CHARS-PRESENT (BFONT) (IL:* IL:\; "Edited 16-Nov-2025 17:52 by mth") + (IL:* IL:\; "Edited 14-Nov-2025 16:40 by mth") + (UNLESS (TYPEP BFONT 'BDF-FONT) + (ERROR "BFONT is not a BDF-FONT.")) + (LET ((UCHAR-PRESENT (MAKE-ARRAY 256 :INITIAL-CONTENTS (LOOP :FOR I :FROM 0 :TO 255 :COLLECT + (MAKE-ARRAY 256 :ELEMENT-TYPE + 'BIT :INITIAL-ELEMENT 0))))) + (LOOP :FOR GL :IN (BF-GLYPHS BFONT) + :WITH V :DO (SETQ V (GLYPH-ENCODING GL)) + (WHEN (AND (LISTP V) + (EQ (FIRST V) + -1)) + (SETQ V (OR (SECOND V) + -1))) + (WHEN (UTOMCODE? V) + (SETF (BIT (AREF UCHAR-PRESENT (LRSH V 8)) + (LOGAND V 255)) + 1))) + UCHAR-PRESENT)) + (DEFUN GET-FAMILY-FACE-SIZE-FROM-NAME (BDFONT) (IL:* IL:\; "Edited 30-Apr-2025 13:18 by mth") (IL:* IL:\; "Edited 23-Apr-2025 16:20 by mth") (IL:* IL:\; "Edited 5-Feb-2025 12:56 by mth") @@ -509,15 +594,24 @@ X)) Y)))) -(DEFUN READ-BDF (PATH &OPTIONAL VERBOSE) (IL:* IL:\; "Edited 30-Apr-2025 13:37 by mth") +(DEFUN READ-BDF (PATH &KEY VERBOSE MCCS-ONLY (EXTERNAL-FORMAT :ISO8859/1)) + (IL:* IL:\; "Edited 16-Nov-2025 22:37 by mth") + (IL:* IL:\; "Edited 14-Nov-2025 16:35 by mth") + (IL:* IL:\; "Edited 30-Apr-2025 13:37 by mth") (IL:* IL:\; "Edited 24-Apr-2025 00:44 by mth") (IL:* IL:\; "Edited 17-Apr-2025 15:10 by mth") (IL:* IL:\; "Edited 12-Jul-2024 23:02 by mth") (LET (PROPS PROPS-COMPLETE CHARS-COUNT FONT-COMPLETE FONT POS KEY V VV LINE ITEMS GL (NGLYPHS 0) + (UCHAR-PRESENT (MAKE-ARRAY 256 :INITIAL-CONTENTS (LOOP :FOR I :FROM 0 :TO 255 :COLLECT + (MAKE-ARRAY 256 :ELEMENT-TYPE + 'BIT :INITIAL-ELEMENT 0)))) (*PACKAGE* (FIND-PACKAGE "BDF"))) + + (IL:* IL:|;;| "Note: The EXTERNAL-FORMAT *ought* to be :UTF-8 for the BDF files from otf2bdf, but I'm seeing :ISO8859/1. I don't know why! But I'm setting the default :EXTERNAL-FORMAT appropriately for this.") + (WITH-OPEN-FILE - (FILE-STREAM PATH :ELEMENT-TYPE 'CHARACTER :DIRECTION :INPUT) + (FILE-STREAM PATH :ELEMENT-TYPE 'CHARACTER :DIRECTION :INPUT :EXTERNAL-FORMAT EXTERNAL-FORMAT) (LOOP :WHILE (STRING-EQUAL "COMMENT" (SETQ KEY (READ FILE-STREAM))) :DO @@ -609,21 +703,29 @@ (ERROR "Invalid BDF file - CHARS count (~A) is invalid or missing." NGLYPHS)) (SETF (BF-GLYPHS FONT) - (LOOP :REPEAT NGLYPHS :COLLECT - (PROG1 (SETQ GL (READ-GLYPH FILE-STREAM FONT)) + (LOOP :REPEAT NGLYPHS :NCONC + (PROGN (SETQ GL (READ-GLYPH FILE-STREAM FONT)) - (IL:* IL:|;;| + (IL:* IL:|;;|  "Any GLYPH with ENCODING of -1 is taken as the SLUG glyph. If multiple, the last applies.") - (SETQ V (GLYPH-ENCODING GL)) - (WHEN (AND (LISTP V) - (EQ (FIRST V) - -1)) - (SETQ V (OR (SECOND V) - -1))) - (WHEN (EQ V -1) - (SETF (BF-SLUG FONT) - GL)))))) + (SETQ V (GLYPH-ENCODING GL)) + (WHEN (AND (LISTP V) + (EQ (FIRST V) + -1)) + (SETQ V (OR (SECOND V) + -1))) + (COND + ((EQ V -1) + (SETF (BF-SLUG FONT) + GL) + (LIST GL)) + ((UTOMCODE? V) + (SETF (BIT (AREF UCHAR-PRESENT (LRSH V 8)) + (LOGAND V 255)) + 1) + (LIST GL)) + (T NIL)))))) (ENDFONT (SETQ FONT-COMPLETE T)))))))) (DESTRUCTURING-BIND (FAMILY (WEIGHT SLANT EXPANSION) SIZE) @@ -633,7 +735,7 @@ "Name: ~A~%Family: ~A~%Size: ~A~%Weight: ~A~%Slant: ~A~%Expansion: ~A~%" (BF-NAME FONT) FAMILY SIZE WEIGHT SLANT EXPANSION)) - (VALUES FONT FAMILY WEIGHT SLANT EXPANSION SIZE))))) + (VALUES FONT FAMILY WEIGHT SLANT EXPANSION SIZE UCHAR-PRESENT))))) (DEFUN READ-DELIMITED-LIST-FROM-STRING (INPUT-STRING &OPTIONAL (DELIMIT #\])) (IL:* IL:\; "Edited 20-Aug-2024 16:46 by mth") @@ -822,6 +924,25 @@  "UNMAPPEDGLYPHS are never written. (Unicode encoding is > xFFFF, or encoding low byte is FF)") (VALUES FONTDESC CSETS UNMAPPED-FONTDESC UNICODE-CSETS UNMAPPEDGLYPHS)))) + +(DEFUN WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE (BDFONT DEST-DIR &KEY FAMILY SIZE FACE ROTATION DEVICE + &AUX FULLFILENAME) + (IL:* IL:\; "Edited 16-Nov-2025 17:32 by mth") + (UNLESS (TYPEP BDFONT 'BDF-FONT) + (ERROR "Not a BDF-FONT: ~S ~%" BDFONT)) + (DESTRUCTURING-BIND (FN-FAMILY FN-FACE FN-SIZE) + (GET-FAMILY-FACE-SIZE-FROM-NAME BDFONT) + (SETQ FAMILY (OR FAMILY FN-FAMILY)) + (SETQ FACE (OR FACE FN-FACE)) + (SETQ SIZE (OR SIZE FN-SIZE)) + (MULTIPLE-VALUE-BIND (FONTDESC CSETS) + (BDF-TO-FONTDESCRIPTOR BDFONT FAMILY SIZE FACE ROTATION DEVICE) + (SETQ FULLFILENAME (MEDLEYFONT.WRITE.FONT FONTDESC (MEDLEYFONT.FILENAME FONTDESC NIL + NIL DEST-DIR))) + + (IL:* IL:|;;| "These correspond to the charsets ACTUALLY written.") + + (VALUES FULLFILENAME FONTDESC CSETS)))) (IL:DECLARE\: IL:EVAL@COMPILE IL:DONTCOPY (IL:FILESLOAD (IL:SYSLOAD) @@ -833,8 +954,9 @@ ) (DEFINE-FILE-ENVIRONMENT "READ-BDF" :PACKAGE (DEFPACKAGE "BDF" (:USE "XCL" "LISP") - (:EXPORT "READ-BDF" - "WRITE-BDF-TO-DISPLAYFONT-FILES") + (:EXPORT "READ-BDF" "BUILD-COMPOSITE" + "WRITE-BDF-TO-DISPLAYFONT-FILES" + "WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE") (:IMPORT-FROM "IL" "BITBLT" "BITMAPCREATE" "BITMAPHEIGHT" "BITMAPWIDTH" "BLACKSHADE" "BLTSHADE" "BOLD" "COMPRESSED" @@ -842,16 +964,19 @@ "FONTP" "FONTPROP" "INPUT" "ITALIC" "LIGHT" "LRSH" "MEDIUM" "REGULAR" "TCONC" "UTOMCODE" "UTOMCODE?" - "WRITESTRIKEFONTFILE")) + "WRITESTRIKEFONTFILE" + "MEDLEYFONT.FILENAME" + "MEDLEYFONT.WRITE.FONT")) :READTABLE "XCL" :COMPILER :COMPILE-FILE) (IL:PUTPROPS IL:READ-BDF IL:DATABASE IL:NO) (IL:DECLARE\: IL:DONTCOPY - (IL:FILEMAP (NIL (2497 10576 (BDF-TO-CHARSETINFO 2497 . 10576)) (10578 16996 (BDF-TO-FONTDESCRIPTOR -10578 . 16996)) (16998 20538 (GET-FAMILY-FACE-SIZE-FROM-NAME 16998 . 20538)) (20540 27970 ( -GLYPHS-BY-CHARSET 20540 . 27970)) (27972 29397 (PACKFILENAME.STRING 27972 . 29397)) (29399 36358 ( -READ-BDF 29399 . 36358)) (36360 36683 (READ-DELIMITED-LIST-FROM-STRING 36360 . 36683)) (36685 43176 ( -READ-GLYPH 36685 . 43176)) (43178 43919 (SPLIT-FONT-NAME 43178 . 43919)) (43921 47827 ( -WRITE-BDF-TO-DISPLAYFONT-FILES 43921 . 47827))))) + (IL:FILEMAP (NIL (2686 11251 (BDF-TO-CHARSETINFO 2686 . 11251)) (11253 17671 (BDF-TO-FONTDESCRIPTOR +11253 . 17671)) (17673 21069 (BUILD-COMPOSITE 17673 . 21069)) (21071 22140 (GET-CHARS-PRESENT 21071 . +22140)) (22142 25682 (GET-FAMILY-FACE-SIZE-FROM-NAME 22142 . 25682)) (25684 33114 (GLYPHS-BY-CHARSET +25684 . 33114)) (33116 34541 (PACKFILENAME.STRING 33116 . 34541)) (34543 42891 (READ-BDF 34543 . 42891 +)) (42893 43216 (READ-DELIMITED-LIST-FROM-STRING 42893 . 43216)) (43218 49709 (READ-GLYPH 43218 . +49709)) (49711 50452 (SPLIT-FONT-NAME 49711 . 50452)) (50454 54360 (WRITE-BDF-TO-DISPLAYFONT-FILES +50454 . 54360)) (54362 55433 (WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE 54362 . 55433))))) IL:STOP diff --git a/lispusers/READ-BDF.DFASL b/lispusers/READ-BDF.DFASL index 927778eaf9838aafa5ba0d1b7cc9d57d42e6156d..93b1686ca5bbc85890ce4bd3a75cca537caa0c06 100644 GIT binary patch delta 10920 zcmb7K4SZD9m4ElmWbz$Cn1m2wGKLUhU?7B#1Qf|+<|Q-9WbR~Uk`N1wD1pfbp#pxi z3X?!Vt*H{8;5>>}ODbKr`)fif@)f4kw#xo&S1l^I{nS>g)vC){x7(HMIp@BaNo?JA z#k_Ogx&M3ax#!+{?z#8P`^VVjakhW$%}XlN{9A9|c5A0)Q&(@N<<@PT8+LSVv@Bk3 z^=!SvT2)!K%u-dga@n$#%c|1qZGQJunE&>^jT^qS0N@sYcW$|*bDIV0EQnVc9ZzHB zNs;}7CS{G6`vQ@{6rW;qSnC}1k-=$otuD92YUj;f?so+g^%tz9d~i;?4|qga1KirI zINVBy!{zt7Z5{R86R_61+)6~9J;vk$W*wX%09djROFXt_B{G;hNlNM#B0w!LTyC;Q zRAc1eU}~Gq=d#tg6@TQwU|O?njooSU`IUfXhxc@N4>(;Ol#w9Y!<%hhuglYrfT>yP zjmAw$ z*C8OfO*6bUdsD233;h8f7(>e94h0>*HkOv7jx=&IDI&}?aGwiYhyFtkp-}xLv&^T` zf5hXU+>;bw>(!%4?Zvg}5tT(AmLr4azco|TS*99!Nu}yH^|Dp!_f6ZwjyaKs2D8EF zR(qh;r&xJiqhb%_Xbz@1422OjBcd8KhXmu2?By;`K=F}4)^-v#>ePg79vn2*xdQ%V zDoN2x(sY`k(=4546Af#Q99>}6X`Z%oCbX;V#D5W0)^7dc?x`~`Sm|n+QJq{S7I=)5=)0i|XFlcsvY?7sUecY?~ z0xrc*qhRv4yBq^Y zCe)hZL|y;EDf+*nBd54mM;_`Q5ZJu{N(FUspU2vNB!<|hBjO{WtBAw^_W~d-t^!9y zM`$s`eL5mBW;)_<3~|4Xhz(>zT*=p`^y#a*G4V0h6&>l<6ajny5I8ns7zf@kHd1lv4XF%x)=LqH%aawWQBt4)r(Y3bsS-Oy7|0$e@)dNk_!g7<)DSQO5SG zJ2IaR+i_w1h2%d+_BX`-JF?Fa`#Q4!LhNN^RbnrFiO6d~BCje$Ua2N>aW0Xc%^>o! zn2#^TT0aT~qn_QhC&6s>+gZEWQFZ>bC27B!p*b=&hf#B=ch4#U)9;;DmFi&Wq~$zB zn|bOx)9S*juG8+y(7t3^p?!I&cK0GpnWNp4qbW>NFw$gW*ia-91r$BND0+ZVOuAeT zD~cXg6g{jcdRS3JSkV+cswjF?Q6Q>>seyc5QGreiby`GW2de+y|1j)Ow;s|IoFWu< zV&PoPrlKJzNKrg?j!{OllkAYTI}EOSgkB=_)aZE_z=pcIb_x%`h%l2}SGR`O*H@Ah zUOM70CJ3>x<559UGqfS1T2dk`nQVSLZCp6TPfKo+!VjzW^ z7>fb^lK`UsVuW>s97BAZXeTl9I>-XXvt#o}S7w@m`*Q*pn`AmJc?#~o6D`MQpbkty;5Cw@xBM4C>H`#?pK7i7mbsq_ z%YNA3-!CE_%lK@NWEm7B6$p|GZ+I7$l%yG8=j=BW((t}8oybqbME$8@rUoGbLrg4) zT@^u$T~&XXe&g8vxj$mS@|n4dU!;mJK}MWdyBO_WCHZUgyhP8-^n6`CICBdHVdh5x zv(batT4WayyBgVRh`kY61F@@smEIxt24w$@*z1vfn%I@dex2AA$POSYy}p^q;1Y6ioYV&@)zzy>MTothI6Erp(BYJ$ttG#7*a z5l9lR=2D+2XfKZgvb9DA|K}wDjWFuXVt@=Esmo_?XFJuWW_O2sCwdK+dM>?@3cZzr z+0pJ`PBauW!(Sf!<-=bA{1w7qQM8VB)Z?17%Yi$3op5H zfLjK5$r(4k>t#iVE8!L>D;eOHvm>+XmV_-sUHzel>p;x|TTy3S8a|_;P(jcFbzQ+y zXu2%QOUb|kqtYH;%1}_k%UG~H%FEFr7L6qx!VQ^ubFA7%1cwTPKk2dr2Pp=F$ z5AgDF&oW+qWyk~UxkkR=VzBCT6JK~`sA+&NJmcn-SBBgJyz(rsD8p`)%3#ds#9iE? zR$o&RE&yLahb(=JT^SlN^8#*pvGFGoNWd$W=*ZhVOV}lW>n1~Uc{V_K-X!c>I+kaf zjnO9eMR0f{&%4MAd(zPzFb*+5zS#s$u|z{*FbO>qA`gKhEHMv_a0?5Th_>^{RWbjR zg4+b2Pp<3k=J`A)Cd&y{M7w##m7(qdUUAVK6PqvcGE%LR&1lhP3HRJgF<8*`@)vmy z_9q*;Qsjz|(_1rvW?sQioWn~m1}k8aaf=My7@cV1=Hr3b6v+#gpiPaj_Z)z9!;C?b zEMSs8lfa}Bhp?%Hh;CEDq>-Y&i3w2DbFrJ}^}GTuB}is68V}CQ4nbnrT%a0?d3ZXk zTfBYXm&e;m-8!Zb zV80-cZe#U!K`QlHAem2W4;JQ9p?p=4cLn*SATJ9tjHc^12`*1PhidX3)DUL9$hVU= zVizRXYZGi8 z)P`%@m}l&iLu^+^3GsU7f5HYmVt~ z3i$Kfo0&RxQ(2hJXhzJVgxPctkuLSUc_m>Lb$vpDM~`S+1bum3XZMw3Js~~3cFvJc zhE9b}ht7n~hQ>qZLcuJEB-v4MXgUxY$*rY@H59sEh=xM_U56<`lRWA~K`=+c7~hf zU>-Zm^Wtexku zp!u7lOyOqQ&yH}jXe&E9D(#NFX);71{ zy$cqt(TFrA5UT(YhHDhOGsO7H=26*RWD7KuNLS;kr9TSEcS(XPPWd27l!mDA zTRVlGJBWl_f-DxoWJuPsY`%upW;hO7l`sZt?M_{)cx?eE9`;ZGmxXA+v(4=&L+Zn&iv6wqNjGPoc<@RnVE%ipPVTVyal zVRYQyUT9&VBQm%=p(|eYxqS&+;+=t<*DJ!GbX#8gI)?#v{_m7Rig;`GR6r%VCrWNdLz7a{ z2b&lzO|400Acb{#rip4j=4^WN#MvfTlH)z@=4YZV`P9g9CoCv0C}R3rVv)h5I@~sJ z#HB+t0gi4UvZ)y+j<_$e`Fvqp$BiUDo=GH>+^fNTx}zHA+ccNQ6>!iI0Y^{Qd z21sD6P?n*~tgt64c_7G+f#Dz$0I*+%8?zJ`DKg`)XVpS*cT42ZSaJCajJ2rmUFVv8 z7N^rK(1;1XQZxjd_!=Oq&Z*vtyp-q`dWoui71w6BX9;_!A!-cvx^GkzvmdC@ih0*P zi+#R|VFLGVn;kC8QSvTTpu%T}{gFCtey-z3ZO~#8B3HFQKa2qgI=Pwy%%{~-5>b=j zQvE@nsEw>A!PXJ`Lp3yiX~wgYVCuK$FSPy`1$0DY#EN>v>h%!d&{Y@~%xAu_MGJmz zfYc$QGM|!(MHvNPxYmEdFbU|G$PPo9e2Nslf$aB)MSv`_ zJz~Xi_Dz(EnL$!NI4mguqi4qgacHJM`Y|b?{PHWr{uTgib&PG1SS5JS*lzPtKJ!8x zt{A$UgRRD53a`n<@D&R<8u;{` z0ry>G2T6+6*aZ#-C7LryAf~{d)V3uSW>@cA za*2JSdY8Jw=v{F|%>D=2C#fP1r}Pl9dyw6WtTZgl-BT;xmY|(YN=G~QIlQF=-ZWDX z<4!0`YH8&GrHdq2#a3{YK~E=tE>rWD-3A|`zPhYD-7jX+CrtCJE{V4_}vYZRwAs8Sg1f@e{*4^E@S zRNE{$&2kU1PHVO#o4{SE75Ev^)}T~qfn>~35(!FDWYCBkF8TdMh!x3gim#6Q74q#d z5KL3nK(O&Z$gJnSW}Aqe3Q9BiIZgPiA5%%^W!k z*^fYd{Lm%6r=_6qZ8UgFkb8q_KaGWA@sA-Z*3tKn#dWk!VuVl$d}1}IPIdWtP0g|u zix$(8l(WjI>{t#GInw}7Rw$-|aij3qg+khoHiA<#qLk$B@BgavLZMnzJ!ifsi>5$} z5&%-7&I_i;caItl10Y^4C;H7%!(O$wx}2?54^~&phB@j_t6Oqj(i_MUsq?7(eRXlo zY&HzfnMuazsC*xCYJZK9^^z?XR)4jkWhPu69cvs1Yn>NT$PDafUQ~6hywQJrCJ1TU zc3#K>Av{9`BlDdXvZ!mvEzS!C@bcz~bMSb-Sf)X z(TR5T8!PkKM)l;%_H3wJ%95!RS5}c)czsRy&U6NwJC4FZ{IU%H!$klOKpJdayky1F zDm=6s*TZy3Z?m~u6~9$adyOLAV!mpufnt-GnDA=&SZhHd;x&dEg9xlgkzJ*=A<3gA zL%Wy5BoI?I1Y#MZxjc6fvh?+YokR9(IvCj7i9M`-ay?JM8q&*JtLnL7R?0b1GOTXB zVRl|IrN+{J`q&{Ci{KPeAHSg}Tx>=Sa!D$*WW;avxDOi3DR>*+fE+p*E=9yAW+*T` zAPSrmTBPh8`NGM;C4xwZ%WGMSr&9b2(tX32+Bc%sWqUY|vBFV!b zk5bFFc?t!o1>uq;!X44^4&VkiI ztHybuh*pyLd@3(zF<;?a?PnCx^)hxLlM3o^S4vy!4fvd` zo~FgikHC~!QFj+>Rgc$|R<4iWM>Myq*%dIl311{~h+UM4NW&DV*prOTS<_az}Y1s0@INQrk!`VK0WZ{V6kM7h^j!KsQGygXlX%q8y9 zEDb&Zp`zE4@dpOTy^8%+EsZBxyL!2CXSm%6wj_>q^ivwsdW6cB07Sbj)nSqM>dPLFeU;st%wgGhGfoFAX;@; z{qWh}jaMVm6QnwnLgdM85Xr(#UqiM`P4^aW*o6vTLWCSfb}6!AZ5i12ARV0F0Y z04z~BlF0?Ir=zoO6J4g~_GIkv4`2-b_U{X^6u%yP_BV-P{SHgNyrLNO4b9W7wdh|b z^pP{QYmR@w(`+65z|k}15^E*iEA5&qJqm80pEw;*E|@NiaF%vRZYT zZ+1#8gWvEOwPk9RZ=NMd%lW%M%&hoFf0(J|k7dj0|KFcxKH_)e)M~xpJ=w!4u*uI& GO#CmwnW|6# delta 8117 zcmai33w)DRmjCXTG%0;RC~cvogiuPUK)_OJ(qRoMC6zRe!syqcc8c*_BZQ_MCISB;ZeX ze{FKkJ@dPq0sZ#`bh|SJ&i(?%2Bd<{sPkHr(1{yLod@_xF2lvDp^c z{da7$&%bg0LfibBrPVhsU09vdpoF~F!@|8=H*dM6`<^+rMK-|q+_tV~vke=~Nw*q4 zp2La_i9K#OAN+yeafnE zlgD4*qy#k-Sp2F_2?RX;MihnIVUY4%{2(Mg-{|cOG==QWPJ2edV0=PAsc&iUc-?-*=bjr12cZM%FKr~~xHGGw3U%a=;iQN# z<3a(iCro|Xu{{b0*Vtrel5GmJ4!*|JQNBDk!C7LjoQM~Gn^X72YX@d7l&foazWG+R zgx_o4!W{fl^TeowX%0hKg6Ac;L32oneQ|R@_4vc?APHn`8&RW9P1wi2xY6kehs;!y zt=Ht}G*72vb!yRRfldo`TC8oGnBY-><6nZy+P$Fa`akGlQet;p4umuZcqZScw779f z$r(5`c_1~WoDw&NJgdn)@m#0c=68XYo$4xbVvf%ZUat>{DFr77f@;7W46pXMLj*8| zIy|m$li*z$-Yn$RRACksdZE4DT~E^wMwrCJ6UrF!w!!rB?lEQKci@DkEVc(gx@*k5 zwRJHmMlLsL+a*cb8G}KZL|DYMyP%2L*^ruRE-()v#_08g!ZGb`9ib0bN5o^K>`+X* zhfm9y5C!*$rVmRnQPA3q)7t0`N^(rQ4^-qsC)c?}$L~rjx?e}>&Jw=TQM+UPG3|!{ z7z1k3&eJRR#QLK#Evnb1-Kp0OU{f4;3;;Rl=IB-(F_=R1>xhiwbp%f#w(E$@YzRc1 zv^PBt?CSyDnDj*HiuT1cMF4*U2z0zYef_zM*n|9;+?n=A68oiW$4j6@vPfk3Bfv7v zfNKu^<0jSF46z_FS#$8byjkovUYpl#apY;Zyd>!WHQF)q*Lg#X?cfb#pN`g{O_xai zHnQ&%dk)!Gi9L(#-xGTVSx)Tfdx)Hh5P8c@Fx7{PR3sTEA`w9FslV7b)%IY<~Y`STWMbjc0coq5`3 z(-LjV0`2?rG-bNBwLnvtreON8U@lN3v9=XGYbbiwP)xd9&lQTED-=CfD0;3?^jx9n zxkAx%g#x)EN*zqn6_x6=Os7^#CeZx<{=zOJ80blBl3sY>0G5YG~6h1hSS-ZE|?&1!UjVXSw84P zq^qnxfG!#HX2lGBdA(zD|KYyB3s+2px1e1^OI7x)alpA}~?uiby?2ge(69$XsD|Dvoqz zSFgkUR|b~~GJ#{#b+`+ep3@Pi0~rFRNRr<3e*;pVpmcolf}(YGUkJ-i#9}d#@mSV3 z6DP}pNcr}}sbJ!CU?lxJd?ZS)F+&+m`^)2rydnblRl`ILQU|7H7?QpXKW(-A2iog;2nj;+I0~5@hEQ zyBOIi#MUBfAa)V3(nrKDM3y>X3y^)9q&3JsO6+`O`;nE-t|xNFN91&^HXWs>L^gkm zvN@~1KASTW-&XP@eA`MV7rbMvAeAd7XiB~YNmqeHUS4^P8<|LL@5#b}?}O(=erHv@ zY;qMpQQA=AVw#H;!8r`3H@jZ-hr$Y^p-q|#5Upj6X6m9z9;dk({3DPg9?i{nOzx;k z11wqtga6_TpaG)6BPPgjl4qA~VeS0Bvc70v#FFfd6u_qtKE?2v1fNp)lqH?C+dkFe zj1-u_8PaI!#&ZB`iCB}WE-8!HlB!j;T^LlY{i^MGFP8N>E8=Xb73ysLs`dD>$<7+v zz=l}wUMFaI=nhmFb><*R_V$)WtkBjIDTl5rl4|)Fln)L|52)n~1va&UMJkhOg&e8U zt%=M^sybg5nUm(Nk+~^ett_0B^r@AXFAVtl)yflowerG%AGlvOtFz8UZhUS=i#q4R zKuf_rid;U!8kgt*XRfl*?eu@YTCjD=)E@M<*rwa6s1E8M`oWtWce#T2D2< zA%O(EVoe_VP#r7mlE8J>LW|TgfU1SpU_a2YYFVK%+2TD14sTWq #wb8*-(4=F%# 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 E7YxRW@Bjb+