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