Annotation of nono/util/ttfext/ttfext.cpp, revision 1.1

1.1     ! root        1: //
        !             2: // nono
        !             3: // Copyright (C) 2022 nono project
        !             4: // Licensed under nono-license.txt
        !             5: //
        !             6: 
        !             7: //
        !             8: // TTF から埋め込みビットマップを抜き出して BDF にする。
        !             9: //
        !            10: 
        !            11: // TTF の構造についてはこの辺参照。
        !            12: // - https://learn.microsoft.com/en-us/typography/opentype/spec/ > Tables
        !            13: //
        !            14: // - OpenType ファイル構造
        !            15: //   https://aznote.jakou.com/prog/opentype/02_struct.html
        !            16: 
        !            17: #include "header.h"
        !            18: #include "autofd.h"
        !            19: #include "mystring.h"
        !            20: #include <errno.h>
        !            21: #include <fcntl.h>
        !            22: #include <iconv.h>
        !            23: #include <unistd.h>
        !            24: #include <vector>
        !            25: 
        !            26: #define HOWMANY(x, y)  (((x) + (y - 1)) / (y))
        !            27: 
        !            28: static constexpr uint32
        !            29: FOURCC(const char *str)
        !            30: {
        !            31:        return (str[0] << 24) | (str[1] << 16) | (str[2] << 8) | str[3];
        !            32: }
        !            33: 
        !            34: static int debug = 1;
        !            35: static const char *new_family_name;
        !            36: 
        !            37: //
        !            38: // ファイルアクセス
        !            39: //
        !            40: class File
        !            41: {
        !            42:  public:
        !            43:        File() {
        !            44:                fd = -1;
        !            45:        }
        !            46:        File(const File& rhs) {
        !            47:                fd = rhs.fd;
        !            48:        }
        !            49:        ~File() {
        !            50:                Close();
        !            51:        }
        !            52: 
        !            53:        bool Create(const std::string& filename_) {
        !            54:                filename = filename_;
        !            55:                fd = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
        !            56:                if (fd < 0) {
        !            57:                        err(1, "open: %s", filename.c_str());
        !            58:                }
        !            59:                return true;
        !            60:        }
        !            61:        bool Open(const std::string& filename_) {
        !            62:                filename = filename_;
        !            63:                fd = open(filename.c_str(), O_RDONLY);
        !            64:                if (fd < 0) {
        !            65:                        err(1, "open: %s", filename.c_str());
        !            66:                }
        !            67:                return true;
        !            68:        }
        !            69: 
        !            70:        void Close() {
        !            71:                if (fd >= 0) {
        !            72:                        close(fd);
        !            73:                }
        !            74:        }
        !            75: 
        !            76:        ssize_t Read(void *buf, size_t len) {
        !            77:                auto r = read(fd, buf, len);
        !            78:                if (r < 0) {
        !            79:                        err(1, "File.Read(%d)", fd);
        !            80:                }
        !            81:                return r;
        !            82:        }
        !            83: 
        !            84:        ssize_t Write(const void *buf, size_t len) {
        !            85:                auto r = write(fd, buf, len);
        !            86:                if (r < 0) {
        !            87:                        err(1, "File.Write(%d)", fd);
        !            88:                }
        !            89:                return r;
        !            90:        }
        !            91: 
        !            92:        // TTF はビッグエンディアンなので、専用読み込みルーチンを用意
        !            93:        uint8 Read8() {
        !            94:                uint8 buf[1];
        !            95:                Read(&buf, sizeof(buf));
        !            96:                return buf[0];
        !            97:        }
        !            98: 
        !            99:        uint16 Read16() {
        !           100:                uint8 buf[2];
        !           101:                Read(&buf, sizeof(buf));
        !           102:                return (buf[0] << 8) | buf[1];
        !           103:        }
        !           104: 
        !           105:        uint32 Read32() {
        !           106:                uint8 buf[4];
        !           107:                Read(&buf, sizeof(buf));
        !           108:                return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
        !           109:        }
        !           110: 
        !           111:        // 便利用
        !           112:        ssize_t Write(const std::string& str) {
        !           113:                return Write(str.data(), str.length());
        !           114:        }
        !           115: 
        !           116:        ssize_t Print(const char *fmt, ...) {
        !           117:                char buf[1024];
        !           118:                va_list ap;
        !           119:                va_start(ap, fmt);
        !           120:                vsnprintf(buf, sizeof(buf), fmt, ap);
        !           121:                va_end(ap);
        !           122:                return Write(buf, strlen(buf));
        !           123:        }
        !           124: 
        !           125:        off_t Seek(off_t off) {
        !           126:                auto r = lseek(fd, off, SEEK_SET);
        !           127:                if (r < 0) {
        !           128:                        err(1, "File.Seek(%d, %jd)", fd, (intmax_t)off);
        !           129:                }
        !           130:                return r;
        !           131:        }
        !           132: 
        !           133:        off_t Tell() const {
        !           134:                auto r = lseek(fd, 0, SEEK_CUR);
        !           135:                if (r < 0) {
        !           136:                        err(1, "File.Tell(%d)", fd);
        !           137:                }
        !           138:                return r;
        !           139:        }
        !           140: 
        !           141:        const std::string& GetName() const { return filename; }
        !           142: 
        !           143:  private:
        !           144:        int fd {};
        !           145:        std::string filename {};
        !           146: };
        !           147: 
        !           148: // UTF-16 文字列 srcbuf を UTF-8 文字列に変換して返す。
        !           149: // from はエンコーディング名で "utf-16be" とか。
        !           150: // UTF-16 は '\0' を含むので std::string が使えず他のエンコーディングの
        !           151: // 場合と共通化しづらい。
        !           152: static std::string
        !           153: FromUTF16(const char *from, const std::vector<uint8>& srcbuf)
        !           154: {
        !           155:        const char *to = "utf-8";
        !           156:        auto cd = iconv_open(to, from);
        !           157:        if (cd == (iconv_t)-1) {
        !           158:                err(1, "iconv_open(%s, %s)", to, from);
        !           159:        }
        !           160: 
        !           161:        const char *src = (const char *)&srcbuf[0];
        !           162:        size_t srcleft = srcbuf.size();
        !           163:        std::vector<char> dstbuf(srcbuf.size() * 3);    // 適当
        !           164:        char *dst = &dstbuf[0];
        !           165:        size_t dstleft = dstbuf.size();
        !           166: 
        !           167:        auto r = ICONV(cd, &src, &srcleft, &dst, &dstleft);
        !           168:        iconv_close(cd);
        !           169:        if (r == (size_t)-1 && errno != EILSEQ) {
        !           170:                err(1, "iconv failed");
        !           171:        }
        !           172: 
        !           173:        dst[dstbuf.size() - dstleft] = '\0';
        !           174:        return std::string(dstbuf.data());
        !           175: }
        !           176: 
        !           177: 
        !           178: //
        !           179: // ここから TTF
        !           180: //
        !           181: 
        !           182: struct NameRecord
        !           183: {
        !           184:        uint16 platformID;
        !           185:        uint16 encodingID;
        !           186:        uint16 languageID;
        !           187:        uint16 nameID;
        !           188:        uint16 length;
        !           189:        uint16 stringOffset;
        !           190: 
        !           191:        std::string name;
        !           192: 
        !           193:        // nameID の表示用文字列を返す
        !           194:        std::string GetNameIDStr() const {
        !           195:                return GetNameIDStr(nameID);
        !           196:        }
        !           197:        static std::string GetNameIDStr(uint id);
        !           198: };
        !           199: 
        !           200: // 各パラメータの意味はここのサイトの中ほどにある g の図参照。
        !           201: // https://learn.microsoft.com/en-us/typography/opentype/spec/eblc
        !           202: //
        !           203: // horiAdvance, vertAdvance が要するにフォントサイズ。
        !           204: struct BigGlyphMetrics
        !           205: {
        !           206:        uint8 height;           // Number of rows of data.
        !           207:        uint8 width;            // Number of columns of data.
        !           208:        int8  horiBearingX;     // Distance in pixels from the horizontal origin to
        !           209:                                                // the left edge of the bitmap.
        !           210:        int8  horiBearingY;     // Distance in pixels from the horizontal origin to
        !           211:                                                // the top edge of the bitmap.
        !           212:        uint8 horiAdvance;      // Horizontal advance width in pixels.
        !           213:        int8  vertBearingX;     // Distance in pixels from the vertical origin to
        !           214:                                                // the left edge of the bitmap.
        !           215:        int8  vertBearingY;     // Distance in pixels from the vertical origin to
        !           216:                                                // the top edge of the bitmap.
        !           217:        uint8 vertAdvance;      // Vertical advance width in pixels.
        !           218: 
        !           219:        // file からこの構造体をロードする
        !           220:        void Load(File& file);
        !           221: };
        !           222: 
        !           223: // IndexSubTable にいろいろ混ぜてしまう。
        !           224: class IndexSubTable
        !           225: {
        !           226:  public:
        !           227:        // indexSubTable
        !           228:        uint16 firstGlyphIndex;
        !           229:        uint16 lastGlyphIndex;
        !           230:        uint32 additionalOffsetToIndexSubTable; // サブテーブルまでのオフセット
        !           231: 
        !           232:        // indexSubHeader
        !           233:        uint16 indexFormat;                             // Format of this IndexSubTable.
        !           234:        uint16 imageFormat;                             // Format of EBDT image data.
        !           235:        uint32 imageDataOffset;                 // Offset to image data in EBDT table.
        !           236: 
        !           237:        // indexSubTable1
        !           238:        // first..last までの何かの EBDT 内でのオフセット
        !           239:        std::vector<uint32> sbitOffset;
        !           240: 
        !           241:        // indexSubTable2
        !           242:        uint32 imageSize;                               // All the glyphs are of the same size.
        !           243:        BigGlyphMetrics bigMetrics;             // All glyphs have the same metrics;
        !           244:                                                                        // glyph data may be compressed,
        !           245:                                                                        // byte-aligned, or bit-aligned.
        !           246: };
        !           247: 
        !           248: class SbitLineMetrics
        !           249: {
        !           250:  public:
        !           251:        int8  ascender;
        !           252:        int8  descender;        // 上を正として、ベースラインからの距離。なので負。
        !           253:        uint8 widthMax;
        !           254:        int8  caretSlopeNumerator;
        !           255:        int8  caretSlopeDenominator;
        !           256:        int8  caretOffset;
        !           257:        int8  minOriginSB;
        !           258:        int8  minAdvanceSB;
        !           259:        int8  maxBeforeBL;
        !           260:        int8  minAfterBL;
        !           261:        int8  pad1;
        !           262:        int8  pad2;
        !           263: 
        !           264:        // file からこの構造体をロードする
        !           265:        void Load(File& file);
        !           266: };
        !           267: 
        !           268: class BitmapSize
        !           269: {
        !           270:  public:
        !           271:        // flags。BigGlyphMetrics とかで使う。
        !           272:        enum {
        !           273:                HORIZONTAL_METRICS      = 0x01,
        !           274:                VERTICAL_METRICS        = 0x02,
        !           275:        };
        !           276: 
        !           277:  public:
        !           278:        uint32 indexSubTableArrayOffset;
        !           279:        uint32 indexTablesSize;
        !           280:        uint32 numberOfIndexSubTables;
        !           281:        uint32 colorRef;
        !           282:        SbitLineMetrics hori;
        !           283:        SbitLineMetrics vert;
        !           284:        uint16 startGlyphIndex;
        !           285:        uint16 endGlyphIndex;
        !           286:        uint8 ppemX;
        !           287:        uint8 ppemY;
        !           288:        uint8 bitDepth;
        !           289:        uint8 flags;
        !           290: 
        !           291:        std::vector<IndexSubTable> indexSubTable {};
        !           292: };
        !           293: 
        !           294: class EncodingRecord
        !           295: {
        !           296:  public:
        !           297:        uint16 platformID;
        !           298:        uint16 encodingID;
        !           299:        uint32 subtableOffset;
        !           300: 
        !           301:        std::string ToString() const;
        !           302: };
        !           303: 
        !           304: class CMAPSubTableFormat4
        !           305: {
        !           306:  public:
        !           307:        uint16 format;                                  // Format number is set to 4.
        !           308: 
        !           309:        uint16 length;                                  // This is the length in bytes of the
        !           310:                                                                        // subtable.
        !           311:        uint16 language;
        !           312:        uint16 segCountX2;                              // 2 × segCount.
        !           313: 
        !           314:        uint16 searchRange;                             // Maximum power of 2 less than or
        !           315:                                                                        // equal to segCount,
        !           316:                                                                        // times 2 ((2**floor(log2(segCount))) * 2
        !           317: 
        !           318:        uint16 entrySelector;                   // Log_2 of the maximum power of 2 less
        !           319:                                                                        // than or equal to numTables
        !           320:                                                                        // (log2(searchRange/2),
        !           321:                                                                        // which is equal to floor(log2(segCount)))
        !           322: 
        !           323:        uint16 rangeShift;                              // segCount times 2, minus searchRange
        !           324:                                                                        // ((segCount * 2) - searchRange)
        !           325: 
        !           326:        std::vector<uint16> endCode;    // End characterCode for each segment,
        !           327:                                                                        // last=0xFFFF.
        !           328: 
        !           329:        std::vector<uint16> startCode;  // Start character code for each segment.
        !           330:        std::vector<int16>  idDelta;    // Delta for all character codes in segment.
        !           331:        std::vector<uint16> idRangeOffsets;
        !           332:                                                                        // Offsets into glyphIdArray or 0
        !           333:        std::vector<uint16> glyphIdArray;
        !           334:                                                                        // Glyph index array (arbitrary length)
        !           335: };
        !           336: 
        !           337: enum NameID
        !           338: {
        !           339:        Copyright = 0,
        !           340:        FontFamilyName,
        !           341:        FontSubfamilyName,
        !           342:        UniqueFontIdentifier,
        !           343:        FullFontName,
        !           344:        VersionString,
        !           345:        PostScriptName,
        !           346:        Trademark,
        !           347:        ManufacturerName,
        !           348:        Designer,
        !           349:        Description,
        !           350:        URLVendor,
        !           351:        URLDesigner,
        !           352:        LicenseDescription,
        !           353:        LicenseInfoURL,
        !           354:        reserved,
        !           355:        TypoGraphicFamilyName,
        !           356:        TypographicSubfamilyName,
        !           357:        CompatibleFull,
        !           358:        SampleText,
        !           359:        PostScriptCIDFindfontName,
        !           360:        WWSFamilyName,
        !           361:        WWSSubfamilyName,
        !           362:        LightBackgroundPalette,
        !           363:        DarkBackgroundPalette,
        !           364:        VariationsPostScriptNamePrefix,
        !           365: };
        !           366: 
        !           367: class TTF_name
        !           368: {
        !           369:        enum {
        !           370:                Platform_Unicode = 0,
        !           371:                Platform_Macintosh = 1,
        !           372:                Platform_Windows = 3,
        !           373:        };
        !           374: 
        !           375:  public:
        !           376:        TTF_name(File& file, off_t offset);
        !           377: 
        !           378:        // 指定の NameID の値を取得する
        !           379:        std::string GetName(NameID id) const;
        !           380: 
        !           381:  private:
        !           382:        static std::string GetPlatformIDStr(uint id);
        !           383: 
        !           384:        std::vector<NameRecord> nameRecord {};
        !           385: };
        !           386: 
        !           387: class TTF_EBLC
        !           388: {
        !           389:  public:
        !           390:        TTF_EBLC(const File& file_, off_t offset);
        !           391: 
        !           392:        std::vector<BitmapSize> bitmapSizes {};
        !           393: 
        !           394:        // グリフ数を返す
        !           395:        int GetGlyphCount() const { return endGlyphIndex - startGlyphIndex + 1; }
        !           396:        // フォントパラメータを返す
        !           397:        int GetFontsize() const { return fontsize; }
        !           398:        int GetAscent() const   { return ascent; }
        !           399:        int GetDescent() const  { return descent; }
        !           400: 
        !           401:  private:
        !           402:        File file {};
        !           403: 
        !           404:        uint startGlyphIndex {};
        !           405:        uint endGlyphIndex {};
        !           406:        uint fontsize {};
        !           407:        int  ascent {};
        !           408:        int  descent {};                // 正数とする
        !           409: };
        !           410: 
        !           411: 
        !           412: class TTF_EBDT
        !           413: {
        !           414:  public:
        !           415:        TTF_EBDT(const File& file_, off_t offset);
        !           416: };
        !           417: 
        !           418: class TTF_cmap
        !           419: {
        !           420:  public:
        !           421:        TTF_cmap(const File& file_, off_t offset);
        !           422: 
        !           423:        void MakeASCIIMap(std::vector<uint>& asciimap);
        !           424:        void MakeJISMap(std::vector<uint>& jismap);
        !           425: 
        !           426:  private:
        !           427:        int Code2GID(int code) const;
        !           428: 
        !           429:        File file {};
        !           430:        std::vector<EncodingRecord> encodingRecords {};
        !           431:        CMAPSubTableFormat4 sub;
        !           432: };
        !           433: 
        !           434: 
        !           435: //
        !           436: // TTF を扱うクラス。
        !           437: // フィールド名などは概ね公式ドキュメントに従う。
        !           438: //
        !           439: class TTFFile
        !           440: {
        !           441:        static const uint32 NAME_name = FOURCC("name");
        !           442:        static const uint32 NAME_EBDT = FOURCC("EBDT");
        !           443:        static const uint32 NAME_EBLC = FOURCC("EBLC");
        !           444:        static const uint32 NAME_cmap = FOURCC("cmap");
        !           445: 
        !           446:  public:
        !           447:        TTFFile();
        !           448:        ~TTFFile();
        !           449: 
        !           450:        bool Open(const File& file_);
        !           451:        void SaveBDFAscii(File& outfile);
        !           452:        void SaveBDFKanji(File& outfile);
        !           453: 
        !           454:        // ASCII から GID への変換テーブル
        !           455:        std::vector<uint> asciimap {};
        !           456:        // JIS から GID への変換テーブル
        !           457:        std::vector<uint> jismap {};
        !           458: 
        !           459:        // グリフデータ。GID => data[]
        !           460:        std::vector<std::vector<uint8>> glyph {};
        !           461: 
        !           462:  private:
        !           463:        void LoadGlyph();
        !           464:        void LoadGlyphFormat1_7(const IndexSubTable& sub);
        !           465:        void LoadGlyphFormat2_5(const IndexSubTable& sub);
        !           466:        std::string DebugBitmapData(int, const std::vector<uint8>& data);
        !           467:        void WriteBDFHeader(File& outfile, int width);
        !           468:        std::string MakeChar(uint code, int, int, const std::vector<uint8>& data);
        !           469: 
        !           470:        File file {};
        !           471:        std::unique_ptr<TTF_name> name {};
        !           472:        std::unique_ptr<TTF_EBLC> eblc {};
        !           473:        std::unique_ptr<TTF_cmap> cmap {};
        !           474:        int fontsize {};
        !           475:        int ascent {};
        !           476:        int descent {};         // 正数とする
        !           477: 
        !           478:        uint32 offset_name {};
        !           479:        uint32 offset_EBDT {};
        !           480:        uint32 offset_EBLC {};
        !           481:        uint32 offset_cmap {};
        !           482: };
        !           483: 
        !           484: // コンストラクタ
        !           485: TTFFile::TTFFile()
        !           486: {
        !           487:        asciimap.resize(256);
        !           488:        // JIS は上位下位とも (0x21 .. 0x7e) の範囲
        !           489:        jismap.resize(0x5e * 0x5e);
        !           490: }
        !           491: 
        !           492: // デストラクタ
        !           493: TTFFile::~TTFFile()
        !           494: {
        !           495: }
        !           496: 
        !           497: // ファイルから情報を取得
        !           498: bool
        !           499: TTFFile::Open(const File& file_)
        !           500: {
        !           501:        file = file_;
        !           502: 
        !           503:        // 先頭のファイルヘッダ(オフセットテーブルというらしい)は 12バイト
        !           504:        uint32 version = file.Read32();
        !           505:        uint32 numTables = file.Read16();
        !           506:        uint32 searchRange __unused = file.Read16();
        !           507:        uint32 entrySelector __unused = file.Read16();
        !           508:        uint32 rangeShift __unused = file.Read16();
        !           509:        if (debug) {
        !           510:                printf("version = %08x\n", version);
        !           511:                printf("numTables = %d\n", numTables);
        !           512:        }
        !           513: 
        !           514:        // テーブルを調べて、必要なセクションの開始位置を取得
        !           515:        for (int i = 0; i < numTables; i++) {
        !           516:                uint32_t namecc = file.Read32();
        !           517:                uint32_t csum   = file.Read32();
        !           518:                uint32_t offset = file.Read32();
        !           519:                uint32_t len = file.Read32();
        !           520: 
        !           521:                (void)csum;
        !           522: 
        !           523:                if (debug) {
        !           524:                        printf("[%d] %c%c%c%c off=%08x len=%08x\n",
        !           525:                                i,
        !           526:                                (namecc >> 24),
        !           527:                                (namecc >> 16) & 0xff,
        !           528:                                (namecc >> 8)  & 0xff,
        !           529:                                (namecc)       & 0xff,
        !           530:                                offset,
        !           531:                                len);
        !           532:                }
        !           533: 
        !           534:                if (namecc == NAME_name) {
        !           535:                        offset_name = offset;
        !           536:                }
        !           537:                if (namecc == NAME_EBDT) {
        !           538:                        offset_EBDT = offset;
        !           539:                }
        !           540:                if (namecc == NAME_EBLC) {
        !           541:                        offset_EBLC = offset;
        !           542:                }
        !           543:                if (namecc == NAME_cmap) {
        !           544:                        offset_cmap = offset;
        !           545:                }
        !           546:        }
        !           547: 
        !           548:        // 必要なセクションが全部揃ってなければエラー終了
        !           549:        if (offset_name == 0) {
        !           550:                errx(1, "%s: no name section", file.GetName().c_str());
        !           551:        }
        !           552:        if (offset_EBDT == 0) {
        !           553:                errx(1, "%s: no EBDT section", file.GetName().c_str());
        !           554:        }
        !           555:        if (offset_EBLC == 0) {
        !           556:                errx(1, "%s: no EBLC section", file.GetName().c_str());
        !           557:        }
        !           558:        if (offset_cmap == 0) {
        !           559:                errx(1, "%s: no cmap section", file.GetName().c_str());
        !           560:        }
        !           561: 
        !           562:        // name セクションの読み込み。
        !           563:        // name セクションは Copyright などのプロパティなど。
        !           564:        name.reset(new TTF_name(file, offset_name));
        !           565: 
        !           566:        // EBLC セクションの読み込み。
        !           567:        // EBLC セクションはビットマップグリフに関する情報(サイズや格納方法)。
        !           568:        eblc.reset(new TTF_EBLC(file, offset_EBLC));
        !           569:        fontsize = eblc->GetFontsize();
        !           570:        ascent   = eblc->GetAscent();
        !           571:        descent  = eblc->GetDescent();
        !           572:        // それによってグリフ領域を確保。
        !           573:        glyph.resize(eblc->GetGlyphCount());
        !           574: 
        !           575:        // cmap セクションの読み込み。
        !           576:        // cmap セクションは文字コードと GID(内部のGlyph ID) とのマッピングなど。
        !           577:        cmap.reset(new TTF_cmap(file, offset_cmap));
        !           578: 
        !           579:        // グリフインデックスを求める。
        !           580:        cmap->MakeASCIIMap(asciimap);
        !           581:        cmap->MakeJISMap(jismap);
        !           582: 
        !           583:        // EBDT セクションからフォントデータを読み込み。
        !           584:        // EBDT セクションはほぼビットマップデータ置き場。
        !           585:        LoadGlyph();
        !           586: 
        !           587:        return true;
        !           588: }
        !           589: 
        !           590: // eblc で列挙されているすべてのグリフを読み込む。
        !           591: void
        !           592: TTFFile::LoadGlyph()
        !           593: {
        !           594:        // bitmapSizez[]->indexSubTable[] の2段階のテーブル。
        !           595:        for (const auto& bs : eblc->bitmapSizes) {
        !           596:                for (const auto& sub : bs.indexSubTable) {
        !           597:                        // indexFormat はグリフのパラメータを持っているこのインデックス
        !           598:                        // 自体が (共用体のようになっていて) どの形式かを示している。
        !           599:                        // imageFormat は EBDT 内のグリフデータがどの形式かを示している。
        !           600:                        // indexFormat と imageFormat は対象のグリフの性質によって
        !           601:                        // ある程度組み合わせが決まっている。
        !           602:                        if (sub.indexFormat == 1 && sub.imageFormat == 7) {
        !           603:                                LoadGlyphFormat1_7(sub);
        !           604:                        } else if (sub.indexFormat == 2 && sub.imageFormat == 5) {
        !           605:                                LoadGlyphFormat2_5(sub);
        !           606:                        } else {
        !           607:                                // 他の組み合わせは出てこないので無視
        !           608:                                errx(1, "Unsupported index=%d image=%d format",
        !           609:                                        sub.indexFormat, sub.imageFormat);
        !           610:                        }
        !           611:                }
        !           612:        }
        !           613: }
        !           614: 
        !           615: // indexFormat=1, imageFormat=7
        !           616: void
        !           617: TTFFile::LoadGlyphFormat1_7(const IndexSubTable& sub)
        !           618: {
        !           619:        if (sub.lastGlyphIndex - sub.firstGlyphIndex + 1 != sub.sbitOffset.size()) {
        !           620:                errx(1, "sbitOffset mismatch?");
        !           621:        }
        !           622: 
        !           623:        // indexFormat==1 は
        !           624:        // GlyphIndex の数分 sbitOffset[] で EBDT 内オフセットが指定してある。
        !           625:        //
        !           626:        // 例えば
        !           627:        //  firstGlyphIndex = 0
        !           628:        //  lastGlyphIndex  = 2
        !           629:        //  imageDataOffset = 00000004
        !           630:        //  sbitOffset[]    = { 00000000, 0000000c, 00000015 }
        !           631:        // の場合、グリフデータは EBDT 先頭から数えて
        !           632:        // GID=0 は $00000004+$00000000 = $00000004 から
        !           633:        // GID=1 は $00000004+$0000000c = $00000010 から
        !           634:        // GID=2 は $00000004+$00000015 = $00000019 から
        !           635:        // 始まる。
        !           636:        //
        !           637:        // imageFormat==7 は "big metrics, bit-aligned data" 形式で、EBDT 内の
        !           638:        // 指定オフセットの位置には
        !           639:        //  BigGlyphMetrics bigMetrics;
        !           640:        //  uint8           imageData[];
        !           641:        // が記録されている。
        !           642:        //
        !           643:        // 例えば三点リーダのような疎なグリフに有効で、
        !           644:        // BigGlyphMetrics でオフセットが指定されており、
        !           645:        // imageData[] は実際の描画点のある下図中枠の部分だけでよい、という構造。
        !           646:        //
        !           647:        //   BigGlyphMetrics.horiBearingX
        !           648:        //   |->|
        !           649:        //   +-------------+
        !           650:        //   |             |
        !           651:        //   |  +-------+  |---
        !           652:        //   |  |X  X  X|  | ^
        !           653:        //   |  +-------+  | | BigGlyphMetrics.horiBearingY
        !           654:        //   |             | |
        !           655:        //   +-------------+--- Baseline
        !           656:        //   |             |
        !           657:        //   +-------------+
        !           658: 
        !           659:        for (int i = 0, end = sub.sbitOffset.size(); i < end; i++) {
        !           660:                uint32 offset = sub.sbitOffset[i];
        !           661:                uint gid = sub.firstGlyphIndex + i;
        !           662: 
        !           663:                file.Seek(offset_EBDT + sub.imageDataOffset + offset);
        !           664:                BigGlyphMetrics big;
        !           665:                big.Load(file);
        !           666:                if (debug) {
        !           667:                        printf(" %08x %dx%d horiXY=(%d,%d) vertXY=(%d,%d) Adv=%dx%d\n",
        !           668:                                offset,
        !           669:                                big.width, big.height,
        !           670:                                big.horiBearingX,
        !           671:                                big.horiBearingY,
        !           672:                                big.vertBearingX,
        !           673:                                big.vertBearingY,
        !           674:                                big.horiAdvance,
        !           675:                                big.vertAdvance);
        !           676:                }
        !           677: 
        !           678:                // 64x64 ドットの領域を用意して一旦左上詰めで imageData[] を読み込む。
        !           679:                std::vector<uint64> map;
        !           680:                int bytelen = HOWMANY(big.width * big.height, 8);
        !           681:                int width = big.width;
        !           682:                uint64 input = 0;
        !           683:                int nbit = 0;
        !           684:                for (int j = 0; j < bytelen; j++) {
        !           685:                        // 入力キューに下から詰めていく
        !           686:                        input = (input << 8) | file.Read8();
        !           687:                        nbit += 8;
        !           688:                        // 1ライン分を超えてる間切り出す
        !           689:                        while (nbit >= width) {
        !           690:                                uint64 bytedata;
        !           691:                                // 1ライン分を右詰めして..
        !           692:                                bytedata = input >> (nbit - width);
        !           693:                                // MSB 詰めし直して..
        !           694:                                bytedata <<= (64 - width);
        !           695:                                // そのまま map の MSB 詰め
        !           696:                                map.push_back(bytedata);
        !           697:                                // 取り出した分を除外
        !           698:                                input <<= 64 - (nbit - width);
        !           699:                                input >>= 64 - (nbit - width);
        !           700:                                nbit -= width;
        !           701:                        }
        !           702:                }
        !           703: 
        !           704:                // X方向へオフセット。
        !           705:                // HORIZONTAL_METRICS では horiBearingX は左に入れるパディング。
        !           706:                for (auto& m : map) {
        !           707:                        m >>= big.horiBearingX;
        !           708:                }
        !           709:                // Y方向へオフセット。
        !           710:                // HORIZONTAL_METRICS では horiBearingY は基準線からグリフ片の
        !           711:                // 上端までの上方向へのオフセット。基準線は ascent, descent のやつ。
        !           712:                int paddingY = big.vertAdvance - 2 - big.horiBearingY;
        !           713:                for (int j = 0; j < paddingY; j++) {
        !           714:                        map.insert(map.begin(), 0);
        !           715:                }
        !           716:                // 縦が足りない分はここで補充
        !           717:                map.resize(big.vertAdvance);
        !           718: 
        !           719:                // horiAdvance * vertAdvance ビット分を書き出す。
        !           720:                std::vector<uint8> data;
        !           721:                for (int y = 0; y < big.vertAdvance; y++) {
        !           722:                        for (int x = 0; x < big.horiAdvance; x += 8) {
        !           723:                                uint8 c = map[y] >> (56 - x);
        !           724:                                data.push_back(c);
        !           725:                        }
        !           726:                }
        !           727: 
        !           728:                if (0 && debug) {
        !           729:                        printf("gid=%d\n", gid);
        !           730:                        printf("%s\n", DebugBitmapData(big.horiAdvance, data).c_str());
        !           731:                }
        !           732: 
        !           733:                glyph[gid] = data;
        !           734:        }
        !           735: }
        !           736: 
        !           737: // indexFormat=2, imageFormat=5
        !           738: void
        !           739: TTFFile::LoadGlyphFormat2_5(const IndexSubTable& sub)
        !           740: {
        !           741:        // indexFormat==2 "All glyphs have identical metrics" は
        !           742:        //  uint32 imageSize;
        !           743:        //  BigGlyphMetrics bigMetrics;
        !           744:        // を持っており、ここでメトリックが指定されている。
        !           745:        // EBDT 内のオフセットは imageDataOffset。
        !           746:        //
        !           747:        // imageFormat==5 "metrics in EBLC, bit-aligned image data only" は、
        !           748:        // データをビット境界で詰め込んであるのでここでバイト境界にする
        !           749:        // (横 12bit を横 2バイトにする)。
        !           750: 
        !           751:        int nglyph = sub.lastGlyphIndex - sub.firstGlyphIndex + 1;
        !           752:        uint32 bytes  = sub.imageSize;
        !           753:        file.Seek(offset_EBDT + sub.imageDataOffset);
        !           754:        for (int i = 0; i < nglyph; i++) {
        !           755:                uint32 gid = sub.firstGlyphIndex + i;
        !           756: 
        !           757:                std::vector<uint8> data;
        !           758:                int width = sub.bigMetrics.width;       // ショートカット
        !           759:                uint64 input = 0;                                       // とりあえず横64まで
        !           760:                int nbit = 0;                                           // input 内の有効ビット
        !           761:                for (int j = 0; j < bytes; j++) {
        !           762:                        // 入力キューに下から詰めていく
        !           763:                        input = (input << 8) | file.Read8();
        !           764:                        nbit += 8;
        !           765:                        // 1ライン分を超えたら切り出す
        !           766:                        if (nbit >= width) {
        !           767:                                uint32 bytedata;
        !           768:                                // 1ライン分を右詰めして..
        !           769:                                bytedata = input >> (nbit - width);
        !           770:                                // MSB 詰めし直して..
        !           771:                                bytedata <<= (32 - width);
        !           772:                                // 上から1バイトずつ取り出す
        !           773:                                for (int k = 0; k < width; k += 8) {
        !           774:                                        data.push_back(bytedata >> 24);
        !           775:                                        bytedata <<= 8;
        !           776:                                }
        !           777:                                // 取り出した部分を除外
        !           778:                                input <<= 64 - (nbit - width);
        !           779:                                input >>= 64 - (nbit - width);
        !           780:                                nbit -= width;
        !           781:                        }
        !           782:                }
        !           783:                glyph[gid] = data;
        !           784: 
        !           785:                if (0 && debug) {
        !           786:                        printf("gid=%d\n", gid);
        !           787:                        printf("%s\n", DebugBitmapData(width, data).c_str());
        !           788:                }
        !           789:        }
        !           790: }
        !           791: 
        !           792: // デバッグ用のビットマップデータの表示文字列を返す。
        !           793: // widthbit はフォントの横幅(ビット)。
        !           794: // data は byte-aligned のデータ。
        !           795: std::string
        !           796: TTFFile::DebugBitmapData(int widthbit, const std::vector<uint8>& data)
        !           797: {
        !           798:        std::string str;
        !           799: 
        !           800:        int wbyte = HOWMANY(widthbit, 8);
        !           801:        std::vector<char> buf(widthbit + 2);
        !           802: 
        !           803:        for (int i = 0, end = data.size(); i < end; i += wbyte) {
        !           804:                uint32 v = 0;
        !           805:                for (int j = 0; j < wbyte; j++) {
        !           806:                        v = (v << 8) | data[i + j];
        !           807:                }
        !           808:                v <<= (32 - wbyte * 8);
        !           809:                int b;
        !           810:                for (b = 0; b < wbyte * 8; b++, v <<= 1) {
        !           811:                        buf[b] = ((int32)v < 0) ? '@' : '.';
        !           812:                }
        !           813:                buf[b++] = '\n';
        !           814:                buf[b] = '\0';
        !           815: 
        !           816:                str += std::string(buf.data());
        !           817:        }
        !           818:        return str;
        !           819: }
        !           820: 
        !           821: // ASCII 部分 (というか半角部分) を BDF で保存する。
        !           822: void
        !           823: TTFFile::SaveBDFAscii(File& outfile)
        !           824: {
        !           825:        // ヘッダ出力
        !           826:        WriteBDFHeader(outfile, 1);
        !           827: 
        !           828:        // ここからグリフ出力。
        !           829:        // 先に要素数を出力する必要があるので一旦集める。
        !           830:        std::vector<std::string> glypharray;
        !           831:        for (int code = 0, end = asciimap.size(); code < end; code++) {
        !           832:                int gid = asciimap[code];
        !           833:                if (gid != 0) {
        !           834:                        auto line = MakeChar(code, fontsize / 2, fontsize, glyph[gid]);
        !           835:                        glypharray.push_back(line);
        !           836:                }
        !           837:        }
        !           838: 
        !           839:        // 出力
        !           840:        outfile.Print("CHARS %d\n", (int)glypharray.size());
        !           841:        outfile.Write("\n");
        !           842:        for (const auto& line : glypharray) {
        !           843:                outfile.Write(line.c_str());
        !           844:        }
        !           845:        outfile.Write("ENDFONT\n");
        !           846: }
        !           847: 
        !           848: // 全角部分を BDF で保存する。
        !           849: void
        !           850: TTFFile::SaveBDFKanji(File& outfile)
        !           851: {
        !           852:        // ヘッダ出力
        !           853:        WriteBDFHeader(outfile, 2);
        !           854: 
        !           855:        // ここからグリフ出力。
        !           856:        // 先に要素数を出力する必要があるので一旦集める。
        !           857:        std::vector<std::string> glypharray;
        !           858:        for (int h = 0; h < 0x5e; h++) {
        !           859:                for (int l = 0; l < 0x5e; l++) {
        !           860:                        int idx = h * 0x5e + l;
        !           861:                        int gid = jismap[idx];
        !           862:                        int jh = 0x21 + h;
        !           863:                        int jl = 0x21 + l;
        !           864:                        int jiscode = (jh << 8) + jl;
        !           865:                        if (gid != 0) {
        !           866:                                auto line = MakeChar(jiscode, fontsize, fontsize, glyph[gid]);
        !           867:                                glypharray.push_back(line);
        !           868:                        }
        !           869:                }
        !           870:        }
        !           871: 
        !           872:        // 出力
        !           873:        outfile.Print("CHARS %d\n", (int)glypharray.size());
        !           874:        outfile.Write("\n");
        !           875:        for (const auto& line : glypharray) {
        !           876:                outfile.Write(line.c_str());
        !           877:        }
        !           878:        outfile.Write("ENDFONT\n");
        !           879: }
        !           880: 
        !           881: // width は 1 なら半角、2 なら全角。
        !           882: void
        !           883: TTFFile::WriteBDFHeader(File& outfile, int width)
        !           884: {
        !           885:        // よく分からん
        !           886:        std::string foundry = name->GetName(NameID::ManufacturerName);
        !           887:        std::string family_name = name->GetName(NameID::PostScriptName);
        !           888:        std::string weight = name->GetName(NameID::FontSubfamilyName);
        !           889:        const char *slant = "R";
        !           890:        const char *setwidth = "Normal";
        !           891:        const char *addstyle = "";
        !           892:        int pixelsize = fontsize;
        !           893:        int pointsize = 150;
        !           894:        int resolutionX = 75;
        !           895:        int resolutionY = 75;
        !           896:        char spacing = 'C';
        !           897:        int average_width = fontsize * 10;      // ?
        !           898:        const char *charset_registry;
        !           899:        if (width == 1) {
        !           900:                charset_registry = "ASCII";
        !           901:        } else {
        !           902:                charset_registry = "JISX0208.1983";
        !           903:        }
        !           904:        const char *charset_encoding = "0";
        !           905: 
        !           906:        if (foundry.empty()) {
        !           907:                foundry = "misc";
        !           908:        }
        !           909: 
        !           910:        // SIL OFL は改変フォントに元フォントと同じ名前を使ってはいけない。
        !           911:        // TTF -> BDF の形式変更も改変になるようにも読めるので、別の名前を使う。
        !           912:        if (new_family_name) {
        !           913:                family_name = new_family_name;
        !           914:        }
        !           915: 
        !           916:        outfile.Write("STARTFONT 2.1\n");
        !           917:        outfile.Print("FONT -%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%c-%d-%s-%s\n",
        !           918:                foundry.c_str(),
        !           919:                family_name.c_str(),
        !           920:                weight.c_str(),
        !           921:                slant,
        !           922:                setwidth,
        !           923:                addstyle,
        !           924:                pixelsize,
        !           925:                pointsize,
        !           926:                resolutionX,
        !           927:                resolutionY,
        !           928:                spacing,
        !           929:                average_width,
        !           930:                charset_registry,
        !           931:                charset_encoding);
        !           932:        outfile.Print("SIZE %d %d %d\n", fontsize, resolutionX, resolutionY);
        !           933:        outfile.Print("FONTBOUNDINGBOX %d %d 0 %d\n",
        !           934:                fontsize / 2 * width, fontsize, -descent);
        !           935:        outfile.Write("\n");
        !           936: 
        !           937:        // プロパティ。先頭行に要素数を出力する必要があるので一旦集める
        !           938:        std::vector<std::string> prop;
        !           939: #define PROP(str...)   prop.push_back(string_format(str));
        !           940: 
        !           941:        PROP("FONTNAME_REGISTRY \"\"");
        !           942:        PROP("FOUNDRY \"%s\"", foundry.c_str());
        !           943:        PROP("FAMILY_NAME \"%s\"", family_name.c_str());
        !           944:        PROP("WEIGHT_NAME \"%s\"", weight.c_str());
        !           945:        PROP("SLANT \"%s\"", slant);
        !           946:        PROP("SETWIDTH_NAME \"%s\"", setwidth);
        !           947:        PROP("ADD_STYLE_NAME \"%s\"", addstyle);
        !           948:        PROP("PIXEL_SIZE %d", fontsize);
        !           949:        PROP("POINT_SIZE %d", pointsize);
        !           950:        PROP("RESOLUTION_X %d", resolutionX);
        !           951:        PROP("RESOLUTION_Y %d", resolutionY);
        !           952:        PROP("SPACING \"%c\"", spacing);
        !           953:        PROP("AVERAGE_WIDTH %d", average_width);
        !           954:        PROP("CHARSET_REGISTRY \"%s\"", charset_registry);
        !           955:        PROP("CHARSET_ENCODING \"%s\"", charset_encoding);
        !           956:        PROP("COPYRIGHT \"%s License: %s %s\"",
        !           957:                name->GetName(NameID::Copyright).c_str(),
        !           958:                name->GetName(NameID::LicenseDescription).c_str(),
        !           959:                name->GetName(NameID::LicenseInfoURL).c_str());
        !           960:        PROP("WEIGHT 10");
        !           961:        PROP("DEFAULT_CHAR 0"); // ?
        !           962:        PROP("FONT_ASCENT %d", ascent);
        !           963:        PROP("FONT_DESCENT %d", descent);
        !           964: 
        !           965:        // 勝手に追加。
        !           966:        std::string v;
        !           967:        v = name->GetName(NameID::FullFontName);
        !           968:        if (v.empty() == false) {
        !           969:                PROP("_COMMENT_TTF_FULL_FONT_NAME \"%s\"", v.c_str());
        !           970:        }
        !           971:        v = name->GetName(NameID::VersionString);
        !           972:        if (v.empty() == false) {
        !           973:                PROP("_COMMENT_TTF_VERSION \"%s\"", v.c_str());
        !           974:        }
        !           975:        v = name->GetName(NameID::Designer);
        !           976:        if (v.empty() == false) {
        !           977:                PROP("_COMMENT_TTF_DESIGNER \"%s\"", v.c_str());
        !           978:        }
        !           979:        v = name->GetName(NameID::URLVendor);
        !           980:        if (v.empty() == false) {
        !           981:                PROP("_COMMENT_TTF_URL_VENDOR \"%s\"", v.c_str());
        !           982:        }
        !           983:        v = name->GetName(NameID::URLDesigner);
        !           984:        if (v.empty() == false) {
        !           985:                PROP("_COMMENT_TTF_URL_DESIGNER \"%s\"", v.c_str());
        !           986:        }
        !           987: 
        !           988:        outfile.Print("STARTPROPERTIES %d\n", (int)prop.size());
        !           989:        for (const auto& s : prop) {
        !           990:                outfile.Write(s + "\n");
        !           991:        }
        !           992:        outfile.Write("ENDPROPERTIES\n");
        !           993:        outfile.Write("\n");
        !           994: }
        !           995: 
        !           996: // 1文字分の STARTCHAR .. ENDCHAR までを文字列にしたものを返す。
        !           997: std::string
        !           998: TTFFile::MakeChar(uint code, int w, int h, const std::vector<uint8>& data)
        !           999: {
        !          1000:        std::string lines;
        !          1001: 
        !          1002:        // STARTCHAR のほうはほぼコメント。ENCODING のほうが文字のコード。
        !          1003:        if (code < 0x100) {
        !          1004:                lines += string_format("STARTCHAR %02x\n", code);
        !          1005:        } else {
        !          1006:                lines += string_format("STARTCHAR %04x\n", code);
        !          1007:        }
        !          1008:        lines += string_format("ENCODING %d\n", code);
        !          1009:        lines += "SWIDTH 960 0\n";
        !          1010:        lines += string_format("DWIDTH %d 0\n", w);
        !          1011:        lines += string_format("BBX %d %d 0 %d\n", w, h, -descent);
        !          1012:        lines += "BITMAP\n";
        !          1013:        // 読み込んだグリフデータは左端を必ずバイト境界から始めてある。
        !          1014:        int widthbytes = data.size() / h;
        !          1015:        for (int y = 0, yend = data.size(); y < yend; y += widthbytes) {
        !          1016:                for (int x = 0; x < widthbytes; x++) {
        !          1017:                        lines += string_format("%02x", data[y + x]);
        !          1018:                }
        !          1019:                lines += "\n";
        !          1020:        }
        !          1021:        lines += "ENDCHAR\n";
        !          1022:        lines += "\n";
        !          1023: 
        !          1024:        return lines;
        !          1025: }
        !          1026: 
        !          1027: 
        !          1028: //
        !          1029: // name セクション
        !          1030: //
        !          1031: 
        !          1032: // コンストラクタ
        !          1033: TTF_name::TTF_name(File& file, off_t offset_name)
        !          1034: {
        !          1035:        file.Seek(offset_name);
        !          1036:        uint32 version = file.Read16();
        !          1037:        uint32 count = file.Read16();
        !          1038:        uint32 storageOffset = file.Read16();
        !          1039: 
        !          1040:        nameRecord.resize(count);
        !          1041:        for (int i = 0; i < count; i++) {
        !          1042:                auto& rec = nameRecord[i];
        !          1043:                rec.platformID = file.Read16();
        !          1044:                rec.encodingID = file.Read16();
        !          1045:                rec.languageID = file.Read16();
        !          1046:                rec.nameID     = file.Read16();
        !          1047:                rec.length    = file.Read16();
        !          1048:                rec.stringOffset = file.Read16();
        !          1049:        }
        !          1050:        for (auto& rec : nameRecord) {
        !          1051:                file.Seek(offset_name + storageOffset + rec.stringOffset);
        !          1052:                if (rec.platformID == Platform_Windows) {
        !          1053:                        // UTF-16BE エンコーディングと規定されている
        !          1054:                        std::vector<uint8> src(rec.length);
        !          1055:                        for (int j = 0; j < rec.length; j++) {
        !          1056:                                src[j] = file.Read8();
        !          1057:                        }
        !          1058:                        rec.name = FromUTF16("utf-16be", src);
        !          1059:                } else {
        !          1060:                        // とりあえず ASCII だと思ってそのまま使う
        !          1061:                        for (int j = 0; j < rec.length; j++) {
        !          1062:                                rec.name += (char)file.Read8();
        !          1063:                        }
        !          1064:                }
        !          1065:        }
        !          1066: 
        !          1067:        if (debug) {
        !          1068:                for (int i = 0, end = nameRecord.size(); i < end; i++) {
        !          1069:                        const auto& rec = nameRecord[i];
        !          1070:                        printf("nameRecord[%d].platformID = %d(%s)\n",
        !          1071:                                i, rec.platformID, GetPlatformIDStr(rec.platformID).c_str());
        !          1072:                        printf("nameRecord[%d].encodingID = %d\n", i, rec.encodingID);
        !          1073:                        printf("nameRecord[%d].languageID = %d\n", i, rec.languageID);
        !          1074:                        printf("nameRecord[%d].nameID = %d(%s)\n", i, rec.nameID,
        !          1075:                                rec.GetNameIDStr().c_str());
        !          1076:                        printf("nameRecord[%d].name = \"%s\"\n", i, rec.name.c_str());
        !          1077:                }
        !          1078:        }
        !          1079:        (void)version;
        !          1080:        (void)storageOffset;
        !          1081: }
        !          1082: 
        !          1083: // 指定の文字列を返す。
        !          1084: // とりあえず ASCII だけっぽい Macintosh のほうから返す。どうしたもんか。
        !          1085: std::string
        !          1086: TTF_name::GetName(NameID id) const
        !          1087: {
        !          1088:        for (const auto& rec : nameRecord) {
        !          1089:                if (rec.platformID == Platform_Macintosh && rec.nameID == id) {
        !          1090:                        return rec.name;
        !          1091:                }
        !          1092:        }
        !          1093:        return "";
        !          1094: }
        !          1095: 
        !          1096: // デバッグ用に platformID を文字列にして返す
        !          1097: /*static*/ std::string
        !          1098: TTF_name::GetPlatformIDStr(uint id)
        !          1099: {
        !          1100:        static const char * const platform_names[] = {
        !          1101:                "Unicode",
        !          1102:                "Macintosh",
        !          1103:                "",
        !          1104:                "Windows",
        !          1105:        };
        !          1106: 
        !          1107:        if (id < countof(platform_names)) {
        !          1108:                return platform_names[id];
        !          1109:        } else {
        !          1110:                return "";
        !          1111:        }
        !          1112: }
        !          1113: 
        !          1114: 
        !          1115: //
        !          1116: // EBLC セクション
        !          1117: //
        !          1118: 
        !          1119: // コンストラクタ
        !          1120: TTF_EBLC::TTF_EBLC(const File& file_, off_t offset_EBLC)
        !          1121:        : file(file_)
        !          1122: {
        !          1123:        startGlyphIndex = 0;
        !          1124:        endGlyphIndex = 65536;
        !          1125: 
        !          1126:        // EBLC ヘッダ
        !          1127:        //  uint16 majorVersion;
        !          1128:        //  uint16 minorVersion;
        !          1129:        //  uint32 numSizes;
        !          1130:        //  BitmapSize bitmapSizes[];
        !          1131:        // numSizes フィールドの次から、BitmapSize が numSizes 個並んでるっぽい。
        !          1132: 
        !          1133:        file.Seek(offset_EBLC);
        !          1134:        auto majorVersion = file.Read16();
        !          1135:        auto minorVersion = file.Read16();
        !          1136:        if (majorVersion != 2 || minorVersion != 0) {
        !          1137:                errx(1, "Unknown EBLC version %d.%d", majorVersion, minorVersion); 
        !          1138:        }
        !          1139:        uint32 numSizes = file.Read32();
        !          1140:        if (debug) {
        !          1141:                printf("EBLC.numSizes=%d\n", numSizes);
        !          1142:        }
        !          1143: 
        !          1144:        // bitmapSizes[] を全部読み込む
        !          1145:        bitmapSizes.resize(numSizes);
        !          1146:        for (int i = 0; i < numSizes; i++) {
        !          1147:                auto& bs = bitmapSizes[i];
        !          1148:                bs.indexSubTableArrayOffset = file.Read32();
        !          1149:                bs.indexTablesSize = file.Read32();
        !          1150:                bs.numberOfIndexSubTables = file.Read32();
        !          1151:                bs.colorRef = file.Read32();
        !          1152:                bs.hori.Load(file);
        !          1153:                bs.vert.Load(file);
        !          1154:                bs.startGlyphIndex = file.Read16();
        !          1155:                bs.endGlyphIndex = file.Read16();
        !          1156:                bs.ppemX = file.Read8();
        !          1157:                bs.ppemY = file.Read8();
        !          1158:                bs.bitDepth = file.Read8();
        !          1159:                bs.flags = file.Read8();
        !          1160:                if (debug) {
        !          1161:                        printf("bitmapSizes[%d]\n", i);
        !          1162: #define A(fmt, name)   printf(" %-24s = " #fmt "\n", #name, bs.name)
        !          1163:                        A($%08x, indexSubTableArrayOffset);
        !          1164:                        A($%08x, indexTablesSize);
        !          1165:                        A(%d, numberOfIndexSubTables);
        !          1166:                        A(%d, colorRef);
        !          1167:                        A(%d, startGlyphIndex);
        !          1168:                        A(%d, endGlyphIndex);
        !          1169:                        A(%d, ppemX);
        !          1170:                        A(%d, ppemY);
        !          1171:                        A(%d, bitDepth);
        !          1172:                        A($%x, flags);
        !          1173: 
        !          1174:                        if (bs.flags == BitmapSize::HORIZONTAL_METRICS) {
        !          1175:                                A(%d, hori.ascender);
        !          1176:                                A(%d, hori.descender);
        !          1177:                        }
        !          1178:                }
        !          1179:                if (bs.flags != BitmapSize::HORIZONTAL_METRICS) {
        !          1180:                        errx(1, "Unsupported bitmap flags $%x", bs.flags);
        !          1181:                }
        !          1182: 
        !          1183:                // bitmapSizes[] 全体に渡る start..end
        !          1184:                startGlyphIndex = std::max(startGlyphIndex, (uint)bs.startGlyphIndex);
        !          1185:                endGlyphIndex   = std::min(endGlyphIndex,   (uint)bs.endGlyphIndex);
        !          1186: 
        !          1187:                // 代表値みたいなのは本当はないかもだが、必要なのでとりあえず。
        !          1188:                if (fontsize == 0) {
        !          1189:                        fontsize = bs.ppemY;
        !          1190:                } else if (fontsize != bs.ppemY) {
        !          1191:                        errx(1, "bs.ppemY=%d but already has %d", bs.ppemY, fontsize);
        !          1192:                }
        !          1193:                if (ascent == 0) {
        !          1194:                        ascent = bs.hori.ascender;
        !          1195:                } else if (ascent != bs.hori.ascender) {
        !          1196:                        errx(1, "bs.hori.ascender=%d but already has %d",
        !          1197:                                bs.hori.ascender, ascent);
        !          1198:                }
        !          1199:                // (TTF の) descender は負数、(BDF の) descent は正数ということにする。
        !          1200:                if (descent == 0) {
        !          1201:                        descent = -bs.hori.descender;
        !          1202:                } else if (descent != bs.hori.descender) {
        !          1203:                        errx(1, "bs.hori.descender=%d but already has (-)%d",
        !          1204:                                bs.hori.descender, descent);
        !          1205:                }
        !          1206:        }
        !          1207: 
        !          1208:        // 各 bitmapSizes には indexSubTables[] 配列がある。
        !          1209:        for (auto& bs : bitmapSizes) {
        !          1210:                bs.indexSubTable.resize(bs.numberOfIndexSubTables);
        !          1211: 
        !          1212:                // indexSubTableArrayOffset は EBLC 先頭からのオフセット。
        !          1213:                // indexSubTable[] 自体にはグリフ範囲と additional へのオフセットのみ
        !          1214:                // 記録されている。additional のほうはフォーマットによってサイズが
        !          1215:                // 異なるためだと思う。
        !          1216:                // additional が指す先は indexSubHeader で、ここの indexFormat で
        !          1217:                // この後に続く indexSubTable の種別が決まっている。
        !          1218:                //
        !          1219:                // indexSubTable {
        !          1220:                //   { firstGlyphIndex, lastGlyphIndex }
        !          1221:                //   additionalOffsetToIndexSubTable ----+
        !          1222:                // }                                     |
        !          1223:                //                                       |
        !          1224:                // +-------------------------------------+
        !          1225:                // |
        !          1226:                // +-> indexSubHeader {
        !          1227:                //   indexFormat = 1;
        !          1228:                //   imageFormat;
        !          1229:                //   imageDataOffset --> EBDT 内のオフセット
        !          1230:                // }
        !          1231:                // indexSubTable1 {
        !          1232:                //   IndexSubType;
        !          1233:                //   sbitOffset[];
        !          1234:                // }
        !          1235:                // 
        !          1236:                file.Seek(offset_EBLC + bs.indexSubTableArrayOffset);
        !          1237:                for (int j = 0; j < bs.numberOfIndexSubTables; j++) {
        !          1238:                        auto& sub = bs.indexSubTable[j];
        !          1239:                        sub.firstGlyphIndex = file.Read16();
        !          1240:                        sub.lastGlyphIndex  = file.Read16();
        !          1241:                        sub.additionalOffsetToIndexSubTable = file.Read32();
        !          1242:                        if (debug) {
        !          1243:                                printf("indexSubTable[%d] GlyphIndex=%4d-%4d "
        !          1244:                                        "additionalOffsetToIndexSubTable=$%08x\n", j,
        !          1245:                                        sub.firstGlyphIndex,
        !          1246:                                        sub.lastGlyphIndex,
        !          1247:                                        sub.additionalOffsetToIndexSubTable);
        !          1248:                        }
        !          1249:                }
        !          1250: 
        !          1251:                for (int j = 0; j < bs.numberOfIndexSubTables; j++) {
        !          1252:                        auto& sub = bs.indexSubTable[j];
        !          1253:                        file.Seek(offset_EBLC
        !          1254:                                + bs.indexSubTableArrayOffset
        !          1255:                                + sub.additionalOffsetToIndexSubTable);
        !          1256:                        // ここが indexSubType header
        !          1257:                        sub.indexFormat = file.Read16();
        !          1258:                        sub.imageFormat = file.Read16();
        !          1259:                        sub.imageDataOffset = file.Read32();
        !          1260:                        if (debug) {
        !          1261:                                printf("[%d] indexFormat=%d imageFormat=%d "
        !          1262:                                        "imageDataOffset=$%08x\n",
        !          1263:                                        j, sub.indexFormat, sub.imageFormat, sub.imageDataOffset);
        !          1264:                        }
        !          1265: 
        !          1266:                        switch (sub.indexFormat) {
        !          1267:                         case 1:        // indexSubTable1
        !          1268:                         {
        !          1269:                                // IndexSubType header;
        !          1270:                                // Offset32     sbitOffset[];
        !          1271:                                //
        !          1272:                                // 公式ドキュメントの改行位置が微妙に分からんけど。
        !          1273:                                //  sbitOffsets[glyphIndex] + imageDataOffset = glyphData;
        !          1274:                                //  sizeOfArray = (lastGlyph - firstGlyph + 1) + 1
        !          1275:                                //                + 1 pad if needed;
        !          1276:                                int nglyph = sub.lastGlyphIndex - sub.firstGlyphIndex + 1;
        !          1277:                                sub.sbitOffset.resize(nglyph);
        !          1278:                                for (int i = 0; i < nglyph; i++) {
        !          1279:                                        uint32 offset = file.Read32();
        !          1280:                                        sub.sbitOffset[i] = offset;
        !          1281:                                        if (debug) {
        !          1282:                                                printf(" %08x", offset);
        !          1283:                                                if (i % 8 == 7) {
        !          1284:                                                        printf("\n");
        !          1285:                                                }
        !          1286:                                        }
        !          1287:                                }
        !          1288:                                if (debug) {
        !          1289:                                        printf("\n");
        !          1290:                                }
        !          1291:                                break;
        !          1292:                         }
        !          1293: 
        !          1294:                         case 2:        // indexSubTable2
        !          1295:                         {
        !          1296:                                // IndexSubType    header;
        !          1297:                                // uint32          imageSize;
        !          1298:                                // BigGlyphMetrics bigMetrics;
        !          1299:                                sub.imageSize = file.Read32();
        !          1300:                                auto& big = sub.bigMetrics;
        !          1301:                                big.Load(file);
        !          1302:                                if (debug) {
        !          1303:                                        printf(" imageSize=%d bigMetrics=%dx%d\n",
        !          1304:                                                sub.imageSize, big.width, big.height);
        !          1305:                                }
        !          1306:                                break;
        !          1307:                         }
        !          1308: 
        !          1309:                         default:
        !          1310:                                errx(1, "unsupported indexSubTable%d", sub.indexFormat);
        !          1311:                        }
        !          1312:                }
        !          1313:        }
        !          1314: }
        !          1315: 
        !          1316: 
        !          1317: //
        !          1318: // cmap セクション
        !          1319: //
        !          1320: 
        !          1321: // コンストラクタ
        !          1322: TTF_cmap::TTF_cmap(const File& file_, off_t offset)
        !          1323:        : file(file_)
        !          1324: {
        !          1325:        // cmap ヘッダ
        !          1326:        //
        !          1327:        //  uint16 version;             // Table version number (0).
        !          1328:        //  uint16 numTables;   // Number of encoding tables that follow.
        !          1329:        //  EncodingRecord encodingRecords[numTables];
        !          1330: 
        !          1331:        file.Seek(offset);
        !          1332:        uint16 version = file.Read16();
        !          1333:        uint16 numTables = file.Read16();
        !          1334: 
        !          1335:        if (version != 0) {
        !          1336:                errx(1, "Unsupported cmap version %d\n", version);
        !          1337:        }
        !          1338: 
        !          1339:        printf("cmap:\n");
        !          1340:        encodingRecords.resize(numTables);
        !          1341:        for (int i = 0; i < encodingRecords.size(); i++) {
        !          1342:                auto& enc = encodingRecords[i];
        !          1343:                enc.platformID = file.Read16();
        !          1344:                enc.encodingID = file.Read16();
        !          1345:                enc.subtableOffset = file.Read32();
        !          1346: 
        !          1347:                if (debug) {
        !          1348:                        printf("[%d] %s subtableOffset=%08x\n", i,
        !          1349:                                enc.ToString().c_str(),
        !          1350:                                enc.subtableOffset);
        !          1351:                }
        !          1352:        }
        !          1353: 
        !          1354:        // とりあえず Unicode2.0 BMP のみターゲットにする。
        !          1355:        EncodingRecord *uenc = NULL;
        !          1356:        for (auto& enc : encodingRecords) {
        !          1357:                if (enc.platformID == 0 && enc.encodingID == 3) {
        !          1358:                        uenc = &enc;
        !          1359:                }
        !          1360:        }
        !          1361:        if (uenc == NULL) {
        !          1362:                errx(1, "cmap: Unicode2.0 BMP mapping not found");
        !          1363:        }
        !          1364: 
        !          1365:        // Unicode2.0 BMP の場合 offset の先は Format4 らしい。
        !          1366:        // subtableOffset は cmap 先頭からのオフセット。
        !          1367:        off_t tablestart = file.Seek(offset + uenc->subtableOffset);
        !          1368:        sub.format = file.Read16();
        !          1369:        sub.length = file.Read16();
        !          1370:        sub.language = file.Read16();
        !          1371:        sub.segCountX2 = file.Read16();
        !          1372:        sub.searchRange = file.Read16();
        !          1373:        sub.entrySelector = file.Read16();
        !          1374:        sub.rangeShift = file.Read16();
        !          1375: 
        !          1376:        int segCount = sub.segCountX2 / 2;
        !          1377:        sub.endCode.resize(segCount);
        !          1378:        sub.startCode.resize(segCount);
        !          1379:        sub.idDelta.resize(segCount);
        !          1380:        sub.idRangeOffsets.resize(segCount);
        !          1381: 
        !          1382:        for (int i = 0; i < segCount; i++) {
        !          1383:                sub.endCode[i] = file.Read16();
        !          1384:        }
        !          1385:        file.Read16();  // reservedPad
        !          1386:        for (int i = 0; i < segCount; i++) {
        !          1387:                sub.startCode[i] = file.Read16();
        !          1388:        }
        !          1389:        for (int i = 0; i < segCount; i++) {
        !          1390:                sub.idDelta[i] = file.Read16();
        !          1391:        }
        !          1392:        for (int i = 0; i < segCount; i++) {
        !          1393:                sub.idRangeOffsets[i] = file.Read16();
        !          1394:        }
        !          1395:        // glyphIdArray[] の長さは直接は書いてないが、sub.length が
        !          1396:        // (たぶん subtable 先頭からの) subtable のバイト長らしいので、そこまで。
        !          1397:        int remain = sub.length - (file.Tell() - tablestart);
        !          1398:        sub.glyphIdArray.resize(remain / 2);
        !          1399:        for (int i = 0; i < remain / 2; i++) {
        !          1400:                sub.glyphIdArray[i] = file.Read16();
        !          1401:        }
        !          1402: 
        !          1403:        if (debug) {
        !          1404:                printf("cmap SubTable Format4\n");
        !          1405:                printf(" format=%d\n", sub.format);
        !          1406:                printf(" length=%d\n", sub.length);
        !          1407:                printf(" language=%d\n", sub.language);
        !          1408:                printf(" segCountX2=%d\n", sub.segCountX2);
        !          1409:                printf(" searchRange=%d\n", sub.searchRange);
        !          1410:                printf(" entrySelector=%d\n", sub.entrySelector);
        !          1411:                printf(" rangeShift=%d\n", sub.rangeShift);
        !          1412:                printf(" glyphIdArray[%d]\n", (int)sub.glyphIdArray.size());
        !          1413:        }
        !          1414: }
        !          1415: 
        !          1416: // ASCII というか1バイトコードのマップを作成する。
        !          1417: // asciimap は 0-255 確保されていること。
        !          1418: void
        !          1419: TTF_cmap::MakeASCIIMap(std::vector<uint>& asciimap)
        !          1420: {
        !          1421:        // ASCII + 1バイトコード
        !          1422:        for (int code = 1, end = asciimap.size(); code < end; code++) {
        !          1423:                asciimap[code] = Code2GID(code);
        !          1424:        }
        !          1425:        if (debug) {
        !          1426:                printf("asciimap:\n");
        !          1427:                for (int i = 0, end = asciimap.size(); i < end; i++) {
        !          1428:                        printf(" %3d", asciimap[i]);
        !          1429:                        if (i % 16 == 15) {
        !          1430:                                printf("\n");
        !          1431:                        }
        !          1432:                }
        !          1433:        }
        !          1434: }
        !          1435: 
        !          1436: // JIS コードのマップを作成する。
        !          1437: // jismap はあらかじめ確保されていること。
        !          1438: void
        !          1439: TTF_cmap::MakeJISMap(std::vector<uint>& jismap)
        !          1440: {
        !          1441:        auto cd = iconv_open("unicodebig", "iso-2022-jp");
        !          1442:        if (cd == (iconv_t)-1) {
        !          1443:                err(1, "iconv_open");
        !          1444:        }
        !          1445: 
        !          1446:        for (int h = 0; h < 0x5e; h++) {
        !          1447:                for (int l = 0; l < 0x5e; l++) {
        !          1448:                        int idx = h * 0x5e + l;
        !          1449: 
        !          1450:                        // JIS コードを Unicode コードポイントに変換
        !          1451:                        int jh = 0x21 + h;
        !          1452:                        int jl = 0x21 + l;
        !          1453:                        std::string srcbuf = string_format("\x1b$B%c%c\x1b(B", jh, jl);
        !          1454:                        const char *src = &srcbuf[0];
        !          1455:                        size_t srcleft = srcbuf.length();
        !          1456:                        std::array<uint8, 4> dstbuf;
        !          1457:                        char *dst = (char *)dstbuf.data();
        !          1458:                        size_t dstleft = dstbuf.size();
        !          1459:                        auto r = ICONV(cd, &src, &srcleft, &dst, &dstleft);
        !          1460:                        if (r == (size_t)-1) {
        !          1461:                                // JIS コードの割り当てがないところでは EILSEQ になる
        !          1462:                                if (errno == EILSEQ) {
        !          1463:                                        continue;
        !          1464:                                }
        !          1465:                                err(1, "iconv %02x%02x failed", jh, jl);
        !          1466:                        }
        !          1467:                        if (r != 0) {
        !          1468:                                errx(1, "iconv: invalid conversions is %zd", r);
        !          1469:                        }
        !          1470:                        if (dstleft != 2) {
        !          1471:                                errx(1, "iconv %02x%02x: dstleft=%zd", jh, jl, dstleft);
        !          1472:                        }
        !          1473:                        // UnicodeBig は2バイト。
        !          1474:                        // ビッグエンディアンで取り出しているので上位、下位の順
        !          1475:                        uint32 ucode = (dstbuf[0] << 8) | dstbuf[1];
        !          1476: 
        !          1477:                        // JIS コードの {jh,jl} を Unicode に変換して GID を引く。
        !          1478:                        auto gid = Code2GID(ucode);
        !          1479:                        jismap[idx] = gid;
        !          1480:                }
        !          1481:        }
        !          1482:        iconv_close(cd);
        !          1483: 
        !          1484:        if (0 && debug) {
        !          1485:                printf("jismap:\n");
        !          1486:                for (int h = 0; h < 0x5e; h++) {
        !          1487:                        for (int l = 0; l < 0x5e; l++) {
        !          1488:                                int idx = h * 0x5e + l;
        !          1489:                                int gid = jismap[idx];
        !          1490:                                if (gid != 0) {
        !          1491:                                        printf(" JIS%02x%02x GID=%4d\n", h + 0x21, l + 0x21, gid);
        !          1492:                                }
        !          1493:                        }
        !          1494:                }
        !          1495:        }
        !          1496: }
        !          1497: 
        !          1498: // 文字コード code からグリフインデックス(GID)を求める
        !          1499: int
        !          1500: TTF_cmap::Code2GID(int code) const
        !          1501: {
        !          1502:        // endCode[] から code を含むセグメント i を求める
        !          1503:        int i = -1;
        !          1504:        for (int j = 0; j < sub.endCode.size(); j++) {
        !          1505:                if (code <= sub.endCode[j]) {
        !          1506:                        i = j;
        !          1507:                        break;
        !          1508:                }
        !          1509:        }
        !          1510:        if (i < 0) {
        !          1511:                return 0;
        !          1512:        }
        !          1513: 
        !          1514:        // このセグメントの開始コードより若ければ、対応インデックスなし
        !          1515:        int start = sub.startCode[i];
        !          1516:        if (code < start) {
        !          1517:                return 0;
        !          1518:        }
        !          1519: 
        !          1520:        if (sub.idRangeOffsets[i] == 0) {
        !          1521:                return (code + sub.idDelta[i]) & 0xffff;
        !          1522:        } else {
        !          1523:                errx(1, "idRangeOffset != 0");
        !          1524:        }
        !          1525: }
        !          1526: 
        !          1527: 
        !          1528: // BigGlyphMetrics を読み込む。file は更新される。
        !          1529: void
        !          1530: BigGlyphMetrics::Load(File& file)
        !          1531: {
        !          1532:        height                  = file.Read8();
        !          1533:        width                   = file.Read8();
        !          1534:        horiBearingX    = file.Read8();
        !          1535:        horiBearingY    = file.Read8();
        !          1536:        horiAdvance             = file.Read8();
        !          1537:        vertBearingX    = file.Read8();
        !          1538:        vertBearingY    = file.Read8();
        !          1539:        vertAdvance             = file.Read8();
        !          1540: }
        !          1541: 
        !          1542: // SbitLineMetrics を読み込む。file は更新される。
        !          1543: void
        !          1544: SbitLineMetrics::Load(File& file)
        !          1545: {
        !          1546:        ascender                = file.Read8();
        !          1547:        descender               = file.Read8();
        !          1548:        widthMax                = file.Read8();
        !          1549:        caretSlopeNumerator             = file.Read8();
        !          1550:        caretSlopeDenominator   = file.Read8();
        !          1551:        caretOffset             = file.Read8();
        !          1552:        minOriginSB             = file.Read8();
        !          1553:        minAdvanceSB    = file.Read8();
        !          1554:        maxBeforeBL             = file.Read8();
        !          1555:        minAfterBL              = file.Read8();
        !          1556:        pad1                    = file.Read8();
        !          1557:        pad2                    = file.Read8();
        !          1558: }
        !          1559: 
        !          1560: // NameRecord.NameID の名前を文字列で返す
        !          1561: /*static*/ std::string
        !          1562: NameRecord::GetNameIDStr(uint id)
        !          1563: {
        !          1564:        static const char * const names[] = {
        !          1565:                "Copyright notice",                             // 0
        !          1566:                "Font Family name",                             // 1
        !          1567:                "Font Subfamily name",                  // 2
        !          1568:                "Unique font identifier",               // 3
        !          1569:                "Full font name",                               // 4
        !          1570:                "Version string",                               // 5
        !          1571:                "PostScript name",                              // 6
        !          1572:                "Trademark",                                    // 7
        !          1573:                "Manufacturer Name",                    // 8
        !          1574:                "Designer",                                             // 9
        !          1575:                "Description",                                  // 10
        !          1576:                "URL Vendor",                                   // 11
        !          1577:                "URL Designer",                                 // 12
        !          1578:                "License Description",                  // 13
        !          1579:                "License Info",                                 // 14
        !          1580:                "Reserved",                                             // 15
        !          1581:                "Typographic Family name",              // 16
        !          1582:                "Typographic Subfamily name",   // 17
        !          1583:                "Compatible Full",                              // 18
        !          1584:                "Sample Text",                                  // 19
        !          1585:                "PostScript CID findfont name", // 20
        !          1586:                "WWS Family Name",                              // 21
        !          1587:                "WWS Subfamily Name",                   // 22
        !          1588:                "Light Background Palette",             // 23   
        !          1589:                "Dark Background Palette",              // 24
        !          1590:                "Variations PostScript Name Prefix",    // 25
        !          1591:        };
        !          1592:        if (id < countof(names)) {
        !          1593:                return names[id];
        !          1594:        } else {
        !          1595:                return "";
        !          1596:        }
        !          1597: }
        !          1598: 
        !          1599: // EncodingRecord のデバッグ表示用文字列を返す
        !          1600: std::string
        !          1601: EncodingRecord::ToString() const
        !          1602: {
        !          1603:        std::string str;
        !          1604: 
        !          1605:        str = string_format("platformID=%d", platformID);
        !          1606:        static const char * const platform_names[] = {
        !          1607:                "Unicode",
        !          1608:                "Macintosh",
        !          1609:                "ISO(deprecated)",
        !          1610:                "Windows",
        !          1611:                "Custom",
        !          1612:        };
        !          1613:        if (platformID < countof(platform_names)) {
        !          1614:                str += string_format("(%s)", platform_names[platformID]);
        !          1615:        }
        !          1616: 
        !          1617:        str += string_format(" encodingID=%d", encodingID);
        !          1618:        if (platformID == 0) {
        !          1619:                static const char * encoding_names_0[] = {
        !          1620:                        "1.0",
        !          1621:                        "1.1",
        !          1622:                        "ISO/IEC 10646",
        !          1623:                        "2.0 BMP",
        !          1624:                        "2.0 full",
        !          1625:                        "Variation Sequences",
        !          1626:                        "full reportoire",
        !          1627:                };
        !          1628:                if (encodingID < countof(encoding_names_0)) {
        !          1629:                        str += string_format("(%s)", encoding_names_0[encodingID]);
        !          1630:                }
        !          1631:        } else if (platformID == 3) {
        !          1632:                static const char * encoding_names_3[] = {
        !          1633:                        "Symbol",
        !          1634:                        "Unicode BMP",
        !          1635:                        "ShiftJIS",
        !          1636:                        "PRC",
        !          1637:                        "Big5",
        !          1638:                        "Wansung",
        !          1639:                        "Johab",
        !          1640:                        "Reserved",
        !          1641:                        "Reserved",
        !          1642:                        "Reserved",
        !          1643:                        "Unicode full reportoire",
        !          1644:                };
        !          1645:                if (encodingID < countof(encoding_names_3)) {
        !          1646:                        str += string_format("(%s)", encoding_names_3[encodingID]);
        !          1647:                }
        !          1648:        }
        !          1649: 
        !          1650:        return str;
        !          1651: }
        !          1652: 
        !          1653: 
        !          1654: [[noreturn]] static void
        !          1655: usage()
        !          1656: {
        !          1657:        errx(1, "usage: [-f family_name] infile.TTF outascii.BDF outkanji.BDF");
        !          1658: }
        !          1659: 
        !          1660: int
        !          1661: main(int ac, char *av[])
        !          1662: {
        !          1663:        int c;
        !          1664: 
        !          1665:        while ((c = getopt(ac, av, "f:")) != -1) {
        !          1666:                switch (c) {
        !          1667:                 case 'f':
        !          1668:                        new_family_name = optarg;
        !          1669:                        break;
        !          1670:                 default:
        !          1671:                        usage();
        !          1672:                        break;
        !          1673:                }
        !          1674:        }
        !          1675:        ac -= optind;
        !          1676:        av += optind;
        !          1677: 
        !          1678:        if (ac < 3) {
        !          1679:                usage();
        !          1680:        }
        !          1681: 
        !          1682:        File infile;
        !          1683:        infile.Open(av[0]);
        !          1684: 
        !          1685:        TTFFile ttf;
        !          1686:        ttf.Open(infile);
        !          1687: 
        !          1688:        File outfile1;
        !          1689:        outfile1.Create(av[1]);
        !          1690:        ttf.SaveBDFAscii(outfile1);
        !          1691: 
        !          1692:        File outfile2;
        !          1693:        outfile2.Create(av[2]);
        !          1694:        ttf.SaveBDFKanji(outfile2);
        !          1695: 
        !          1696:        return 0;
        !          1697: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.