afdko

CID-keyed UFOs in afdko

Introduction

With the correct data included in a UFO you can create a hybrid CID-keyed/name-keyed source file that can be used in most of the afdko tools that understand CID-keyed fonts like makeotf, tx, and mergefonts.

Some examples:

Convert an existing CID-keyed OTF to CID-keyed UFO

tx -ufo -o cid-keyed.ufo cid-keyed.otf

Subset a CID-keyed UFO using CID ranges with the -g flag

tx -g /0,/959-/978,/59120-/59130 -ufo -o cid-keyed-subset.ufo cid-keyed.ufo

Create a name-keyed source using -decid

tx -t1 -o name-keyed.pfa -decid -fd 1 cid-keyed.ufo

Merge multiple CID-keyed UFOs into a single UFO

mergefonts merged-cid-keyed.ufo part1.map part1.ufo part2.map part2.ufo part3.map part3.ufo

Requirements

The following keys are required in lib.plist to make a UFO CID-keyed

com.adobe.type.ROS

The value for this key must be written in the form {Registry}-{Ordering}-{Supplement}. Information about available ROS can be found at https://github.com/adobe-type-tools/cmap-resources

Some typical ROSes are

All except Adobe-Identity-0 have a fixed CID order that must be followed, but the glyphs in the UFO can be named anything that is legal in the UFO format. The UFO glyphOrder may also be any order you choose. If you run tx alone you will see the order as saved in the UFO, but when converting with tx -t1 or tx -cff or building an OTF with makeotf the glyphs will be rearranged into CID order.

com.adobe.type.postscriptCIDMap

The glyph name to CID mapping must be a dict with the UFO glyph names as keys and their corresponding CID numbers as values. When converting an existing CID-keyed source (OTF, CFF, Type1) to UFO with a command like tx -ufo -o source.ufo source.otf the glyph names will default to cidXXXXX because no names can be stored in those source formats. If you change the names in the UFO be sure to update the CID mapping and groups.plist to use the new names.

com.adobe.type.postscriptFDArray

The FDArray is a list of font dictionaries containing hinting data. All keys are optional and will use default values if not specified, but there must be at least one dict in the array and a corresponding group in groups.plist.

    <key>com.adobe.type.postscriptFDArray</key>
    <array>
    <dict>
    </dict>
    </array>

An example of one FontDict in Python dumped from Source Han Serif JP ExtraLight

 {'FontMatrix': [0.001, 0, 0, 0.001, 0, 0],
  'FontName': 'SourceHanSerifJP-ExtraLight-HWidth',
  'PaintType': 0,
  'PrivateDict': {'ExpansionFactor': 0.06,
                  'postscriptBlueFuzz': 1,
                  'postscriptBlueScale': 0.039625,
                  'postscriptBlueShift': 7,
                  'postscriptBlueValues': [-18, 0, 521, 538, 746, 764, 794, 794],
                  'postscriptOtherBlues': [-283, -283, -273, -273, -176, -176],
                  'postscriptStdHW': [34],
                  'postscriptStdVW': [50],
                  'postscriptStemSnapH': [34, 45, 59],
                  'postscriptStemSnapV': [28, 50, 60]}}]

FDArraySelect groups in groups.plist

In addition to the lib.plist keys there must be a groups.plist file containing one group for each FontDict in com.adobe.type.postscriptFDArray. Each group must be named in the following format

FDArraySelect.{required-fdarray-index}.{optional-descriptive-name}

The following keys are equivalent to the parser

The optional descriptive name is only for user convenience such as when viewing and creating groups in a font editor. When dumping with tx or building an OTF with makeotf the names for each font dict will come from the FontName key in each dict in com.adobe.type.postscriptFDArray.

Using Python with CID-keyed UFOs

Any Python library that supports UFO can handle CID-keyed UFOs since the CID-specific data is added to the existing lib.plist and groups.plist files. For example, using fontParts to make an existing UFO CID-keyed:

from fontParts.world import OpenFont

font = OpenFont('cid-keyed-source.ufo')

# Set the ROS
font.lib['com.adobe.type.ROS'] = 'Adobe-Japan1-7'

# Set up a preliminary CID mapping based on the current glyph order
font.lib['com.adobe.type.postscriptCIDMap'] = {name: i for i, name in enumerate(font.lib['public.glyphOrder'])}

# Set up a basic FDArray until you are ready to add hinting data
fontname = font.info.familyName.replace(" ", "")
font.lib['com.adobe.type.postscriptFDArray'] = [{'FontName': f'{fontname}-FD0'}]

# Create a single FontDict that corresponds to the one in the FDArray above
font.groups[f'FDArraySelect.0.{fontname}-FD0'] = font.glyphOrder

font.save()

Document Version History

Version 1.0 — April 10 2023