From a8a0313bd967c3cdaf7675e3ded79d8d882933b9 Mon Sep 17 00:00:00 2001 From: Matt Heffron Date: Tue, 24 Feb 2026 23:46:23 -0800 Subject: [PATCH] A few fixes and performance improvements --- lispusers/READ-BDF | 414 ++++++++++++++++++++++----------------- lispusers/READ-BDF.DFASL | Bin 24873 -> 26036 bytes lispusers/READ-BDF.TEDIT | Bin 13003 -> 13399 bytes 3 files changed, 239 insertions(+), 175 deletions(-) diff --git a/lispusers/READ-BDF b/lispusers/READ-BDF index 3a1d3bdf..d0eb0cbb 100644 --- a/lispusers/READ-BDF +++ b/lispusers/READ-BDF @@ -1,18 +1,16 @@ (DEFINE-FILE-INFO PACKAGE (DEFPACKAGE "BDF" (USE "XCL" "LISP") (EXPORT "READ-BDF" "BUILD-COMPOSITE" "WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE") (IMPORT-FROM "IL" "BITBLT" "BITMAPBIT" "BITMAPCREATE" -"BITMAPHEIGHT" "BITMAPWIDTH" "BLACKSHADE" "BLTSHADE" "BOLD" "COMPRESSED" "CHARSETINFO" "CHARSETPROP" -"DISPLAY" "FONTDESCRIPTOR" "FONTP" "FONTPROP" "INPUT" "ITALIC" "LIGHT" "LRSH" "MCCS" "MEDIUM" -"REGULAR" "TCONC" "UTOMCODE?" "MEDLEYFONT.FILENAME" "MEDLEYFONT.WRITE.FONT")) READTABLE "XCL" BASE -10) +"BITMAPHEIGHT" "BITMAPWIDTH" "BLACKSHADE" "BLTSHADE" "BOLD" "COMPRESSED" "CHARSETINFO" "DISPLAY" +"FONTDESCRIPTOR" "FONTP" "FONTPROP" "INPUT" "ITALIC" "LIGHT" "LRSH" "MCCS" "MEDIUM" "REGULAR" "TCONC" +"UTOMCODE?" "MEDLEYFONT.FILENAME" "MEDLEYFONT.WRITE.FONT")) READTABLE "XCL" BASE 10) -(IL:FILECREATED " 8-Dec-2025 12:13:40" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;9| 51309 +(IL:FILECREATED "23-Feb-2026 20:11:48" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;20| 54795 :EDIT-BY "mth" - :CHANGES-TO (IL:FUNCTIONS BDF-TO-CHARSETINFO BDF-TO-FONTDESCRIPTOR) - (FILE-ENVIRONMENTS "READ-BDF") + :CHANGES-TO (IL:FUNCTIONS READ-GLYPH) - :PREVIOUS-DATE " 8-Dec-2025 12:12:47" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;8| + :PREVIOUS-DATE "23-Feb-2026 17:38:07" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;19| ) @@ -20,7 +18,7 @@ (IL:RPAQQ IL:READ-BDFCOMS ((IL:STRUCTURES BDF-FONT GLYPH XLFD) - (IL:VARIABLES MAXCHARSET MAXTHINCHAR NOMAPPINGCHARSET) + (IL:VARIABLES GLYPH-PROCESSING-HOOK MAXCHARSET MAXTHINCHAR NOMAPPINGCHARSET) (IL:FUNCTIONS BDF-TO-CHARSETINFO BDF-TO-FONTDESCRIPTOR BUILD-COMPOSITE CHAR-PRESENT-BIT COUNT-MCHARS GLYPHS-BY-CHARSET PACKFILENAME.STRING READ-BDF READ-DELIMITED-LIST-FROM-STRING READ-GLYPH WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE @@ -71,6 +69,8 @@ (CHARSET¬REGISTRY NIL :TYPE STRING) (CHARSET¬ENCODING NIL :TYPE STRING)) +(DEFVAR GLYPH-PROCESSING-HOOK NIL) + (DEFCONSTANT MAXCHARSET 255) (DEFCONSTANT MAXTHINCHAR 255) @@ -126,7 +126,7 @@ (IMAGEWIDTHS (IL:\\CREATECSINFOELEMENT)) (DLEFT 0) GLYPHS-LIMITS BMAP OFFSETS HEIGHT WIDTHS) - (CHARSETPROP CSINFO 'IL:CSCHARENCODING 'MCCS) + (IL:CHARSETPROP CSINFO 'IL:CSCHARENCODING 'MCCS) (LOOP :FOR XGL :IN CSGLYPHS :DO (LET* ((MCODE (CAR XGL)) (GL (CDR XGL)) (GWIDTH (GLYPH-WIDTH GL)) @@ -309,7 +309,9 @@ (LIST CSET))))) (LIST FONTDESC CHARSETS)))) -(DEFUN BUILD-COMPOSITE (FONTS &KEY VERBOSE) (IL:* IL:\; "Edited 1-Dec-2025 23:07 by mth") +(DEFUN BUILD-COMPOSITE (FONTS &KEY VERBOSE (BLOCKING T)) + (IL:* IL:\; "Edited 19-Feb-2026 21:45 by mth") + (IL:* IL:\; "Edited 1-Dec-2025 23:07 by mth") (IL:* IL:\; "Edited 30-Nov-2025 12:32 by mth") (IL:* IL:\; "Edited 26-Nov-2025 21:23 by mth") (IL:* IL:\; "Edited 18-Nov-2025 21:22 by mth") @@ -327,53 +329,61 @@ (WHEN VERBOSE (FORMAT *STANDARD-OUTPUT* "~&Loading initial font file: ~A~%" (NAMESTRING BASE-FONT) )) - (SETQ BASE-FONT (READ-BDF BASE-FONT :MCCS-ONLY T :VERBOSE VERBOSE))) + (SETQ BASE-FONT (READ-BDF BASE-FONT :MCCS-ONLY T :VERBOSE VERBOSE)) + (WHEN BLOCKING (IL:BLOCK))) ((NOT (BDF-FONT-P BASE-FONT)) (ERROR "Initial font (~S) is not a BDF-FONT, nor string, nor pathname." BASE-FONT))) (WHEN VERBOSE (FORMAT *STANDARD-OUTPUT* "~&Initial font contains ~D MCCS characters.~%" (SETQ CHAR-COUNT (COUNT-MCHARS BASE-FONT)))) (SETQ MCHAR-PRESENT (BF-MCHAR-PRESENT BASE-FONT)) - (LOOP :FOR FILL-FONT :IN FILL-FROM :WITH PREV-CC :WHEN FILL-FONT :DO - (COND - ((OR (STRINGP FILL-FONT) - (PATHNAMEP FILL-FONT)) - (UNLESS (IL:INFILEP FILL-FONT) - (ERROR "Subsequent font ~S doesn't exist or is unreadable." (NAMESTRING - FILL-FONT))) - (WHEN VERBOSE - (FORMAT *STANDARD-OUTPUT* "~&Loading subsequent font file: ~A~%" (NAMESTRING - FILL-FONT))) - (SETQ FILL-FONT (READ-BDF FILL-FONT :MCCS-ONLY T :VERBOSE VERBOSE))) - ((NOT (BDF-FONT-P FILL-FONT)) - (ERROR "Subsequent font (~S) is not a BDF-FONT, nor string, nor pathname." - FILL-FONT))) - (SETQ PREV-CC CHAR-COUNT) - (LOOP :FOR GL :IN (BF-GLYPHS FILL-FONT) - :WITH V :DO (SETQ V (GLYPH-ENCODING GL)) - (WHEN (AND (LISTP V) - (EQ (FIRST V) - -1)) - (SETQ V (OR (SECOND V) - -1))) + (LOOP :FOR FILL-FONT :IN FILL-FROM :WITH PREV-CC :WITH FF-NAME :WHEN FILL-FONT :DO + (FLET ((MERGE-GLYPH (GL &AUX V) + (SETQ V (GLYPH-ENCODING GL)) + (WHEN (AND (LISTP V) + (EQ (FIRST V) + -1)) + (SETQ V (OR (SECOND V) + -1))) - (IL:* IL:|;;| + (IL:* IL:|;;|  "Need to change this use of UTOMCODE? based on the CHARSET¬REGISTRY of the XLFD of FILL-FONT") - (WHEN (AND (UTOMCODE? V) - (ZEROP (CHAR-PRESENT-BIT MCHAR-PRESENT V))) - (CHAR-PRESENT-BIT MCHAR-PRESENT V 1) + (WHEN (AND (UTOMCODE? V) + (ZEROP (CHAR-PRESENT-BIT MCHAR-PRESENT V))) + (CHAR-PRESENT-BIT MCHAR-PRESENT V 1) - (IL:* IL:|;;| + (IL:* IL:|;;|  "What other bookkeping of BASE-FONT needs to be done when adding a glyph? Any?") - (PUSH GL (BF-GLYPHS BASE-FONT)))) - (SETQ CHAR-COUNT (COUNT-MCHARS BASE-FONT)) - (WHEN VERBOSE - (FORMAT *STANDARD-OUTPUT* "~&Font ~A supplied ~D additional MCCS characters.~%" - (NAMESTRING FILL-FONT) - (- (SETQ CHAR-COUNT (COUNT-MCHARS BASE-FONT)) - PREV-CC)))) + (PUSH GL (BF-GLYPHS BASE-FONT))) + NIL)) + (COND + ((OR (STRINGP FILL-FONT) + (PATHNAMEP FILL-FONT)) + (SETQ FF-NAME (NAMESTRING FILL-FONT)) + (UNLESS (IL:INFILEP FILL-FONT) + (ERROR "Subsequent font ~S doesn't exist or is unreadable." FF-NAME)) + (WHEN VERBOSE (FORMAT *STANDARD-OUTPUT* + "~&Loading subsequent font file: ~A~%" FF-NAME)) + (LET ((GLYPH-PROCESSING-HOOK #'MERGE-GLYPH)) + (READ-BDF FILL-FONT :MCCS-ONLY T :VERBOSE VERBOSE) + (SETQ FILL-FONT NIL)) + (WHEN BLOCKING (IL:BLOCK))) + ((NOT (BDF-FONT-P FILL-FONT)) + (ERROR "Subsequent font (~S) is not a BDF-FONT, nor string, nor pathname." + FF-NAME))) + (SETQ PREV-CC CHAR-COUNT) + (WHEN FILL-FONT + (LOOP :FOR GL :IN (BF-GLYPHS FILL-FONT) + :DO + (MERGE-GLYPH GL))) + (SETQ CHAR-COUNT (COUNT-MCHARS BASE-FONT)) + (WHEN VERBOSE + (FORMAT *STANDARD-OUTPUT* + "~&Font ~A supplied ~D additional MCCS characters.~%" FF-NAME + (- (SETQ CHAR-COUNT (COUNT-MCHARS BASE-FONT)) + PREV-CC))))) BASE-FONT)) (DEFUN CHAR-PRESENT-BIT (BM MCODE &OPTIONAL (NEWBIT -1 SBIT) @@ -472,6 +482,7 @@ Y)))) (DEFUN READ-BDF (PATH &KEY VERBOSE MCCS-ONLY (EXTERNAL-FORMAT :ISO8859/1)) + (IL:* IL:\; "Edited 19-Feb-2026 21:42 by mth") (IL:* IL:\; "Edited 1-Dec-2025 22:40 by mth") (IL:* IL:\; "Edited 30-Nov-2025 11:59 by mth") (IL:* IL:\; "Edited 28-Nov-2025 17:39 by mth") @@ -586,13 +597,12 @@ (PLUSP NGLYPHS)) (ERROR "Invalid BDF file - CHARS count (~A) is invalid or missing." NGLYPHS)) - (LOOP :REPEAT NGLYPHS :WITH ENC :WITH MC :DO (SETQ GL (READ-GLYPH - FILE-STREAM - FONT)) + (LOOP :REPEAT NGLYPHS :WITH ENC :WITH MC :DO + (SETQ GL (READ-GLYPH FILE-STREAM FONT :MCCS-ONLY MCCS-ONLY)) (SETQ ENC (GLYPH-ENCODING GL)) (WHEN (AND (LISTP ENC) - (EQ (FIRST ENC) - -1)) + (EQL (FIRST ENC) + -1)) (SETQ ENC (OR (SECOND ENC) -1))) (COND @@ -615,143 +625,195 @@ (IL:* IL:|;;| "It ought to be safe to share the bitmap") - (TCONC MAPPED-GLYPHS CGL) + (WHEN (AND GLYPH-PROCESSING-HOOK (FUNCTIONP + GLYPH-PROCESSING-HOOK + )) + (SETQ CGL (FUNCALL GLYPH-PROCESSING-HOOK CGL))) + (WHEN CGL (TCONC MAPPED-GLYPHS CGL)) (CHAR-PRESENT-BIT MCHAR-PRESENT CC 1))) - (T (TCONC UNMAPPED-GLYPHS GL)))) + ((NOT MCCS-ONLY) + (WHEN (AND GLYPH-PROCESSING-HOOK (FUNCTIONP GLYPH-PROCESSING-HOOK) + ) + (SETQ GL (FUNCALL GLYPH-PROCESSING-HOOK GL))) + (WHEN GL (TCONC UNMAPPED-GLYPHS GL))))) (SETF (BF-GLYPHS FONT) (CAR MAPPED-GLYPHS)) (SETF (BF-UNMAPPED¬GLYPHS FONT) (CAR UNMAPPED-GLYPHS))) - (ENDFONT (SETQ FONT-COMPLETE T)))))))) - (WHEN VERBOSE + (ENDFONT (SETQ FONT-COMPLETE T))))))))) + (WHEN VERBOSE - (IL:* IL:|;;| "The SIZE reported needs clarification:") + (IL:* IL:|;;| "The SIZE reported needs clarification:") - (FORMAT *STANDARD-OUTPUT* "Name: ~A~%Family: ~A~%Sizes: Font: ~A Pixel: ~A Point: ~A (decipoints)~%Weight: ~A~%Slant: ~A~%Expansion: ~A~%" - (BF-NAME FONT) - (XLFD-FAMILY XLFD) - (FIRST (BF-SIZE FONT)) - (XLFD-PIXEL¬SIZE XLFD) - (XLFD-POINT¬SIZE XLFD) - (XLFD-WEIGHT XLFD) - (XLFD-SLANT XLFD) - (XLFD-SETWIDTH¬NAME XLFD))) - FONT))) + (FORMAT *STANDARD-OUTPUT* "Name: ~A~%Family: ~A~%Sizes: Font: ~A Pixel: ~A Point: ~A (decipoints)~%Weight: ~A~%Slant: ~A~%Expansion: ~A~%Glyphs: ~D~%Unmapped glyphs: ~D~%" + (BF-NAME FONT) + (XLFD-FAMILY XLFD) + (FIRST (BF-SIZE FONT)) + (XLFD-PIXEL¬SIZE XLFD) + (XLFD-POINT¬SIZE XLFD) + (XLFD-WEIGHT XLFD) + (XLFD-SLANT XLFD) + (XLFD-SETWIDTH¬NAME XLFD) + (LENGTH (BF-GLYPHS FONT)) + (LENGTH (BF-UNMAPPED¬GLYPHS FONT)))) + FONT)) (DEFUN READ-DELIMITED-LIST-FROM-STRING (INPUT-STRING &OPTIONAL (DELIMIT #\])) (IL:* IL:\; "Edited 20-Aug-2024 16:46 by mth") (WITH-INPUT-FROM-STRING (SI (CONCATENATE 'STRING INPUT-STRING " " (STRING DELIMIT))) (READ-DELIMITED-LIST DELIMIT SI))) -(DEFUN READ-GLYPH (FILE-STREAM FONT) (IL:* IL:\; "Edited 26-Nov-2025 23:32 by mth") +(DEFUN READ-GLYPH (FILE-STREAM FONT &KEY MCCS-ONLY) (IL:* IL:\; "Edited 23-Feb-2026 20:11 by mth") + (IL:* IL:\; "Edited 19-Feb-2026 15:46 by mth") + (IL:* IL:\; "Edited 26-Nov-2025 23:32 by mth") (IL:* IL:\; "Edited 17-Nov-2025 20:03 by mth") (IL:* IL:\; "Edited 23-Apr-2025 17:53 by mth") (IL:* IL:\; "Edited 21-Apr-2025 13:37 by mth") (IL:* IL:\; "Edited 19-Apr-2025 09:32 by mth") (IL:* IL:\; "Edited 17-Apr-2025 18:14 by mth") (IL:* IL:\; "Edited 21-Aug-2024 01:10 by mth") - (LET ((GLYPH (MAKE-GLYPH :SWIDTH (COPY-LIST (BF-SWIDTH FONT)) - :DWIDTH - (COPY-LIST (BF-DWIDTH FONT)) - :SWIDTH1 - (COPY-LIST (BF-SWIDTH1 FONT)) - :DWIDTH1 - (COPY-LIST (BF-DWIDTH1 FONT)) - :VVECTOR - (COPY-LIST (BF-VVECTOR FONT)))) - CHAR-COMPLETE LINE ITEMS V KEY POS STARTED BBW BBH) - (LOOP :UNTIL CHAR-COMPLETE :DO (SETQ LINE (READ-LINE FILE-STREAM)) - (WHEN LINE (IL:* IL:\; "Ignore blank lines") - (MULTIPLE-VALUE-SETQ (KEY POS) - (READ-FROM-STRING LINE)) - (WHEN (<= POS (LENGTH LINE)) - (SETQ LINE (SUBSEQ LINE POS))) - (COND - ((EQ KEY 'COMMENT) (IL:* IL:\; "Ignore COMMENT lines") + (LET + ((GLYPH (MAKE-GLYPH :SWIDTH (COPY-LIST (BF-SWIDTH FONT)) + :DWIDTH + (COPY-LIST (BF-DWIDTH FONT)) + :SWIDTH1 + (COPY-LIST (BF-SWIDTH1 FONT)) + :DWIDTH1 + (COPY-LIST (BF-DWIDTH1 FONT)) + :VVECTOR + (COPY-LIST (BF-VVECTOR FONT)))) + CHAR-COMPLETE ENC LINE ITEMS V KEY POS STARTED BBW BBH) + (LOOP + :UNTIL CHAR-COMPLETE :DO (SETQ LINE (READ-LINE FILE-STREAM)) + (WHEN LINE (IL:* IL:\; "Ignore blank lines") + (MULTIPLE-VALUE-SETQ (KEY POS) + (READ-FROM-STRING LINE)) + (WHEN (<= POS (LENGTH LINE)) + (SETQ LINE (SUBSEQ LINE POS))) + (COND + ((EQ KEY 'COMMENT) (IL:* IL:\; "Ignore COMMENT lines") (IL:* IL:\;  "Probably aren't \"legal\" here, anyway.") - ) - ((EQ KEY 'STARTCHAR) - (WHEN STARTED (ERROR "Invalid BDF file - STARTCHAR inside glyph.")) - (SETF STARTED T) - (SETF (GLYPH-NAME GLYPH) - (STRING LINE))) - (T (UNLESS STARTED (ERROR + ) + ((EQ KEY 'STARTCHAR) + (WHEN STARTED (ERROR "Invalid BDF file - STARTCHAR inside glyph.")) + (SETF STARTED T) + (SETF (GLYPH-NAME GLYPH) + (STRING LINE))) + (T + (UNLESS STARTED (ERROR "Invalid BDF file - glyph has not been started. STARTCHAR missing." - )) - (SETQ ITEMS (READ-DELIMITED-LIST-FROM-STRING LINE)) - (CASE KEY - (ENCODING (SETF (GLYPH-ENCODING GLYPH) - (IF (EQL -1 (FIRST ITEMS)) - ITEMS - (FIRST ITEMS)))) - (SWIDTH (SETF (GLYPH-SWIDTH GLYPH) - ITEMS)) - (DWIDTH (SETF (GLYPH-DWIDTH GLYPH) - ITEMS)) - (SWIDTH1 (SETF (GLYPH-SWIDTH1 GLYPH) - ITEMS)) - (DWIDTH1 (SETF (GLYPH-DWIDTH1 GLYPH) - ITEMS)) - (VVECTOR (SETF (GLYPH-VVECTOR GLYPH) - ITEMS)) - (BBX (SETF (GLYPH-BBW GLYPH) - (SETQ BBW (FIRST ITEMS)) - (GLYPH-BBH GLYPH) - (SETQ BBH (SECOND ITEMS)) - (GLYPH-BBXOFF0 GLYPH) - (THIRD ITEMS) - (GLYPH-BBYOFF0 GLYPH) - (FOURTH ITEMS))) - (BITMAP (UNLESS (ZEROP (* BBW BBH)) + )) + (SETQ ITEMS (READ-DELIMITED-LIST-FROM-STRING LINE)) + (CASE KEY + (ENCODING (SETF (GLYPH-ENCODING GLYPH) + (SETQ ENC (IF (EQL -1 (FIRST ITEMS)) + ITEMS + (FIRST ITEMS))))) + (SWIDTH (SETF (GLYPH-SWIDTH GLYPH) + ITEMS)) + (DWIDTH (SETF (GLYPH-DWIDTH GLYPH) + ITEMS)) + (SWIDTH1 (SETF (GLYPH-SWIDTH1 GLYPH) + ITEMS)) + (DWIDTH1 (SETF (GLYPH-DWIDTH1 GLYPH) + ITEMS)) + (VVECTOR (SETF (GLYPH-VVECTOR GLYPH) + ITEMS)) + (BBX (SETF (GLYPH-BBW GLYPH) + (SETQ BBW (FIRST ITEMS)) + (GLYPH-BBH GLYPH) + (SETQ BBH (SECOND ITEMS)) + (GLYPH-BBXOFF0 GLYPH) + (THIRD ITEMS) + (GLYPH-BBYOFF0 GLYPH) + (FOURTH ITEMS))) + (BITMAP + (UNLESS (ZEROP (* BBW BBH)) (IL:* IL:\; + "Don't bother creating a BITMAP with no area") + (IF (AND MCCS-ONLY (NOT (UTOMCODE? ENC))) + (PROGN + (IL:* IL:|;;| + "This is the case of skipping over non-MCCS encoded glyph when MCCS-ONLY") - (IL:* IL:|;;| "Don't bother creating a BITMAP with no area") + (LOOP :REPEAT BBH :DO (READ-LINE FILE-STREAM))) + (LET* + ((BM (BITMAPCREATE BBW BBH 1)) + (BM.BASE (IL:|fetch| IL:BITMAPBASE IL:|of| BM)) + (BM.RASTERWIDTH (IL:|fetch| IL:BITMAPRASTERWIDTH IL:|of| BM)) + (NBYTES (CEILING BBW 8)) + (NCHARS (* 2 NBYTES)) + (NWORDS (CEILING BBW 16)) + BITS WORDINDEX) + (LABELS ((CHAR-HEX-VALUE (C) + (IF (CHARACTERP C) + (COND + ((CHAR<= #\0 C #\9) + (- (CHAR-CODE C) + (IL:CONSTANT (CHAR-CODE #\0)))) + ((CHAR<= #\A C #\F) - (LET* ((BM (BITMAPCREATE BBW BBH 1)) - (BM.BASE (IL:|fetch| IL:BITMAPBASE IL:|of| BM)) - (BM.RASTERWIDTH (IL:|fetch| IL:BITMAPRASTERWIDTH - IL:|of| BM)) - (NBYTES (CEILING BBW 8)) - (NCHARS (* 2 NBYTES)) - (NWORDS (CEILING BBW 16)) - BITS BYTEPOS WORDINDEX) - (LOOP :WITH BITROW = 0 :REPEAT BBH :DO - (SETQ LINE (STRING-TRIM '(#\Space #\Tab) - (READ-LINE FILE-STREAM))) - (UNLESS (AND (EQUAL NCHARS (LENGTH LINE)) - (SETQ BITS - (PARSE-INTEGER LINE :RADIX 16 - :JUNK-ALLOWED T))) - (ERROR - "Invalid BDF file - bad line in BITMAP: ~A" - LINE)) - (WHEN (ODDP NBYTES) - (SETQ BITS (ASH BITS 8))) - (SETQ WORDINDEX (* BITROW BM.RASTERWIDTH)) - (SETQ BYTEPOS (* 16 (1- NWORDS))) - (LOOP :REPEAT NWORDS :DO - (IL:\\PUTBASE BM.BASE WORDINDEX - (LDB (BYTE 16 BYTEPOS) - BITS)) - (INCF WORDINDEX) - (DECF BYTEPOS 16)) - (INCF BITROW)) - (SETF (GLYPH-BITMAP GLYPH) - BM)))) - (ENDCHAR (SETQ CHAR-COMPLETE T))))))) - (SETF (GLYPH-ASCENT GLYPH) - (+ (GLYPH-BBH GLYPH) - (GLYPH-BBYOFF0 GLYPH))) - (SETF (GLYPH-DESCENT GLYPH) - (ABS (MIN 0 (GLYPH-BBYOFF0 GLYPH)))) - (SETF (GLYPH-WIDTH GLYPH) - (MAX (+ (MAX 0 (GLYPH-BBXOFF0 GLYPH)) - (GLYPH-BBW GLYPH)) - (FIRST (GLYPH-DWIDTH GLYPH)))) - GLYPH)) + (IL:* IL:|;;| + "The (- (CHAR-CODE #\\A) 10) accomplishes adding 10 after the outer subtraction") + + (- (CHAR-CODE C) + (IL:CONSTANT (- (CHAR-CODE #\A) + 10)))) + ((CHAR<= #\a C #\f) + + (IL:* IL:|;;| + "The (- (CHAR-CODE #\\a) 10) accomplishes adding 10 after the outer subtraction") + + (- (CHAR-CODE C) + (IL:CONSTANT (- (CHAR-CODE #\a) + 10)))) + (T 0)) + 0)) + (PARSE-WORDS + NIL + (LOOP :FOR I :FROM 0 :TO (1- NCHARS) + :BY 4 :WITH C3LIMIT = (- NCHARS 3) + :WITH C4LIMIT = (- NCHARS 4) + :COLLECT + (+ (ASH (CHAR-HEX-VALUE (CHAR LINE I)) + 12) + (ASH (CHAR-HEX-VALUE (CHAR LINE (+ 1 I))) + 8) + (ASH (CHAR-HEX-VALUE (AND (<= I C3LIMIT) + (CHAR LINE (+ 2 I)))) + 4) + (CHAR-HEX-VALUE (AND (<= I C4LIMIT) + (CHAR LINE (+ 3 I)))))))) + (LOOP :WITH BITROW = 0 :REPEAT BBH :DO + (SETQ LINE (STRING-TRIM '(#\Space #\Tab) + (READ-LINE FILE-STREAM))) + (UNLESS (EQUAL NCHARS (LENGTH LINE)) + (ERROR "Invalid BDF file - bad line in BITMAP: ~A" + LINE)) + (SETQ BITS (PARSE-WORDS)) + (SETQ WORDINDEX (* BITROW BM.RASTERWIDTH)) + (LOOP :REPEAT NWORDS :DO (IL:\\PUTBASE BM.BASE WORDINDEX + (POP BITS)) + (INCF WORDINDEX)) + (INCF BITROW))) + (SETF (GLYPH-BITMAP GLYPH) + BM))))) + (ENDCHAR (SETQ CHAR-COMPLETE T))))))) + (SETF (GLYPH-ASCENT GLYPH) + (+ (GLYPH-BBH GLYPH) + (GLYPH-BBYOFF0 GLYPH))) + (SETF (GLYPH-DESCENT GLYPH) + (ABS (MIN 0 (GLYPH-BBYOFF0 GLYPH)))) + (SETF (GLYPH-WIDTH GLYPH) + (MAX (+ (MAX 0 (GLYPH-BBXOFF0 GLYPH)) + (GLYPH-BBW GLYPH)) + (FIRST (GLYPH-DWIDTH GLYPH)))) + GLYPH)) (DEFUN WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE (BDFONT DEST-DIR &KEY FAMILY SIZE FACE ROTATION DEVICE - &AUX FULLFILENAME) + TEST &AUX FULLFILENAME) + (IL:* IL:\; "Edited 23-Feb-2026 15:57 by mth") + (IL:* IL:\; "Edited 17-Feb-2026 14:17 by mth") (IL:* IL:\; "Edited 2-Dec-2025 14:47 by mth") (IL:* IL:\; "Edited 30-Nov-2025 16:03 by mth") (IL:* IL:\; "Edited 28-Nov-2025 17:56 by mth") @@ -769,8 +831,10 @@ (IL:* IL:|;;| "CSETS correspond to the charsets actually present in the FONTDESC.") - (SETQ FULLFILENAME (MEDLEYFONT.WRITE.FONT FONTDESC (MEDLEYFONT.FILENAME FONTDESC NIL NIL - DEST-DIR))) + (SETQ FULLFILENAME (IF TEST + "WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE TEST" + (MEDLEYFONT.WRITE.FONT FONTDESC (MEDLEYFONT.FILENAME NIL FONTDESC + NIL NIL DEST-DIR)))) (LIST FULLFILENAME FONTDESC CSETS))) (DEFUN XLFD-SPLIT-FONT-NAME (NAME) (IL:* IL:\; "Edited 26-Nov-2025 09:43 by mth") @@ -880,21 +944,21 @@ "BITMAPCREATE" "BITMAPHEIGHT" "BITMAPWIDTH" "BLACKSHADE" "BLTSHADE" "BOLD" "COMPRESSED" "CHARSETINFO" - "CHARSETPROP" "DISPLAY" "FONTDESCRIPTOR" - "FONTP" "FONTPROP" "INPUT" "ITALIC" - "LIGHT" "LRSH" "MCCS" "MEDIUM" "REGULAR" - "TCONC" "UTOMCODE?" "MEDLEYFONT.FILENAME" + "DISPLAY" "FONTDESCRIPTOR" "FONTP" + "FONTPROP" "INPUT" "ITALIC" "LIGHT" "LRSH" + "MCCS" "MEDIUM" "REGULAR" "TCONC" + "UTOMCODE?" "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 (3116 10226 (BDF-TO-CHARSETINFO 3116 . 10226)) (10228 16847 (BDF-TO-FONTDESCRIPTOR -10228 . 16847)) (16849 20782 (BUILD-COMPOSITE 16849 . 20782)) (20784 21533 (CHAR-PRESENT-BIT 20784 . -21533)) (21535 21819 (COUNT-MCHARS 21535 . 21819)) (21821 24856 (GLYPHS-BY-CHARSET 21821 . 24856)) ( -24858 26283 (PACKFILENAME.STRING 24858 . 26283)) (26285 35760 (READ-BDF 26285 . 35760)) (35762 36085 ( -READ-DELIMITED-LIST-FROM-STRING 35762 . 36085)) (36087 43085 (READ-GLYPH 36087 . 43085)) (43087 44472 -(WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE 43087 . 44472)) (44474 46891 (XLFD-SPLIT-FONT-NAME 44474 . 46891) -) (46893 49905 (XLFD-TO-FACE 46893 . 49905))))) + (IL:FILEMAP (NIL (3086 10199 (BDF-TO-CHARSETINFO 3086 . 10199)) (10201 16820 (BDF-TO-FONTDESCRIPTOR +10201 . 16820)) (16822 21401 (BUILD-COMPOSITE 16822 . 21401)) (21403 22152 (CHAR-PRESENT-BIT 21403 . +22152)) (22154 22438 (COUNT-MCHARS 22154 . 22438)) (22440 25475 (GLYPHS-BY-CHARSET 22440 . 25475)) ( +25477 26902 (PACKFILENAME.STRING 25477 . 26902)) (26904 37150 (READ-BDF 26904 . 37150)) (37152 37475 ( +READ-DELIMITED-LIST-FROM-STRING 37152 . 37475)) (37477 46234 (READ-GLYPH 37477 . 46234)) (46236 47972 +(WRITE-BDF-TO-MEDLEYDISPLAYFONT-FILE 46236 . 47972)) (47974 50391 (XLFD-SPLIT-FONT-NAME 47974 . 50391) +) (50393 53405 (XLFD-TO-FACE 50393 . 53405))))) IL:STOP diff --git a/lispusers/READ-BDF.DFASL b/lispusers/READ-BDF.DFASL index d112551a33a34cb55bdf139532ba2581acd4bb7f..f6be19701634a0d06749d47962cb96b425e17229 100644 GIT binary patch delta 8648 zcmai43w)DBw*Tf!nm!3>nub!K4Uh6D4YcJ^LA1@|Ym+v~7n1Y^NI*nGTLkKRRnoe; zzA92?!F-6jzE@pbTUf6GLVWFB>#loU1-&SUqQ7YB>^{!~(e6iq$tFKsnQw4}SK)n09)!jFNc=bX+^iG5OmoYYh z7s~y7_Nu4&3d7M>cWPUou`N*V)!Zqbk=ovu-QtS)UG9Jqg_gpGKzrEd3`bOt5{>#p z4Njk`Hi782zMP<|&Es=LqDqYFxloV!{2|0NH{WC|WNv<^aR!vn8H?@MWvsi|AMiRo zYA~!u{V@f^P8bWzu_@1^Hiu%)AZa9h`80IY>27z948uz@ipHXNRzz`moo;VEs8BMd z6_7!MQ7=@$H^s~5kR}^`o--N__+!p`6-EiUf-p9YN-Z(fS?}_oHUE~edrEC?inG*D zT|C;yheKq_c(*n7=k|!qwcxk4CE#Y;U*CahtQ}_2-qOnM`C)ONMoT|G~@x-i} zhiM+e#1uEDxIyzsFvFQ))gOu}5dvgwGf|^XO*r)SKBL$G;b!s6x zMe|s7z@}5XPK$KPV_6H69{B7rL06N4vq#ebGXchd7mQ4cG3s9?CMeep%i@J~cdKnQ zV>v-37V&$c!>gVHt`SxuF~1U}c2l&~?~VBc?;Yhc1zt6iS~5jTm8i6|z(lFwG+=~D ztU9bkRkqFU))X=q9RQqAi7Fv*HoiQ&0=(+3?72|Bm^};1li6iZX62MaIWK1(lvm{- z-k;-`0-tBnZj&TwXHRbrF?WK}YAWqro34 zxexkgLSM0E_%pecAopSJJl&Ag3v`D*ozIMx8%{x`Q)v9@!=0)$ZpeuMK zv65chX}r4Acum@7o|zT4kun~6npYta%GdEc`8evY&G^bBl^OM;$pTpr3cTr|N?wCls z@aB3~&>ztGR>j}o6R|QQ8gQW}gZvoSNieG8)um1-S5dj86qn(J zQk48tX%&=34lElT>-aAF9R7);jDKk#ySH%i9^5)_ObswzIbkMWQdVM7#Fk+@rH4{G zSjh%HV7IMp>-Sj_Yh;ULV45%0H_@xqL;iA*hhLbs7?UK;0MWfi&WBiSqSzKT0h@&h zF5vP9S)S%&EX8F2VYU>H-xbh&{4ZrQ`MqURDU7f~e1BPQ@u5ugUiP{?R%>MNR|uU5 z`3-NL-aguF(V7_i*+-i9zA?SY$c}Q`?Agg<7*S4AH)0=#VKbCSM2)N?@OXv_s^8n*3KmP6+aNYCAtPdtCBk zY-%SB#LgLIr;f5kBP@HH`m@&sc~Ovl%vnK?CkomaMM{R+*fpd5T1MF*NSi%TbTl5T zC}WQz zd2r-e8cq?LAc$EIiHP)(AVV5TO24A6KOlP_v1j;^su~FBW2(oSA4u(xp+(NST9Qg% zMCT`L79{!|CkJ{(SVJ6CTC$K zJurTFkY|TA3@a_VK#EG<=Y0OZ-Z>*ql3~HAp+vo zZEDhbM@W3j8XdHx1+$yRRW16kKhsB5En+yFMUt>dKAW7+azZ2DH1bL5#G!)5Jtg`V z*~LKT>rDp0#?YRskg(krw}Qp?v}z>_`v;0tD~sDZ#T+;RmqB9SBuowoyyV|AX~eK$ zaD{3r|U#cGzI$S~!o%x1ELmQB+BB zBk!C)KAGCum*ozqo+gO26_BgV4qjQrI ztlg?LBOw=;^N?*A6e)Qi<6WX=n~Y`gc-L z({3c=pQUZbwh3}Q_0EEU*hZMTYz!h!H56z^WEOl&R@fEu;rE~|r^S|iO_0Y&`bs|| zHb1V72Qk^|plY^N2$1$K_^gGCA+}$!(8reYXBU>std1XDxQHF&1vT^85ck!%GH{H$ z_&qh#*hcY{6R#%rRVF0%{Zv_zb}872#OK*Bol9rr0k}Lh0Y7c1WFx zeRaY$<@WwW&$?y(jd%DB{p)%X+x=|)*;73U%`Y=Rd)J@c=O>?hu){Yb`)S*p?MdvJ ziM7t;$R^!i2mYvgd+~ur;+kx>biyK~`f`Ixq(MK|8NmdY$4uQ!yv)E~pWF~I__U>q zw54;<@aC9G$F*9+D$Ub^XHzdEN1QtPhRcbawu}F{<}3U0B-YsRZb8h{X5n96^tjO@ zYo5IiEP9+dYfyw{ot;9~KvD~Vm3|-=1*9(p%M%w*n@_+Y1lUn*dxF^GO#+#VUQDMe zS0v(U7n(ohFJJ5&w-}|rMltzfWQ89u;l?GCGrcXEw+_zP6-(xgA&H5Mh>KsdBtrRH zF6D183BsA9agZqqC+Tey7as}Xuh=D5;XXJKjrainF6q&&K zhL{gqHc-nNeypx!*#{J2WDBNcc7xdD2|-qhEF2T0QII-8778*;NS2CPq1fj~N&PBr zc7-RdB3F>}^{s8hQO2bDfJpcA`}|kWY3RCXW8#{{j?Lc9WzAQwU%hc7JfU254v3;> z;Rp%TILL2yt+E+izM&~JTUS69EKWZtC$wn0OP zPxg$WxiMpTrm5OAm^?_EA z2zwGK)!f-?HAA*k%{zTVCesDln=^rEBIH-|iUp~1XnoU1KGLVHXm8M;BsR|9JJJ30uXGqT8G zM-d$$x0#CUa;+6f4IqmllmQRNVJHRr3XCR~%|&!S^;v_gI2V%a4w5~^YZ_E`oZs0n zgRSAuHO#PgFU1Cld#G18ZEz6yTSG~*+eX;+*`V8qPgA8JY3zrrn5l%kcyQI+i1d<+ z8@q`Z&d_n%@Tef$1i4ZWO004|n)V&2$)I~0DUsiy-f}U(W8s_4eVP-8eRWmUuD>>u*LV zv06yb1wh)4$UK2aq~!rXr1uGt^dtEpP-YZ!v;5Sqj+{12h|^E*K81s|3b@JD0L>gt7t3E)GyEv`Vb5_KXd zBJ8!?sn%Ta|JqZN>SK->TP~AV2xG)3+jnWLaJP}bO2dbgYSwn1eyU4Oh#gKbro`}k zMq*#@>V5nd>Z;`a#DT<1xTmFb;+$JTg3jKybkoDP4a&c+8;PP4L{a$>h%1Cp5x28L zs$FJr^Dun+SHS5pi8P}nKARLTN{frZsknXHpu7w!aOIIs?U|`sc2cw?h3R#0O%Z3I zVU1wjpgdc(JadlJaN9v$=brJR&-$}LcB((v8)PfsvNp)R*4uZ9=AaD%Iy<3*$r029 z=$jXcW&!y8g&?E>_V9&Z9)S1i;GT>1p;+8%GwYucgc-8ghCeJQ%gkm?Hzp~^Lk+4n zP~q2AKzf+pp;}ejduO)|vU&cU0h*fr#BFfe+~ZJf@#3^v+NLI}gX_pl}6zT19%`9d#xk6=+d8Am!=7v+KoNtHm~Z z`fh#p`qv#8JQENNq8zg5)OOf5QC|^6!2gD9aL4RW?O;bouPTmLkR8wKZ51LyLf2$I z44}@NXgL^`cD4BQHr3t>1{J3pEt1w+B%Obsgp)m<5uI?8Qk%Z+a@@-f`xBCEYc`<+tROT_*d1zsSw8(DF<{V!zc4#$XXB@G`Ss;4z%XV4`; zo(ep>L~B7Zp9C>KvdPFwPa#X!0}+177Wo=%dYeW?TO>O2%hw{ikI?Io{ROezw;*BJ z;!?Qr3IaDqkR;`ebmo>f&Jn0M-&>r}W;h@+q7<3Drhpr{krrdm%liCqQ}A?u;%;i{ zlA8`EM;ndM7#eLfoYV2}^!-g;#={2&cKG2188%xOf6jE`T`rmlvq0wKIim+EqPG7A zF#Y;E@%FV|-0|Oiv8&PtBi!lhS_GtL-Of4@tl#abJYpGk&%_{2wed<1#vZN}H2T1ONNXz&uiBBDH;8Up7j*JaCB%(`Te@HZ}}>dRE_uRXUiTU2&wZTbqn&R=SYiW>=?77q5VhO+_BZ!L|*%?z@$YKClP zO`o5TgF7h94-~4F1pFmj;w_U>#7fnoS`)>^#SjqNa?THk+5Co9_{9cY=u#7u()0Y4 z)=6xDpJ<&BP2ri*rv$=U?q*Hhgi`FdxY1fN*J_@a=qHYUzqyENua^^fZ6XrsLlL_k zH(=%ARNpNAC7W8}aKzv#4I!QqCtUm)%i)hYLjFMK-j=q{Sar>nr01Fy5Q($s$8ET_ z(0Ny`S%A@53B*cBw|nm90xD0hl_Ux?N)JjI6`qozLW=-7qR#G zmCIeBkyCF*bQxp?*#^qT2)D)GlpBA*b2yatPS0JFaD#PzP@8i6PY58dH z$IC03{qVKa+-oEdHLGrL=|NDOI6=av*Psf)@AzFo?hJMinngiM;a#j`#pP ztHKT()j-KWKFQBqHh#&M?eNQWS?_W|mhl zEi*cfc5vW`=4TiOmFsJmJCu%VC@S%F9eVF|#8JS}S?V}G)@7~&>bknFI=y@EleXa8 zwJxiiz0Y^{-e;fJKKuOn?Z30fpJzW?w_%Anw*7lMI=h@VbZ_f&cJAov*x9w&>0Inm zx;kAAa~l>o=QS*yH-Bm4T(i#|3tlZ2+J4jKj$39s7dt_^>&8u8JDjL8dsJuMz|(6P zE90f|zQO{p-yMyq@kmsQ3_c?NkS+J7_xBsyf<8s}rnxbFpx?CG9SyiWK{W;~=8(HB zs{-x&`wO5R_Xom=>0aJzEMZ=LpRoqYQ$|NCicIjd27-#qtA!$3ED%>o&w>iBNK}of z;ke5ah!a}q)mot%q8Fs*SR@#TyL=k-5_X4#FNM?+*IYig7ZCn-&e0i71!>OG_sZ#h+y6D| z2l^NCqqz&%5`NFrAfJ=xWGi?i@4F4k%=A6|mRLO6>W#NXRhQ;zR=shnt}tCOl%;ub znj3UQf?3UrXn}BCjq-A0wXo^Da78OTGF2gv$0xZv% z>&94Mtr$ds2c}L5gjagBHX5%vq{gEGZwwY!)NzT`Xgr|CsGb{J6HwxQ!7Jl@p1|kT zQcIp_nI|f%SHoyDSZHt}SF8-om)m5sdvzbVd*!l1Q;%sj?1Z#w4wR=&*FgEHsS3); z<~k_7=J`N5VX6FnTUar1JlB9#ZeZ9o|7>t@cKG&#!{IBNeAe>WxWet^q0yMb2 zpne*u`+9ZwLnA*0-8|40dzqgsmKmG@FPWOD-YCY4_i;w(WKwFI@gOYo@gqjIRE7Wnr{a5)R@hxrT6dj6)p zirc487%VP*7U%etDc@ndvZ@yFLf%&8;CrWbr&e5}-%_amQ|=P|)<%8T9Njll-)+@> zO!ti)8J{F3+b3tKF-vo^G(SttSz4H-#aU{}Qfrplved5MMyu(YoCO@Tn#Hu5C#*#& z+}0*q(b1TQCos>4r!CqpPJ+)J3IwzK8a1%eFP0)F7IbGo)U=XP+;|j#!KqgbD59~f2X>AyxF3+F!-~NH4jE>T8!*9{=lqRsdE@C-Xk?)4?{OOYBZ`vy9qqM zT93^Y+++|n?8WFERU<*TEwH{$C$fM<-auq7NaO^Al>3&hrsvopup$zv=#L;CDRK~Cr@e{gnbDeAKCj!Uf;EPrKoBU1*Au6GQD zuVGI~!q4%GbNkpQ{5$j9vp-)<(C60(;t-?|2ZEPnWD=^i5&9N?dtQ@U!LF{+eMqJX zQY?r>M0#70VI3uy zSQQmBvKjyJk~NMi&;mjOTaa{9=`YH`%FwYAVeD5RodP4H`u3~z*JTalqRz16spe;r zr;>@{Oo&|CKfsg_+wjl;TNEZBVcD-)ZVzX@6j;_vQ5o%J)JuV;L@(igbo11)5@8M9 zl%%lG4}u3_;gT(3$s`k2_;1T-maJFO9?ilMRv{rLZ2OYE-3j}C&3=bwGwkc`P2Q*3 z*o`BXdXo=mHkl<-*w%OB2->ibje}cTG#hm`(wjV92bG@eOeE|X%^Jvd?nq3|Xm+Y- zlWpjM4E_v^+#=TGY5(zx>~xBO3V5AXe6|pWlL^Vs4E+5>AZ3vZMQ|!7*2UuPu;Pv? zF0C~V$E!|{6ft?v2>jCxn6Pm}T5Q-DZZhdl>7VuIK>+kUY zMvo%-0#GvsW=Py1a3b`fXgQ&;f?A3_L6jYa^I&R?Yjg`=hjQy_GvlFR*Ajak*=zJx zBqfN*Iml8LAm;)rjf}}j|3d5z?b|M`k4Kyh|K`u%< zHDb5YkM!1?MSBmC+_j>O^yOSXf%yvwyqw1;F0`eJL67~B&M5m=L3%_?35Z&QATvmp zPHP!Vsww34##~xB*pB^v0P~k0amW2QhY0+Rl;ET*yM^8lh?tv*6ch3y10$gZ{{D(+0d7@qP>aE{fiJ%{1lQ$V*S^7)^Umu6jpWL&N!X3XA3>Jk zkfeKvy@>4X$V!hj3xvOWoul%#QmVXW+zp=RtZLQbDqU)_cPTc=UIrJ?oSYIqzo{EC z++Q@!fgWFOaxTN*%kplLq>2+=TR(fWCn+KeMWAx|NmJ7QjZMnj-a%v(%}EaRbqw)Z z_n8ulBpaYhQyt_YEs%fI>8(bIAM!Y-(giJ>VYn02Hh2Jq-9dQV;E6QyuRZ0q*9*xr zlL-eyr$CPKo!-c_Xe~9Etwdx4wvUKw!g!AFQa0KQU-^e;P<+}9emOD^4H!P;my{aW z@C=`#u3|mBM_px|LZQ+>Y!^csFy!+;s18~FFCe8k#>{|xhBz~Dza_2^xHP}kHiPis>@^Fu~y#dpUn^Y9k%2C;e7vaxo|kpb9QK8q=H}Y&ozB*@DEGJ zJJ(qGh3*|Ei$hp* zH8#wj4)ba}18Y~X+8u0FW3FHzhQZSK6e(ipF_sa#6xjlbj7Dh;F?@t9qg3hl#NzHX zJVpX}$j0lQuaBoN6vlb=t^-EhaE(#E%3R28@H0r5GO<p%uL?iR8u@Ve`z83KDfYsk;2jJXeheN+@XC&B8U|boetUw86+S{`1MK5kL*uOs zIwP~_RWB29XE!qvcAF$6((pW2e0b?emk9hZpAh-Yq|$VMo}vZ=p+MXiBzzNpH8PEv z_{B)$thHeFTZ7HVBjjpau)r{t#5ahpy7-z^i@g7@8;jEY%vrN{y}VBNAdKI6NMDn_ zPXgZz_wr9yZA_g=4kala9t|T&NVNio_YBDQH__Q&`_O>=6No_YkBWqqJ*`<~wq9VL zJ=z?iz;QAtVphVse?V@_R*ugMAEXFJs+sOne#+{ED%=DEDY-uU#1#^v<*8Bi$wL0d zkrsAoUrS#Lv&oqO_Eom|kZ5hOLG)!GejCgM@YQdF`2aqB1qeC1)MA5}%^tl1B@n_l z+n)uGFUxkt62-RSOtu0!uEk~xer)A85i?`*|%qAR}ZL;P#mncKbBCvAE&7JUn2KO@YJ0x< zZ2y4tBF(8-L9skqnV8%EptQY?YjwxUnz!KirEL~kx^D4?NjOpB=VYLnbTeBuN6!bB z9@U)N@;`!JwQ6nhIp}qpMJw+)dk%ci$`f@Ntq%I$cWj|%*DB9I+YD0HoJ3xhGFoNN z>xtQ*r_I=AISU-Zu zE8s;D{G9H>PrEBsd|b$sj>#L5b<+X<9(H?`^pY(K`vfK_um!>qyf?(O&LQsI8*`NlNaZ z+*a-od38rUfQFsoGD`{)7bGmmJV9m&QXxpGAQJ^KLWssI6E8lxGUYF+LGFZHu&^2O zVV64?)Yhm9j$d8}Xin6v1lpi)_6fQry@af|2Z?=-Mlb=Lq6;>~`~qqsA-ZAb3uG1{bi@3f+}V}q*nzp z1Rj{+@fwYfjb1v6QgWZBE8^z->k3_2M7G|moTV#O#QtT9u1q5C-8}qSjW*vgqT=^? zYuntSh44vhuX8UY%kT63Z7XHz1^!l>s}OcTo46mO$N48~V{-BtzUumll^Y=NWx^Qn zAO>9-upy8YCzEb^c+){&kD)E;a5&(Ew3(!ng$RFWm`~VDziOh+c5Bu65#A*!g@UvMG0s4nN}{J4j!mP;&z*ZC(c_F`s^If;>R%@0ta8 zg4JNWoJ5|TLZpU&vEgJ>J!OsZ^WrkE_u0oY-3Ujvx!&BjU-qk%@!!NH*hm5%icC`tV@y#<|S?+)lFf+(JRb zc+c~fI%-odW52({ha+wtw-+8wh=|kv@-3wB$`)wPg=Gk|hL#DWM)#HLz7ie2nfu7~ zvuU1#+a*fvFC!ZsXH{68e6=kCA6QiS?N8nt1Dj?qzhTp))LHEDe*8g@i~NJsudo!r z{sVD0WOZm*Bg1dC!!J7HU7kTUL@mUM-ih@P`Ueycfr!{37|jX72X+4Crat2ei@t*I z?wrn=_%A!B<~K3;jn1eq;Lmq%HSB-NPG!I_jwHX^E191RVfo~2VHEbE!4XGFSW zD0JvlcuO*7%-Cwn5GZsg^bZK>|Ik9$(xLCggtQM1?%jRv?tAwQFX9jRN*9N0O4tFk zpX$(VwV!=_`(thqI$Pe(^KG}^YYJ8itDAu0vUVYLkt5R~i6Eny(RV!^0y51phjrJRwQd5ZFhm(oQw$-ZX-bPE+Xo~;d=*u|z_Dy& z%kJtRv00w-7#U1hiw2mhmIQ*BWC1FkO*8W!O(8~Pn(|zw{BpUzQmsM9cXs^}mQJgO z;6}4AB>9a}*&oaoj_o;nw!8b%v-Y|>eIKGEMJSRql_Xcn3zl$>a^AWh%ld*yDPbsg zk;5<}Ly2?Dx!NhB?D_yS0y2>|!i+ayjAJTKpCTFMW^+M&UVK>oT)w=gJt@CFetm0U zwIbh~v?XB}xz-ma#w%?@{5BqFJ<-tbKk6w!lU7%bBHt_Lzeqz^=9#up{xQF6ckW)N p6?;|iuDob{(dwRK4SJ5>1aaJkRnxY7r!(6B#VeJynpSyp_7_vhr40Z8 delta 167 zcmV;Y09gOmXv<}ga}Y#jV`Xh(WpW@gEFdy4GBvS;?F9lU36p#VWCJcBMU&$OzLTH_ zvy)N?a|C5%E+CVR12dER2pqG=2@nIb-3+M%0Sc3U5+k!X5!xC7F0&IXTLKCQ00000 z1poj54U>2-a{*72>@HXVER#hqBLPU0TP-31LzA#Cx&bA#YcSdY0T#1ZGQ