|
|
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.