BDFMANGLE(1)

NAME
bdfmangle - transform a bdf font file into another bdf font file

SYNOPSIS
bdfmangle [OPTION...] BDFFILE MGLFILE...

DESCRIPTION
Bdfmangle is a tool to select, rename, reorder, or manipulate glyphs in a bdf font file. It reads a bdf font file (or a concatenation of bdf font files via cat BDFFILE1 BDFFILE2 ... | bdfmangle - MGLFILE...), manipulates the preamble and the glyphs of the font file as specified in MGLFILEs and prints the result to standard output.

OPTIONS

-h, --help
    Output a usage message and exit.

-c, --compatibility-mode
    Start bdfmangle in compatibility mode (see LIMITATIONS below).

MGL COMMAND SYNOPSIS
When bdfmangle reads the bdf file(s), it stores the first preamble that it encounters (discarding any further preambles) and the glyphs (bitmaps) of the bdf file(s). Every glyph is stored independently under its name (the string following the STARTCHAR keyword) and under its encoding (the number following the ENCODING keyword). Formally, a glyph

    STARTCHAR glyphname
    ENCODING number
    glyphdata
    ENDCHAR

is turned into

    nameindex[glyphname] := glyphdata;
    encodingindex[number] := glyphdata;

If several glyphs have the same name or the same encoding, the last one overwrites the earlier ones. The preamble and glyphs are then processed according to the commands in the mgl file(s). Commands are separated by newlines. In contrast to bdf files, numbers in mgl files can be specified either decimally, or octally (with a 0 prefix), or hexadecimally (with a 0x prefix).

  Preamble commands

FONT fontnamesuffix
    Replaces the trailing part of the font name in the preamble by fontnamesuffix. The length of the the replaced part is determined by the number of hyphens in fontnamesuffix (the total number of hyphens is left unchanged).

DEFAULT_CHAR number
    Replaces the DEFAULT_CHAR declaration in the preamble.

CHARSET_REGISTRY string
    Replaces the CHARSET_REGISTRY declaration in the preamble.

CHARSET_ENCODING string
    Replaces the CHARSET_ENCODING declaration in the preamble.

  Output commands

PUT glyphname [number]
    Prints the glyph stored at glyphname with the encoding number. If no number is specified, the encoding of the last glyph printed is incremented by 1.

PUTAS glyphname newglyphname [number]
    Prints the glyph stored at glyphname with the new name newglyphname and the encoding number. If no number is specified, the encoding of the last glyph printed is incremented by 1.

PUTENC oldencoding [number]
    Prints the glyph stored at oldencoding with the encoding number. If no number is specified, the encoding of the last glyph printed is incremented by 1.

PUTENCAS oldencoding newglyphname [number]
    Prints the glyph stored at oldencoding with the new name newglyphname and the encoding number. If no number is specified, the encoding of the last glyph printed is incremented by 1.

  Error handling commands

NODEFAULT
    Specifies that bdfmangle is aborted if unassigned glyph names or encodings are used in an output command.

DEFAULT glyphname
    Specifies that the glyph currently stored with the name glyphname is taken as a replacement whenever an unassigned glyph name or encoding is used in an output command.

DEFAULTENC number
    Specifies that the glyph currently stored with the encoding number is taken as a replacement whenever an unassigned glyph name or encoding is used in an output command.

DEFAULTOMIT
    Specifies that output commands using unassigned glyph names or encodings are ignored.

  Copy commands

COPYTO glyphname newglyphname
    Copies the glyph stored at glyphname to newglyphname, i.e., nameindex[newglyphname] := nameindex[glyphname]; The glyph is afterwards accessible both under its old and its new name. If some other glyph was previously stored at newglyphname, it is no longer accessible by its name (but it may still be accessible by its encoding).

COPYTOENC glyphname newnumber
    Copies the glyph stored at glyphname to the encoding newnumber, i.e., encodingindex[newnumber] := nameindex[glyphname]; The glyph is afterwards accessible both under its old name and its new encoding. If some other glyph was previously stored at the encoding newnumber, it is no longer accessible by its encoding (but it may still be accessible by its name).

COPYENCTO number newglyphname
    Copies the glyph stored at the encoding number to newglyphname, i.e., nameindex[newglyphname] := encodingindex[number]; The glyph is afterwards accessible both under the encoding number and its new name. If some other glyph was previously stored at newglyphname, it is no longer accessible by its name (but it may still be accessible by its encoding).

COPYENCTOENC number newnumber
    Copies the glyph stored at the encoding number to the encoding newnumber, i.e., encodingindex[newnumber] := encodingindex[number]; The glyph is afterwards accessible both under its old and its new encoding. If some other glyph was previously stored at the encoding newnumber, it is no longer accessible by its encoding (but it may still be accessible by its name).

COPYTOENCPN glyphname newnumber

COPYENCTOENCPN number newnumber
    These two commands are similar to COPYTOENC and COPYENCTOENC. The only difference is that if some glyph was previously stored at the encoding newnumber, then the name that was previously associated with this glyph is preserved and not replaced by the name associated with the name glyphname or the encoding number, respectively. For instance, assume that there is a glyph stored at encoding 0x0041 with the name "LtCapA" and a glyph stored at encoding 0x0410 with the name "CyCapA". Then "COPYENCTOENC 0x0041 0x0410" overwrites both the glyph stored at 0x0410 and its name, therefore "PUT 0x0410" would print a glyph with the name ""LtCapA". On the other hand, "COPYENCTOENCPN 0x0041 0x0410" overwrites the glyph previously stored at 0x0410 but preserves its old name "CyCapA", therefore "PUT 0x0410" would print a glyph with the name ""CyCapA".

UNDEF glyphname
    Deletes the glyph stored at glyphname, i.e., nameindex[glyphname] := undef;

UNDEFENC number
    Deletes the glyph stored at the encoding number, i.e., encodingindex[number] := undef;

  Glyph manipulation commands

MERGE glyphname1 glyphname2
    Sets every pixel in the glyph data of glyphname2 to black if the corresponding pixel of the glyph data of glyphname1 is black. Both glyphname1 and glyphname2 must be defined. MERGE computes a logical "or" of the glyphs.

INTERSECT glyphname1 glyphname2
    Sets every pixel in the glyph data of glyphname2 to white if the corresponding pixel of the glyph data of glyphname1 is white. Both glyphname1 and glyphname2 must be defined. INTERSECT computes a logical "and" of the glyphs. If other boolean operations are needed, they can be obtained by combining MERGE, INTERSECT, and INVERT commands appropriately.

OVERWRITE glyphname1 glyphname2
    Replaces every pixel row in the glyph data of glyphname2 by the corresponding pixel row of the glyph data of glyphname1, provided that the latter row contains at least one black pixel. (This is equivalent to "INVLINEMASK glyphname1 glyphname2" followed by "MERGE glyphname1 glyphname2".) Both glyphname1 and glyphname2 must be defined.

LINEMASK glyphname1 glyphname2
    Replaces every pixel row in the glyph data of glyphname2 by an empty pixel row, provided that the corresponding pixel row in the glyph data of glyphname1 is empty (that is, contains no black pixel). Both glyphname1 and glyphname2 must be defined.

INVLINEMASK glyphname1 glyphname2
    Replaces every pixel row in the glyph data of glyphname2 by an empty pixel row, provided that the corresponding pixel row in the glyph data of glyphname1 contains at least one black pixel. Both glyphname1 and glyphname2 must be defined.

SHIFTUP number glyphname
    Shifts the pixels in the glyph data of glyphname upward by number rows. Pixels in the highest number rows are discarded.

SHIFTDOWN number glyphname
    Shifts the pixels in the glyph data of glyphname downward by number rows. Pixels in the lowest number rows are discarded.

SHIFTLEFT number glyphname
    Shifts the pixels in the glyph data of glyphname to the left by number columns. Pixels in the leftmost number columns are discarded.

SHIFTRIGHT number glyphname
    Shifts the pixels in the glyph data of glyphname to the right by number columns. Pixels in the rightmost number columns are discarded.

SHIFTUPMAX number glyphname

SHIFTDOWNMAX number glyphname

SHIFTLEFTMAX number glyphname

SHIFTRIGHTMAX number glyphname
    These four commands are similar to SHIFTUP, SHIFTDOWN, SHIFTLEFT, and SHIFTRIGHT, except that the pixels in the glyph data are shifted at most so far to the left, right, top, or bottom that they touch the margin of the bounding box. For instance, if a glyph has three white columns at the left margin, then "SHIFTLEFTMAX 2 glyphname" behaves like "SHIFTLEFT 2 glyphname", but "SHIFTLEFTMAX 5 glyphname" behaves like "SHIFTLEFT 3 glyphname", so that no black pixel is shifted out of the bounding box.

VALIGN param glyphname1 glyphname2
    Shifts the pixels in the glyph data of glyphname2 up or down such that the top edges (if param = T), vertical centers (if param = C), or bottom edges (if param = B) of glyphname1 and glyphname2 are aligned. It is also possible to specify different alignment points for glyphname1 and glyphname2 by setting param = TC/TB/CT/CB/BT/BC. For instance if param = TB then the top edge of glyphname1 and the bottom edge of glyphname2 are aligned (that is, glyphname2 is shifted vertically until it is directly above glyphname1), and if param = CT then the center of glyphname1 and the top edge of glyphname2 are aligned. VALIGN behaves like SHIFTUPMAX and SHIFTDOWNMAX in that no black pixel of glyphname2 is shifted out of the bounding box. Since the center of a glyph may be on a pixel row or between two pixel rows, aligning with respect to the center is not always possible exactly. Any fractional shift distance is rounded down towards zero (e.g., if centering a glyph with respect to another one would require to shift it by 2.5 pixels (up or down), it is shifted by 2 pixels instead). Example: Suppose that rows are numbered top-down starting at 1, that the topmost black pixel of tmp1 is in row 2 and the bottommost black pixel is in row 5, which means that the vertical center is between rows 3 and 4, and that the topmost black pixel of tmp2 is in row 7 and the bottommost black pixel is in row 13, which means that the vertical center is at row 10. To align the bottom edge of tmp2 with the bottom edge of tmp1, we'd need to shift tmp2 8 rows up, but then the black pixels in rows 7 and 8 would be shifted out of the bounding box. Consequently, "VALIGN B tmp1 tmp2" shifts the pixels of tmp2 only 6 rows up. "VALIGN BT tmp1 tmp2" shifts the pixels of tmp2 1 row up; the bottom edge of tmp1 (below row 5) is now aligned with the top edge of tmp2 (above row 6). Aligning the bottom edge of tmp1 and the center of tmp2 would mean to shift the pixels of tmp2 4.5 rows up; 4.5 is rounded down to 4; therefore "VALIGN BC tmp1 tmp2" shifts the pixels of tmp2 4 rows up.

HALIGN param glyphname1 glyphname2
    Shifts the pixels in the glyph data of glyphname2 left or right such that the left edges (if param = L), horizontal centers (if param = C), or right edges (if param = R) of glyphname1 and glyphname2 are aligned. It is also possible to specify different alignment points for glyphname1 and glyphname2 by setting param = LC/LR/CL/CR/RL/RC. For instance if param = LR then the left edge of glyphname1 and the right edge of glyphname2 are aligned (that is, glyphname2 is shifted horizontally until it is directly feft of glyphname1), and if param = CL then the center of glyphname1 and the left edge of glyphname2 are aligned. HALIGN behaves like SHIFTLEFTMAX and SHIFTRIGHTMAX in that no black pixel of glyphname2 is shifted out of the bounding box. Since the center of a glyph may be on a pixel column or between two pixel columns, aligning with respect to the center is not always possible exactly. Any fractional shift distance is rounded down towards zero (e.g., if centering a glyph with respect to another one would require to shift it by 2.5 pixels (left or right), it is shifted by 2 pixels instead.

VFLIP glyphname
    Flips the glyph of glyphname vertically (top/bottom). White columns on the left and/or right hand side and white rows at the top and/or bottom are left unchanged.

HFLIP glyphname
    Flips the glyph of glyphname horizontally (left/right). White columns on the left and/or right hand side and white rows at the top and/or bottom are left unchanged.

ROTATE glyphname
    Rotates the glyph of glyphname by 180°. (This is equivalent to "HFLIP glyphname" followed by "VFLIP glyphname".) White columns on the left and/or right hand side and white rows at the top and/or bottom are left unchanged.

HFLIPBBX glyphname

VFLIPBBX glyphname

ROTATEBBX glyphname
    These operations are similar to HFLIP, VFLIP, and ROTATE, except that the entire bounding box, including white columns and rows at the margin, is flipped or rotated. For instance, if a glyph has three white columns at the left margin and one white column at the right margin, then HFLIP and ROTATE will not change that, whereas HFLIPBBX and ROTATEBBX yield a glyph with one white column at the left margin and three white columns at the right margin.

SLANT number1 [number2] glyphname
    Slants the glyph of glyphname to the right side. The argument number1 determines how much the glyph is slanted: vertical lines in the original glyph are turned into lines with a slant of (number1/10) in the modified glyph. In other words, number1/10 is the cotangent of the slanting angle: higher values of number1 mean the slanting angle is smaller, that is, that former vertical lines are steeper. Typical values of number1 range from 25 (corresponding to a 22° angle) to 55 (corresponding to a 10° angle). If number1 is negative, the glyph is slanted to the left side. The optional argument number2 determines the number of the lowest pixel row that is neither shifted to the left nor to the right, that is, the base line of the slant. Pixel rows are counted from top to bottom and the topmost pixel row has number 1. If number2 is omitted, it is implicitly set to ceiling(height/2 + number1/20), where height is the number of pixel rows in the bitmap; this results in a slanting that is symmetric with respect to the bounding box.

INVERT glyphname
    Inverts all pixels in the glyph of glyphname.

  Bounding box manipulation commands

UNPAD glyphname
    Removes empty rows at the top and bottom of the glyph and empty columns at the left and right margin of the glyph and adapts the glyph's bounding box accordingly.

PAD glyphname
    If the bounding box of the glyph is smaller than the FONTBOUNDINGBOX of the font, adds empty rows at the top and bottom of the glyph and empty columns at the left and right margin of the glyph until the glyph's bounding box agrees with the FONTBOUNDINGBOX.

  Conditional commands

IFDEF glyphname cmd
    If there is a glyph stored at glyphname, executes the mgl command cmd; otherwise does nothing.

IFUNDEF glyphname cmd
    If there is no glyph stored at glyphname, executes the mgl command cmd; otherwise does nothing.

IFDEFENC number cmd
    If there is a glyph stored at the encoding number, executes the mgl command cmd; otherwise does nothing.

IFUNDEFENC number cmd
    If there is no glyph stored at the encoding number, executes the mgl command cmd; otherwise does nothing.

IFFONT string cmd
    If the FONT declaration in the preamble contains string as a substring, executes the mgl command cmd; otherwise does nothing. The string string may not contain whitespace.

IFNOTFONT string cmd
    If the FONT declaration in the preamble does not contain string as a substring, executes the mgl command cmd; otherwise does nothing. The string string may not contain whitespace.

IFFILE filename cmd
    If the file filename exists, executes the mgl command cmd; otherwise does nothing. The string filename may not contain whitespace. Environment variables ($HOME or ${HOME}) in filename are expanded.

IFNOTFILE filename cmd
    If the file filename does not exist, executes the mgl command cmd; otherwise does nothing. The string filename may not contain whitespace. Environment variables ($HOME or ${HOME}) in filename are expanded.

IFEQ glyphname1 glyphname2 cmd
    If the glyph data of glyphname1 and glyphname2 (excluding name and encoding) agrees, executes the mgl command cmd; otherwise does nothing.

IFNOTEQ glyphname1 glyphname2 cmd
    If the glyph data of glyphname1 and glyphname2 (excluding name and encoding) differs, executes the mgl command cmd; otherwise does nothing.

IFMATCH pattern string cmd
    Pattern is a non-empty string that does not contain spaces, tabs, or semicolons. If pattern matches string, executes the mgl command cmd; otherwise does nothing. Within pattern, "?" matches an arbitrary character, "!" matches an uppercase letter ([A-Z]), "*" matches an arbitrary (possibly empty) string, "@" matches an arbitrary (possibly empty) string that does not contain any uppercase letter, and every other character matches itself. For instance, "LtCapE*" matches "LtCapE", "LtCapEAcute", and "LtCapEth", "LtCapE@" matches "LtCapE" and "LtCapEth", but not "LtCapEAcute", and "Lt???E!@" matches "LtCapEAcute", and "LtSmlEAcute", but not "LtCapE", "LtSmlE", "LtCapEth", or "LtSmlEth".

IFNOTMATCH pattern string cmd
    If pattern does not match string, executes the mgl command cmd; otherwise does nothing.

  Iteration commands

FOREACH pattern1 ... patternn ; cmd
    Every pattern is a non-empty string that does not contain spaces, tabs, or semicolons. If a pattern contains at least one of the characters "?", "*", "!", or "@", it represents every glyph name currently used in the glyphname index that is matched by it (as described above for IFMATCH). If a pattern has the form "#d", "#o", or "#x", it represents every encoding currently used in the encoding index (decimally, octally (without leading "0"), or hexadecimally (at least four digits, without leading "0x")). Every other pattern represents itself. For each string str that is represented by one of the patterns, every occurrence of "%%" in cmd is replaced by "%", every other occurrence of "%" in cmd is replaced by str, and the resulting command is executed.

  Other commands

INCLUDE filename
    Includes the mgl file filename. The string filename may not contain whitespace. Environment variables ($HOME or ${HOME}) in filename are expanded.

RETURN
    Stops executing commands in the current mgl file. If RETURN is executed in a top level mgl file, bdfmangle continues with the next top level mgl file, if any. If RETURN is executed in an included file, bdfmangle continues with the commands in the including file.

ABORT [number]
    Aborts bdfmangle with exit status number (default: 0) without generating any bdf output. This can be used for consistency checking, such as

    if printf 'IFEQ LtCapA CyCapA ABORT 0\nABORT 1\n' | \
    bdfmangle test.bdf - ; then
        echo "Glyphs LtCapA and CyCapA look the same"
    else
        echo "Glyphs LtCapA and CyCapA look differently"
    fi

MESSAGE arbitrary-text
    Writes arbitrary-text to stderr.

COMMENT arbitrary-text
    Adds "COMMENT arbitrary-text" to the output bdf file.

# arbitrary-text
    Does nothing. This is for comments that should not be copied to the output.

  Command sequences

cmd ; ... ; cmd
    Executes each of the commands sequentially. Note that both semicolons and newlines can be used to separate commands, but that they behave differently with respect to comments (both "COMMENT" and "#"), conditional commands, and iteration commands. These commands extend always until the end of the line and are not terminated by a semicolon. E.g., in

        IFUNDEF Foo COPY LtCapA Foo
        MESSAGE done!

    the MESSAGE command is always executed; in

        IFUNDEF Foo COPY LtCapA Foo ; MESSAGE done!

    it is only executed if Foo was undefined; and in

        COMMENT This is a comment ; MESSAGE done!

    and

        # This is a comment ; MESSAGE done!

    it is never executed.

LIMITATIONS
Control flow operations (conditional and iteration commands) are rather rudimentary.

Unlike PUT... and COPY... commands, glyph manipulation commands accept only glyph names as arguments, not glyph encodings. To perform glyph manipulation commands on glyphs specified by encoding, copy them to temporary names first (see EXAMPLES below).

If glyph names in the bdf input file contain spaces or tabs, these are replaced by underscores.

Bdfmangle uses a semicolon as a separator in command sequences and FOREACH loops. However, the semicolon is also a legal character in glyph names in bdf files, and therefore one might want to use it within mgl command arguments. To avoid ambiguities, bdfmangle switches to compatibility mode as soon as it encounters a semicolon in a glyph name in the bdf input file. In compatibility mode, semicolon is a legal character in mgl command arguments, but command sequences and FOREACH loops are no longer available.

The BDF format does not require padding: Empty rows and columns may be left out in the bitmap data, therefore different glyphs may have different bounding boxes. However, all the glyph manipulation commands manipulate only the bitmap data within the given bounding box and leave pixels outside the bounding box unchanged. Consequently, the resulting glyphs will look differently depending on whether the original glyphs are padded or not. Moreover, those glyph manipulation commands that operate on two glyphs, such as MERGE or HALIGN, work only for pairs of glyphs that have identical bounding boxes, or in other words, that are padded in the same way. If necessary, use the PAD command first.

There is a limit on the number of nested inclusion commands.

If the input of bdfmangle is a concatenation of bdf font files, the second and all further preambles are discarded, even if they contain copyright or licensing information that has to be kept intact. It may be necessary to restore such data manually.

EXAMPLES
Print the Greek uppercase letters of UW ttyp0 with Unicode encoding. If some letters are undefined, use the identlcally looking latin letters:

    IFUNDEF GkCapAlp COPYTO LtCapA GkCapAlp
    IFUNDEF GkCapBet COPYTO LtCapB GkCapBet
    IFUNDEF GkCapEps COPYTO LtCapE GkCapEps
    # and so on
    IFUNDEF GkCapTau COPYTO LtCapT GkCapTau
    IFUNDEF GkCapUps COPYTO LtCapY GkCapUps
    IFUNDEF GkCapChi COPYTO LtCapX GkCapChi
    PUT GkCapAlp 0x0391
    PUT GkCapBet 0x0392
    PUT GkCapGam 0x0393
    # and so on
    PUT GkCapChi 0x03A7
    PUT GkCapPsi 0x03A8
    PUT GkCapOme 0x03A9

Produce a new glyph LtSmlCTilde from LtSmlC and CombTilde. If both the base letter and the accent have an even width, or if both have an odd width, the accent should be horizontally centered over the base letter, otherwise it should be slightly moved to the right side. There should be a one pixel vertical distance between the base letter and the accent, if possible:

    # Copy CombTilde to tmp and shift directly above LtSmlC,
    # if possible:
    COPYTO CombTilde tmp
    VALIGN TB LtSmlC tmp
    # Shift tmp one more pixel to the top, if possible:
    SHIFTUPMAX 1 tmp
    # Shift tmp to the right until it touches the right margin,
    # then center it horizontally over LtSmlC.
    # (Use SHIFTLEFTMAX if the accent should lean to the left
    # if it cannot be centered exactly):
    SHIFTRIGHTMAX 9999 tmp
    HALIGN C LtSmlC tmp
    # Merge base letter and accent to generate LtSmlCTilde:
    COPYTO LtSmlC LtSmlCTilde
    MERGE tmp LtSmlCTilde
    PUT LtSmlCTilde

Do the same for LtSmlCTilde, LtSmlSTilde, and LtSmlZTilde:

    FOREACH LtSmlC LtSmlS LtSmlZ ; COPYTO CombTilde tmp ; VALIGN TB % tmp ; SHIFTUPMAX 1 tmp ; SHIFTRIGHTMAX 9999 tmp ; HALIGN C % tmp ; COPYTO % %Tilde ; MERGE tmp %Tilde ; PUT %Tilde

(Note that the entire command must go into one line!)

Print all Cyrillic letters, accents, and combining marks of UW ttyp0 (with unspecified encoding):

    FOREACH *Cy!* ; PUT %

Remove the padding of all glyphs and print them in numerical order.

    # The UNPAD command requires a glyphname as argument, but
    # we want to access glyphs by encoding, so that we can
    # print them afterwards in numerical order. Therefore we
    # copy all glyphs to a temporary name first and copy them
    # back afterwards, preserving the original name:
    FOREACH #d ; COPYENCTO % tmp ; UNPAD tmp ; COPYTOENCPN tmp %
    FOREACH #d ; PUTENC %

(If there are any glyphs that share the same encoding, only the last one will be printed).

Note that the following would not work as expected:

    FOREACH * ; UNPAD %
    FOREACH #d ; PUTENC %

This would modify the glyphs stored in the glyphname index and print the glyphs stored in the encoding index, but these are independent from each other.

COPYRIGHT
Copyright © 2012-2025 Uwe Waldmann.

Bdfmangle may be freely used, copied, or modified under the terms of the MIT license.