From 39ebd40da4582455cf8ede892b72e71db15cc323 Mon Sep 17 00:00:00 2001 From: Matt Heffron Date: Mon, 28 Apr 2025 11:40:08 -0700 Subject: [PATCH] READ-BDF: Handle newly encountered cases in BDF files. (#2108) * Better handling of a glyph with ENCODING of -1. I treat it as the _slug_ glyph, instead of the _default_ of a solid block. * Handle scrambled bitmaps issue #2109. Glyphs with zero width bitmap *and* zero advance (_escapement_) caused miscalculated glyph offsets into the CHARSETINFO bitmap. * Allow and ignore COMMENT lines preceding the STARTFONT line. Add error checking for extracting font FAMILY, SIZE, FACE, etc. from the BDF-FONT object. Add recommendation to documentation to write the DISPLAYFONT files to a directory separate from the system's IL:DISPLAYFONTDIRECTORIES locations. * Account for glyphs with a negative initial offset. * Add VERBOSE optional parameter to READ-BDF to report font internal FAMILY, FACE, etc. Change &OPTIONAL parameters of WRITE-BDF-TO-DISPLAYFONT-FILES to &KEY to simplify calling. (No need to remember the order.) Add CHAR-SETS and WRITE-UNMAPPED parameters to WRITE-BDF-TO-DISPLAYFONT-FILES to allow some level of control of which DISPLAYFONT files are written. Updated documentation, and added warning note about font's FAMILY containing any digits. --- lispusers/READ-BDF | 906 ++++++++++++++++++++++----------------- lispusers/READ-BDF.DFASL | Bin 19858 -> 20849 bytes lispusers/READ-BDF.TEDIT | Bin 6302 -> 9640 bytes 3 files changed, 504 insertions(+), 402 deletions(-) diff --git a/lispusers/READ-BDF b/lispusers/READ-BDF index afc91dd6..9173001d 100644 --- a/lispusers/READ-BDF +++ b/lispusers/READ-BDF @@ -5,25 +5,25 @@ BITMAPHEIGHT BITMAPWIDTH BLACKSHADE BLTSHADE BOLD CONDENSED CHARSETINFO DISPLAY FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITESTRIKEFONTFILE)) READTABLE "XCL" BASE 10) -(IL:FILECREATED " 5-Mar-2025 12:44:10" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;39| 42641 +(IL:FILECREATED "25-Apr-2025 10:10:08" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;57| 47436 :EDIT-BY "mth" - :CHANGES-TO (IL:FUNCTIONS BDF-TO-CHARSETINFO READ-GLYPH) + :CHANGES-TO (IL:FUNCTIONS WRITE-BDF-TO-DISPLAYFONT-FILES READ-BDF BDF-TO-CHARSETINFO READ-GLYPH + GET-FAMILY-FACE-SIZE-FROM-NAME SPLIT-FONT-NAME) - :PREVIOUS-DATE "26-Feb-2025 15:23:23" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;37| + :PREVIOUS-DATE "23-Apr-2025 17:55:25" IL:|{DSK}matt>Interlisp>medley>lispusers>READ-BDF.;54| ) (IL:PRETTYCOMPRINT IL:READ-BDFCOMS) (IL:RPAQQ IL:READ-BDFCOMS - ((IL:STRUCTURES BDF-FONT GL-LIMITS GLYPH) + ((IL:STRUCTURES BDF-FONT GLYPH) (IL:VARIABLES MAXCHARSET MAXTHINCHAR NOMAPPINGCHARSET) - (IL:FUNCTIONS FIXUP-CHARSETINFO GET-FAMILY-FACE-SIZE-FROM-NAME PACKFILENAME.STRING - READ-BDF READ-DELIMITED-LIST-FROM-STRING READ-GLYPH) - (IL:FUNCTIONS BDF-TO-CHARSETINFO BDF-TO-FONTDESCRIPTOR GET-GLYPH-LIMITS GLYPHS-BY-CHARSET - SPLIT-FONT-NAME WRITE-BDF-TO-DISPLAYFONT-FILES) + (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:DECLARE\: IL:EVAL@COMPILE IL:DONTCOPY (IL:FILES (IL:LOADCOMP) IL:FONT)) (FILE-ENVIRONMENTS "READ-BDF") @@ -31,90 +31,287 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST IL:READ-BDF))) (DEFSTRUCT (BDF-FONT (:CONC-NAME "BF-")) + "Main structure to hold a parsed BDF font file" (NAME NIL :TYPE STRING) (SIZE NIL :TYPE LIST) (BOUNDINGBOX NIL :TYPE LIST) (METRICSSET 0 :TYPE (INTEGER 0 2)) (PROPERTIES NIL :TYPE LIST) - SWIDTH DWIDTH SWIDTH1 DWIDTH1 VVECTOR (GLYPHS NIL :TYPE LIST)) + SWIDTH DWIDTH SWIDTH1 DWIDTH1 VVECTOR (GLYPHS NIL :TYPE LIST) + (SLUG NIL :TYPE GLYPH)) -(DEFSTRUCT (GL-LIMITS (:CONC-NAME "GLIM-")) +(DEFSTRUCT GLYPH + "This is an individual BDF glyph. Includes some values calculted for creating CHARSETINFO" + (NAME NIL :TYPE STRING) + ENCODING SWIDTH DWIDTH SWIDTH1 DWIDTH1 VVECTOR BBW BBH BBXOFF0 BBYOFF0 BITMAP (XCODE 0 :TYPE INTEGER) - (GLYPH NIL :TYPE GLYPH) (WIDTH 0 :TYPE INTEGER) (ASCENT 0 :TYPE INTEGER) (DESCENT 0 :TYPE INTEGER)) -(DEFSTRUCT GLYPH - (NAME NIL :TYPE STRING) - ENCODING SWIDTH DWIDTH SWIDTH1 DWIDTH1 VVECTOR BBW BBH BBXOFF0 BBYOFF0 BITMAP) - (DEFCONSTANT MAXCHARSET 255) (DEFCONSTANT MAXTHINCHAR 255) (DEFCONSTANT NOMAPPINGCHARSET (1+ MAXCHARSET)) -(DEFUN FIXUP-CHARSETINFO (CSINFO ASCENT DESCENT SLUGWIDTH) - (IL:* IL:\; "Edited 3-Feb-2025 19:19 by mth") - (LET* ((CSASCENT (IL:|fetch| (CHARSETINFO IL:CHARSETASCENT) IL:|of| CSINFO)) - (CSDESCENT (IL:|fetch| (CHARSETINFO IL:CHARSETDESCENT) IL:|of| CSINFO)) - (WIDTHS (IL:|fetch| (CHARSETINFO IL:WIDTHS) IL:|of| CSINFO)) - (BMAP (IL:|fetch| (CHARSETINFO IL:CHARSETBITMAP) IL:|of| CSINFO)) - (AMARGIN (- ASCENT CSASCENT)) - (DMARGIN (- DESCENT CSDESCENT)) - NEWBMAP) - (SETQ NEWBMAP (BITMAPCREATE (+ (BITMAPWIDTH BMAP) - SLUGWIDTH) - (+ ASCENT DESCENT) - 1)) - (BITBLT BMAP 0 0 NEWBMAP 0 DMARGIN (BITMAPWIDTH BMAP) - (BITMAPHEIGHT BMAP) - 'INPUT - 'IL:REPLACE) - (BLTSHADE BLACKSHADE NEWBMAP (1+ (BITMAPWIDTH BMAP)) - 0 - (1- SLUGWIDTH) - (+ ASCENT DESCENT) - 'IL:REPLACE) - (IL:|replace| (CHARSETINFO IL:CHARSETBITMAP) IL:|of| CSINFO IL:|with| NEWBMAP) - (IL:|replace| (CHARSETINFO IL:CHARSETASCENT) IL:|of| CSINFO IL:|with| ASCENT) - (IL:|replace| (CHARSETINFO IL:CHARSETDESCENT) IL:|of| CSINFO IL:|with| DESCENT) - (LOOP :FOR I :FROM 0 :TO (+ MAXTHINCHAR 2) - :WHEN - (ZEROP (\\FGETWIDTH WIDTHS I)) - :DO - (\\FSETWIDTH WIDTHS I SLUGWIDTH)))) +(DEFUN BDF-TO-CHARSETINFO (FONT CSET SLUG-OR-WIDTH &OPTIONAL MAP-UNKNOWN-TO-PRIVATE) + (IL:* IL:\; "Edited 23-Apr-2025 17:53 by mth") + (IL:* IL:\; "Edited 21-Apr-2025 16:23 by mth") + (IL:* IL:\; "Edited 30-Jan-2025 16:40 by mth") + (LET (GBCS CSGLYPHS CSLIMITS) + (UNLESS (AND (INTEGERP CSET) + (<= 0 CSET MAXCHARSET)) + (ERROR "Invalid Character set: ~S" CSET) -(DEFUN GET-FAMILY-FACE-SIZE-FROM-NAME (BDFONT) (IL:* IL:\; "Edited 5-Feb-2025 12:56 by mth") + (IL:* IL:|;;| "Can we get here? I think not!") + + (SETQ CSET 0)) + (SETQ GBCS (COND + ((LISTP FONT) + + (IL:* IL:|;;| + "Assuming that FONT is already the LIST of ALIST form of result from GLYPHS-BY-CHARSET") + + FONT) + ((BDF-FONT-P FONT) + + (IL:* IL:|;;| + "If passed a BDF-FONT, look only at glyphs in the mapped charsets") + + (FIRST (GLYPHS-BY-CHARSET FONT MAP-UNKNOWN-TO-PRIVATE))) + (T (ERROR "Invalid FONT: ~S" FONT)))) + (WHEN (SETQ CSGLYPHS (SECOND (ASSOC CSET GBCS))) + (LET ((TOTAL-WIDTH 0) + (ASCENT 0) + (DESCENT 0) + (FIRSTCHAR MOST-POSITIVE-FIXNUM) + (LASTCHAR MOST-NEGATIVE-FIXNUM) + (CSINFO (IL:|create| CHARSETINFO)) + (DLEFT 0) + SLUG SLUGWIDTH GLYPHS-LIMITS BMAP OFFSETS HEIGHT WIDTHS) + (COND + ((GLYPH-P SLUG-OR-WIDTH) + (SETQ SLUG SLUG-OR-WIDTH) + (SETQ SLUGWIDTH (1+ (GLYPH-WIDTH SLUG))) + (SETQ ASCENT (MAX ASCENT (GLYPH-ASCENT SLUG))) + (SETQ DESCENT (MAX DESCENT (GLYPH-DESCENT SLUG)))) + ((INTEGERP SLUG-OR-WIDTH) + (SETQ SLUGWIDTH SLUG-OR-WIDTH)) + (T (ERROR "Invalid SLUG-OR-WIDTH: ~S" SLUG-OR-WIDTH))) + (SETQ CSGLYPHS (LOOP :FOR XGL :IN CSGLYPHS :COLLECT (LET* ((XCODE (CAR XGL)) + (GL (CDR XGL)) + (GWIDTH (GLYPH-WIDTH + GL)) + (ASC (GLYPH-ASCENT GL)) + (DSC (GLYPH-DESCENT + GL))) + + (IL:* IL:|;;| "It's possible that ALL glyphs in the character set are above the baseline. In that case, the GLYPH-DESCENT calculated by READ-GLYPH will not give a useful value, since it is >= 0. Investigate correcting this.") + + (IL:* IL:|;;| +  + "Is the above statement actually true?") + + (SETF (GLYPH-XCODE GL) + XCODE) + (SETQ FIRSTCHAR + (MIN FIRSTCHAR XCODE + )) + (SETQ LASTCHAR + (MAX LASTCHAR XCODE) + ) + (INCF TOTAL-WIDTH GWIDTH) + (SETQ ASCENT + (MAX ASCENT ASC)) + (SETQ DESCENT + (MAX DESCENT DSC)) + GL))) + (IL:|replace| (CHARSETINFO IL:CHARSETASCENT) IL:|of| CSINFO IL:|with| ASCENT) + (IL:|replace| (CHARSETINFO IL:CHARSETDESCENT) IL:|of| CSINFO IL:|with| DESCENT) + (SETQ OFFSETS (IL:|fetch| (CHARSETINFO IL:OFFSETS) IL:|of| CSINFO)) + + (IL:* IL:|;;| + "Initialize the offsets to the TOTAL-WIDTH (without the SLUG. It will be added later)") + + (IL:|for| I IL:|from| 0 IL:|to| (+ MAXTHINCHAR 2) IL:|do| (\\FSETOFFSET OFFSETS I + TOTAL-WIDTH)) + (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| (\\FSETWIDTH WIDTHS I + SLUGWIDTH)) + (IL:|replace| (CHARSETINFO IL:IMAGEWIDTHS) IL:|of| CSINFO IL:|with| WIDTHS) + + (IL:* IL:|;;| "JDS 12/4/92: Apparently, these fields can be signed values, if all chars, e.g., ride above the base line. ") + + (IL:* IL:|;;| " From \\READSTRIKEFONTFILE, so -ve DESCENT is possible?") + + (SETQ HEIGHT (+ ASCENT DESCENT)) + (SETQ BMAP (BITMAPCREATE (+ TOTAL-WIDTH SLUGWIDTH) + HEIGHT 1)) + (IL:|replace| (CHARSETINFO IL:CHARSETBITMAP) IL:|of| CSINFO IL:|with| BMAP) + (LOOP :FOR GL :IN CSGLYPHS :WITH GLBM :WITH GLW :WITH XCODE :DO (SETQ GLBM + (GLYPH-BITMAP + GL)) + (SETQ GLW (GLYPH-WIDTH GL)) + (SETQ XCODE (GLYPH-XCODE GL)) + (BITBLT GLBM 0 0 BMAP (+ DLEFT (MAX 0 (GLYPH-BBXOFF0 GL))) + (+ DESCENT (GLYPH-BBYOFF0 GL)) + (BITMAPWIDTH GLBM) + (BITMAPHEIGHT GLBM) + 'INPUT + 'IL:REPLACE) + (\\FSETOFFSET OFFSETS XCODE DLEFT) + (\\FSETOFFSET WIDTHS XCODE GLW) + (INCF DLEFT GLW)) + + (IL:* IL:|;;| "Now insert the SLUG glyph into the BMAP, or make a slug (block)") + + (IF SLUG + (LET ((GLBM (GLYPH-BITMAP SLUG))) + (BITBLT GLBM 0 0 BMAP (+ TOTAL-WIDTH (MAX 0 (GLYPH-BBXOFF0 SLUG))) + (+ DESCENT (GLYPH-BBYOFF0 SLUG)) + (BITMAPWIDTH GLBM) + (BITMAPHEIGHT GLBM) + 'INPUT + 'IL:REPLACE)) + (BLTSHADE BLACKSHADE BMAP (1+ TOTAL-WIDTH) + 0 + (1- SLUGWIDTH) + (+ ASCENT DESCENT) + 'IL:REPLACE)) + CSINFO)))) + +(DEFUN BDF-TO-FONTDESCRIPTOR (BDFONT FAMILY SIZE FACE ROTATION DEVICE &OPTIONAL + MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) + (IL:* IL:\; "Edited 21-Apr-2025 16:03 by mth") + (IL:* IL:\; "Edited 30-Jan-2025 21:27 by mth") + (WHEN (AND (BDF-FONT-P BDFONT) + FAMILY) (IL:* IL:\; "FAMILY Cannot be NIL") + (PROG* ((SLUG (BF-SLUG BDFONT)) + (SLUGWIDTH (AND SLUG (GLYPH-WIDTH SLUG))) + FONTDESC DEV GBCSL CHARSETS) + (WHEN (FONTP FAMILY) + (RETURN (BDF-TO-FONTDESCRIPTOR BDFONT (FONTPROP FAMILY 'IL:FAMILY) + (OR SIZE (FONTPROP FAMILY 'IL:SIZE)) + (OR FACE (FONTPROP FAMILY 'IL:FACE)) + (OR ROTATION (FONTPROP FAMILY 'IL:ROTATION)) + (OR DEVICE (FONTPROP FAMILY 'IL:DEVICE)) + MAP-UNKNOWN-TO-PRIVATE))) + (WHEN (LISTP FAMILY) + (RETURN (BDF-TO-FONTDESCRIPTOR BDFONT (FIRST FAMILY) + (OR (SECOND FAMILY) + SIZE) + (OR (THIRD FAMILY) + FACE "MRR") + (OR (FOURTH FAMILY) + ROTATION 0) + (OR (FIFTH FAMILY) + DEVICE + 'DISPLAY) + MAP-UNKNOWN-TO-PRIVATE))) + (SETQ FAMILY (\\FONTSYMBOL FAMILY)) + (UNLESS (AND (INTEGERP SIZE) + (PLUSP SIZE)) + (ERROR "Invalid SIZE: ~S~%" SIZE)) + (COND + ((NULL ROTATION) + (SETQ ROTATION 0)) + ((NOT (AND (INTEGERP ROTATION) + (>= ROTATION 0))) + (IL:\\ILLEGAL.ARG ROTATION))) + (SETQ DEV DEVICE) + (SETQ DEV (COND + ((NULL DEVICE) + 'DISPLAY) + ((AND (SYMBOLP DEVICE) + (NOT (EQ DEVICE T))) + + (IL:* IL:|;;| + "Maybe wrong case or package, but we bet it's OK and defer expensive coercion until we've failed.") + + DEVICE) + ((STRINGP DEVICE) + (INTERN (STRING-UPCASE DEVICE) + "IL")) + (T (IL:\\ILLEGAL.ARG DEVICE)))) + (SETQ FACE (\\FONTFACE FACE NIL DEV)) + (SETQ GBCSL (GLYPHS-BY-CHARSET BDFONT MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING)) + (UNLESS SLUGWIDTH + + (IL:* IL:|;;| + "If GLYPHS-BY-CHARSET didn't determine the SLUG width, use 60% of the SIZE, at least 1") + + (SETQ SLUGWIDTH (OR (THIRD GBCSL) + (MAX 1 (ROUND (* 0.6 SIZE)))))) + (FLET ((GBCS-TO-FONTDESC + (GBCS FAMILY) + (LET (FONTDESC CHARSETS) + (WHEN GBCS + (SETQ FONTDESC + (IL:|create| FONTDESCRIPTOR + IL:FONTDEVICE IL:_ DEV + IL:FONTFAMILY IL:_ FAMILY + IL:FONTSIZE IL:_ SIZE + IL:FONTFACE IL:_ FACE + IL:|\\SFAscent| IL:_ 0 + IL:|\\SFDescent| IL:_ 0 + IL:|\\SFHeight| IL:_ 0 + IL:ROTATION IL:_ ROTATION + IL:FONTDEVICESPEC IL:_ (LIST FAMILY SIZE FACE ROTATION + DEV))) + (SETQ CHARSETS (LOOP :FOR CS :IN GBCS :WITH CSET :WITH CSINFO :NCONC + (WHEN (<= 0 (SETQ CSET (FIRST CS)) + MAXCHARSET) + (SETQ CSINFO (BDF-TO-CHARSETINFO + GBCS CSET (OR SLUG (1+ + SLUGWIDTH + )))) + (\\INSTALLCHARSETINFO FONTDESC CSINFO CSET) + (LIST CSET))))) + (LIST FONTDESC CHARSETS)))) + (RETURN (VALUES-LIST (NCONC (GBCS-TO-FONTDESC (FIRST GBCSL) + FAMILY) + (GBCS-TO-FONTDESC (SECOND GBCSL) + (\\FONTSYMBOL (CONCATENATE 'STRING + (SYMBOL-NAME FAMILY) + "-UNMAPPED"))) + (LIST (ASSOC NOMAPPINGCHARSET (FIRST GBCSL) + :TEST + #'EQL))))))))) + +(DEFUN GET-FAMILY-FACE-SIZE-FROM-NAME (BDFONT) (IL:* IL:\; "Edited 23-Apr-2025 16:20 by mth") + (IL:* IL:\; "Edited 5-Feb-2025 12:56 by mth") (UNLESS (TYPEP BDFONT 'BDF-FONT) - (ERROR "Not a BDF-FONT: ~S~%" BDFONT)) + (ERROR "Not a BDF-FONT: ~S~%" BDFONT)) (DESTRUCTURING-BIND (FOUNDRY FAMILY WEIGHT SLANT EXPANSION ADD_STYLE_NAME PIXEL-SIZE POINT-SIZE) (SPLIT-FONT-NAME (BF-NAME BDFONT)) (IL:* IL:\; "Parse as XLFD format") (DECLARE (IGNORE FOUNDRY ADD_STYLE_NAME)) (IL:* IL:\;  "Don't need FOUNDRY or ADD_STYLE_NAME") (SETQ FAMILY (REMOVE #\Space FAMILY :TEST #'CHAR=)) - (SETQ WEIGHT (OR (CDR (ASSOC (CHAR-UPCASE (ELT WEIGHT 0)) - '((#\R . MEDIUM) - (#\M . MEDIUM) - (#\N . MEDIUM) - (#\B . BOLD) - (#\D . BOLD) - (#\L . LIGHT)))) + (SETQ WEIGHT (OR (AND WEIGHT (CDR (ASSOC (CHAR-UPCASE (ELT WEIGHT 0)) + '((#\R . MEDIUM) + (#\M . MEDIUM) + (#\N . MEDIUM) + (#\B . BOLD) + (#\D . BOLD) + (#\L . LIGHT))))) 'MEDIUM)) (IL:* IL:\; "DemiBold => BOLD") - (SETQ SLANT (OR (CDR (ASSOC (STRING-UPCASE SLANT) - '(("R" . REGULAR) - ("I" . ITALIC) - ("O" . ITALIC)))) + (SETQ SLANT (OR (AND SLANT (CDR (ASSOC (STRING-UPCASE SLANT) + '(("R" . REGULAR) + ("I" . ITALIC) + ("O" . ITALIC))))) 'REGULAR)) (IL:* IL:\; "Oblique => ITALIC") (IL:* IL:\; "Ignore others") - (SETQ EXPANSION (OR (CDR (ASSOC (CHAR-UPCASE (ELT EXPANSION 0)) - '((#\R . REGULAR) - (#\N . REGULAR) - (#\B . BOLD) - (#\S . CONDENSED) - (#\C . CONDENSED)))) + (SETQ EXPANSION (OR (AND EXPANSION (CDR (ASSOC (CHAR-UPCASE (ELT EXPANSION 0)) + '((#\R . REGULAR) + (#\N . REGULAR) + (#\B . BOLD) + (#\S . CONDENSED) + (#\C . CONDENSED))))) 'REGULAR)) (IL:* IL:\;  "S is for \"SemiCondensed\", Assuming \"Condensed\"") @@ -137,6 +334,139 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST POINT-SIZE (FIRST (BF-SIZE BDFONT)))))) +(DEFUN GLYPHS-BY-CHARSET (FONT &OPTIONAL MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) + (IL:* IL:\; "Edited 21-Apr-2025 15:48 by mth") + (IL:* IL:\; "Edited 9-Jan-2025 11:23 by mth") + (LET* ((NCSETS (+ MAXCHARSET 2)) + (CSETS (MAKE-ARRAY NCSETS :INITIAL-CONTENTS (LOOP :REPEAT NCSETS :COLLECT (CONS NIL)))) + (UTOXFN (COND + (RAW-UNICODE-MAPPING #'IDENTITY) + (MAP-UNKNOWN-TO-PRIVATE #'UTOXCODE) + (T #'UTOXCODE?))) + (SLUG (BF-SLUG FONT)) + (SLUGWIDTH (AND SLUG (GLYPH-WIDTH SLUG))) + NOMAPPINGCSETS ENC XCODE XCS) + (UNLESS (OR MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) + (SETQ NOMAPPINGCSETS (MAKE-ARRAY NCSETS :INITIAL-CONTENTS (LOOP :REPEAT NCSETS :COLLECT + (CONS NIL))))) + (FLET ((PUT-GLYPH-IN-CHARSET-ARRAY (CODE GLYPH CSARRAY) + (TCONC (AREF CSARRAY (LRSH CODE 8)) + (CONS (LOGAND CODE 255) + GLYPH)))) + (LOOP :FOR GL :IN (BF-GLYPHS FONT) + :UNLESS + (EQ GL SLUG) + :DO + (SETQ XCS NIL) + (SETQ ENC (GLYPH-ENCODING GL)) + (WHEN (LISTP ENC) + + (IL:* IL:|;;| + "Should happen only if -1 is first on ENCODING line in BDF file") + + (SETQ ENC (OR (SECOND ENC) + -1)) + + (IL:* IL:|;;| + "The -1 case of the (OR ...) shouldn't happen. The (EQ GL SLUG) test above should have caught it") + + ) + (SETQ XCODE (AND (INTEGERP ENC) + (PLUSP ENC) + (FUNCALL UTOXFN ENC))) + (IF RAW-UNICODE-MAPPING + (COND + ((> ENC 65535) + (WARN "~&Unicode encoding is beyond 16 bits: ~5X" ENC) + (TCONC (AREF CSETS NOMAPPINGCHARSET) + (CONS ENC GL))) + ((AND NIL (= 255 (LOGAND ENC 255))) + + (IL:* IL:|;;| + "Temporarily? disable this warning in RAW-UNICODE-MAPPING mode") + + (WARN + "~&Unicode encoding char byte (~2X,FF)=(~O,377) may not =FF in FONTDESCRIPTOR" + (LRSH ENC 8) + (LRSH ENC 8)) + (TCONC (AREF CSETS NOMAPPINGCHARSET) + (CONS ENC GL))) + (T (PUT-GLYPH-IN-CHARSET-ARRAY ENC GL CSETS))) + (COND + ((NULL XCODE) + + (IL:* IL:|;;| "These assoc with the Unicode encoding") + + (COND + ((OR (> ENC 65535) + (= 255 (LOGAND ENC 255))) + + (IL:* IL:|;;| + "Unicode encoding is > xFFFF, or encoding low byte is FF, put it in the NOMAPPINGCHARSET") + + (TCONC (AREF CSETS NOMAPPINGCHARSET) + (CONS ENC GL))) + (T (PUT-GLYPH-IN-CHARSET-ARRAY ENC GL NOMAPPINGCSETS)))) + ((AND (INTEGERP XCODE) + (<= 0 XCODE 65535)) + + (IL:* IL:|;;| + "These assoc with the 8 bit character code within the charset") + + (PUT-GLYPH-IN-CHARSET-ARRAY XCODE GL CSETS) + + (IL:* IL:|;;| "Default SLUG width is width of A.") + + (WHEN (AND (NOT SLUGWIDTH) + (= ENC (CHAR-CODE #\A))) + + (IL:* IL:|;;| "A is the same code in XCCS and UNICODE ") + + (IL:* IL:|;;| + "Comparing with ENC, not XCODE, to look only in charset 0") + + (SETQ SLUGWIDTH (GLYPH-WIDTH GL)))) + ((LISTP XCODE) + + (IL:* IL:|;;| + "These assoc with the 8 bit character code within the charset (like above)") + + (LOOP :FOR XC :IN XCODE :WITH CS :UNLESS (MEMBER (SETQ CS + (LRSH XC 8)) + XCS) + :DO + (PUSH CS XCS) + (PUT-GLYPH-IN-CHARSET-ARRAY XC GL CSETS))) + (T (ERROR "Invalid XCODE: ~A~%")))))) + + (IL:* IL:|;;| "Extract the lists from the TCONC pointers") + + (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :DO (SETF (AREF CSETS I) + (SORT (REMOVE-DUPLICATES + (CAR (AREF CSETS I)) + :TEST + #'EQUAL) + #'< :KEY #'CAR))) + (SETQ CSETS (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :NCONC + (LET ((CS (AREF CSETS I))) + (WHEN CS + (LIST (LIST I CS)))))) + + (IL:* IL:|;;| "Likewise for the NOMAPPINGCSETS, if any.") + + (WHEN NOMAPPINGCSETS + (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :DO + (SETF (AREF NOMAPPINGCSETS I) + (SORT (REMOVE-DUPLICATES (CAR (AREF NOMAPPINGCSETS I)) + :TEST + #'EQUAL) + #'< :KEY #'CAR))) + (SETQ NOMAPPINGCSETS (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :NCONC + (LET ((CS (AREF NOMAPPINGCSETS I))) + (WHEN CS + (LIST (LIST I CS))))))) + (LIST CSETS NOMAPPINGCSETS SLUGWIDTH))) + (DEFMACRO PACKFILENAME.STRING (&WHOLE WHOLE) (IL:* IL:\; "Edited 1-Feb-2025 23:17 by mth") `(IL:PACKFILENAME.STRING ,@(LOOP :FOR X :IN (CDR WHOLE) :BY @@ -157,17 +487,21 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST X)) Y)))) -(DEFUN READ-BDF (PATH) (IL:* IL:\; "Edited 26-Feb-2025 15:22 by mth") - (IL:* IL:\; "Edited 23-Sep-2024 12:37 by mth") - (IL:* IL:\; "Edited 22-Aug-2024 16:43 by mth") - (IL:* IL:\; "Edited 17-Jul-2024 14:45 by mth") +(DEFUN READ-BDF (PATH &OPTIONAL VERBOSE) (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 (NGLYPHS 0) + (PROPS PROPS-COMPLETE CHARS-COUNT FONT-COMPLETE FONT POS KEY V VV LINE ITEMS GL (NGLYPHS 0) (*PACKAGE* (FIND-PACKAGE "BDF"))) (WITH-OPEN-FILE (FILE-STREAM PATH :ELEMENT-TYPE 'CHARACTER :DIRECTION :INPUT) - (UNLESS (STRING-EQUAL "STARTFONT" (READ FILE-STREAM)) + (LOOP :WHILE (STRING-EQUAL "COMMENT" (SETQ KEY (READ FILE-STREAM))) + :DO + + (IL:* IL:|;;| "Ignore initial COMMENT lines.") + + (READ-LINE FILE-STREAM)) + (UNLESS (STRING-EQUAL "STARTFONT" KEY) (ERROR "Invalid BDF file - must begin with STARTFONT.")) (IL:* IL:|;;| "ignore the file format version number") @@ -252,8 +586,30 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST (ERROR "Invalid BDF file - CHARS count (~A) is invalid or missing." NGLYPHS)) (SETF (BF-GLYPHS FONT) - (LOOP :REPEAT NGLYPHS :COLLECT (READ-GLYPH FILE-STREAM FONT)))) + (LOOP :REPEAT NGLYPHS :COLLECT + (PROG1 (SETQ GL (READ-GLYPH FILE-STREAM FONT)) + + (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)))))) (ENDFONT (SETQ FONT-COMPLETE T)))))))) + (WHEN VERBOSE + (DESTRUCTURING-BIND (FAMILY (WEIGHT SLANT EXPANSION) + SIZE) + (GET-FAMILY-FACE-SIZE-FROM-NAME FONT) + (FORMAT *STANDARD-OUTPUT* + "Name: ~A~%Family: ~A~%Size: ~A~%Weight: ~A~%Slant: ~A~%Expansion: ~A~%" + (BF-NAME FONT) + FAMILY SIZE WEIGHT SLANT EXPANSION))) FONT))) (DEFUN READ-DELIMITED-LIST-FROM-STRING (INPUT-STRING &OPTIONAL (DELIMIT #\])) @@ -261,11 +617,10 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST (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 5-Mar-2025 12:20 by mth") - (IL:* IL:\; "Edited 26-Feb-2025 15:23 by mth") - (IL:* IL:\; "Edited 2-Feb-2025 20:29 by mth") - (IL:* IL:\; "Edited 23-Sep-2024 12:38 by mth") - (IL:* IL:\; "Edited 22-Aug-2024 20:53 by mth") +(DEFUN READ-GLYPH (FILE-STREAM FONT) (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 @@ -352,327 +707,63 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST (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 BDF-TO-CHARSETINFO (FONT CSET &OPTIONAL MAP-UNKNOWN-TO-PRIVATE) - (IL:* IL:\; "Edited 5-Mar-2025 12:39 by mth") - (IL:* IL:\; "Edited 3-Feb-2025 16:02 by mth") - (IL:* IL:\; "Edited 30-Jan-2025 16:40 by mth") - (LET (GBCS CSGLYPHS CSLIMITS) - (UNLESS (AND (INTEGERP CSET) - (<= 0 CSET MAXCHARSET)) - (ERROR "Invalid Character set: ~S" CSET) +(DEFUN SPLIT-FONT-NAME (NAME) (IL:* IL:\; "Edited 23-Apr-2025 16:22 by mth") + (IL:* IL:\; "Edited 31-Jan-2025 22:20 by mth") - (IL:* IL:|;;| "Can we get here?") + (IL:* IL:|;;| "First, check if it COULD be in XLFD format") - (SETQ CSET 0)) - (SETQ GBCS (COND - ((TYPEP FONT 'BDF-FONT) - (GLYPHS-BY-CHARSET FONT MAP-UNKNOWN-TO-PRIVATE)) - ((LISTP FONT) + (COND + ((POSITION #\- NAME :TEST #'CHAR=) + (LOOP :FOR I = (IF (CHAR= #\- (ELT NAME 0)) + 1 + 0) + THEN + (1+ J) + :AS J = (POSITION #\- NAME :START I :TEST #'CHAR=) + :COLLECT + (SUBSEQ NAME I J) + :WHILE J)) + (T + (IL:* IL:|;;| "Return the NAME as FAMILY with a NIL FOUNDRY") - (IL:* IL:|;;| - "Assuming that FONT is already the A-LIST form of result from GLYPHS-BY-CHARSET") + (LIST NIL NAME)))) - FONT) - (T (ERROR "Invalid FONT: ~S" FONT)))) - (WHEN (SETQ CSGLYPHS (SECOND (ASSOC CSET GBCS))) - (LET ((TOTAL-WIDTH 0) - (ASCENT 0) - (DESCENT 0) - (FIRSTCHAR MOST-POSITIVE-FIXNUM) - (LASTCHAR MOST-NEGATIVE-FIXNUM) - (CSINFO (IL:|create| CHARSETINFO)) - (DLEFT 0) - GLYPHS-LIMITS BMAP OFFSETS HEIGHT WIDTHS) - (SETQ GLYPHS-LIMITS (LOOP :FOR XGL :IN CSGLYPHS :COLLECT - (LET* ((XCODE (CAR XGL)) - (GL (CDR XGL)) - (GLIMITS (GET-GLYPH-LIMITS GL)) - (GWIDTH (GLIM-WIDTH GLIMITS)) - (ASC (GLIM-ASCENT GLIMITS)) - (DSC (GLIM-DESCENT GLIMITS))) - - (IL:* IL:|;;| "It's possible that ALL glyphs in the character set are above the baseline. In that case, the GLIM-DESCENT calculated by GET-GLYPH-LIMITS will not give a useful value, since it is >= 0. Investigate correcting this.") - - (SETF (GLIM-GLYPH GLIMITS) - GL) - (SETF (GLIM-XCODE GLIMITS) - XCODE) - (SETQ FIRSTCHAR (MIN FIRSTCHAR XCODE)) - (SETQ LASTCHAR (MAX LASTCHAR XCODE)) - (INCF TOTAL-WIDTH GWIDTH) - (SETQ ASCENT (MAX ASCENT ASC)) - (SETQ DESCENT (MAX DESCENT DSC)) - GLIMITS))) - (IL:|replace| (CHARSETINFO IL:CHARSETASCENT) IL:|of| CSINFO IL:|with| ASCENT) - (IL:|replace| (CHARSETINFO IL:CHARSETDESCENT) IL:|of| CSINFO IL:|with| DESCENT) - (SETQ OFFSETS (IL:|fetch| (CHARSETINFO IL:OFFSETS) IL:|of| CSINFO)) - - (IL:* IL:|;;| - "Initialize the offsets to the TOTAL-WIDTH (without the SLUG. It will be added later)") - - (IL:|for| I IL:|from| 0 IL:|to| (+ MAXTHINCHAR 2) IL:|do| (\\FSETOFFSET OFFSETS I - TOTAL-WIDTH)) - (SETQ WIDTHS (IL:|fetch| (CHARSETINFO IL:WIDTHS) IL:|of| CSINFO)) - - (IL:* IL:|;;| - "Initialize the widths to 0, the width of the slug will be set in FIXUP-CHARSETINFO") - - (IL:|for| I IL:|from| 0 IL:|to| (+ MAXTHINCHAR 2) IL:|do| (\\FSETWIDTH WIDTHS I 0)) - (IL:|replace| (CHARSETINFO IL:IMAGEWIDTHS) IL:|of| CSINFO IL:|with| WIDTHS) - - (IL:* IL:|;;| "JDS 12/4/92: Apparently, these fields can be signed values, if all chars, e.g., ride above the base line. ") - - (IL:* IL:|;;| " From \\READSTRIKEFONTFILE, so -ve DESCENT is possible?") - - (SETQ HEIGHT (+ ASCENT DESCENT)) - (SETQ BMAP (BITMAPCREATE TOTAL-WIDTH HEIGHT 1)) - (LOOP :FOR GLIM :IN GLYPHS-LIMITS :WITH GL :WITH GLBM :WITH GLW :WITH XCODE :DO - (SETQ GL (GLIM-GLYPH GLIM)) - (SETQ GLBM (GLYPH-BITMAP GL)) - (SETQ GLW (GLIM-WIDTH GLIM)) - (SETQ XCODE (GLIM-XCODE GLIM)) - (BITBLT GLBM 0 0 BMAP (+ DLEFT (GLYPH-BBXOFF0 GL)) - (+ DESCENT (GLYPH-BBYOFF0 GL)) - (BITMAPWIDTH GLBM) - (BITMAPHEIGHT GLBM) - 'INPUT - 'IL:REPLACE) - (\\FSETOFFSET OFFSETS XCODE DLEFT) - (\\FSETOFFSET WIDTHS XCODE GLW) - (INCF DLEFT GLW)) - (IL:|replace| (CHARSETINFO IL:CHARSETBITMAP) IL:|of| CSINFO IL:|with| BMAP) - CSINFO)))) - -(DEFUN BDF-TO-FONTDESCRIPTOR (BDFONT FAMILY SIZE FACE ROTATION DEVICE &OPTIONAL - MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) - (IL:* IL:\; "Edited 5-Feb-2025 14:53 by mth") - (IL:* IL:\; "Edited 30-Jan-2025 21:27 by mth") - (WHEN (AND (BDF-FONT-P BDFONT) - FAMILY) (IL:* IL:\; "FAMILY Cannot be NIL") - (PROG (FONTDESC DEV GBCSL CHARSETS) - (WHEN (LISTP FAMILY) - (RETURN (BDF-TO-FONTDESCRIPTOR BDFONT (FIRST FAMILY) - (OR (SECOND FAMILY) - SIZE) - (OR (THIRD FAMILY) - FACE "MRR") - (OR (FOURTH FAMILY) - ROTATION 0) - (OR (FIFTH FAMILY) - DEVICE - 'DISPLAY) - MAP-UNKNOWN-TO-PRIVATE))) - (WHEN (FONTP FAMILY) - (RETURN (BDF-TO-FONTDESCRIPTOR BDFONT (FONTPROP FAMILY 'IL:FAMILY) - (OR SIZE (FONTPROP FAMILY 'IL:SIZE)) - (OR FACE (FONTPROP FAMILY 'IL:FACE)) - (OR ROTATION (FONTPROP FAMILY 'IL:ROTATION)) - (OR DEVICE (FONTPROP FAMILY 'IL:DEVICE)) - MAP-UNKNOWN-TO-PRIVATE))) - (SETQ FAMILY (\\FONTSYMBOL FAMILY)) - (UNLESS (AND (INTEGERP SIZE) - (PLUSP SIZE)) - (ERROR "Invalid SIZE: ~S~%" SIZE)) - (COND - ((NULL ROTATION) - (SETQ ROTATION 0)) - ((NOT (AND (INTEGERP ROTATION) - (>= ROTATION 0))) - (IL:\\ILLEGAL.ARG ROTATION))) - (SETQ DEV DEVICE) - (SETQ DEV (COND - ((NULL DEVICE) - 'DISPLAY) - ((AND (SYMBOLP DEVICE) - (NOT (EQ DEVICE T))) (IL:* IL:\; - "Maybe wrong case or package, but we bet it's OK and defer expensive coercion until we've failed.") - DEVICE) - ((STRINGP DEVICE) - (INTERN (STRING-UPCASE DEVICE) - "IL")) - (T (IL:\\ILLEGAL.ARG DEVICE)))) - (SETQ FACE (\\FONTFACE FACE NIL DEV)) - (SETQ GBCSL (GLYPHS-BY-CHARSET BDFONT MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING)) - (FLET ((GBCS-TO-FONTDESC (GBCS FAMILY) - (LET (FONTDESC CHARSETS) - (WHEN GBCS - (SETQ FONTDESC - (IL:|create| FONTDESCRIPTOR - IL:FONTDEVICE IL:_ DEV - IL:FONTFAMILY IL:_ FAMILY - IL:FONTSIZE IL:_ SIZE - IL:FONTFACE IL:_ FACE - IL:|\\SFAscent| IL:_ 0 - IL:|\\SFDescent| IL:_ 0 - IL:|\\SFHeight| IL:_ 0 - IL:ROTATION IL:_ ROTATION - IL:FONTDEVICESPEC IL:_ - (LIST FAMILY SIZE FACE ROTATION DEV))) - (SETQ CHARSETS - (LOOP :FOR CS :IN GBCS :WITH CSET :WITH CSINFO :NCONC - (WHEN (<= 0 (SETQ CSET (FIRST CS)) - MAXCHARSET) - (SETQ CSINFO (BDF-TO-CHARSETINFO GBCS CSET)) - (\\INSTALLCHARSETINFO FONTDESC CSINFO CSET) - (LIST (CONS CSET CSINFO))))) - (SETQ CHARSETS (LOOP :FOR CSP :IN CHARSETS :WITH ASCENT = - (FONTPROP FONTDESC 'IL:ASCENT) - :WITH DESCENT = (FONTPROP FONTDESC - 'IL:DESCENT) - :WITH SLUGWIDTH = (1+ (\\AVGCHARWIDTH - FONTDESC)) - :COLLECT - (PROGN (FIXUP-CHARSETINFO (CDR CSP) - ASCENT DESCENT SLUGWIDTH) - (CAR CSP))))) - (LIST FONTDESC CHARSETS)))) - (RETURN (VALUES-LIST (NCONC (GBCS-TO-FONTDESC (FIRST GBCSL) - FAMILY) - (GBCS-TO-FONTDESC (SECOND GBCSL) - (\\FONTSYMBOL (CONCATENATE 'STRING (SYMBOL-NAME - FAMILY) - "-UNMAPPED"))) - (LIST (ASSOC NOMAPPINGCHARSET (FIRST GBCSL) - :TEST - #'EQ))))))))) - -(DEFUN GET-GLYPH-LIMITS (GLYPH) (IL:* IL:\; "Edited 2-Feb-2025 21:07 by mth") - (IL:* IL:\; "Edited 29-Jan-2025 16:28 by mth") - (LET* ((BBYOFF0 (GLYPH-BBYOFF0 GLYPH)) - (ASCENT (+ (GLYPH-BBH GLYPH) - BBYOFF0)) - (DESCENT (ABS (MIN 0 BBYOFF0))) - (GWIDTH (MAX (+ (GLYPH-BBXOFF0 GLYPH) - (GLYPH-BBW GLYPH)) - (FIRST (GLYPH-DWIDTH GLYPH))))) - (MAKE-GL-LIMITS :WIDTH GWIDTH :ASCENT ASCENT :DESCENT DESCENT))) - -(DEFUN GLYPHS-BY-CHARSET (FONT &OPTIONAL MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) - (IL:* IL:\; "Edited 5-Feb-2025 12:53 by mth") - (IL:* IL:\; "Edited 3-Feb-2025 23:00 by mth") - (IL:* IL:\; "Edited 2-Feb-2025 20:29 by mth") - (IL:* IL:\; "Edited 28-Jan-2025 23:09 by mth") - (IL:* IL:\; "Edited 27-Jan-2025 17:22 by mth") - (IL:* IL:\; "Edited 23-Jan-2025 17:58 by mth") - (IL:* IL:\; "Edited 9-Jan-2025 11:23 by mth") - (LET* ((NCSETS (+ MAXCHARSET 2)) - (CSETS (MAKE-ARRAY NCSETS :INITIAL-CONTENTS (LOOP :REPEAT NCSETS :COLLECT (CONS NIL)))) - (UTOXFN (COND - (RAW-UNICODE-MAPPING #'IDENTITY) - (MAP-UNKNOWN-TO-PRIVATE #'UTOXCODE) - (T #'UTOXCODE?))) - NOMAPPINGCSETS ENC XCODE CS XCS) - (UNLESS (OR MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) - (SETQ NOMAPPINGCSETS (MAKE-ARRAY NCSETS :INITIAL-CONTENTS (LOOP :REPEAT NCSETS :COLLECT - (CONS NIL))))) - (FLET ((PUT-GLYPH-IN-CHARSET-ARRAY (CODE GLYPH CSARRAY) - (TCONC (AREF CSARRAY (LRSH CODE 8)) - (CONS (LOGAND CODE 255) - GLYPH)))) - (LOOP :FOR GL :IN (BF-GLYPHS FONT) - :DO - (SETQ XCS NIL) - (SETQ ENC (GLYPH-ENCODING GL)) - (SETQ XCODE (FUNCALL UTOXFN ENC)) - (IF RAW-UNICODE-MAPPING - (COND - ((> ENC 65535) - (WARN "~&Unicode encoding is beyond 16 bits: ~5X" ENC) - (TCONC (AREF CSETS NOMAPPINGCHARSET) - (CONS ENC GL))) - ((AND NIL (= 255 (LOGAND ENC 255))) - - (IL:* IL:|;;| - "Temporarily? disable this warning in RAW-UNICODE-MAPPING mode") - - (WARN - "~&Unicode encoding char byte (~2X,FF)=(~O,377) may not =FF in FONTDESCRIPTOR" - (LRSH ENC 8) - (LRSH ENC 8)) - (TCONC (AREF CSETS NOMAPPINGCHARSET) - (CONS ENC GL))) - (T (PUT-GLYPH-IN-CHARSET-ARRAY ENC GL CSETS))) - (COND - ((NULL XCODE) - - (IL:* IL:|;;| "These assoc with the Unicode encoding") - - (COND - ((OR (> ENC 65535) - (= 255 (LOGAND ENC 255))) - - (IL:* IL:|;;| - "Unicode encoding is > xFFFF, or encoding low byte is FF, put it in the NOMAPPINGCHARSET") - - (TCONC (AREF CSETS NOMAPPINGCHARSET) - (CONS ENC GL))) - (T (PUT-GLYPH-IN-CHARSET-ARRAY ENC GL NOMAPPINGCSETS)))) - ((AND (INTEGERP XCODE) - (<= 0 XCODE 65535)) - - (IL:* IL:|;;| - "These assoc with the 8 bit character code within the charset") - - (PUT-GLYPH-IN-CHARSET-ARRAY XCODE GL CSETS)) - ((LISTP XCODE) - - (IL:* IL:|;;| - "These assoc with the 8 bit character code within the charset (like above)") - - (LOOP :FOR XC :IN XCODE :UNLESS (MEMBER (SETQ CS (LRSH XC 8)) - XCS) - :DO - (PUSH CS XCS) - (PUT-GLYPH-IN-CHARSET-ARRAY XC GL CSETS))) - (T (ERROR "Invalid XCODE: ~A~%")))))) - - (IL:* IL:|;;| "Extract the lists from the TCONC pointers") - - (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :DO (SETF (AREF CSETS I) - (SORT (REMOVE-DUPLICATES - (CAR (AREF CSETS I)) - :TEST - #'EQUAL) - #'< :KEY #'CAR))) - (SETQ CSETS (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :NCONC - (LET ((CS (AREF CSETS I))) - (WHEN CS - (LIST (LIST I CS)))))) - - (IL:* IL:|;;| "Likewise for the NOMAPPINGCSETS, if any.") - - (WHEN NOMAPPINGCSETS - (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :DO - (SETF (AREF NOMAPPINGCSETS I) - (SORT (REMOVE-DUPLICATES (CAR (AREF NOMAPPINGCSETS I)) - :TEST - #'EQUAL) - #'< :KEY #'CAR))) - (SETQ NOMAPPINGCSETS (LOOP :FOR I :FROM 0 :TO NOMAPPINGCHARSET :NCONC - (LET ((CS (AREF NOMAPPINGCSETS I))) - (WHEN CS - (LIST (LIST I CS))))))) - (LIST CSETS NOMAPPINGCSETS))) - -(DEFUN SPLIT-FONT-NAME (NAME) (IL:* IL:\; "Edited 31-Jan-2025 22:20 by mth") - (LOOP :FOR I = (IF (CHAR= #\- (ELT NAME 0)) - 1 - 0) - THEN - (1+ J) - :AS J = (POSITION #\- NAME :START I :TEST #'CHAR=) - :COLLECT - (SUBSEQ NAME I J) - :WHILE J)) - -(DEFUN WRITE-BDF-TO-DISPLAYFONT-FILES (BDFONT DEST-DIR &OPTIONAL MAP-UNKNOWN-TO-PRIVATE - RAW-UNICODE-MAPPING FAMILY SIZE FACE ROTATION DEVICE) - (IL:* IL:\; "Edited 5-Feb-2025 15:05 by mth") +(DEFUN WRITE-BDF-TO-DISPLAYFONT-FILES (BDFONT DEST-DIR &KEY FAMILY SIZE FACE ROTATION DEVICE + (CHAR-SETS T) + MAP-UNKNOWN-TO-PRIVATE WRITE-UNMAPPED + RAW-UNICODE-MAPPING) + (IL:* IL:\; "Edited 25-Apr-2025 10:08 by mth") + (IL:* IL:\; "Edited 24-Apr-2025 00:09 by mth") + (IL:* IL:\; "Edited 21-Apr-2025 16:03 by mth") (IL:* IL:\; "Edited 3-Feb-2025 23:18 by mth") (UNLESS (TYPEP BDFONT 'BDF-FONT) (ERROR "Not a BDF-FONT: ~S~%" BDFONT)) + (COND + ((EQ CHAR-SETS T) (IL:* IL:\; "This means ALL charsets") + ) + ((NULL CHAR-SETS) + (SETQ CHAR-SETS '(0)) (IL:* IL:\; "Only charset 0") + ) + ((AND (INTEGERP CHAR-SETS) + (<= 0 CHAR-SETS MAXCHARSET)) (IL:* IL:\; "A single integer charset") + (SETQ CHAR-SETS (LIST CHAR-SETS))) + ((AND (LISTP CHAR-SETS) + (EVERY #'(LAMBDA (CS) + (AND (INTEGERP CS) + (<= 0 CS MAXCHARSET))) + CHAR-SETS))) + (T (ERROR "Invalid specification of :CHAR-SETS ~S~%" CHAR-SETS))) (DESTRUCTURING-BIND (FN-FAMILY FN-FACE FN-SIZE) (GET-FAMILY-FACE-SIZE-FROM-NAME BDFONT) (SETQ FAMILY (OR FAMILY FN-FAMILY)) @@ -683,17 +774,29 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST (MULTIPLE-VALUE-BIND (FONTDESC CSETS UNMAPPED-FONTDESC UNICODE-CSETS UNMAPPEDGLYPHS) (BDF-TO-FONTDESCRIPTOR BDFONT FAMILY SIZE FACE ROTATION DEVICE MAP-UNKNOWN-TO-PRIVATE RAW-UNICODE-MAPPING) + (UNLESS (EQ CHAR-SETS T) + (SETQ CSETS (INTERSECTION CHAR-SETS CSETS)) + (SETQ UNICODE-CSETS (INTERSECTION CHAR-SETS UNICODE-CSETS))) (LOOP :FOR CS :IN CSETS :DO (WRITESTRIKEFONTFILE FONTDESC CS (PACKFILENAME.STRING :BODY DEST-DIR :NAME (\\FONTFILENAME FAMILY SIZE FACE "DISPLAYFONT" CS)))) - (LOOP :FOR CS :IN UNICODE-CSETS :DO (WRITESTRIKEFONTFILE - UNMAPPED-FONTDESC CS - (PACKFILENAME.STRING :BODY DEST-DIR :NAME - (\\FONTFILENAME (FONTPROP - UNMAPPED-FONTDESC - 'IL:FAMILY) - SIZE FACE "DISPLAYFONT" CS)))) + (IF WRITE-UNMAPPED + (LOOP :FOR CS :IN UNICODE-CSETS :DO (WRITESTRIKEFONTFILE + UNMAPPED-FONTDESC CS + (PACKFILENAME.STRING + :BODY DEST-DIR :NAME + (\\FONTFILENAME (FONTPROP + UNMAPPED-FONTDESC + 'IL:FAMILY) + SIZE FACE "DISPLAYFONT" CS)))) + (SETQ UNICODE-CSETS NIL)) + + (IL:* IL:|;;| "These correspond to the charsets ACTUALLY written.") + + (IL:* IL:|;;| + "UNMAPPEDGLYPHS are never written. (Unicode encoding is > xFFFF, or encoding low byte is FF)") + (VALUES FONTDESC CSETS UNMAPPED-FONTDESC UNICODE-CSETS UNMAPPEDGLYPHS)))) (IL:DECLARE\: IL:EVAL@COMPILE IL:DONTCOPY @@ -719,11 +822,10 @@ FONTPROP INPUT ITALIC LIGHT LRSH MEDIUM REGULAR TCONC UTOXCODE UTOXCODE? WRITEST (IL:PUTPROPS IL:READ-BDF IL:DATABASE IL:NO) (IL:DECLARE\: IL:DONTCOPY - (IL:FILEMAP (NIL (2291 3912 (FIXUP-CHARSETINFO 2291 . 3912)) (3914 6946 ( -GET-FAMILY-FACE-SIZE-FROM-NAME 3914 . 6946)) (6948 8373 (PACKFILENAME.STRING 6948 . 8373)) (8375 14009 - (READ-BDF 8375 . 14009)) (14011 14334 (READ-DELIMITED-LIST-FROM-STRING 14011 . 14334)) (14336 20558 ( -READ-GLYPH 14336 . 20558)) (20560 25963 (BDF-TO-CHARSETINFO 20560 . 25963)) (25965 32055 ( -BDF-TO-FONTDESCRIPTOR 25965 . 32055)) (32057 32654 (GET-GLYPH-LIMITS 32057 . 32654)) (32656 38670 ( -GLYPHS-BY-CHARSET 32656 . 38670)) (38672 39035 (SPLIT-FONT-NAME 38672 . 39035)) (39037 41216 ( -WRITE-BDF-TO-DISPLAYFONT-FILES 39037 . 41216))))) + (IL:FILEMAP (NIL (2427 10386 (BDF-TO-CHARSETINFO 2427 . 10386)) (10388 16258 (BDF-TO-FONTDESCRIPTOR +10388 . 16258)) (16260 19623 (GET-FAMILY-FACE-SIZE-FROM-NAME 16260 . 19623)) (19625 26436 ( +GLYPHS-BY-CHARSET 19625 . 26436)) (26438 27863 (PACKFILENAME.STRING 26438 . 27863)) (27865 34669 ( +READ-BDF 27865 . 34669)) (34671 34994 (READ-DELIMITED-LIST-FROM-STRING 34671 . 34994)) (34996 41484 ( +READ-GLYPH 34996 . 41484)) (41486 42227 (SPLIT-FONT-NAME 41486 . 42227)) (42229 46011 ( +WRITE-BDF-TO-DISPLAYFONT-FILES 42229 . 46011))))) IL:STOP diff --git a/lispusers/READ-BDF.DFASL b/lispusers/READ-BDF.DFASL index 87b9e2076c1839e153a03b33a72db37016e29bfa..631844166fd9d07c74def7e4f73c1263b6b29804 100644 GIT binary patch literal 20849 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 C^M=F# literal 19858 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 V0ZSz!C`N!a;ud=YjE65?{J%KXL>>SD diff --git a/lispusers/READ-BDF.TEDIT b/lispusers/READ-BDF.TEDIT index 50e3c235f0fbbc4c0f6e80621e9144061bb0b8cd..8f6e2ec54277d7d0aa52a6e7c53c832eb004d43b 100644 GIT binary patch literal 9640 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 delta 1422 zcmaKr&ubGw6vy8ro5W30Yt!1aso11Mi}ltyb9vYKfsHgJlFSq@eL^rF6`{g=Y8L=nc4TZ-mE>Zyto|3*495f zeP$BHcF^0g6S1H6rnl+0+Py|A40en5(3_j?1b0?$hmC5hR$plb?RF#FwvW7$xnzHM zT~o2EiElQZT()n$eE-0|Zu)=HuZ)c{v-U7swvWA%?Iz##H&YkPR52a4di8Gl=!kyb zr{WRWvy%f;><@C^lg76%=hr7Th#&KXGW_xER$;+@DrD`w!iHTfT*} zoK8q-$k~BtniAVVbth27^n`32J2OTX8^flE^5pN-ccZ;}uTlMPp7^ML76lR=i#TreBhb(K1~%Bk%X>&4Z# zzZT2BOgVtKM7lz{D~POK_*N814GFB+5f2EYjnj*F<@pwMT zuXti1!CeBGq?lq;hSC2Ob4`JB+EX%t3Gj>(DGOV&3=(PS$9h?U30^KaUxVSwC7%Vs zu89W2w2~4gAYwuWnT)GFBA06sUJ%R4!=rYBoL1uc3YDNG$+MsY`6QV1h(UfEgwMeQ z!uvlwP;JM!(^5hM6{}VoH7=D}ijbmo`Ja)%wdV?zvY^@GHAyUZrx5hW3f(6`jWf?oQ+~TvUxCZU;3or;`-M)dzqpkH#oo U3wre%4?aJ<%NO{7p6q}83oj*VA^-pY