|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.