Commit 916fa5a
Subdirectory for open font format tools
Changed files (27)
addedlibbirdfont/OpenFontFormat/CffTable.valaaddedlibbirdfont/OpenFontFormat/Cmap.valaaddedlibbirdfont/OpenFontFormat/CvtTable.valaaddedlibbirdfont/OpenFontFormat/DirectoryTable.valaaddedlibbirdfont/OpenFontFormat/FontData.valaaddedlibbirdfont/OpenFontFormat/GaspTable.valaaddedlibbirdfont/OpenFontFormat/GdefTable.valaaddedlibbirdfont/OpenFontFormat/GlyfData.valaaddedlibbirdfont/OpenFontFormat/GlyfTable.valaaddedlibbirdfont/OpenFontFormat/GposTable.valaaddedlibbirdfont/OpenFontFormat/HeadTable.valaaddedlibbirdfont/OpenFontFormat/HheaTable.valaaddedlibbirdfont/OpenFontFormat/HmtxTable.valaaddedlibbirdfont/OpenFontFormat/Kern.valaaddedlibbirdfont/OpenFontFormat/LocaTable.valaaddedlibbirdfont/OpenFontFormat/MaxpTable.valaaddedlibbirdfont/OpenFontFormat/NameTable.valaaddedlibbirdfont/OpenFontFormat/OffsetTable.valaaddedlibbirdfont/OpenFontFormat/OpenFontFormatReader.valaaddedlibbirdfont/OpenFontFormat/OpenFontFormatWriter.valaaddedlibbirdfont/OpenFontFormat/Os2Table.valaaddedlibbirdfont/OpenFontFormat/OtfInputStream.valaaddedlibbirdfont/OpenFontFormat/PostTable.valaaddedlibbirdfont/OpenFontFormat/Table.valadeletedlibbirdfont/OpenFontFormatReader.valadeletedlibbirdfont/OpenFontFormatWriter.valamodifiedscripts/bavala.py
Diff
new file mode 100644@@ -0,0 +1,204 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++/** Type2, PostScript outlines */+class CffTable : Table {++ static const uint8 HSTEM = 1;+ static const uint8 VSTEM = 3;+ static const uint8 VMOVETO = 4;+ static const uint8 HLINETO = 6;+ static const uint8 VLINETO = 7;+ static const uint8 ENDCHAR = 14;+ static const uint8 HMOVETO = 22;+ static const uint8 RMOVETO = 21;+ + static const uint8 CHARSET = 15;+ static const uint8 ENCODING = 16;++ static const uint8 VERSION = 0;++ public int version;++ FontData dis;++ public CffTable () {+ id = "CFF ";+ }++ uint32 read_offset (uint size) throws GLib.Error {+ switch (size) {+ case 0:+ warning ("offset size is zero");+ return dis.read_byte ();+ case 1:+ return dis.read_byte ();+ case 2:+ return dis.read_ushort ();+ case 4:+ return dis.read_ulong ();+ default:+ warn_if_reached ();+ break;+ }+ + warning ("offset size is zero");+ return 0;+ }++ List<uint32> read_index () throws Error {+ uint32 offset_size, off;+ int entries;+ List<uint32> offsets = new List<uint32> ();+ + entries = dis.read_ushort ();+ printd (@"number of entries $(entries)\n");+ + if (entries == 0) {+ printd ("skip index");+ return offsets;+ }+ + offset_size = dis.read ();+ printd (@"Offset size $(offset_size)\n");+ + // read the end offset as well+ for (int i = 0; i <= entries; i++) {+ off = read_offset (offset_size);+ printd (@"offset $(off)\n");+ offsets.append (off);+ }+ + return offsets;+ }++ public override void parse (FontData dis) throws Error {+ uint v1, v2, offset_size, header_size, len;+ string data;+ List<uint32> offsets, dict_index;+ int id, val;+ int off; // offset relative to table position+ + dis.seek (offset);+ this.dis = dis;+ + printd ("Parse CFF.\n");+ v1 = dis.read ();+ v2 = dis.read ();+ printd (@"Version $v1.$v2\n");+ header_size = dis.read ();+ printd (@"Header size $(header_size)\n");+ offset_size = dis.read ();+ printd (@"Offset size $(offset_size)\n");+ + // name index+ offsets = read_index ();+ + // name data+ for (int i = 0; i < offsets.length () - 1; i++) {+ off = (int) offsets.nth (i).data;+ len = offsets.nth (i + 1).data - off;+ //dis.seek (offset + off + header_size);+ data = dis.read_string (len);+ print (@"Found name $data\n"); + } ++ // dict index+ print (@"dict index\n");+ dict_index = read_index ();++ // dict data+ id = 0;+ val = 0;+ for (int i = 0; i < dict_index.length () - 1; i++) {+ off = (int) offsets.nth (i).data;+ len = dict_index.nth (i + 1).data - dict_index.nth (i).data;+ //dis.seek (offset + off + header_size);+ + //for (int j = 0; j < len; j++) {+ + if (dis.next_is_operator ()) {+ id = dis.read ();+ + if (id == 12) {+ id = dis.read ();+ } else {+ switch (id) {+ case 0:+ version = val;+ break;+ default:+ stderr.printf ("unknown operator");+ break;+ }+ } + } else {+ val = dis.read_charstring_value (); + }++ printd (@"$i: id $(id)\n");+ printd (@"val $(val)\n");+ //printd (@"B $(dis.read ())\n");+ //} + } ++ // string index+ read_index ();+ }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();+ string name = "typeface";+ + // header+ fd.add_byte (1); // format version (1.0)+ fd.add_byte (0);+ + fd.add_byte (4); // header size+ fd.add_byte (2); // offset field size - ushort+ + // name index:+ fd.add_ushort (1); // number of entries+ fd.add_byte (2); // offset field size+ fd.add_ushort (1); // offset + fd.add ((uint8) name.length); // length of string+ fd.add_str (name);+ + // top dict index+ fd.add_ushort (1); // number of entries+ fd.add_byte (2); // offset field size+ fd.add_ushort (1); // offset+ fd.add_ushort (2); // offset++ fd.add_charstring_value (0);+ fd.add_byte (CHARSET);++ // string index+ fd.add_byte (0);++ // TODO: glyph gid to cid map+ fd.add_byte (0);+ + fd.pad ();+ + this.font_data = fd;+ }+}++}new file mode 100644@@ -0,0 +1,447 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class CmapSubtable : Table {++ // Override these methods in subtables for each format.+ + /** Obtain length of subtable in bytes. */+ public virtual uint get_length () {+ warning ("Invalid CmapSubtable");+ return 0;+ }+ + /** Get char code for a glyph id. */+ public virtual unichar get_char (uint32 i) {+ warning ("Invalid CmapSubtable");+ return 0;+ }+ + public void print_cmap () {+ StringBuilder s;+ unichar c;+ for (uint32 i = 0; i < get_length (); i++) {+ s = new StringBuilder ();+ c = get_char (i);+ s.append_unichar (c);+ printd (@"Char: $(s.str) val ($((uint32)c))\tindice: $(i)\n");+ }+ }+}++/** Format 4 cmap subtable */+class CmapSubtableWindowsUnicode : CmapSubtable {+ uint16 format = 0;+ HashTable <uint64?, unichar> table = new HashTable <uint64?, unichar> (int64_hash, int_equal);+ + public CmapSubtableWindowsUnicode () {+ }+ + ~CmapSubtableWindowsUnicode () {++ }++ public override uint get_length () {+ return table.size ();+ }+ + public override unichar get_char (uint32 indice) {+ int64? c = table.lookup (indice);+ + if (c == 0 && indice == 0) {+ return 0;+ }+ + if (c == 0) {+ while (table.lookup (--indice) == 0) {+ if (indice == 0) {+ return 0;+ }+ } + + warning (@"There is no char for glyph number $indice in cmap table. table.size: $(table.size ()))");+ return 0;+ }+ + return (unichar) c;+ }+ + public override void parse (FontData dis) throws GLib.Error {+ dis.seek (offset);+ + format = dis.read_ushort ();+ + switch (format) {+ case 4:+ parse_format4 (dis);+ break;+ + default:+ stderr.printf (@"CmapSubtable is in format $format, it is not supportet (yet).\n");+ break;+ }+ }+ + public void parse_format4 (FontData dis) throws GLib.Error {+ uint16 lang;+ uint16 seg_count_x2;+ uint16 seg_count;+ uint16 search_range;+ uint16 entry_selector;+ uint16 range_shift;++ uint16* end_char = null;+ uint16* start_char = null;+ int16* id_delta = null;+ uint16* id_range_offset = null;+ uint16* glyph_id_array = null;+ + uint32 gid_len;+ + length = dis.read_ushort ();+ lang = dis.read_ushort ();+ seg_count_x2 = dis.read_ushort ();+ search_range = dis.read_ushort ();+ entry_selector = dis.read_ushort ();+ range_shift = dis.read_ushort ();+ + return_if_fail (seg_count_x2 % 2 == 0);++ seg_count = seg_count_x2 / 2;++ end_char = new uint16[seg_count];+ for (int i = 0; i < seg_count; i++) {+ end_char[i] = dis.read_ushort ();+ }+ + if (end_char[seg_count - 1] != 0xFFFF) {+ warning ("end_char is $(end_char[seg_count - 1]), expecting 0xFFFF.");+ }+ + dis.read_ushort (); // Reserved+ + start_char = new uint16[seg_count];+ for (int i = 0; i < seg_count; i++) {+ start_char[i] = dis.read_ushort ();+ }++ id_delta = new int16[seg_count];+ for (int i = 0; i < seg_count; i++) {+ id_delta[i] = dis.read_short ();+ }++ id_range_offset = new uint16[seg_count];+ for (int i = 0; i < seg_count; i++) {+ id_range_offset[i] = dis.read_ushort ();+ }++ if (length == 0) {+ warning ("cmap subtable version 4 has length 0.");+ return;+ }++ gid_len = (length - 16 - 8 * seg_count) / 2;+ glyph_id_array = new uint16[gid_len];+ for (int i = 0; i < gid_len; i++) {+ glyph_id_array[i] = dis.read_ushort ();+ }+ + // map all values in a hashtable+ int indice = 0;+ unichar character = 0;+ uint32 id;+ for (uint16 i = 0; i < seg_count && start_char[i] != 0xFFFF; i++) {+ + // print_range (start_char[i], end_char[i], id_delta[i], id_range_offset[i]);+ + uint16 j = 0;+ do {+ character = start_char[i] + j;+ indice = start_char[i] + id_delta[i] + j;+ + if (id_range_offset[i] == 0) {+ table.insert (indice, character);+ } else {+ // the awkward indexing trick:+ id = id_range_offset[i] / 2 + j + i - seg_count;+ + if (!(0 <= id < gid_len)) {+ warning (@"(0 <= id < gid_len) (0 <= $id < $gid_len)");+ break;+ }+ + indice = glyph_id_array [id] + id_delta[i];+ + StringBuilder s = new StringBuilder ();+ s.append_unichar (character);+ + table.insert (indice, character);+ }+ + j++;+ } while (character != end_char[i]);+ + }+ + if (end_char != null) delete end_char;+ if (start_char != null) delete start_char;+ if (id_delta != null) delete id_delta;+ if (id_range_offset != null) delete id_range_offset;+ if (glyph_id_array != null) delete glyph_id_array;+ + // it has a character for every glyph indice+ // assert (validate_subtable ());+ }+ + public void process (FontData fd, GlyfTable glyf_table) throws GLib.Error {+ GlyphRange glyph_range = new GlyphRange ();+ unowned List<UniRange> ranges;++ uint16 seg_count_2;+ uint16 seg_count;+ uint16 search_range;+ uint16 entry_selector;+ uint16 range_shift; + + uint16 gid_length = 0;+ + uint32 indice;+ uint32 first_assigned = 1;+ + foreach (Glyph g in glyf_table.glyphs) {+ if (!g.is_unassigned ()) {+ glyph_range.add_single (g.unichar_code);+ }+ }+ + // glyph_range.print_all ();+ + ranges = glyph_range.get_ranges ();+ seg_count = (uint16) ranges.length () + 1;+ seg_count_2 = seg_count * 2;+ search_range = 2 * largest_pow2 (seg_count);+ entry_selector = largest_pow2_exponent (seg_count);+ range_shift = seg_count_2 - search_range;+ + // format+ fd.add_ushort (4);+ + // length of subtable+ fd.add_ushort (16 + 8 * seg_count + gid_length);+ + // language+ fd.add_ushort (0);+ + fd.add_ushort (seg_count_2);+ fd.add_ushort (search_range);+ fd.add_ushort (entry_selector);+ fd.add_ushort (range_shift);+ + // end codes+ indice = first_assigned;+ foreach (UniRange u in ranges) {+ if (u.stop >= 0xFFFF) {+ warning ("Not implemented yet.");+ }+ + fd.add_ushort ((uint16) u.stop);+ indice += u.length ();+ }+ fd.add_ushort (0xFFFF);+ + fd.add_ushort (0); // Reserved+ + // start codes+ indice = first_assigned; // since first glyph are notdef, null and nonmarkingreturn+ foreach (UniRange u in ranges) {+ if (u.start >= 0xFFFF) {+ warning ("Not implemented yet.");+ }+ + fd.add_ushort ((uint16) u.start);+ indice += u.length ();+ }+ fd.add_ushort (0xFFFF);++ // delta+ indice = first_assigned;+ foreach (UniRange u in ranges) {+ + if ((u.start - indice) > 0xFFFF && u.start > indice) {+ warning ("Need range offset.");+ }+ + fd.add_ushort ((uint16) (indice - u.start));+ indice += u.length ();+ }+ fd.add_ushort (1);+ + // range offset+ foreach (UniRange u in ranges) {+ if (u.stop <= 0xFFFF) {+ fd.add_ushort (0);+ } else {+ warning ("Not implemented yet.");+ }+ }+ fd.add_ushort (0);+ + // Fixa: implement rest of type 4 (mind gid_length in length field)+ }+}++class CmapTable : Table { + + GlyfTable glyf_table; + List<CmapSubtable> subtables;++ public CmapTable(GlyfTable gt) {+ glyf_table = gt;+ subtables = new List<CmapSubtable> ();+ id = "cmap";+ }+ + public unichar get_char (uint32 i) {+ return get_prefered_table ().get_char (i) ;+ }+ + CmapSubtable get_prefered_table () {+ if (subtables.length () == 0) {+ warning ("No cmap table has been parsed.");+ return new CmapSubtable ();+ }+ + return subtables.first ().data;+ }+ + public override string get_id () {+ return "cmap";+ }+ + public override void parse (FontData dis) throws GLib.Error {+ uint16 version;+ uint16 nsubtables;+ + uint16 platform;+ uint16 encoding;+ uint32 sub_offset;+ + CmapSubtable subtable;++ return_if_fail (offset > 0 && length > 0);+ + dis.seek (offset);+ + version = dis.read_ushort ();+ nsubtables = dis.read_ushort ();++ printd (@"cmap version: $version\n");+ printd (@"cmap subtables: $nsubtables\n");+ + if (version != 0) {+ warning (@"Bad version for cmap table: $version expecting 0. Number of subtables: $nsubtables");+ return;+ }+ + for (uint i = 0; i < nsubtables; i++) {+ platform = dis.read_ushort ();+ encoding = dis.read_ushort ();+ sub_offset = dis.read_ulong (); + + if (platform == 3 && encoding == 1) {+ printd (@"Parsing Unicode BMP (UCS-2) Platform: $platform Encoding: $encoding\n");+ subtable = new CmapSubtableWindowsUnicode ();+ subtable.offset = offset + sub_offset;+ subtables.append (subtable);+ } else {+ stderr.printf (@"Unknown cmap format. Platform: $platform Encoding: $encoding.\n");+ }+ + if (encoding == 3) {+ stderr.printf ("Font contains a cmap table with the obsolete encoding 3.\n");+ }+ }+ + if (subtables.length () == 0) {+ warning ("No suitable cmap subtable found.");+ }+ + foreach (CmapSubtable t in subtables) {+ t.parse (dis);+ t.print_cmap ();+ }++ }+ + /** Character to glyph mapping */+ public void process (GlyfTable glyf_table) throws GLib.Error {+ FontData fd = new FontData ();+ CmapSubtableWindowsUnicode cmap = new CmapSubtableWindowsUnicode ();+ uint16 n_encoding_tables;+ uint32 subtable_offset = 0;+ + n_encoding_tables = 1;+ + fd.add_u16 (0); // table version+ fd.add_u16 (n_encoding_tables);+ + fd.add_u16 (3); // platform + fd.add_u16 (1); // encoding (Format Unicode UCS-4)++ subtable_offset = fd.length () + 4;+ printd (@"subtable_offset: $(subtable_offset)\n");+ + fd.add_ulong (subtable_offset);+ cmap.process (fd, glyf_table);++ // padding+ fd.pad ();++ this.font_data = fd;+ }+}++/** Largest power of two less than max. */+internal static uint16 largest_pow2 (uint16 max) {+ uint16 x = 1;+ uint16 l = 0;+ + while (x <= max) {+ l = x;+ x = x << 1;+ }+ + return l;+}++internal static uint16 largest_pow2_exponent (uint16 max) {+ uint16 exp = 0;+ uint16 l = 0;+ uint16 x = 0;+ + while (x <= max) {+ l = exp;+ exp++;+ x = 1 << exp;+ } + + return l;+}++}new file mode 100644@@ -0,0 +1,40 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class CvtTable : Table {+ + public CvtTable () {+ id = "cvt ";+ }+ + public override void parse (FontData dis) throws Error {+ }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();+ + fd.add_ushort (0);+ fd.pad ();+ + this.font_data = fd;+ }++}++}new file mode 100644@@ -0,0 +1,437 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++/** Table with list of tables sorted by table tag. */+class DirectoryTable : Table {+ + public CmapTable cmap_table;+ public CvtTable cvt_table;+ public GaspTable gasp_table;+ public GdefTable gdef_table;+ public GlyfTable glyf_table;+ public GposTable gpos_table;+ public HeadTable head_table;+ public HheaTable hhea_table;+ public HmtxTable hmtx_table;+ public KernTable kern_table;+ public MaxpTable maxp_table;+ public NameTable name_table;+ public Os2Table os_2_table;+ public PostTable post_table;+ public LocaTable loca_table;+ + public OffsetTable offset_table;+ + List<Table> tables;+ + public DirectoryTable () {+ offset_table = new OffsetTable (this);+ + loca_table = new LocaTable ();+ gasp_table = new GaspTable ();+ gdef_table = new GdefTable ();+ glyf_table = new GlyfTable (loca_table);+ cmap_table = new CmapTable (glyf_table);+ cvt_table = new CvtTable ();+ head_table = new HeadTable (glyf_table);+ hmtx_table = new HmtxTable (head_table, glyf_table);+ hhea_table = new HheaTable (glyf_table, head_table, hmtx_table);+ kern_table = new KernTable (glyf_table);+ gpos_table = new GposTable ();+ maxp_table = new MaxpTable (glyf_table);+ name_table = new NameTable ();+ os_2_table = new Os2Table (); + post_table = new PostTable (glyf_table);+ + id = "Directory table";+ }++ public void process () throws GLib.Error {+ // generate font data+ glyf_table.process ();+ gasp_table.process ();+ gdef_table.process ();+ cmap_table.process (glyf_table);+ cvt_table.process ();+ hmtx_table.process ();+ hhea_table.process ();+ maxp_table.process ();+ name_table.process ();+ os_2_table.process (glyf_table);+ head_table.process ();+ loca_table.process (glyf_table, head_table);+ post_table.process ();+ kern_table.process ();+ gpos_table.process (kern_table);+ + offset_table.process ();+ process_directory (); // this table+ }++ public unowned List<Table> get_tables () {+ if (tables.length () == 0) {+ tables.append (offset_table);+ tables.append (this);+ + if (kern_table.kerning_pairs > 0) {+ // FIXME: Firefox will ignore the Kern table of GPOS is present + // but not adjust the values according to the GPOS table.+ // + // Maybe there is a problem with gpos_table that I am not aware of.+ tables.append (gpos_table);+ }+ + // tables.append (gdef_table); // invalid table+ tables.append (os_2_table);+ tables.append (cmap_table);+ // tables.append (cvt_table);+ // tables.append (gasp_table);+ tables.append (glyf_table);+ tables.append (head_table);+ tables.append (hhea_table);+ tables.append (hmtx_table);++ if (kern_table.kerning_pairs > 0) {+ tables.append (kern_table);+ }+ + tables.append (loca_table);+ tables.append (maxp_table);+ tables.append (name_table);+ tables.append (post_table);+ }++ return tables;+ }++ public void set_offset_table (OffsetTable ot) {+ offset_table = ot;+ }+ + public new void parse (FontData dis, File file, OpenFontFormatReader reader_callback) throws Error {+ StringBuilder tag = new StringBuilder ();+ uint32 checksum;+ uint32 offset;+ uint32 length;+ + return_if_fail (offset_table.num_tables > 0);+ + for (uint i = 0; i < offset_table.num_tables; i++) {+ tag.erase ();+ + tag.append_unichar ((unichar) dis.read_byte ());+ tag.append_unichar ((unichar) dis.read_byte ());+ tag.append_unichar ((unichar) dis.read_byte ());+ tag.append_unichar ((unichar) dis.read_byte ());+ + checksum = dis.read_ulong ();+ offset = dis.read_ulong ();+ length = dis.read_ulong ();+ + printd (@"$(tag.str) \toffset: $offset \tlength: $length \tchecksum: $checksum.\n");+ + if (tag.str == "cvt") {+ cvt_table.id = tag.str;+ cvt_table.checksum = checksum;+ cvt_table.offset = offset;+ cvt_table.length = length;+ } else if (tag.str == "hmtx") {+ hmtx_table.id = tag.str;+ hmtx_table.checksum = checksum;+ hmtx_table.offset = offset;+ hmtx_table.length = length;+ } else if (tag.str == "hhea") {+ hhea_table.id = tag.str;+ hhea_table.checksum = checksum;+ hhea_table.offset = offset;+ hhea_table.length = length; + } else if (tag.str == "loca") {+ loca_table.id = tag.str;+ loca_table.checksum = checksum;+ loca_table.offset = offset;+ loca_table.length = length; + } else if (tag.str == "cmap") {+ cmap_table.id = tag.str;+ cmap_table.checksum = checksum;+ cmap_table.offset = offset;+ cmap_table.length = length;+ } else if (tag.str == "maxp") {+ maxp_table.id = tag.str;+ maxp_table.checksum = checksum;+ maxp_table.offset = offset;+ maxp_table.length = length;+ } else if (tag.str == "gasp") {+ gasp_table.id = tag.str;+ gasp_table.checksum = checksum;+ gasp_table.offset = offset;+ gasp_table.length = length;+ } else if (tag.str == "gdef") {+ gdef_table.id = tag.str;+ gdef_table.checksum = checksum;+ gdef_table.offset = offset;+ gdef_table.length = length;+ } else if (tag.str == "glyf") {+ glyf_table.id = tag.str;+ glyf_table.checksum = checksum;+ glyf_table.offset = offset;+ glyf_table.length = length;+ } else if (tag.str == "head") {+ head_table.id = tag.str;+ head_table.checksum = checksum;+ head_table.offset = offset;+ head_table.length = length;+ } else if (tag.str == "name") {+ name_table.id = tag.str;+ name_table.checksum = checksum;+ name_table.offset = offset;+ name_table.length = length;+ } else if (tag.str == "OS/2") {+ os_2_table.id = tag.str;+ os_2_table.checksum = checksum;+ os_2_table.offset = offset;+ os_2_table.length = length;+ } else if (tag.str == "post") {+ post_table.id = tag.str;+ post_table.checksum = checksum;+ post_table.offset = offset;+ post_table.length = length;+ } else if (tag.str == "kern") {+ kern_table.id = tag.str;+ kern_table.checksum = checksum;+ kern_table.offset = offset;+ kern_table.length = length;+ } else if (tag.str == "GPOS") {+ gpos_table.id = tag.str;+ gpos_table.checksum = checksum;+ gpos_table.offset = offset;+ gpos_table.length = length;+ }+ }++ head_table.parse (dis);+ + if (!validate_tables (dis, file)) {+ warning ("Missing required table or bad checksum.");+ }+ + hhea_table.parse (dis);+ reader_callback.set_limits ();+ + name_table.parse (dis);+ post_table.parse (dis);+ os_2_table.parse (dis);+ maxp_table.parse (dis);+ loca_table.parse (dis, head_table, maxp_table);+ hmtx_table.parse (dis, hhea_table, loca_table);+ cmap_table.parse (dis);+ gpos_table.parse (dis);+ + if (kern_table.has_data ()) {+ kern_table.parse (dis);+ }+ + glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table, kern_table);+ + if (kern_table.has_data ()) {+ gasp_table.parse (dis);+ }+ + if (kern_table.has_data ()) {+ cvt_table.parse (dis);+ }+ }+ + public bool validate_tables (FontData dis, File file) {+ bool valid = true;+ + try {+ dis.seek (0);+ + if (!validate_checksum_for_entire_font (dis, file)) {+ warning ("file has invalid checksum");+ } else {+ printd ("Font file has valid checksum.\n");+ }++ // Skip validation of head table for now it should be simple but it seems to+ // be broken in some funny way.++ if (!glyf_table.validate (dis)) {+ warning ("glyf_table has invalid checksum");+ valid = false;+ }+ + if (!maxp_table.validate (dis)) {+ warning ("maxp_table has is invalid checksum");+ valid = false;+ }+ + if (!loca_table.validate (dis)) {+ warning ("loca_table has invalid checksum");+ valid = false;+ }+ + if (!cmap_table.validate (dis)) {+ warning ("cmap_table has invalid checksum");+ valid = false;+ }+ + if (!hhea_table.validate (dis)) {+ warning ("hhea_table has invalid checksum");+ valid = false;+ }+ + if (!hmtx_table.validate (dis)) {+ warning ("hmtx_table has invalid checksum");+ valid = false;+ }++ if (!name_table.validate (dis)) {+ warning ("name_table has invalid checksum");+ valid = false;+ }++ if (!os_2_table.validate (dis)) {+ warning ("os_2_table has invalid checksum");+ valid = false;+ }++ if (!post_table.validate (dis)) {+ warning ("post_table has invalid checksum");+ valid = false;+ }+ + if (kern_table.has_data () && !kern_table.validate (dis)) {+ warning ("kern_table has invalid checksum");+ valid = false;+ }+ + if (!gpos_table.validate (dis)) {+ warning ("gpos_table has invalid checksum");+ valid = false;+ } + } catch (GLib.Error e) {+ warning (e.message);+ valid = false;+ }+ + return valid;+ }+ + bool validate_checksum_for_entire_font (FontData dis, File f) throws GLib.Error {+ uint p = head_table.offset + head_table.get_checksum_position ();+ uint32 checksum_font, checksum_head;++ checksum_head = head_table.get_font_checksum ();+ + dis.seek (0);+ + // zero out checksum entry in head table before validating it+ dis.write_at (p + 0, 0);+ dis.write_at (p + 1, 0);+ dis.write_at (p + 2, 0);+ dis.write_at (p + 3, 0);+ + checksum_font = (uint32) (0xB1B0AFBA - dis.check_sum ());++ if (checksum_font != checksum_head) {+ warning (@"Fontfile checksum in head table does not match calculated checksum. checksum_font: $checksum_font checksum_head: $checksum_head");+ return false;+ }+ + return true;+ }+ + public long get_font_file_size () {+ long length = 0;+ + foreach (Table t in tables) {+ length += t.get_font_data ().length_with_padding ();+ }+ + return length;+ }+ + public void process_directory () throws GLib.Error {+ create_directory (); // create directory without offsets to calculate length of offset table and checksum for entre file+ create_directory (); // generate a valid directory+ }++ // Check sum adjustment for the entire font+ public uint32 get_font_file_checksum () {+ uint32 check_sum = 0;+ foreach (Table t in tables) {+ t.get_font_data ().continous_check_sum (ref check_sum);+ }+ return check_sum;+ }++ public void create_directory () throws GLib.Error {+ FontData fd;+ + uint32 table_offset = 0;+ uint32 table_length = 0;+ + uint32 check_sum = 0;+ + fd = new FontData ();++ return_if_fail (offset_table.num_tables > 0);+ + table_offset += offset_table.get_font_data ().length_with_padding ();+ + if (this.font_data != null) {+ table_offset += this.get_font_data ().length_with_padding ();+ }++ head_table.set_check_sum_adjustment (0); // Set this to zero, calculate checksums and update the value+ head_table.process ();+ + // write the directory + foreach (Table t in tables) {+ + if (t is DirectoryTable || t is OffsetTable) {+ continue;+ }+ + printd (@"c $(t.id) offset: $(table_offset) len with pad $(t.get_font_data ().length_with_padding ())\n");++ table_length = t.get_font_data ().length (); // without padding+ + fd.add_tag (t.get_id ()); // name of table+ fd.add_u32 (t.get_font_data ().check_sum ());+ fd.add_u32 (table_offset);+ fd.add_u32 (table_length);+ + table_offset += t.get_font_data ().length_with_padding ();+ }++ // padding+ fd.pad ();+ + this.font_data = fd;++ check_sum = get_font_file_checksum ();+ head_table.set_check_sum_adjustment ((uint32)(0xB1B0AFBA - check_sum));+ head_table.process (); // update the value + }+ +}++}new file mode 100644@@ -0,0 +1,419 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class FontData : Object {++ // Read pointer+ uint rp = 0;++ // Write pointer+ uint wp = 0;+ + // length without padding+ uint32 len = 0;+ uint32 padding = 0;+ + uint32 capacity;+ + public uint8* table_data = null;+ + public FontData (uint32 size = 1024) {+ capacity = size;+ table_data = malloc (size);+ }+ + ~FontData () {+ if (table_data != null) {+ delete table_data;+ table_data = null;+ }+ }+ + public void write_at (uint pos, uint8 new_data) throws GLib.Error+ requires (pos <= capacity) + { + if (unlikely (pos >= len)) {+ warning ("end of table reached");+ assert (false);+ }+ + table_data[pos]= new_data;+ }+ + public void write_table (OtfInputStream dis, uint32 offset, uint32 length) throws Error {+ uint32 l = length + (length % 4); // padding after end of table+ uint8 b;+ + if (length >= l) {+ expand (l);+ }+ + if (table_data == null) {+ warning ("Failed to allocate memory for ttf data.");+ return;+ }+ + seek (0);+ dis.seek (offset);+ + wp = 0;+ while (wp < l) {+ b = dis.read_byte ();+ add (b);+ }+ + rp = 0;+ }+ + public uint length_with_padding () {+ return len;+ } + + public uint length () {+ return len - padding;+ }+ + public void pad () {+ while (len % 4 != 0) {+ add (0);+ padding++;+ }+ }+ + /** Add additional checksum data to this checksum. */+ public void continous_check_sum (ref uint32 current_check_sum) {+ uint trp = rp;+ uint l;+ + if (length_with_padding () % 4 != 0) {+ warning ("Table is not padded to correct size.");+ }+ + seek (0);++ l = (length () % 4 > 0) ? length () / 4 + 1 : length () / 4; ++ for (uint32 i = 0; i < l; i++) {+ current_check_sum += read_uint32 ();+ }+ + rp = trp;+ + return;+ }++ public uint32 check_sum () {+ uint32 sum = 0;+ continous_check_sum (ref sum);+ return sum;+ }+ + public void seek_end () {+ seek (len);+ }+ + public void seek (uint i) {+ rp = i;+ wp = i;+ }++ public void seek_relative (uint i) {+ rp += i;+ wp += i;+ }++ /** Returns true if next byte is a CFF operator */+ public bool next_is_operator () {+ uint8 o = read ();+ seek_relative (-1);+ return (0 <= o <=21);+ }+ + public uint8 read () {+ if (unlikely (rp >= len)) {+ warning ("end of table reached");+ return 0;+ }+ + return table_data [rp++];+ }+ + public Fixed read_fixed () {+ return (Fixed) read_uint32 ();+ }++ public uint32 read_ulong () {+ return read_uint32 ();+ }++ public uint16 read_ushort () {+ uint16 f;+ f = read () << 8;+ f += read ();+ return f;+ }+ + public int16 read_int16 () {+ int16 f;+ f = read () << 8;+ f += read ();+ return f; + }+ + public int16 read_short () throws Error {+ return read_int16 ();+ }+ + public uint32 read_uint32 () {+ uint32 f;+ f = read () << 8 * 3;+ f += read () << 8 * 2;+ f += read () << 8 * 1;+ f += read () << 8 * 0;+ return f;+ }++ public uint64 read_uint64 () {+ uint64 f;+ f = (uint64) read () << 8 * 7;+ f += (uint64) read () << 8 * 6;+ f += (uint64) read () << 8 * 5;+ f += (uint64) read () << 8 * 4;+ f += (uint64) read () << 8 * 3;+ f += (uint64) read () << 8 * 2;+ f += (uint64) read () << 8 * 1;+ f += (uint64) read () << 8 * 0;+ return f;+ }++ public F2Dot14 read_f2dot14 () throws Error {+ F2Dot14 f = (F2Dot14) read_int16 ();+ return f;+ }++ public uint64 read_udate () throws Error {+ return read_uint64 ();+ }++ public uint8 read_byte () throws Error {+ return read ();+ }++ public char read_char () throws Error {+ return (char) read_byte ();+ }++ public string read_string (uint length) throws Error {+ StringBuilder str = new StringBuilder ();+ char c;+ for (int i = 0; i < length; i++) {+ c = read_char ();+ str.append_c (c);+ }+ return str.str;+ }++ public int read_charstring_value () throws Error {+ uint8 a, b;+ a = read ();+ + if (32 <= a <= 246) {+ return a - 139;+ }+ + b = read ();+ + if (247 <= a <= 250) { + return (a - 247) * 256 + b + 108;+ }+ + if (251 <= a <= 254) {+ return -((a - 251) * 256) - b - 108;+ }++ if (a == 255) {+ // Implement it+ warning ("fractions not implemented yet.");+ }++ stderr.printf (@"unexpected value: $a\n");+ warn_if_reached ();+ return 0;+ }+ + public void add_fixed (Fixed f) throws Error {+ add_u32 (f);+ }++ public void add_short (int16 d) throws Error {+ add_16 (d);+ }+ + public void add_ushort (uint16 d) throws Error {+ add_u16 (d);+ }+ + public void add_ulong (uint32 d) throws Error {+ add_u32 (d);+ }+ + public void add_byte (uint8 b) throws Error {+ add (b);+ }+ + private void expand (uint extra_bytes = 1024) {+ capacity += extra_bytes;+ table_data = (uint8*) try_realloc (table_data, capacity);+ + if (table_data == null) {+ warning ("Out of memory.");+ capacity = 0;+ } + }+ + public void add (uint8 d) {+ if (unlikely (len == capacity)) {+ expand ();+ }+ + table_data[wp] = d;++ if (wp == len) {+ len++;+ }+ + wp++;+ }++ public void add_littleendian_u16 (uint32 i) {+ add ((int8) (i & 0x00FF));+ add ((int8) ((i & 0xFF00) >> 8));+ }+ + public void add_u16 (uint16 d) {+ uint16 n = d >> 8;+ add ((uint8)n);+ add ((uint8)(d - (n << 8)));+ }++ public void add_16 (int16 i) {+ uint8 s = (uint8) (i >> 8);+ + add ((uint8) s);+ add ((uint8) (i - (s << 8)));+ }++ public void add_littleendian_u32 (uint32 i) {+ add ((int8) (i & 0x000000FF));+ add ((int8) ((i & 0x0000FF00) >> 8));+ add ((int8) ((i & 0x00FF0000) >> 16));+ add ((int8) ((i & 0xFF000000) >> 24));+ }+ + public void add_u32 (uint32 i) {+ uint32 s = (uint16) (i >> 16);+ + add_u16 ((uint16) s);+ add_u16 ((uint16) (i - (s << 16)));+ }++ public void add_64(int64 i) {+ int64 s = (int32) (i >> 32);+ + add_u32 ((int32) s);+ add_u32 ((int32)(i - (s << 32))); + }+ + public void add_str_littleendian_utf16 (string s) {+ add_str_utf16 (s, true);+ }+ + public void add_str_utf16 (string s, bool little_endian = false) {+ int index = 0;+ unichar c;+ uint8 c0;+ uint8 c1;+ int l = 0;+ + while (s.get_next_char (ref index, out c)) {+ c0 = (uint8) (c >> 8);+ c1 = (uint8) (c - (c0 << 8));+ + if (little_endian) {+ add (c1);+ add (c0);+ } else {+ add (c0);+ add (c1);+ }+ + l += 2;+ }+ + assert (l == 2 * s.char_count ());+ }+ + public void add_str (string s) {+ uint8[] data = s.data;+ for (int n = 0; n < data.length; n++) { + add (data[n]);+ } + }+ + public void add_tag (string s) + requires (s.length == 4 && s.data.length == 4) {+ add_str (s);+ }++ public void add_charstring_value (int v) throws Error {+ int w;+ uint8 t;+ + if (unlikely (!(-1131 <= v <= 1131))) {+ warning ("charstring value out of range");+ v = 0; + }+ + if (-107 <= v <= 107) {+ add_byte ((uint8) (v + 139));+ } else if (107 < v <= 1131) {+ // positive value+ w = v;+ v -= 108;+ + t = 0;+ while (v >= 256) {+ v -= 256;+ t++;+ }+ + add_byte (t + 247);+ add_byte ((uint8) (w - 108 - (t * 256))); + } else if (-1131 <= v < -107) {+ // negative value+ v = -v - 108;+ add_byte ((uint8) ((v >> 8) + 251));+ add_byte ((uint8) (v & 0xFF));+ } else {+ // Todo add fraction+ }+ }+}++}new file mode 100644@@ -0,0 +1,42 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class GaspTable : Table {+ + public GaspTable () {+ id = "gasp";+ }+ + public override void parse (FontData dis) throws Error {+ }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();++ fd.add_ushort (0);+ fd.add_ushort (0);++ fd.pad ();+ + this.font_data = fd;+ }++}++}new file mode 100644@@ -0,0 +1,47 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class GdefTable : Table {+ + public GdefTable () {+ id = "GDEF";+ }+ + public override void parse (FontData dis) throws Error {+ }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();++ fd.add_ulong (0x00010002);+ fd.add_ushort (0); // class def+ fd.add_ushort (0); // attach list+ fd.add_ushort (0); // ligature carret+ fd.add_ushort (0); // mark attach+ fd.add_ushort (0); // mark glyf+ fd.add_ushort (0); // mark glyf set def+ + fd.pad ();+ + this.font_data = fd;+ }++}++}new file mode 100644@@ -0,0 +1,225 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++using Math;++namespace BirdFont {++class Coordinate {+ /** TTF coordinate flags. */++ public static const uint8 NONE = 0;+ public static const uint8 ON_PATH = 1 << 0;+ public static const uint8 X_SHORT_VECTOR = 1 << 1;+ public static const uint8 Y_SHORT_VECTOR = 1 << 2;+ public static const uint8 REPEAT = 1 << 3;++ // same flag or short vector sign flag+ public static const uint8 X_IS_SAME = 1 << 4; + public static const uint8 Y_IS_SAME = 1 << 5;+ public static const uint8 X_SHORT_VECTOR_POSITIVE = 1 << 4;+ public static const uint8 Y_SHORT_VECTOR_POSITIVE = 1 << 5;++}++/** Data for one entry in the glyf table. */+class GlyfData : GLib.Object { + public List<Path> paths = new List<Path> ();+ public List<uint16> end_points = new List<uint16> ();+ public List<uint8> flags = new List<uint8> ();+ public List<int16> coordinate_x = new List<int16> ();+ public List<int16> coordinate_y = new List<int16> ();+ + uint16 end_point = 0;+ int16 nflags = 0;+ + Glyph glyph;+ + public int16 bounding_box_xmin = 0;+ public int16 bounding_box_ymin = 0;+ public int16 bounding_box_xmax = 0;+ public int16 bounding_box_ymax = 0;+ + public GlyfData (Glyph g) { + glyph = g;+ + foreach (Path p in g.path_list) {+ paths.append (p.get_quadratic_points ());+ }+ + process_end_points ();+ process_flags ();+ process_x ();+ process_y ();+ process_bounding_box (); + }++ public uint16 get_end_point () {+ return end_point;+ }+ + public int16 get_ncontours () {+ return (int16) paths.length ();+ }++ public int16 get_nflags () {+ return nflags;+ }+ + void process_end_points () { + uint16 last_end_point = 0;+ PointType type;+ + end_point = 0;+ + foreach (Path quadratic in paths) {+ if (quadratic.points.length () == 0) {+ warning (@"No points in path (before conversion $(quadratic.points.length ()))");+ continue;+ }+ + foreach (EditPoint e in quadratic.points) {+ end_point++;+ type = e.get_right_handle ().type;+ + // off curve+ end_point++;+ + if (end_point == 0xFFFF) {+ warning ("Too many points");+ }+ }+ end_points.append (end_point - 1);+ + if (end_point - 1 < last_end_point) {+ warning (@"Next endpoint has bad value. (end_point - 1 < last_end_point) ($(end_point - 1) < $last_end_point)");+ }+ + last_end_point = end_point - 1;+ }+ + assert (end_point > 0); + }++ void process_flags () {+ flags = new List<uint8> ();+ nflags = 0;+ + foreach (Path p in paths) {+ p = p.get_quadratic_points ();+ + foreach (EditPoint e in p.points) {+ flags.append (Coordinate.ON_PATH);+ nflags++;+ + // off curve+ flags.append (Coordinate.NONE);+ nflags++;+ }+ }+ }+ + void process_x () {+ double prev = 0;+ double x;+ + foreach (Path p in paths) {+ foreach (EditPoint e in p.points) {+ x = rint (e.x * UNITS - prev - glyph.left_limit * UNITS);+ coordinate_x.append ((int16) x);+ + prev = rint (e.x * UNITS - glyph.left_limit * UNITS);++ // off curve+ x = rint (e.get_right_handle ().x () * UNITS - prev - glyph.left_limit * UNITS);+ coordinate_x.append ((int16) x);+ + prev = rint (e.get_right_handle ().x () * UNITS - glyph.left_limit * UNITS);+ }+ }+ }+ + void process_y () {+ double prev = 0;+ double y;+ Font font = OpenFontFormatWriter.get_current_font ();+ + foreach (Path p in paths) {+ foreach (EditPoint e in p.points) {+ y = rint (e.y * UNITS - prev + font.base_line * UNITS);+ coordinate_y.append ((int16) y);+ + prev = rint (e.y * UNITS + font.base_line * UNITS);++ // off curve+ y = rint (e.get_right_handle ().y () * UNITS - prev + font.base_line * UNITS);+ coordinate_y.append ((int16) y);+ + prev = rint (e.get_right_handle ().y () * UNITS + font.base_line * UNITS);+ }+ }+ }+ + void process_bounding_box () {+ int16 last = 0; + int i = 0;++ bounding_box_xmin = int16.MAX;+ bounding_box_ymin = int16.MAX;+ bounding_box_xmax = int16.MIN;+ bounding_box_ymax = int16.MIN;+ + if (coordinate_x.length () == 0) {+ warning ("no points in coordinate_y");+ }+ + foreach (int16 c in coordinate_x) {+ c += last;+ + // Only on curve points are good for calculating bounding box+ if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) { + if (c < bounding_box_xmin) bounding_box_xmin = c;+ if (c > bounding_box_xmax) bounding_box_xmax = c;+ }+ + last = c;+ i++;+ }++ if (coordinate_y.length () == 0) {+ warning ("no points in coordinate_y");+ }+ + last = 0;+ i = 0;+ foreach (int16 c in coordinate_y) {+ c += last;+ + if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) {+ if (c < bounding_box_ymin) bounding_box_ymin = c;+ if (c > bounding_box_ymax) bounding_box_ymax = c; + }+ + last = c;+ i++;+ }+ + printd (@"Bounding box: $bounding_box_xmin,$bounding_box_ymin $bounding_box_xmax,$bounding_box_ymax\n");+ }+}++}new file mode 100644@@ -0,0 +1,824 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class GlyfTable : Table {+ // Flags for composite glyph+ static const uint16 BOTH_ARE_WORDS = 1 << 0;+ static const uint16 BOTH_ARE_XY_VALUES = 1 << 1;+ static const uint16 ROUND_TO_GRID = 1 << 2;+ static const uint16 SCALE = 1 << 3;+ static const uint16 RESERVED = 1 << 4;+ static const uint16 MORE_COMPONENTS = 1 << 5;+ static const uint16 SCALE_X_Y = 1 << 6;+ static const uint16 SCALE_WITH_ROTATTION = 1 << 7;+ static const uint16 INSTRUCTIONS = 1 << 8;++ public int16 xmin = int16.MAX;+ public int16 ymin = int16.MAX;+ public int16 xmax = int16.MIN;+ public int16 ymax = int16.MIN;++ public FontData dis;+ public HeadTable head_table;+ public HmtxTable hmtx_table;+ public LocaTable loca_table;+ public CmapTable cmap_table; // cmap and post is null when inistialized and set in parse method+ public PostTable post_table;+ public KernTable kern_table;+ + public List<uint32> location_offsets; ++ // list of glyphs sorted in the order we expect to find them in a+ // ttf font. notdef is the firs glyph followed by null and nonmarkingreturn.+ // after that will all assigned glyphs appear in sorted order, all + // remaining unassigned glyphs is in the last part of the file. + public List<Glyph> glyphs;+ + uint16 max_points = 0;+ uint16 max_contours = 0;+ + double total_width = 0;+ int non_zero_glyphs = 0;+ + public GlyfTable (LocaTable l) {+ id = "glyf";+ loca_table = l;+ location_offsets = new List<uint32> ();+ glyphs = new List<Glyph> ();+ } ++ public int get_gid (string name) {+ int i = 0;+ foreach (Glyph g in glyphs) {+ if (g.name == name) {+ return i;+ }+ + i++;+ }+ + return -1;+ }++ public int16 get_average_width () {+ return (int16) (total_width / non_zero_glyphs);+ }++ public uint16 get_max_contours () {+ return max_contours;+ }++ public uint16 get_max_points () {+ return max_points;+ }++ public uint16 get_first_char () {+ return 32; // space+ }+ + public uint16 get_last_char () {+ unowned List<Glyph> gl = glyphs.last ();+ Glyph g = (!) gl.data;+ + while (gl != glyphs.first ()) {+ g = (!) gl.data;+ + if (!g.is_unassigned ()) {+ break;+ }+ + gl = gl.prev;+ }+ + return (uint16)g.unichar_code; + }++ public void process () throws GLib.Error {+ FontData fd = new FontData ();+ uint last_len = 0;+ uint i = 0;+ uint num_glyphs;+ + create_glyph_table ();+ + num_glyphs = glyphs.length ();+ + foreach (Glyph g in glyphs) {+ // set values for loca table+ assert (fd.length () % 4 == 0);+ location_offsets.append (fd.length ());+ process_glyph (g, fd);++ printd (@"adding glyph: $(g.name)\n");+ printd (@"glyf length: $(fd.length () - last_len)\n");+ printd (@"loca fd.length (): $(fd.length ())\n");++ last_len = fd.length ();+ + ProgressBar.set_progress ((double) (++i) / num_glyphs);+ }++ location_offsets.append (fd.length ()); // last entry in loca table is special+ + // every glyph is padded, no padding to be done here+ assert (fd.length () % 4 == 0);++ font_data = fd; + }++ // necessary in order to have glyphs sorted according to ttf specification+ public void create_glyph_table () {+ Glyph? gl;+ Glyph g;+ Font font = OpenFontFormatWriter.get_current_font ();+ uint32 indice;+ List<Glyph> unassigned_glyphs;+ + // add notdef. character at index zero + other special chars first+ glyphs.append (font.get_not_def_character ());+ glyphs.append (font.get_null_character ());+ glyphs.append (font.get_nonmarking_return ());+ glyphs.append (font.get_space ());+ + unassigned_glyphs = new List<Glyph> ();+ + // add glyphs, first all assigned then the unassigned ones+ for (indice = 0; (gl = font.get_glyph_indice (indice)) != null; indice++) { + g = (!) gl;+ + if (g.unichar_code <= 27) { // skip control characters+ continue;+ }++ if (g.unichar_code == 32) { // skip space+ continue;+ }+ + if (g.name == ".notdef") {+ continue;+ }+ + g.remove_empty_paths ();++ if (!g.is_unassigned ()) {+ glyphs.append (g);+ } else {+ unassigned_glyphs.append (g);+ }+ }+ + foreach (Glyph ug in unassigned_glyphs) {+ // FIXME: ligatures+ // glyphs.append (ug);+ }+ + }++ public void process_glyph (Glyph g, FontData fd) throws GLib.Error {+ uint16 end_point;+ uint16 npoints;+ int16 ncontours;+ int16 nflags;+ int glyph_offset;+ uint len; + uint coordinate_length;++ fd.seek_end (); // append glyph+ + glyph_offset = (int) fd.length ();+ + printd (@"glyph_offset: $(glyph_offset)\n");+ + g.remove_empty_paths ();+ if (g.path_list.length () == 0) {+ // location_offsets == location_offset + 1 to tell parser that this glyf does not have a body+ return;+ }+ + non_zero_glyphs++;+ + ncontours = (int16) g.path_list.length ();+ fd.add_short (ncontours);+ + GlyfData glyf_data = new GlyfData (g);+ + // bounding box+ fd.add_16 (glyf_data.bounding_box_xmin);+ fd.add_16 (glyf_data.bounding_box_ymin);+ fd.add_16 (glyf_data.bounding_box_xmax);+ fd.add_16 (glyf_data.bounding_box_ymax);++ // end points+ foreach (uint16 end in glyf_data.end_points) {+ fd.add_u16 (end);+ }++ fd.add_u16 (0); // instruction length + + uint glyph_header = 12 + ncontours * 2;+ + printd (@"next glyf: $(g.name) ($((uint32)g.unichar_code))\n");+ printd (@"glyf header length: $(glyph_header)\n");+ + end_point = glyf_data.get_end_point ();+ ncontours = glyf_data.get_ncontours ();+ npoints = (ncontours > 0) ? end_point : 0; // +1?+ + if (npoints > max_points) {+ max_points = npoints;+ }+ + if (ncontours > max_contours) {+ max_contours = ncontours;+ }+ + // flags + nflags = glyf_data.get_nflags ();+ if (nflags != npoints) {+ warning (@"(nflags != npoints) ($nflags != $npoints) in glyph $(g.name). ncontours: $ncontours");+ }+ assert (nflags == npoints);+ + foreach (uint8 flag in glyf_data.flags) {+ fd.add_byte (flag);+ }++ printd (@"flags: $(nflags)\n");+ + // x coordinates+ foreach (int16 x in glyf_data.coordinate_x) {+ fd.add_16 (x);+ } ++ // y coordinates+ foreach (int16 y in glyf_data.coordinate_y) {+ fd.add_16 (y);+ }+ + len = fd.length ();+ coordinate_length = fd.length () - nflags - glyph_header;+ printd (@"coordinate_length: $(coordinate_length)\n");+ printd (@"fd.length (): $(fd.length ())\n");+ assert (fd.length () > nflags + glyph_header);+ + printd (@"glyph_offset: $(glyph_offset)\n");+ printd (@"len: $(len)\n");++ // save bounding box for head table+ if (glyf_data.bounding_box_xmin < this.xmin) {+ this.xmin = glyf_data.bounding_box_xmin;+ }+ + if (glyf_data.bounding_box_ymin < this.ymin) {+ this.ymin = glyf_data.bounding_box_ymin;+ }+ + if (glyf_data.bounding_box_xmax > this.xmax) {+ this.xmax = glyf_data.bounding_box_xmax;+ }+ + if (glyf_data.bounding_box_ymax > this.ymax) {+ this.ymax = glyf_data.bounding_box_ymax;+ }+ + // part of average width calculation for OS/2 table+ total_width += xmax - xmin;+ + printd (@"length before padding: $(fd.length ())\n");+ + // all glyphs needs padding for loca table to be correct+ while (fd.length () % 4 != 0) {+ fd.add (0);+ }+ printd (@"length after padding: $(fd.length ())\n");+ }++ public Glyph? read_glyph (string name) throws GLib.Error {+ Glyph? glyph;+ Glyph g;+ int i;+ KernList kl;+ string right;+ double units_per_em;+ double kern_val;+ + i = post_table.get_gid (name);+ + if (i == -1) {+ return null;+ }+ + glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table);+ + if (glyph != null) {+ printd (@"kern: ");+ units_per_em = head_table.get_units_per_em ();+ kl = kern_table.get_all_pairs (i);+ foreach (Kern k in kl.kernings) {+ g = (!) glyph;+ right = post_table.get_name (k.right);+ kern_val = k.kerning * 1000.0 / units_per_em;+ g.add_kerning (right, kern_val);+ }+ }+ + return glyph;+ }+ + public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error {+ this.cmap_table = cmap_table;+ this.post_table = post_table;+ this.loca_table = loca;+ this.hmtx_table = hmtx_table;+ this.head_table = head_table;+ this.kern_table = kern_table;+ this.dis = dis;+ }+ + Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error {+ Glyph glyph = new Glyph ("");+ double xmin, xmax;+ double units_per_em = head_table.get_units_per_em ();+ unichar character = 0;+ string name;+ + character = cmap_table.get_char (index);+ name = post_table.get_name (index);+ + if (name == "") {+ StringBuilder name_c = new StringBuilder ();+ name_c.append_unichar (character);+ name = name_c.str;+ }++ printd (@"name: $(name)\n");++ if (!loca.is_empty (index)) { + glyph = parse_next_glyf (dis, character, index, out xmin, out xmax, units_per_em);+ + glyph.left_limit = xmin - hmtx_table.get_lsb (index);+ glyph.left_limit = 0;+ glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (index);+ } else {+ // add empty glyph+ glyph = new Glyph (name, character);+ glyph.left_limit = -hmtx_table.get_lsb (index);+ glyph.right_limit = hmtx_table.get_advance (index) - hmtx_table.get_lsb (index); + }+ + glyph.name = name;++ if (character == 0) {+ glyph.set_unassigned (true);+ }+ + if (character == 0 && name != "") {+ stderr.printf (@"Got null character\n");+ stderr.printf (@"gid: $index\n");+ stderr.printf (@"char: $((uint) character)\n");+ stderr.printf (@"name: $(name)\n");+ }+ + return glyph;+ }+ + Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error {+ uint16 component_flags = 0;+ uint16 glyph_index;+ int16 arg1 = 0;+ int16 arg2 = 0;++ F2Dot14 scale;+ + F2Dot14 scalex;+ F2Dot14 scaley;+ + F2Dot14 scale01;+ F2Dot14 scale10;++ Glyph glyph, linked_glyph;+ List<int> x = new List<int> ();+ List<int> y = new List<int> ();+ List<int> gid = new List<int> ();+ + double xmin, xmax;+ double units_per_em = head_table.get_units_per_em ();+ + int glid;+ + StringBuilder name = new StringBuilder ();+ name.append_unichar (character);+ + glyph = new Glyph (name.str, character);+ + do {+ component_flags = dis.read_ushort ();+ glyph_index = dis.read_ushort ();+ + if ((component_flags & BOTH_ARE_WORDS) > 0) {+ arg1 = dis.read_short ();+ arg2 = dis.read_short (); + } else if ((component_flags & BOTH_ARE_XY_VALUES) > 0) {+ arg1 = dis.read_byte ();+ arg2 = dis.read_byte ();+ }+ + gid.append (glyph_index);+ x.append (arg1);+ y.append (arg2);++ // if ((component_flags & RESERVED) > 0)+ + if ((component_flags & SCALE) > 0) {+ scale = dis.read_f2dot14 ();+ } else if ((component_flags & SCALE_X_Y) > 0) {+ scalex = dis.read_f2dot14 ();+ scaley = dis.read_f2dot14 ();+ } else if ((component_flags & SCALE_WITH_ROTATTION) > 0) {+ scalex = dis.read_f2dot14 ();+ scale01 = dis.read_f2dot14 ();+ scale10 = dis.read_f2dot14 ();+ scaley = dis.read_f2dot14 ();+ }+ + } while ((component_flags & MORE_COMPONENTS) > 0);+ + + for (int i = 0; i < gid.length (); i++) {+ // compensate xmax ymax with coordinate+ glid = gid.nth (i).data;++ if (glid == pgid) {+ warning ("Cannot link a glyph to it self.");+ continue;+ }++ linked_glyph = parse_next_glyf (dis, character, glid, out xmin, out xmax, units_per_em);+ }+ + return glyph;+ }+ + Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error {+ uint16* end_points = null;+ uint8* instructions = null;+ uint8* flags = null;+ int16* xcoordinates = null;+ int16* ycoordinates = null;+ + int npoints = 0;+ + int16 ncontours;+ int16 ixmin; // set boundries+ int16 iymin;+ int16 ixmax;+ int16 iymax;+ uint16 ninstructions;++ int16 rxmin = int16.MAX; // real xmin+ int16 rymin = int16.MAX;;+ int16 rxmax = int16.MIN;+ int16 rymax = int16.MIN;+ + int nflags;+ + Error? error = null;+ + uint start, end, len;+ + StringBuilder name = new StringBuilder ();+ name.append_unichar (character);++ xmin = 0;+ xmax = 0;++ start = loca_table.get_offset (gid);+ end = loca_table.get_offset (gid + 1);+ len = start - end;++ dis.seek (offset + start);+ + ncontours = dis.read_short ();+ + return_val_if_fail (start < end, new Glyph (""));++ if (ncontours == 0) {+ warning (@"Got zero contours in glyph $(name.str).");++ // should skip body+ }+ + if (ncontours == -1) {+ return parse_next_composite_glyf (dis, character, gid);+ }++ return_val_if_fail (ncontours < len, new Glyph (""));+ + if (ncontours < -1) {+ warning (@"Got $ncontours contours in glyf table.");+ error = new BadFormat.PARSE ("Invalid glyf");+ throw error;+ }+ + ixmin = dis.read_short ();+ iymin = dis.read_short ();+ ixmax = dis.read_short ();+ iymax = dis.read_short ();++ end_points = new uint16[ncontours + 1];+ for (int i = 0; i < ncontours; i++) {+ end_points[i] = dis.read_ushort (); // FIXA: mind shot vector is negative+ + if (i > 0 && end_points[i] < end_points[i -1]) {+ warning (@"Next endpoint has bad value in $(name.str). (end_points[i] > end_points[i -1]) ($(end_points[i]) > $(end_points[i -1])) i: $i ncontours: $ncontours");+ }+ }+ + if (ncontours > 0) {+ npoints = end_points[ncontours - 1] + 1;+ } else {+ npoints = 0;+ }+ + return_val_if_fail (npoints < len, new Glyph.no_lines (""));+ + ninstructions = dis.read_ushort ();+ + return_val_if_fail (ninstructions < len, new Glyph.no_lines (""));+ + instructions = new uint8[ninstructions + 1];+ uint8 repeat;+ for (int i = 0; i < ninstructions; i++) {+ instructions[i] = dis.read_byte ();+ }++ nflags = 0;+ flags = new uint8[npoints + 1];+ for (int i = 0; i < npoints; i++) {+ flags[i] = dis.read_byte ();+ + if ((flags[i] & Coordinate.REPEAT) > 0) {+ repeat = dis.read_byte ();+ + if (i + repeat >= npoints) {+ error = new BadFormat.PARSE ("Too many flags in glyf in glyph $(name.str). (i >= ninstructions).");+ break;+ }+ + for (int j = 0; j < repeat; j++) {+ flags[j + i + 1] = flags[i];+ }+ + nflags += repeat;+ i += repeat;+ }+ + nflags++;+ }+ + if (nflags != npoints) {+ warning (@"(nflags != npoints) ($nflags != $npoints) in $(name.str)");+ error = new BadFormat.PARSE (@"Wrong number of flags in glyph $(name.str). (nflags != npoints) ($nflags != $npoints)");+ }+ + warn_if_fail (nflags == npoints);++ printd (@"npoints: $npoints\n");+ printd (@"ncontours: $ncontours\n");+ printd (@"ninstructions: $ninstructions\n");+ printd (@"nflags: $nflags\n");+ + int16 last = 0;+ xcoordinates = new int16[npoints + 1];+ for (int i = 0; i < npoints; i++) {+ if ((flags[i] & Coordinate.X_SHORT_VECTOR) > 0) { + if ((flags[i] & Coordinate.X_SHORT_VECTOR_POSITIVE) > 0) {+ xcoordinates[i] = last + dis.read_byte ();+ } else {+ xcoordinates[i] = last - dis.read_byte ();+ }+ } else {+ if ((flags[i] & Coordinate.X_IS_SAME) > 0) {+ xcoordinates[i] = last;+ } else {+ xcoordinates[i] = last + dis.read_short ();+ }+ }+ + last = xcoordinates[i];+ + if (last > rxmax) rxmax = last;+ if (last < rxmin) rxmin = last;+ + if (!(ixmin <= last <= ixmax)) {+ stderr.printf (@"x is out of bounds in glyph $(name.str). ($ixmin <= $last <= $ixmax) char $((uint)character)\n");+ }+ + if (!(head_table.xmin <= last <= head_table.xmax)) {+ stderr.printf (@"x is outside of of font bounding box in glyph $(name.str). ($(head_table.xmin) <= $last <= $(head_table.xmax)) char $((uint)character)\n");+ } + }+ + last = 0;+ ycoordinates = new int16[npoints + 1];+ for (int i = 0; i < npoints; i++) {+ if ((flags[i] & Coordinate.Y_SHORT_VECTOR) > 0) { + if ((flags[i] & Coordinate.Y_SHORT_VECTOR_POSITIVE) > 0) {+ ycoordinates[i] = last + dis.read_byte ();+ } else {+ ycoordinates[i] = last - dis.read_byte ();+ }+ } else {+ if ((flags[i] & Coordinate.Y_IS_SAME) > 0) {+ ycoordinates[i] = last;+ } else {+ ycoordinates[i] = last + dis.read_short ();+ }+ }+ + last = ycoordinates[i];++ if (last > rymax) rymax = last;+ if (last < rymin) rymin = last;+ + if (!(iymin <= last <= iymax)) {+ stderr.printf (@"y is out of bounds in glyph $(name.str). ($iymin <= $last <= $iymax) char $((uint)character)\n");+ }+ + if (!(head_table.ymin <= last <= head_table.ymax)) {+ stderr.printf (@"y is outside of of font bounding box in glyph $(name.str). ($(head_table.ymin) <= $last <= $(head_table.ymax)) char $((uint)character)\n");+ }+ }+ + if (rymin != iymin || rxmin != ixmin || rxmax != ixmax || rymax != iymax) {+ warning (@"Warning real boundry for glyph does not match boundry set in glyph header for glyph $(name.str).");+ stderr.printf (@"ymin: $rymin header: $iymin\n");+ stderr.printf (@"xmin: $rxmin header: $ixmin\n");+ stderr.printf (@"ymax: $rymax header: $iymax\n");+ stderr.printf (@"xmax: $rxmax header: $ixmax\n");+ } + + int j = 0;+ int first_point;+ int last_point = 0;+ Glyph glyph;+ double x, y;++ glyph = new Glyph (name.str, character);+ + xmin = ixmin * 1000.0 / units_per_em;+ xmax = ixmax * 1000.0 / units_per_em;+ + for (int i = 0; i < ncontours; i++) {+ x = 0;+ y = 0;+ + Path path = new Path ();+ EditPoint edit_point = new EditPoint ();+ bool prev_is_curve = false;+ + first_point = j;+ last_point = end_points[i];+ for (; j <= end_points[i]; j++) {++ if (j >= npoints) {+ warning (@"j >= npoints in glyph $(name.str). (j: $j, end_points[i]: $(end_points[i]), npoints: $npoints)");+ break;+ }+ + x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width+ y = ycoordinates[j] * 1000.0 / units_per_em;+ + if ((flags[j] & Coordinate.ON_PATH) > 0) {+ // Point+ edit_point = new EditPoint ();+ edit_point.set_position (x, y);+ path.add_point (edit_point);+ + if (prev_is_curve) {+ edit_point.get_left_handle ().set_point_type (PointType.NONE);+ edit_point.get_left_handle ().length = 0;+ } else {+ edit_point.recalculate_linear_handles ();+ }+ + prev_is_curve = false;+ } else {+ + if (prev_is_curve) {+ x = x - (x - edit_point.right_handle.x ()) / 2;+ y = y - (y - edit_point.right_handle.y ()) / 2;++ edit_point = new EditPoint ();+ edit_point.set_position (x, y);+ path.add_point (edit_point);+ }++ x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width+ y = ycoordinates[j] * 1000.0 / units_per_em;++ edit_point.get_left_handle ().set_point_type (PointType.NONE);+ edit_point.get_left_handle ().length = 0;+ + edit_point.type = PointType.CUBIC;+ edit_point.get_right_handle ().set_point_type (PointType.CUBIC);+ edit_point.get_right_handle ().move_to_coordinate (x, y);+ + prev_is_curve = true;+ } + }+ + // last to first point+ if (prev_is_curve) {+ x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width+ y = ycoordinates[first_point] * 1000.0 / units_per_em;+ + x = x - (x - edit_point.right_handle.x ()) / 2;+ y = y - (y - edit_point.right_handle.y ()) / 2;+ + edit_point = new EditPoint ();+ edit_point.set_position (x, y);+ path.add_point (edit_point);+ + x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width+ y = ycoordinates[first_point] * 1000.0 / units_per_em;++ edit_point.get_left_handle ().set_point_type (PointType.CUBIC);+ edit_point.get_left_handle ().length = 0;+ + edit_point.type = PointType.CUBIC;+ edit_point.get_right_handle ().set_point_type (PointType.CUBIC);+ edit_point.get_right_handle ().move_to_coordinate (x, y);+ }+ + // curve last to first+ x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width+ y = ycoordinates[first_point] * 1000.0 / units_per_em;+ edit_point.type = PointType.CUBIC;+ edit_point.get_right_handle ().set_point_type (PointType.CUBIC);+ edit_point.get_right_handle ().move_to_coordinate (x, y);+ + path.close ();+ + glyph.add_path (path);+ }+ + // glyphs with no bounding boxes+ if (ixmax <= ixmin) {+ warning (@"Bounding box is bad. (xmax == xmin) ($xmax == $xmin)");+ + if (glyph.path_list.length () > 0) {+ + Path ps = ((!) glyph.path_list.first ()).data;+ + ps.update_region_boundries ();+ xmin = ps.xmin;+ xmax = ps.xmax;++ foreach (Path p in glyph.path_list) {+ p.update_region_boundries ();+ + if (p.xmin < xmin) {+ xmin = p.xmin;+ }+ + if (p.xmax > xmax) {+ xmax = p.xmax;+ }+ }+ + }+ }+ + if (end_points != null) delete end_points;+ if (instructions != null) delete instructions;+ if (flags != null) delete flags;+ if (xcoordinates != null) delete xcoordinates;+ if (ycoordinates != null) delete ycoordinates;+ + if (error != null) {+ warning ("failed to parse glyph");+ throw (!) error;+ }+ + return glyph;+ }+}++}new file mode 100644@@ -0,0 +1,127 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class GposTable : Table {+ + public GposTable () {+ id = "GPOS";+ }+ + public override void parse (FontData dis) throws Error {+ warning ("Not implemented.");+ }++ public void process (KernTable kern_table) throws GLib.Error {+ FontData fd = new FontData ();+ uint16 num_kerning_values = (uint16) kern_table.kernings.length ();+ int i;+ int size_of_pair;+ + if (num_kerning_values == 0) {+ warning ("No kerning to add to gpos table.");+ }+ + printd (@"Adding $num_kerning_values kerning pairs to gpos table.\n");+ + fd.add_ulong (0x00010000); // table version+ fd.add_ushort (10); // offset to script list+ fd.add_ushort (24); // offset to feature list+ fd.add_ushort (38); // offset to lookup list+ + // script list ?+ fd.add_ushort (0); // number of items in script list+ + // script table+ fd.add_ushort (4); // offset to default language system+ fd.add_ushort (0); // number of languages+ + // script record, none right now+ + // LangSys table + fd.add_ushort (0); // reserved+ fd.add_ushort (0xFFFF); // required features (0xFFFF is none)+ fd.add_ushort (1); // number of features+ fd.add_ushort (0); // feature index+ + // feature table+ fd.add_ushort (1); // number of features+ + fd.add_tag ("kern"); // feature tag+ fd.add_ushort (8); // offset to feature+ + fd.add_ushort (0); // feature prameters (null)+ fd.add_ushort (1); // number of lookups+ fd.add_ushort (0); // lookup indice+ + // lookup table+ fd.add_ushort (1); // number of lookups+ fd.add_ushort (4); // offset to lookup 1+ + fd.add_ushort (2); // lookup type // FIXME + fd.add_ushort (0); // lookup flags+ fd.add_ushort (1); // number of subtables+ fd.add_ushort (8); // array of offsets to subtables+ + // MarkFilteringSet + + // the kerning pair table+ size_of_pair = 6;+ fd.add_ushort (1); // position format+ // offset to coverage table from beginning of kern pair table+ fd.add_ushort (10 + 2 * num_kerning_values + size_of_pair * num_kerning_values); + fd.add_ushort (0x0004); // ValueFormat1 (0x0004 is x advance)+ fd.add_ushort (0); // ValueFormat2 (0 is null)+ fd.add_ushort (num_kerning_values); // n pairs+ + // pair offsets orderd by coverage index+ i = 0;+ foreach (Kern k in kern_table.kernings) {+ fd.add_ushort (10 + 2 * num_kerning_values + i * size_of_pair);+ i++;+ }+ + // pair table + i = 0;+ + foreach (Kern k in kern_table.kernings) {+ // pair value record+ fd.add_ushort (1); // n pair vaules+ fd.add_ushort (k.right); // gid to second glyph+ fd.add_short (k.kerning); // value of ValueFormat1, horizontal adjustment for advance + // value of ValueFormat2 is null+ + ProgressBar.set_progress ((double) (num_kerning_values - ++i) / num_kerning_values);+ }+ + ProgressBar.set_progress (0); // reset progress bar+ + // coverage+ fd.add_ushort (1); // format+ fd.add_ushort (num_kerning_values); // number of gid+ foreach (Kern k in kern_table.kernings) {+ fd.add_ushort (k.left); // gid+ }+ + fd.pad (); + this.font_data = fd;+ }++}++}new file mode 100644@@ -0,0 +1,201 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class HeadTable : Table {++ public int16 xmin = int16.MIN;+ public int16 ymin = int16.MIN;+ public int16 xmax = int16.MAX;+ public int16 ymax = int16.MAX;+ + uint32 adjusted_checksum = 0;++ uint16 mac_style;+ uint16 lowest_PPEM;+ int16 font_direction_hint;+ + public int16 loca_offset_size = 1; // 0 for int16 1 for int32+ int16 glyph_data_format;++ Fixed version;+ Fixed font_revision;+ + uint32 magic_number;+ + uint16 flags;+ + uint64 created;+ uint64 modified;+ + uint16 units_per_em = 2048;+ + const uint8 BASELINE_AT_ZERO = 1 << 0;+ const uint8 LSB_AT_ZERO = 1 << 1;+ + GlyfTable glyf_table;+ + public HeadTable (GlyfTable gt) {+ glyf_table = gt;+ id = "head";+ }+ + public uint32 get_adjusted_checksum () {+ return adjusted_checksum;+ }+ + public double get_units_per_em () {+ return units_per_em * 10; // Fixa: we can refactor this number+ }+ + public override void parse (FontData dis) throws GLib.Error {+ return_if_fail (offset > 0 && length > 0);++ dis.seek (offset);+ + font_data = new FontData (); + + version = dis.read_fixed ();++ if (!version.equals (1, 0)) {+ warning (@"Expecting head version 1.0 got $(version.get_string ())\n");+ }+ + font_revision = dis.read_fixed ();+ adjusted_checksum = dis.read_ulong ();+ magic_number = dis.read_ulong ();+ + if (magic_number != 0x5F0F3CF5) {+ warning (@"Magic number is invalid. Got $(magic_number).");+ return;+ }+ + flags = dis.read_ushort ();+ + if ((flags & BASELINE_AT_ZERO) > 0) {+ printd ("Flag BASELINE_AT_ZERO has been set.\n");+ }++ if ((flags & LSB_AT_ZERO) > 0) {+ printd ("Flags LSB_AT_ZERO has been set.\n");+ }+ + units_per_em = dis.read_ushort ();+ + created = dis.read_udate ();+ modified = dis.read_udate ();+ + xmin = dis.read_short ();+ ymin = dis.read_short ();+ + xmax = dis.read_short ();+ ymax = dis.read_short ();++ printd (@"font boundries:\n");+ printd (@"xmin: $xmin\n");+ printd (@"ymin: $ymin\n");+ printd (@"xmax: $xmax\n");+ printd (@"ymax: $ymax\n");+ + mac_style = dis.read_ushort ();+ lowest_PPEM = dis.read_ushort ();+ font_direction_hint = dis.read_short ();+ + loca_offset_size = dis.read_short ();+ glyph_data_format = dis.read_short ();+ + if (glyph_data_format != 0) {+ warning (@"Unknown glyph data format. Expecting 0 got $glyph_data_format.");+ }+ + printd (@"Version: $(version.get_string ())\n");+ printd (@"flags: $flags\n");+ printd (@"font_revision: $(font_revision.get_string ())\n");+ printd (@"flags: $flags\n");+ printd (@"Units per em: $units_per_em\n");+ printd (@"lowest_PPEM: $lowest_PPEM\n");+ printd (@"font_direction_hint: $font_direction_hint\n");+ printd (@"loca_offset_size: $loca_offset_size\n");+ printd (@"glyph_data_format: $glyph_data_format\n");+ + // Some deprecated values follow here ...+ }+ + public uint32 get_font_checksum () {+ return adjusted_checksum;+ }+ + public void set_check_sum_adjustment (uint32 csa) {+ this.adjusted_checksum = csa;+ }+ + public uint32 get_checksum_position () {+ return 8;+ }+ + public void process () throws GLib.Error {+ FontData font_data = new FontData ();+ Fixed version = 1 << 16;+ Fixed font_revision = 1 << 16;++ font_data.add_fixed (version);+ font_data.add_fixed (font_revision);+ + // Zero on the first run and updated by directory tables checksum calculation+ // for the entire font.+ font_data.add_u32 (adjusted_checksum);+ + font_data.add_u32 (0x5F0F3CF5); // magic number+ + //font_data.add_u16 (BASELINE_AT_ZERO | LSB_AT_ZERO);+ font_data.add_u16 (0); // flags+ + font_data.add_u16 (1000); // units per em (should be a power of two for ttf fonts)+ + font_data.add_64 (0); // creation time since 1904-01-01+ font_data.add_64 (0); // modified time since 1904-01-01++ xmin = glyf_table.xmin;+ ymin = glyf_table.ymin;+ xmax = glyf_table.xmax;+ ymax = glyf_table.ymax;++ printd (@"font boundries:\n");+ printd (@"xmin: $xmin\n");+ printd (@"ymin: $ymin\n");+ printd (@"xmax: $xmax\n");+ printd (@"ymax: $ymax\n");++ font_data.add_short (xmin);+ font_data.add_short (ymin);+ font_data.add_short (xmax);+ font_data.add_short (ymax);+ + font_data.add_u16 (0); // mac style+ font_data.add_u16 (2); // smallest recommended size in pixels, ppem+ font_data.add_16 (2); // deprecated direction hint+ font_data.add_16 (loca_offset_size); // long offset+ font_data.add_16 (0); // Use current glyph data format+ + font_data.pad ();+ + this.font_data = font_data;+ }+}++}new file mode 100644@@ -0,0 +1,123 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class HheaTable : Table {++ Fixed version;+ int16 ascender;+ int16 descender;+ int16 linegap;+ uint16 max_advance;+ int16 min_lsb;+ int16 min_rsb;+ int16 xmax_extent;+ int16 carret_slope;+ int16 carret_slope_run;+ int16 carret_offset;+ + int16 metric_format;+ public int16 num_horizontal_metrics;+ + GlyfTable glyf_table;+ HeadTable head_table;+ HmtxTable hmtx_table;+ + public HheaTable (GlyfTable g, HeadTable h, HmtxTable hm) {+ glyf_table = g;+ head_table = h;+ hmtx_table = hm;+ id = "hhea";+ }+ + public double get_ascender () {+ return ascender * 1000 / head_table.get_units_per_em ();+ }++ public double get_descender () {+ return descender * 1000 / head_table.get_units_per_em ();+ }+ + public override void parse (FontData dis) throws GLib.Error {+ dis.seek (offset);+ + version = dis.read_fixed ();+ + if (!version.equals (1, 0)) {+ warning (@"wrong version in hhea table $(version.get_string ())");+ }+ + ascender = dis.read_short ();+ descender = dis.read_short ();+ linegap = dis.read_short ();+ max_advance = dis.read_ushort ();+ min_lsb = dis.read_short ();+ min_rsb = dis.read_short ();+ xmax_extent = dis.read_short ();+ carret_slope = dis.read_short ();+ carret_slope_run = dis.read_short ();+ carret_offset = dis.read_short ();+ + // reserved x 4+ dis.read_short ();+ dis.read_short ();+ dis.read_short ();+ dis.read_short ();+ + metric_format = dis.read_short ();+ num_horizontal_metrics = dis.read_short ();+ }+ + public void process () throws GLib.Error {+ Font font = OpenFontFormatWriter.get_current_font ();+ FontData fd = new FontData ();+ Fixed version = 1 << 16;+ + fd.add_fixed (version); // table version+ + fd.add_16 ((int16) (-1 * (font.top_position - font.base_line) * UNITS)); // Ascender+ fd.add_16 ((int16) (-1 * font.bottom_position * UNITS)); // Descender+ fd.add_16 (0); // LineGap+ + fd.add_u16 (hmtx_table.max_advance); // maximum advance width value in 'hmtx' table.+ + fd.add_16 (hmtx_table.min_lsb); // min left side bearing+ fd.add_16 (hmtx_table.min_rsb); // min right side bearing+ fd.add_16 (hmtx_table.max_extent); // x max extent Max(lsb + (xMax - xMin))+ + fd.add_16 (1); // caretSlopeRise+ fd.add_16 (0); // caretSlopeRun+ fd.add_16 (0); // caretOffset+ + // reserved+ fd.add_16 (0);+ fd.add_16 (0);+ fd.add_16 (0);+ fd.add_16 (0);+ + fd.add_16 (0); // metricDataFormat 0 for current format.+ + fd.add_u16 ((uint16) glyf_table.glyphs.length()); // numberOfHMetrics Number of hMetric entries in 'hmtx' table++ // padding+ fd.pad ();+ this.font_data = fd;+ }+}++}new file mode 100644@@ -0,0 +1,144 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class HmtxTable : Table {+ + uint32 nmetrics;+ uint32 nmonospaced;+ + uint16* advance_width = null;+ uint16* left_side_bearing = null;+ uint16* left_side_bearing_monospaced = null;+ + public int16 max_advance = 0;+ public int16 max_extent = 0;+ public int16 min_lsb = 0; + public int16 min_rsb = 0;+ + HeadTable head_table;+ GlyfTable glyf_table;+ + public HmtxTable (HeadTable h, GlyfTable gt) {+ head_table = h;+ glyf_table = gt;+ id = "hmtx";+ }+ + ~HmtxTable () {+ if (advance_width != null) delete advance_width;+ if (left_side_bearing != null) delete left_side_bearing; + }++ public double get_advance (uint32 i) {+ if (i >= nmetrics) {+ warning (@"i >= nmetrics $i >= $nmetrics");+ return 0;+ }+ + return_val_if_fail (advance_width != null, 0.0);+ + return advance_width[i] * 1000 / head_table.get_units_per_em ();+ }+ + /** Get left side bearing relative to xmin. */+ public double get_lsb (uint32 i) {+ return_val_if_fail (i < nmetrics, 0.0);+ return_val_if_fail (left_side_bearing != null, 0.0);+ + return left_side_bearing[i] * 1000 / head_table.get_units_per_em ();+ }+ + public new void parse (FontData dis, HheaTable hhea_table, LocaTable loca_table) throws GLib.Error {+ nmetrics = hhea_table.num_horizontal_metrics;+ nmonospaced = loca_table.size - nmetrics;+ + dis.seek (offset);+ + if (nmetrics > loca_table.size) {+ warning (@"(nmetrics > loca_table.size) ($nmetrics > $(loca_table.size))");+ return;+ }+ + printd (@"nmetrics: $nmetrics\n");+ printd (@"loca_table.size: $(loca_table.size)\n");+ + advance_width = new uint16[nmetrics];+ left_side_bearing = new uint16[nmetrics];+ left_side_bearing_monospaced = new uint16[nmonospaced];+ + for (int i = 0; i < nmetrics; i++) {+ advance_width[i] = dis.read_ushort ();+ left_side_bearing[i] = dis.read_short ();+ }+ + for (int i = 0; i < nmonospaced; i++) {+ left_side_bearing_monospaced[i] = dis.read_short ();+ }+ }+ + public void process () {+ FontData fd = new FontData ();++ int16 advance;+ int16 extent;+ int16 rsb;+ int16 lsb;+ + double xmin;+ double ymin;+ double xmax;+ double ymax;+ + // advance and lsb+ foreach (Glyph g in glyf_table.glyphs) {+ g.boundries (out xmin, out ymin, out xmax, out ymax);+ lsb = (int16) (-1 * (g.left_limit - xmin) * UNITS);+ advance = (int16) ((g.right_limit - g.left_limit) * UNITS);+ extent = (int16) (lsb + (xmax - xmin) * UNITS);+ rsb = (int16) (advance - extent);+ + fd.add_u16 (advance);+ fd.add_16 (lsb);+ + if (advance > max_advance) {+ max_advance = advance;+ }+ + if (extent > max_extent) {+ max_extent = extent;+ }+ + if (rsb < min_rsb) {+ min_rsb = rsb;+ }++ if (lsb < min_lsb) {+ min_lsb = lsb;+ }+ }+ + // monospaced lsb ...+ + font_data = fd;+ + warn_if_fail (max_advance != 0);+ }+}++}new file mode 100644@@ -0,0 +1,203 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class Kern : GLib.Object {+ public uint16 left;+ public uint16 right;+ public int16 kerning;+ + public Kern (uint16 l, uint16 r, int16 k) {+ left = l;+ right = r;+ kerning = k;+ }+}++class KernList : GLib.Object {+ public List<Kern> kernings;+ + public KernList () {+ kernings = new List<Kern> ();+ }+}++class KernTable : Table {+ + public static const uint16 HORIZONTAL = 1;+ public static const uint16 MINIMUM = 1 << 1;+ public static const uint16 CROSS_STREAM = 1 << 2;+ public static const uint16 OVERRIDE = 1 << 3;+ public static const uint16 FORMAT = 1 << 8;+ + GlyfTable glyf_table;+ + public List<Kern> kernings = new List<Kern> ();+ public int kerning_pairs = 0;+ + public KernTable (GlyfTable gt) {+ glyf_table = gt;+ id = "kern";+ }+ + public KernList get_all_pairs (int gid) {+ KernList kl = new KernList ();+ + foreach (Kern k in kernings) {+ if (k.left == gid) {+ kl.kernings.append (k);+ }+ }+ + return kl;+ }+ + public override void parse (FontData dis) throws GLib.Error {+ uint16 version;+ uint16 sub_tables;+ + uint16 subtable_version;+ uint16 subtable_length;+ uint16 subtable_flags;++ uint16 search_range;+ uint16 entry_selector;+ uint16 range_shift;+ + uint16 n_pairs;+ + dis.seek (offset);+ + version = dis.read_ushort ();+ warn_if_fail (version == 0);+ sub_tables = dis.read_ushort ();+ + for (uint16 i = 0; i < sub_tables; i++) {+ subtable_version = dis.read_ushort (); + subtable_length = dis.read_ushort (); + subtable_flags = dis.read_ushort ();++ n_pairs = dis.read_ushort ();+ search_range = dis.read_ushort ();+ entry_selector = dis.read_ushort ();+ range_shift = dis.read_ushort ();+ + // TODO: check more flags+ if ((subtable_flags & HORIZONTAL) > 0 && (subtable_flags & CROSS_STREAM) == 0 && (subtable_flags & MINIMUM) == 0) {+ parse_pairs (dis, n_pairs);+ }+ }+ }+ + public void parse_pairs (FontData dis, uint16 n_pairs) throws Error {+ uint16 left;+ uint16 right;+ int16 kerning;+ + for (int i = 0; i < n_pairs; i++) {+ left = dis.read_ushort ();+ right = dis.read_ushort ();+ kerning = dis.read_short ();+ + kernings.append (new Kern (left, right, kerning));+ } + }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();+ uint16 n_pairs = 0;+ + uint16 gid_left;+ int gid_right;+ + uint16 range_shift = 0;+ uint16 entry_selector = 0;+ uint16 search_range = 0;+ + Kern kern;+ int i;+ + fd.add_ushort (0); // version + fd.add_ushort (1); // n subtables++ fd.add_ushort (0); // subtable version ++ foreach (Glyph g in glyf_table.glyphs) {+ foreach (Kerning k in g.kerning) {+ n_pairs++;+ }+ }+ + if (n_pairs > (uint16.MAX - 14) / 6.0) {+ warning ("Too many kerning pairs!"); + n_pairs = (uint16) ((uint16.MAX - 14) / 6.0);+ }+ + this.kerning_pairs = n_pairs;+ + fd.add_ushort (6 * n_pairs + 14); // subtable length+ fd.add_ushort (HORIZONTAL); // subtable flags++ fd.add_ushort (n_pairs);+ + search_range = 6 * largest_pow2 (n_pairs);+ entry_selector = largest_pow2_exponent (n_pairs);+ range_shift = 6 * n_pairs - search_range;+ + fd.add_ushort (search_range);+ fd.add_ushort (entry_selector);+ fd.add_ushort (range_shift);++ gid_left = 0;+ + i = 0;+ foreach (Glyph g in glyf_table.glyphs) {+ + foreach (Kerning k in g.kerning) {+ // n_pairs is used to truncate this table to prevent buffer overflow+ if (n_pairs == i++) {+ break;+ }++ gid_right = glyf_table.get_gid (k.glyph_right);+ + if (gid_right == -1) {+ warning ("right glyph not found in kerning table");+ }+ + kern = new Kern (gid_left, (uint16)gid_right, (int16) (k.val * UNITS));+ + fd.add_ushort (kern.left);+ fd.add_ushort (kern.right);+ fd.add_short (kern.kerning);+ + kernings.append (kern);+ }+ + gid_left++;+ }+ + fd.pad ();+ this.font_data = fd;+ }++}+++}+new file mode 100644@@ -0,0 +1,157 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class LocaTable : Table {+ + uint32* glyph_offsets = null;+ public uint32 size = 0;+ + public LocaTable () {+ id = "loca";+ } + + ~LocaTable () {+ if (glyph_offsets != null) delete glyph_offsets;+ }+ + public uint32 get_offset (uint32 i) {+ return_val_if_fail (glyph_offsets != null, 0);+ + if (size == 0) {+ warning ("No glyphs in loca table");+ }+ + if (!(0 <= i < size + 1)) {+ warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)");+ }+ + return glyph_offsets [i];+ }+ + /** Returns true if glyph at index i is empty and have no body to parse. */+ public bool is_empty (uint32 i) {+ return_val_if_fail (glyph_offsets != null, true);++ if (size == 0) {+ warning ("No glyphs in loca table");+ }+ + if (!(0 <= i < size + 1)) {+ warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)");+ }+ + return glyph_offsets[i] == glyph_offsets[i + 1];+ }+ + public new void parse (FontData dis, HeadTable head_table, MaxpTable maxp_table) throws GLib.Error {+ size = maxp_table.num_glyphs;+ glyph_offsets = new uint32[size + 1];+ + dis.seek (offset);+ + printd (@"size: $size\n");+ printd (@"length: $length\n");+ printd (@"length/4-1: $(length / 4 - 1)\n");+ printd (@"length/2-1: $(length / 2 - 1)\n");+ printd (@"head_table.loca_offset_size: $(head_table.loca_offset_size)\n");+ + switch (head_table.loca_offset_size) {+ case 0:+ for (long i = 0; i < size + 1; i++) {+ glyph_offsets[i] = 2 * dis.read_ushort (); + + if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) {+ warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))");+ }+ }+ break;+ + case 1:+ for (long i = 0; i < size + 1; i++) {+ glyph_offsets[i] = dis.read_ulong ();+ + if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) {+ warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))");+ } + }++ break;+ + default:+ warning ("unknown size for offset in loca table");+ break;+ }+ }++ public void process (GlyfTable glyf_table, HeadTable head_table) {+ FontData fd = new FontData ();+ uint32 last = 0;+ uint32 prev = 0;+ int i = 0;+ + foreach (uint32 o in glyf_table.location_offsets) {+ if (i != 0 && (o - prev) % 4 != 0) {+ warning (@"glyph length is not a multiple of four in gid $i");+ }+ + if (o % 4 != 0) {+ warning ("glyph is not on a four byte boundry");+ assert_not_reached ();+ }+ + prev = o;+ i++;+ }+ + if (head_table.loca_offset_size == 0) {+ foreach (uint32 o in glyf_table.location_offsets) {+ fd.add_u16 ((uint16) (o / 2));+ + if (o < last) {+ warning (@"Loca table must be sorted. ($o < $last)");+ }+ + last = o;+ }+ + } else if (head_table.loca_offset_size == 1) {+ foreach (uint32 o in glyf_table.location_offsets) {+ fd.add_u32 (o);++ if (o < last) {+ warning (@"Loca table must be sorted. ($o < $last)");+ }+ + last = o;+ }+ + } else {+ warn_if_reached ();+ }++ if (!(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1)) {+ warning (@"(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1) ($(glyf_table.location_offsets.length ()) == $(glyf_table.glyphs.length () + 1))");+ }++ fd.pad (); + font_data = fd; + }+}++}new file mode 100644@@ -0,0 +1,81 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class MaxpTable : Table {+ + GlyfTable glyf_table;+ + public uint16 num_glyphs = 0;+ + public MaxpTable (GlyfTable g) {+ glyf_table = g;+ id = "maxp";+ }+ + public override void parse (FontData dis) + requires (offset > 0 && length > 0) {+ Fixed format;+ + dis.seek (offset);+ + format = dis.read_fixed ();+ printd (@"Maxp version: $(format.get_string ())\n");+ + num_glyphs = dis.read_ushort ();+ + if (format == 0x00005000) {+ return;+ }+ + // Format 1.0 continues here+ }+ + public void process () {+ FontData fd = new FontData();++ // Version 0.5 for fonts with cff data and 1.0 for ttf+ fd.add_u32 (0x00010000);+ + if (glyf_table.glyphs.length () == 0) {+ warning ("Zero glyphs in maxp table.");+ }+ + fd.add_u16 ((uint16) glyf_table.glyphs.length ()); // numGlyphs in the font++ fd.add_u16 (glyf_table.get_max_points ()); // max points+ fd.add_u16 (glyf_table.get_max_contours ()); // max contours+ fd.add_u16 (0); // max composite points+ fd.add_u16 (0); // max composite contours+ fd.add_u16 (1); // max zones+ fd.add_u16 (0); // twilight points+ fd.add_u16 (0); // max storage+ fd.add_u16 (0); // max function defs+ fd.add_u16 (0); // max instruction defs+ fd.add_u16 (0); // max stack elements+ fd.add_u16 (0); // max size of instructions+ fd.add_u16 (0); // max component elements+ fd.add_u16 (0); // component depth+ + fd.pad ();+ + this.font_data = fd;+ }+}++}new file mode 100644@@ -0,0 +1,229 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class NameTable : Table {++ public static const uint16 COPYRIGHT_NOTICE = 0;+ public static const uint16 FONT_NAME = 1;+ public static const uint16 SUBFAMILY_NAME = 2;+ public static const uint16 UNIQUE_IDENTIFIER = 3;+ public static const uint16 FULL_FONT_NAME = 4; // name + subfamily+ public static const uint16 VERSION = 5;+ public static const uint16 DESCRIPTION = 10;+ public static const uint16 PREFERED_FAMILY = 16;+ public static const uint16 PREFERED_SUB_FAMILY = 17;+ + List<uint16> identifiers;+ List<string> text;+ + public NameTable () {+ id = "name";+ text = new List<string> ();+ identifiers = new List<uint16> ();+ }+ + public string get_name (uint16 identifier) {+ int i = 0;+ + foreach (uint16 n in identifiers) {+ if (n == identifier) {+ return text.nth (i).data;+ }+ i++;+ }+ + return "";+ }++ public override void parse (FontData dis) throws Error {+ uint16 format;++ dis.seek (offset);+ + format = dis.read_ushort ();+ + switch (format) {+ case 0:+ parse_format0 (dis);+ break;+ + case 1:+ warning ("name table format 1 is not implemented yet");+ break;+ + default:+ warning (@"unknown format $format in name table");+ break;+ }+ }+ + public void parse_format0 (FontData dis) throws Error {+ uint16 count;+ uint16 storage_offset;+ + List<uint16> strlen = new List<uint16> ();+ List<uint16> off = new List<uint16> ();+ List<uint16> name_id = new List<uint16> ();+ List<uint16> encoding_id = new List<uint16> ();+ List<uint16> platform = new List<uint16> ();+ List<uint16> lang = new List<uint16> ();+ + count = dis.read_ushort ();+ storage_offset = dis.read_ushort ();+ + for (int i = 0; i < count; i++) {+ platform.append (dis.read_ushort ());+ encoding_id.append (dis.read_ushort ());+ lang.append (dis.read_ushort ());+ name_id.append (dis.read_ushort ());+ strlen.append (dis.read_ushort ());+ off.append (dis.read_ushort ());+ + identifiers.append (name_id.last ().data); + }++ int plat;+ StringBuilder str;+ for (int i = 0; i < count; i++) {+ plat = platform.nth (i).data;+ dis.seek (offset + storage_offset + off.nth (i).data);+ str = new StringBuilder ();+ + switch (plat) {+ case 1:+ for (int j = 0; j < strlen.nth (i).data; j++) {+ char c = dis.read_char ();+ str.append_c (c);+ }+ break;+ + case 3:+ for (int j = 0; j < strlen.nth (i).data; j += 2) {+ unichar c;+ char c0 = dis.read_char ();+ char c1 = dis.read_char ();+ + c = c0 << 8;+ c += c1;+ + str.append_unichar (c);+ }+ break;+ + default:+ break;+ } + + text.append (str.str);+ printd (@"Name id: $(name_id.nth (i).data) platform: $(platform.nth (i).data) enc: $(encoding_id.nth (i).data) lang: $(lang.nth (i).data) len: $(strlen.nth (i).data) str: \"$(str.str)\"\n"); + }+ }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();+ Font font = OpenFontFormatWriter.get_current_font ();+ uint16 len = 0;+ string t;+ uint16 p;+ uint16 l;+ uint16 num_records;++ List<uint16> type = new List<uint16> ();+ List<string> text = new List<string> ();+ + text.append ("Copyright");+ type.append (COPYRIGHT_NOTICE);+ + text.append (font.get_name ());+ type.append (FONT_NAME);++ text.append ("Regular");+ type.append (SUBFAMILY_NAME);++ text.append (font.get_name ()); // TODO: validate+ type.append (UNIQUE_IDENTIFIER);++ text.append (font.get_name ());+ type.append (FULL_FONT_NAME);+ + // This does for some reason cause an internal error in ms fontvalidatior utility.+ // Head table can't parse integer from string.+ text.append ("Version 1.0");+ type.append (VERSION); + + text.append ("");+ type.append (DESCRIPTION);+ + text.append (font.get_name ());+ type.append (PREFERED_FAMILY);+ + text.append ("Regular");+ type.append (PREFERED_SUB_FAMILY);+ + num_records = (uint16) text.length ();+ + fd.add_ushort (0); // format 1+ fd.add_ushort (2 * num_records); // nplatforms * nrecords + fd.add_ushort (6 + 12 * 2 * num_records); // string storage offset++ for (int i = 0; i < num_records; i++) {+ t = (!) text.nth (i).data;+ p = (!) type.nth (i).data;+ l = (uint16) t.length;+ + fd.add_ushort (1); // platform+ fd.add_ushort (0); // encoding id+ fd.add_ushort (0); // language+ fd.add_ushort (p); // name id + fd.add_ushort (l); // strlen+ fd.add_ushort (len); // offset from begining of string storage+ len += l; + } ++ for (int i = 0; i < num_records; i++) {+ t = (!) text.nth (i).data;+ p = (!) type.nth (i).data;+ l = (uint16) (2 * t.char_count ());++ fd.add_ushort (3); // platform+ fd.add_ushort (1); // encoding id+ fd.add_ushort (0x0409); // language+ fd.add_ushort (p); // name id + fd.add_ushort (l); // strlen+ fd.add_ushort (len); // offset from begining of string storage+ len += l;+ }++ // platform 1+ foreach (string s in text) {+ fd.add_str (s); + }+ + // platform 3+ foreach (string s in text) {+ fd.add_str_utf16 (s); + }++ fd.pad ();+ + this.font_data = fd;+ }+}++}new file mode 100644@@ -0,0 +1,71 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class OffsetTable : Table {+ DirectoryTable directory_table;+ + public uint16 num_tables = 0;+ uint16 search_range = 0;+ uint16 entry_selector = 0;+ uint16 range_shift = 0;+ + public OffsetTable (DirectoryTable t) {+ id = "Offset table";+ directory_table = t;+ }+ + public override void parse (FontData dis) throws Error {+ Fixed version;+ + dis.seek (offset);+ + version = dis.read_fixed ();+ num_tables = dis.read_ushort ();+ search_range = dis.read_ushort ();+ entry_selector = dis.read_ushort ();+ range_shift = dis.read_ushort ();+ + printd (@"Font file version $(version.get_string ())\n");+ printd (@"Number of tables $num_tables\n"); + }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();+ Fixed version = 0x00010000; // sfnt version 1.0 for TTF CFF else use OTTO++ + num_tables = (uint16) directory_table.get_tables ().length () - 2; // number of tables, skip DirectoryTable and OffsetTable+ + search_range = max_pow_2_less_than_i (num_tables) * 16;+ entry_selector = max_log_2_less_than_i (num_tables);+ range_shift = 16 * num_tables - search_range;++ fd.add_fixed (version); + fd.add_u16 (num_tables);+ fd.add_u16 (search_range);+ fd.add_u16 (entry_selector);+ fd.add_u16 (range_shift);+ + // skip padding for offset table + + this.font_data = fd;+ }+}++}new file mode 100644@@ -0,0 +1,133 @@+/*+ Copyright (C) 2012 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++[CCode (cname = "load_freetype_font")]+public extern static StringBuilder? load_freetype_font (string file, out int error);++[CCode (cname = "validate_freetype_font")]+public extern static bool validate_freetype_font (string file);++namespace BirdFont {++[SimpleType]+[CCode (has_type_id = false)] // Vala bug+internal struct Fixed : uint32 {++ public bool @equals (uint16 upper, uint16 lower) {+ uint32 t = (upper << 16) + lower;+ return (t == this); + }++ public string get_string () {+ uint a = this >> 16;+ return @"$(a).$(this - (a << 16))";+ }+}++[SimpleType]+[CCode (has_type_id = false)] // same as above+internal struct F2Dot14 : uint32 {+}++errordomain BadFormat {+ PARSE+}++class OpenFontFormatReader : Object {+ + public DirectoryTable directory_table;+ + OtfInputStream dis;+ File file;+ + public OpenFontFormatReader () {+ directory_table = new DirectoryTable ();+ }+ + public void close () {+ dis.close ();+ }+ + public Glyph? read_glyph (string name) {+ try {+ return directory_table.glyf_table.read_glyph (name);+ } catch (GLib.Error e) {+ warning (e.message);+ }+ + return null;+ }+ + public void parse_index (string file_name) throws Error {+ file = File.new_for_path (file_name);+ if (!file.query_exists ()) {+ throw new FileError.EXIST(@"OpenFontFormatReader: file does not exist. $((!) file.get_path ())");+ }+ + dis = new OtfInputStream (file.read ());+ + parse_index_tables ();+ }+ + void parse_index_tables () throws Error {+ OffsetTable offset_table;+ FontData fd = new FontData ();+ + FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+ uint32 file_size = (uint32) file_info.get_size ();++ try {+ fd.write_table (dis, 0, file_size);+ } catch (GLib.Error e) {+ warning (@"Failed to read font data. $(e.message)");+ }++ offset_table = new OffsetTable (directory_table);+ offset_table.parse (fd);+ + directory_table = new DirectoryTable ();+ directory_table.set_offset_table (offset_table);+ directory_table.parse (fd, file, this);+ }++ public void set_limits () {+ Font f = OpenFontFormatWriter.font;+ + if (is_null (f)) {+ f = BirdFont.get_current_font ();+ }+ + f.top_position = -get_ascender ();+ f.top_limit = f.top_position - 5;+ f.xheight_position = f.top_position - 5;+ f.bottom_position = -get_descender ();+ f.bottom_limit = f.bottom_position + 5; + }++ public double get_ascender () {+ return directory_table.hhea_table.get_ascender ();+ }+ + public double get_descender () {+ return directory_table.hhea_table.get_descender ();+ }+ + public uint32 get_head_checksum () {+ return directory_table.head_table.get_adjusted_checksum ();+ }+}+}new file mode 100644@@ -0,0 +1,87 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++const double UNITS = 10;++class OpenFontFormatWriter : Object {++ DataOutputStream os;+ DirectoryTable directory_table;+ + public static Font font;+ + public OpenFontFormatWriter () {+ directory_table = new DirectoryTable ();+ }+ + public static Font get_current_font () {+ return font;+ }+ + public void open (File file) throws Error {+ assert (!is_null (file));+ + if (file.query_exists ()) {+ warning ("File exists in export.");+ throw new FileError.EXIST("OpenFontFormatWriter: file exists.");+ }+ + os = new DataOutputStream(file.create (FileCreateFlags.REPLACE_DESTINATION));+ }+ + public void write_ttf_font (Font nfont) throws Error {+ long dl;+ uint8* data;+ unowned List<Table> tables;+ FontData fd;+ uint l;+ + font = nfont;+ + directory_table.process (); + tables = directory_table.get_tables ();++ dl = directory_table.get_font_file_size ();+ + if (dl == 0) {+ warning ("font is of zero size.");+ return;+ }+ + foreach (Table t in tables) {+ fd = t.get_font_data ();+ data = fd.table_data;+ l = fd.length_with_padding ();+ + for (int j = 0; j < l; j++) {+ os.put_byte (data[j]);+ }+ }+ }+ + public void close () throws Error {+ os.close ();+ }+}++void printd (string s) {+ //print (s);+}++}new file mode 100644@@ -0,0 +1,113 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class Os2Table : Table {+ + public Os2Table () {+ id = "OS/2";+ }+ + public override void parse (FontData dis) throws Error {+ + }+ + public void process (GlyfTable glyf_table) {+ FontData fd = new FontData ();+ Font font = OpenFontFormatWriter.get_current_font ();+ + int16 ascender;+ int16 descender;+ + fd.add_u16 (0x0002); // USHORT Version 0x0000, 0x0001, 0x0002, 0x0003, 0x0004++ fd.add_16 (glyf_table.get_average_width ()); // SHORT xAvgCharWidth++ fd.add_u16 (400); // USHORT usWeightClass (400 is normal)+ fd.add_u16 (5); // USHORT usWidthClass (5 is normal)+ fd.add_u16 (0); // USHORT fsType++ fd.add_16 (40); // SHORT ySubscriptXSize+ fd.add_16 (40); // SHORT ySubscriptYSize+ fd.add_16 (40); // SHORT ySubscriptXOffset+ fd.add_16 (40); // SHORT ySubscriptYOffset+ fd.add_16 (40); // SHORT ySuperscriptXSize+ fd.add_16 (40); // SHORT ySuperscriptYSize+ fd.add_16 (40); // SHORT ySuperscriptXOffset+ fd.add_16 (40); // SHORT ySuperscriptYOffset+ fd.add_16 (40); // SHORT yStrikeoutSize+ fd.add_16 (200); // SHORT yStrikeoutPosition+ fd.add_16 (40); // SHORT sFamilyClass++ // FIXME: PANOSE+ fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); + fd.add (0); ++ // FIXME:+ fd.add_u32 (0); // ulUnicodeRange1 Bits 0-31+ fd.add_u32 (0); // ULONG ulUnicodeRange2 Bits 32-63+ fd.add_u32 (0); // ULONG ulUnicodeRange3 Bits 64-95+ fd.add_u32 (0); // ULONG ulUnicodeRange4 Bits 96-127++ fd.add_tag ("----"); // VendID++ fd.add_u16 (0); // USHORT fsSelection+ + fd.add_u16 (glyf_table.get_first_char ()); // USHORT usFirstCharIndex+ fd.add_u16 (glyf_table.get_last_char ()); // USHORT usLastCharIndex++ ascender = (int16) (glyf_table.xmax + font.base_line * UNITS);+ descender = (int16) (-glyf_table.xmin + font.base_line * UNITS);+ + fd.add_16 (ascender); // SHORT sTypoAscender+ fd.add_16 (descender); // SHORT sTypoDescender+ fd.add_16 (3); // SHORT sTypoLineGap++ fd.add_u16 (ascender); // USHORT usWinAscent+ fd.add_u16 (descender); // USHORT usWinDescent++ // FIXA:+ fd.add_u32 (0); // ULONG ulCodePageRange1 Bits 0-31+ fd.add_u32 (0); // ULONG ulCodePageRange2 Bits 32-63++ fd.add_16 (ascender); // SHORT sxHeight version 0x0002 and later+ fd.add_16 (ascender); // SHORT sCapHeight version 0x0002 and later++ fd.add_16 (0); // USHORT usDefaultChar version 0x0002 and later+ fd.add_16 (0x0020); // USHORT usBreakChar version 0x0002 and later, also known as space+ + // FIXA: calculate these values+ fd.add_16 (1); // USHORT usMaxContext version 0x0002 and later++ // padding+ fd.pad ();+ + this.font_data = fd;+ }++}++}new file mode 100644@@ -0,0 +1,51 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++/** Reader for otf data types. */+class OtfInputStream : Object {+ + public FileInputStream fin;+ public DataInputStream din;+ + public OtfInputStream (FileInputStream i) throws Error {+ din = new DataInputStream (i);+ fin = i;+ }+ + public void seek (int64 pos) throws Error+ requires (fin.can_seek ()) {+ int64 p = fin.tell (); + fin.seek (pos - p, SeekType.CUR);+ }++ public uint8 read_byte () throws Error {+ return din.read_byte ();+ }+ + public void close () {+ try {+ fin.close ();+ din.close ();+ } catch (GLib.IOError e) {+ warning (e.message);+ }+ }+}++}new file mode 100644@@ -0,0 +1,1254 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class PostTable : Table {+ + GlyfTable glyf_table;+ + List<uint16> index = new List<uint16> ();+ List<string> names = new List<string> ();++ List<string> available_names = new List<string> ();+ + public PostTable (GlyfTable g) {+ id = "post";+ glyf_table = g;+ }+ + public int get_gid (string name) { // FIXME: do fast lookup+ int i = 0;+ int j = 0;+ foreach (string n in names) {+ if (n == name) { + j = 0;+ foreach (uint16 k in index) {+ if (k == i) {+ return j;+ }+ j++;+ }+ + return i;+ }+ i++;+ }+ return -1;+ }++ public string get_name (int gid) {+ int k;+ + if (!(0 <= gid < index.length ())) {+ warning ("gid is out of range.");+ return "";+ }+ + k = (!) index.nth (gid).data;+ + if (gid != 0 && k == 0) {+ warning (@"Glyph $gid is assigned to name .notdef, only gid 0 can be .notdef character.");+ return "";+ }+ + if (!(0 <= k < names.length ())) {+ warning ("k is out of range.");+ return "";+ }+ + return (!) names.nth (k).data;+ }+ + public override void parse (FontData dis) throws Error {+ dis.seek (offset);+ + Fixed format = dis.read_fixed ();+ Fixed italic = dis.read_fixed ();+ + int16 underlie_pos = dis.read_short ();+ int16 underlie_thickness = dis.read_short ();+ uint32 is_fixed_pitch = dis.read_ulong ();+ + uint32 mem_min42 = dis.read_ulong ();+ uint32 mem_max42 = dis.read_ulong ();+ uint32 mem_min1 = dis.read_ulong ();+ uint32 mem_max1 = dis.read_ulong ();+ + uint16 nnames = dis.read_ushort ();+ + if (format != 0x00020000) {+ warning ("Only post tables of version 2 will parset got $(format.get_string ())");+ return;+ }+ + printd (@"format: $(format.get_string ())\n");+ printd (@"italic: $(italic.get_string ())\n");+ printd (@"underlie_pos: $(underlie_pos)\n");+ printd (@"underlie_thickness: $(underlie_thickness)\n");+ printd (@"is_fixed_pitch: $(is_fixed_pitch)\n");+ printd (@"mem_min42: $(mem_min42)\n");+ printd (@"mem_max42: $(mem_max42)\n");+ printd (@"mem_min1: $(mem_min1)\n");+ printd (@"mem_max1: $(mem_max1)\n");+ printd (@"\n");+ + printd (@"Num names: $(nnames)\n");+ + uint16 k;+ int non_standard_names = 0;+ for (uint16 i = 0; i < nnames; i++) {+ k = dis.read_ushort ();+ index.append (k);+ + if (k >= 258) {+ non_standard_names++;+ }+ }+ + add_standard_names ();+ + // read non standard names+ for (int i = 0; i < non_standard_names; i++) {+ uint8 len = dis.read_byte ();+ StringBuilder name = new StringBuilder ();+ + for (int j = 0; j < len; j++) {+ name.append_c (dis.read_char ());+ }++ names.append (name.str);+ }++ populate_available ();+ }++ void populate_available () {+ for (int i = 0; i < index.length (); i++) {+ available_names.append (get_name (i));+ }+ }+ + // the Macintosh standard order+ private void add_standard_names () {+ names.append (".notdef");+ names.append (".null");+ names.append ("nonmarkingreturn");+ names.append ("space");+ names.append ("exclam");+ names.append ("quotedbl");+ names.append ("numbersign");+ names.append ("dollar");+ names.append ("percent");+ names.append ("ampersand");+ names.append ("quotesingle");+ names.append ("parenleft");+ names.append ("parenright");+ names.append ("asterisk");+ names.append ("plus");+ names.append ("comma");+ names.append ("hyphen");+ names.append ("period");+ names.append ("slash");+ names.append ("zero");+ names.append ("one");+ names.append ("two");+ names.append ("three");+ names.append ("four");+ names.append ("five");+ names.append ("six");+ names.append ("seven");+ names.append ("eight");+ names.append ("nine");+ names.append ("colon");+ names.append ("semicolon");+ names.append ("less");+ names.append ("equal");+ names.append ("greater");+ names.append ("question");+ names.append ("at");+ names.append ("A");+ names.append ("B");+ names.append ("C");+ names.append ("D");+ names.append ("E");+ names.append ("F");+ names.append ("G");+ names.append ("H");+ names.append ("I");+ names.append ("J");+ names.append ("K");+ names.append ("L");+ names.append ("M");+ names.append ("N");+ names.append ("O");+ names.append ("P");+ names.append ("Q");+ names.append ("R");+ names.append ("S");+ names.append ("T");+ names.append ("U");+ names.append ("V");+ names.append ("W");+ names.append ("X");+ names.append ("Y");+ names.append ("Z");+ names.append ("bracketleft");+ names.append ("backslash");+ names.append ("bracketright");+ names.append ("asciicircum");+ names.append ("underscore");+ names.append ("grave");+ names.append ("a");+ names.append ("b");+ names.append ("c");+ names.append ("d");+ names.append ("e");+ names.append ("f");+ names.append ("g");+ names.append ("h");+ names.append ("i");+ names.append ("j");+ names.append ("k");+ names.append ("l");+ names.append ("m");+ names.append ("n");+ names.append ("o");+ names.append ("p");+ names.append ("q");+ names.append ("r");+ names.append ("s");+ names.append ("t");+ names.append ("u");+ names.append ("v");+ names.append ("w");+ names.append ("x");+ names.append ("y");+ names.append ("z");+ names.append ("braceleft");+ names.append ("bar");+ names.append ("braceright");+ names.append ("asciitilde");+ names.append ("Adieresis");+ names.append ("Aring");+ names.append ("Ccedilla");+ names.append ("Eacute");+ names.append ("Ntilde");+ names.append ("Odieresis");+ names.append ("Udieresis");+ names.append ("aacute");+ names.append ("agrave");+ names.append ("acircumflex");+ names.append ("adieresis");+ names.append ("atilde");+ names.append ("aring");+ names.append ("ccedilla");+ names.append ("eacute");+ names.append ("egrave");+ names.append ("ecircumflex");+ names.append ("edieresis");+ names.append ("iacute");+ names.append ("igrave");+ names.append ("icircumflex");+ names.append ("idieresis");+ names.append ("ntilde");+ names.append ("oacute");+ names.append ("ograve");+ names.append ("ocircumflex");+ names.append ("odieresis");+ names.append ("otilde");+ names.append ("uacute");+ names.append ("ugrave");+ names.append ("ucircumflex");+ names.append ("udieresis");+ names.append ("dagger");+ names.append ("degree");+ names.append ("cent");+ names.append ("sterling");+ names.append ("section");+ names.append ("bullet");+ names.append ("paragraph");+ names.append ("germandbls");+ names.append ("registered");+ names.append ("copyright");+ names.append ("trademark");+ names.append ("acute");+ names.append ("dieresis");+ names.append ("notequal");+ names.append ("AE");+ names.append ("Oslash");+ names.append ("infinity");+ names.append ("plusminus");+ names.append ("lessequal");+ names.append ("greaterequal");+ names.append ("yen");+ names.append ("mu");+ names.append ("partialdiff");+ names.append ("summation");+ names.append ("product");+ names.append ("pi");+ names.append ("integral");+ names.append ("ordfeminine");+ names.append ("ordmasculine");+ names.append ("Omega");+ names.append ("ae");+ names.append ("oslash");+ names.append ("questiondown");+ names.append ("exclamdown");+ names.append ("logicalnot");+ names.append ("radical");+ names.append ("florin");+ names.append ("approxequal");+ names.append ("Delta");+ names.append ("guillemotleft");+ names.append ("guillemotright");+ names.append ("ellipsis");+ names.append ("nonbreakingspace");+ names.append ("Agrave");+ names.append ("Atilde");+ names.append ("Otilde");+ names.append ("OE");+ names.append ("oe");+ names.append ("endash");+ names.append ("emdash");+ names.append ("quotedblleft");+ names.append ("quotedblright");+ names.append ("quoteleft");+ names.append ("quoteright");+ names.append ("divide");+ names.append ("lozenge");+ names.append ("ydieresis");+ names.append ("Ydieresis");+ names.append ("fraction");+ names.append ("currency");+ names.append ("guilsinglleft");+ names.append ("guilsinglright");+ names.append ("fi");+ names.append ("fl");+ names.append ("daggerdbl");+ names.append ("periodcentered");+ names.append ("quotesinglbase");+ names.append ("quotedblbase");+ names.append ("perthousand");+ names.append ("Acircumflex");+ names.append ("Ecircumflex");+ names.append ("Aacute");+ names.append ("Edieresis");+ names.append ("Egrave");+ names.append ("Iacute");+ names.append ("Icircumflex");+ names.append ("Idieresis");+ names.append ("Igrave");+ names.append ("Oacute");+ names.append ("Ocircumflex");+ names.append ("apple");+ names.append ("Ograve");+ names.append ("Uacute");+ names.append ("Ucircumflex");+ names.append ("Ugrave");+ names.append ("dotlessi");+ names.append ("circumflex");+ names.append ("tilde");+ names.append ("macron");+ names.append ("breve");+ names.append ("dotaccent");+ names.append ("ring");+ names.append ("cedilla");+ names.append ("hungarumlaut");+ names.append ("ogonek");+ names.append ("caron");+ names.append ("Lslash");+ names.append ("lslash");+ names.append ("Scaron");+ names.append ("scaron");+ names.append ("Zcaron");+ names.append ("zcaron");+ names.append ("brokenbar");+ names.append ("Eth");+ names.append ("eth");+ names.append ("Yacute");+ names.append ("yacute");+ names.append ("Thorn");+ names.append ("thorn");+ names.append ("minus");+ names.append ("multiply");+ names.append ("onesuperior");+ names.append ("twosuperior");+ names.append ("threesuperior");+ names.append ("onehalf");+ names.append ("onequarter");+ names.append ("threequarters");+ names.append ("franc");+ names.append ("Gbreve");+ names.append ("gbreve");+ names.append ("Idotaccent");+ names.append ("Scedilla");+ names.append ("scedilla");+ names.append ("Cacute");+ names.append ("cacute");+ names.append ("Ccaron");+ names.append ("ccaron");+ names.append ("dcroat");+ }+ + // mapping with char code to standard order+ int get_standard_index (unichar c) {+ switch (c) {+ // entry 0 is the .notdef+ + case '\0':+ return 1;++ case '\r':+ return 2;++ case ' ': // space+ return 3;++ case '!':+ return 4;++ case '"':+ return 5;++ case '#':+ return 6;++ case '$':+ return 7;++ case '%':+ return 8;++ case '&':+ return 9;++ case '\'':+ return 10;++ case '(':+ return 11;++ case ')':+ return 12;++ case '*':+ return 13;++ case '+':+ return 14;++ case ',':+ return 15;++ case '-':+ return 16;++ case '.':+ return 17;++ case '/':+ return 18;++ case '0':+ return 19;++ case '1':+ return 20;++ case '2':+ return 21;++ case '3':+ return 22;++ case '4':+ return 23;++ case '5':+ return 24;++ case '6':+ return 25;++ case '7':+ return 26;++ case '8':+ return 27;++ case '9':+ return 28;++ case ':':+ return 29;++ case ';':+ return 30;++ case '<':+ return 31;++ case '=':+ return 32;++ case '>':+ return 33;++ case '?':+ return 34;++ case '@':+ return 35;++ case 'A':+ return 36;++ case 'B':+ return 37;++ case 'C':+ return 38;++ case 'D':+ return 39;++ case 'E':+ return 40;++ case 'F':+ return 41;++ case 'G':+ return 42;++ case 'H':+ return 43;++ case 'I':+ return 44;++ case 'J':+ return 45;++ case 'K':+ return 46;++ case 'L':+ return 47;++ case 'M':+ return 48;++ case 'N':+ return 49;++ case 'O':+ return 50;++ case 'P':+ return 51;++ case 'Q':+ return 52;++ case 'R':+ return 53;++ case 'S':+ return 54;++ case 'T':+ return 55;++ case 'U':+ return 56;++ case 'V':+ return 57;++ case 'W':+ return 58;++ case 'X':+ return 59;++ case 'Y':+ return 60;++ case 'Z':+ return 61;++ case '[':+ return 62;++ case '\\':+ return 63;++ case ']':+ return 64;++ case '^':+ return 65;++ case '_':+ return 66;++ case '`':+ return 67;++ case 'a':+ return 68;++ case 'b':+ return 69;++ case 'c':+ return 70;++ case 'd':+ return 71;++ case 'e':+ return 72;++ case 'f':+ return 73;++ case 'g':+ return 74;++ case 'h':+ return 75;++ case 'i':+ return 76;++ case 'j':+ return 77;++ case 'k':+ return 78;++ case 'l':+ return 79;++ case 'm':+ return 80;++ case 'n':+ return 81;++ case 'o':+ return 82;++ case 'p':+ return 83;++ case 'q':+ return 84;++ case 'r':+ return 85;++ case 's':+ return 86;++ case 't':+ return 87;++ case 'u':+ return 88;++ case 'v':+ return 89;++ case 'w':+ return 90;++ case 'x':+ return 91;++ case 'y':+ return 92;++ case 'z':+ return 93;++ case '{':+ return 94;++ case '|':+ return 95;++ case '}':+ return 96;++ case '~':+ return 97;++ case 'ร':+ return 98;++ case 'ร ':+ return 99;++ case 'ร':+ return 100;++ case 'ร':+ return 101;++ case 'ร':+ return 102;++ case 'ร':+ return 103;++ case 'ร':+ return 104;++ case 'รก':+ return 105;++ case 'ร ':+ return 106;++ case 'รข':+ return 107;++ case 'รค':+ return 108;++ case 'รฃ':+ return 109;++ case 'รฅ':+ return 110;++ case 'รง':+ return 111;++ case 'รฉ':+ return 112;++ case 'รจ':+ return 113;++ case 'รช':+ return 114;++ case 'รซ':+ return 115;++ case 'รญ':+ return 116;++ case 'รฌ':+ return 117;++ case 'รฎ':+ return 118;++ case 'รฏ':+ return 119;++ case 'รฑ':+ return 120;++ case 'รณ':+ return 121;++ case 'รฒ':+ return 122;++ case 'รด':+ return 123;++ case 'รถ':+ return 124;++ case 'รต':+ return 125;++ case 'รบ':+ return 126;++ case 'รน':+ return 127;++ case 'รป':+ return 128;++ case 'รผ':+ return 129;++ case 'โ ':+ return 130;++ case 'ยฐ':+ return 131;++ case 'ยข':+ return 132;++ case 'ยฃ':+ return 133;++ case 'ยง':+ return 134;++ case 'โข':+ return 135;++ case 'ยถ':+ return 136;++ case 'ร':+ return 137;++ case 'ยฎ':+ return 138;++ case 'ยฉ':+ return 139;++ case 'โข':+ return 140;++ case 'ยด':+ return 141;++ case 'ยจ':+ return 142;++ case 'โ ':+ return 143;++ case 'ร':+ return 144;++ case 'ร':+ return 145;++ case 'โ':+ return 146;++ case 'ยฑ':+ return 147;++ case 'โค':+ return 148;++ case 'โฅ':+ return 149;++ case 'ยฅ':+ return 150;++ case 'ยต':+ return 151;++ case 'โ':+ return 152;++ case 'โ':+ return 153;++ case 'โ':+ return 154;++ case 'ฯ':+ return 155;++ case 'โซ':+ return 156;++ case 'ยช':+ return 157;++ case 'ยบ':+ return 158;++ case 'โฆ':+ return 159;++ case 'รฆ':+ return 160;++ case 'รธ':+ return 161;++ case 'ยฟ':+ return 162;++ case 'ยก':+ return 163;++ case 'ยฌ':+ return 164;++ case 'โ':+ return 165;++ case 'ฦ':+ return 166;++ case 'โ':+ return 167;++ case 'โ':+ return 168;++ case 'ยซ':+ return 169;++ case 'ยป':+ return 170;++ case 'โฆ':+ return 171;++ case 'ย ': // non breaking space+ return 172;+ + case 'ร':+ return 173;++ case 'ร':+ return 174;++ case 'ร':+ return 175;++ case 'ล':+ return 176;++ case 'ล':+ return 177;++ case 'โ':+ return 178;++ case 'โ':+ return 179;++ case 'โ':+ return 180;++ case 'โ':+ return 181;++ case 'โ':+ return 182;++ case 'โ':+ return 183;++ case 'รท':+ return 184;++ case 'โ':+ return 185;++ case 'รฟ':+ return 186;++ case 'ลธ':+ return 187;++ case 'โ':+ return 188;++ case 'ยค':+ return 189;++ case 'โน':+ return 190;++ case 'โบ':+ return 191;++ case '๏ฌ':+ return 192;++ case '๏ฌ':+ return 193;++ case 'โก':+ return 194;++ case 'ยท':+ return 195;++ case 'โ':+ return 196;++ case 'โ':+ return 197;++ case 'โฐ':+ return 198;++ case 'ร':+ return 199;++ case 'ร':+ return 200;++ case 'ร':+ return 201;++ case 'ร':+ return 202;++ case 'ร':+ return 203;++ case 'ร':+ return 204;++ case 'ร':+ return 205;++ case 'ร':+ return 206;++ case 'ร':+ return 207;++ case 'ร':+ return 208;++ case 'ร':+ return 209;+ + // Machintosh apple goes here+ // return 210;++ case 'ร':+ return 211;++ case 'ร':+ return 212;++ case 'ร':+ return 213;++ case 'ร':+ return 214;++ case 'ฤฑ':+ return 215;++ case 'ห':+ return 216;++ case 'ห':+ return 217;++ case 'ยฏ':+ return 218;++ case 'ห':+ return 219;++ case 'ห':+ return 220;++ case 'ห':+ return 221;++ case 'ยธ':+ return 222;++ case 'ห':+ return 223;++ case 'ห':+ return 224;++ case 'ห':+ return 225;++ case 'ล':+ return 226;++ case 'ล':+ return 227;++ case 'ล ':+ return 228;++ case 'ลก':+ return 229;++ case 'ลฝ':+ return 230;++ case 'ลพ':+ return 231;++ case 'ยฆ':+ return 232;++ case 'ร':+ return 233;++ case 'รฐ':+ return 234;++ case 'ร':+ return 235;++ case 'รฝ':+ return 236;++ case 'ร':+ return 237;++ case 'รพ':+ return 238;++ case 'โ':+ return 239;++ case 'ร':+ return 240;++ case 'ยน':+ return 241;+ + case 'ยฒ':+ return 242;++ case 'ยณ':+ return 243;++ case 'ยฝ':+ return 244;++ case 'ยผ':+ return 245;++ case 'ยพ':+ return 246;++ case 'โฃ':+ return 247;++ case 'ฤ':+ return 248;++ case 'ฤ':+ return 249;++ case 'ฤฐ':+ return 250;++ case 'ล':+ return 251;++ case 'ล':+ return 252;++ case 'ฤ':+ return 253;++ case 'ฤ':+ return 254;++ case 'ฤ':+ return 255;++ case 'ฤ':+ return 256;++ case 'ฤ':+ return 257;+ }+ + return 0;+ }+ + public void process () throws Error {+ FontData fd = new FontData ();+ string n;+ + fd.add_fixed (0x00020000); // Version+ fd.add_fixed (0x00000000); // italicAngle+ + fd.add_short (-2); // underlinePosition+ fd.add_short (1); // underlineThickness++ fd.add_ulong (0); // non zero for monospaced font+ + // mem boundries may be omitted+ fd.add_ulong (0); // min mem for type 42+ fd.add_ulong (0); // max mem for type 42+ + fd.add_ulong (0); // min mem for Type1+ fd.add_ulong (0); // max mem for Type1++ fd.add_ushort ((uint16) glyf_table.glyphs.length ());++ // this part of the spec is so weird+ + fd.add_ushort ((uint16) 0); // first index is .notdef+ index.append (0);+ + assert (names.length () == 0);+ add_standard_names ();++ int index;+ Glyph g;+ for (int i = 1; i < glyf_table.glyphs.length (); i++) {+ g = (!) glyf_table.glyphs.nth (i).data;+ index = get_standard_index (g.unichar_code);+ + if (index != 0) {+ fd.add_ushort ((uint16) index); // use standard name+ } else {+ printd (@"Adding non standard postscript name $(g.get_name ())\n");+ + index = (int) names.length (); // use font specific name+ fd.add_ushort ((uint16) index);+ names.append (g.get_name ());+ }+ + this.index.append ((uint16) index);+ }++ for (int i = 258; i < names.length (); i++) {+ n = (!) names.nth (i).data;+ + if (n.length > 0xFF) {+ warning (@"too long name for glyph $n");+ }+ + fd.add ((uint8) n.length); // length of string+ fd.add_str (n);+ } ++ fd.pad ();+ + this.font_data = fd;+ }++}++}new file mode 100644@@ -0,0 +1,115 @@+/*+ Copyright (C) 2012, 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++namespace BirdFont {++class Table : Object {++ public string id = "NO_ID";++ public uint32 checksum = 0;+ public uint32 offset = 0;+ public uint32 length = 0;++ protected FontData? font_data = null;++ public virtual string get_id () {+ return id;+ }+ + public FontData get_font_data () {+ if (font_data == null) {+ warning (@"No font data for $(id).");+ font_data = new FontData ();+ }+ + return (!) font_data;+ }++ public bool has_data () {+ return length > 0;+ }++ public virtual void parse (FontData dis) throws GLib.Error {+ warning (@"Parse is not implemented for $(id).");+ }++ /** Validate table checksum. */+ public bool validate (FontData dis) {+ bool valid;+ + if (length == 0) {+ stderr.printf (@"Table $id is of zero length.\n"); + valid = false;+ } else {+ valid = Table.validate_table (dis, checksum, offset, length, id);+ }+ + if (!valid) {+ stderr.printf (@"Table $id is invalid.\n");+ }+ + return valid;+ }++ public static bool validate_table (FontData dis, uint32 checksum, uint32 offset, uint32 length, string name) {+ uint32 ch = calculate_checksum (dis, offset, length, name);+ bool c;+ + c = (ch == checksum);+ + if (!c) {+ stderr.printf(@"Checksum does not match data for $(name).\n");+ stderr.printf(@"name: $name, checksum: $checksum, offset: $offset, length: $length\n");+ stderr.printf(@"calculated checksum $(ch)\n");+ }+ + return c; + }+ + public static uint32 calculate_checksum (FontData dis, uint32 offset, uint32 length, string name) {+ uint32 checksum = 0;+ uint32 l;+ + dis.seek (offset);++ l = (length % 4 > 0) ? length / 4 + 1 : length / 4; ++ for (uint32 i = 0; i < l; i++) {+ checksum += dis.read_ulong ();+ }+ + return checksum;+ }+ + public static uint16 max_pow_2_less_than_i (uint16 ind) {+ uint16 last = 0;+ uint16 i = 1;+ + while ((i <<= 1) < ind) {+ last = i;+ }+ + return last;+ }++ public static uint16 max_log_2_less_than_i (uint16 ind) {+ return (uint16) (Math.log (ind) / Math.log (2));+ }+}++}deleted file mode 100644@@ -1,133 +0,0 @@-/*- Copyright (C) 2012 Johan Mattsson-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.-*/--[CCode (cname = "load_freetype_font")]-public extern static StringBuilder? load_freetype_font (string file, out int error);--[CCode (cname = "validate_freetype_font")]-public extern static bool validate_freetype_font (string file);--namespace BirdFont {--[SimpleType]-[CCode (has_type_id = false)] // Vala bug-internal struct Fixed : uint32 {-- public bool @equals (uint16 upper, uint16 lower) {- uint32 t = (upper << 16) + lower;- return (t == this); - }-- public string get_string () {- uint a = this >> 16;- return @"$(a).$(this - (a << 16))";- }-}--[SimpleType]-[CCode (has_type_id = false)] // same as above-internal struct F2Dot14 : uint32 {-}--errordomain BadFormat {- PARSE-}--class OpenFontFormatReader : Object {- - public DirectoryTable directory_table;- - OtfInputStream dis;- File file;- - public OpenFontFormatReader () {- directory_table = new DirectoryTable ();- }- - public void close () {- dis.close ();- }- - public Glyph? read_glyph (string name) {- try {- return directory_table.glyf_table.read_glyph (name);- } catch (GLib.Error e) {- warning (e.message);- }- - return null;- }- - public void parse_index (string file_name) throws Error {- file = File.new_for_path (file_name);- if (!file.query_exists ()) {- throw new FileError.EXIST(@"OpenFontFormatReader: file does not exist. $((!) file.get_path ())");- }- - dis = new OtfInputStream (file.read ());- - parse_index_tables ();- }- - void parse_index_tables () throws Error {- OffsetTable offset_table;- FontData fd = new FontData ();- - FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);- uint32 file_size = (uint32) file_info.get_size ();-- try {- fd.write_table (dis, 0, file_size);- } catch (GLib.Error e) {- warning (@"Failed to read font data. $(e.message)");- }-- offset_table = new OffsetTable (directory_table);- offset_table.parse (fd);- - directory_table = new DirectoryTable ();- directory_table.set_offset_table (offset_table);- directory_table.parse (fd, file, this);- }-- public void set_limits () {- Font f = OpenFontFormatWriter.font;- - if (is_null (f)) {- f = BirdFont.get_current_font ();- }- - f.top_position = -get_ascender ();- f.top_limit = f.top_position - 5;- f.xheight_position = f.top_position - 5;- f.bottom_position = -get_descender ();- f.bottom_limit = f.bottom_position + 5; - }-- public double get_ascender () {- return directory_table.hhea_table.get_ascender ();- }- - public double get_descender () {- return directory_table.hhea_table.get_descender ();- }- - public uint32 get_head_checksum () {- return directory_table.head_table.get_adjusted_checksum ();- }-}-}deleted file mode 100644@@ -1,5201 +0,0 @@-/* - Copyright (C) 2012, 2013 Johan Mattsson - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -using Math; - -namespace BirdFont { - -const double UNITS = 10; - -class OpenFontFormatWriter : Object { - - DataOutputStream os; - DirectoryTable directory_table; - - public static Font font; - - public OpenFontFormatWriter () { - directory_table = new DirectoryTable (); - } - - public static Font get_current_font () { - return font; - } - - public void open (File file) throws Error { - assert (!is_null (file)); - - if (file.query_exists ()) { - warning ("File exists in export."); - throw new FileError.EXIST("OpenFontFormatWriter: file exists."); - } - - os = new DataOutputStream(file.create (FileCreateFlags.REPLACE_DESTINATION)); - } - - public void write_ttf_font (Font nfont) throws Error { - long dl; - uint8* data; - unowned List<Table> tables; - FontData fd; - uint l; - - font = nfont; - - directory_table.process (); - tables = directory_table.get_tables (); - - dl = directory_table.get_font_file_size (); - - if (dl == 0) { - warning ("font is of zero size."); - return; - } - - foreach (Table t in tables) { - fd = t.get_font_data (); - data = fd.table_data; - l = fd.length_with_padding (); - - for (int j = 0; j < l; j++) { - os.put_byte (data[j]); - } - } - } - - public void close () throws Error { - os.close (); - } -} - -/** Reader for otf data types. */ -class OtfInputStream : Object { - - public FileInputStream fin; - public DataInputStream din; - - public OtfInputStream (FileInputStream i) throws Error { - din = new DataInputStream (i); - fin = i; - } - - public void seek (int64 pos) throws Error - requires (fin.can_seek ()) { - int64 p = fin.tell (); - fin.seek (pos - p, SeekType.CUR); - } - - public uint8 read_byte () throws Error { - return din.read_byte (); - } - - public void close () { - try { - fin.close (); - din.close (); - } catch (GLib.IOError e) { - warning (e.message); - } - } -} - -class FontData : Object { - - // Read pointer - uint rp = 0; - - // Write pointer - uint wp = 0; - - // length without padding - uint32 len = 0; - uint32 padding = 0; - - uint32 capacity; - - public uint8* table_data = null; - - public FontData (uint32 size = 1024) { - capacity = size; - table_data = malloc (size); - } - - ~FontData () { - if (table_data != null) { - delete table_data; - table_data = null; - } - } - - public void write_at (uint pos, uint8 new_data) throws GLib.Error - requires (pos <= capacity) - { - if (unlikely (pos >= len)) { - warning ("end of table reached"); - assert (false); - } - - table_data[pos]= new_data; - } - - public void write_table (OtfInputStream dis, uint32 offset, uint32 length) throws Error { - uint32 l = length + (length % 4); // padding after end of table - uint8 b; - - if (length >= l) { - expand (l); - } - - if (table_data == null) { - warning ("Failed to allocate memory for ttf data."); - return; - } - - seek (0); - dis.seek (offset); - - wp = 0; - while (wp < l) { - b = dis.read_byte (); - add (b); - } - - rp = 0; - } - - public uint length_with_padding () { - return len; - } - - public uint length () { - return len - padding; - } - - public void pad () { - while (len % 4 != 0) { - add (0); - padding++; - } - } - - /** Add additional checksum data to this checksum. */ - public void continous_check_sum (ref uint32 current_check_sum) { - uint trp = rp; - uint l; - - if (length_with_padding () % 4 != 0) { - warning ("Table is not padded to correct size."); - } - - seek (0); - - l = (length () % 4 > 0) ? length () / 4 + 1 : length () / 4; - - for (uint32 i = 0; i < l; i++) { - current_check_sum += read_uint32 (); - } - - rp = trp; - - return; - } - - public uint32 check_sum () { - uint32 sum = 0; - continous_check_sum (ref sum); - return sum; - } - - public void seek_end () { - seek (len); - } - - public void seek (uint i) { - rp = i; - wp = i; - } - - public void seek_relative (uint i) { - rp += i; - wp += i; - } - - /** Returns true if next byte is a CFF operator */ - public bool next_is_operator () { - uint8 o = read (); - seek_relative (-1); - return (0 <= o <=21); - } - - public uint8 read () { - if (unlikely (rp >= len)) { - warning ("end of table reached"); - return 0; - } - - return table_data [rp++]; - } - - public Fixed read_fixed () { - return (Fixed) read_uint32 (); - } - - public uint32 read_ulong () { - return read_uint32 (); - } - - public uint16 read_ushort () { - uint16 f; - f = read () << 8; - f += read (); - return f; - } - - public int16 read_int16 () { - int16 f; - f = read () << 8; - f += read (); - return f; - } - - public int16 read_short () throws Error { - return read_int16 (); - } - - public uint32 read_uint32 () { - uint32 f; - f = read () << 8 * 3; - f += read () << 8 * 2; - f += read () << 8 * 1; - f += read () << 8 * 0; - return f; - } - - public uint64 read_uint64 () { - uint64 f; - f = (uint64) read () << 8 * 7; - f += (uint64) read () << 8 * 6; - f += (uint64) read () << 8 * 5; - f += (uint64) read () << 8 * 4; - f += (uint64) read () << 8 * 3; - f += (uint64) read () << 8 * 2; - f += (uint64) read () << 8 * 1; - f += (uint64) read () << 8 * 0; - return f; - } - - public F2Dot14 read_f2dot14 () throws Error { - F2Dot14 f = (F2Dot14) read_int16 (); - return f; - } - - public uint64 read_udate () throws Error { - return read_uint64 (); - } - - public uint8 read_byte () throws Error { - return read (); - } - - public char read_char () throws Error { - return (char) read_byte (); - } - - public string read_string (uint length) throws Error { - StringBuilder str = new StringBuilder (); - char c; - for (int i = 0; i < length; i++) { - c = read_char (); - str.append_c (c); - } - return str.str; - } - - public int read_charstring_value () throws Error { - uint8 a, b; - a = read (); - - if (32 <= a <= 246) { - return a - 139; - } - - b = read (); - - if (247 <= a <= 250) { - return (a - 247) * 256 + b + 108; - } - - if (251 <= a <= 254) { - return -((a - 251) * 256) - b - 108; - } - - if (a == 255) { - // Implement it - warning ("fractions not implemented yet."); - } - - stderr.printf (@"unexpected value: $a\n"); - warn_if_reached (); - return 0; - } - - public void add_fixed (Fixed f) throws Error { - add_u32 (f); - } - - public void add_short (int16 d) throws Error { - add_16 (d); - } - - public void add_ushort (uint16 d) throws Error { - add_u16 (d); - } - - public void add_ulong (uint32 d) throws Error { - add_u32 (d); - } - - public void add_byte (uint8 b) throws Error { - add (b); - } - - private void expand (uint extra_bytes = 1024) { - capacity += extra_bytes; - table_data = (uint8*) try_realloc (table_data, capacity); - - if (table_data == null) { - warning ("Out of memory."); - capacity = 0; - } - } - - public void add (uint8 d) { - if (unlikely (len == capacity)) { - expand (); - } - - table_data[wp] = d; - - if (wp == len) { - len++; - } - - wp++; - } - - public void add_littleendian_u16 (uint32 i) { - add ((int8) (i & 0x00FF)); - add ((int8) ((i & 0xFF00) >> 8)); - } - - public void add_u16 (uint16 d) { - uint16 n = d >> 8; - add ((uint8)n); - add ((uint8)(d - (n << 8))); - } - - public void add_16 (int16 i) { - uint8 s = (uint8) (i >> 8); - - add ((uint8) s); - add ((uint8) (i - (s << 8))); - } - - public void add_littleendian_u32 (uint32 i) { - add ((int8) (i & 0x000000FF)); - add ((int8) ((i & 0x0000FF00) >> 8)); - add ((int8) ((i & 0x00FF0000) >> 16)); - add ((int8) ((i & 0xFF000000) >> 24)); - } - - public void add_u32 (uint32 i) { - uint32 s = (uint16) (i >> 16); - - add_u16 ((uint16) s); - add_u16 ((uint16) (i - (s << 16))); - } - - public void add_64(int64 i) { - int64 s = (int32) (i >> 32); - - add_u32 ((int32) s); - add_u32 ((int32)(i - (s << 32))); - } - - public void add_str_littleendian_utf16 (string s) { - add_str_utf16 (s, true); - } - - public void add_str_utf16 (string s, bool little_endian = false) { - int index = 0; - unichar c; - uint8 c0; - uint8 c1; - int l = 0; - - while (s.get_next_char (ref index, out c)) { - c0 = (uint8) (c >> 8); - c1 = (uint8) (c - (c0 << 8)); - - if (little_endian) { - add (c1); - add (c0); - } else { - add (c0); - add (c1); - } - - l += 2; - } - - assert (l == 2 * s.char_count ()); - } - - public void add_str (string s) { - uint8[] data = s.data; - for (int n = 0; n < data.length; n++) { - add (data[n]); - } - } - - public void add_tag (string s) - requires (s.length == 4 && s.data.length == 4) { - add_str (s); - } - - public void add_charstring_value (int v) throws Error { - int w; - uint8 t; - - if (unlikely (!(-1131 <= v <= 1131))) { - warning ("charstring value out of range"); - v = 0; - } - - if (-107 <= v <= 107) { - add_byte ((uint8) (v + 139)); - } else if (107 < v <= 1131) { - // positive value - w = v; - v -= 108; - - t = 0; - while (v >= 256) { - v -= 256; - t++; - } - - add_byte (t + 247); - add_byte ((uint8) (w - 108 - (t * 256))); - } else if (-1131 <= v < -107) { - // negative value - v = -v - 108; - add_byte ((uint8) ((v >> 8) + 251)); - add_byte ((uint8) (v & 0xFF)); - } else { - // Todo add fraction - } - } -} - -class Coordinate { - /** TTF coordinate flags. */ - - public static const uint8 NONE = 0; - public static const uint8 ON_PATH = 1 << 0; - public static const uint8 X_SHORT_VECTOR = 1 << 1; - public static const uint8 Y_SHORT_VECTOR = 1 << 2; - public static const uint8 REPEAT = 1 << 3; - - // same flag or short vector sign flag - public static const uint8 X_IS_SAME = 1 << 4; - public static const uint8 Y_IS_SAME = 1 << 5; - public static const uint8 X_SHORT_VECTOR_POSITIVE = 1 << 4; - public static const uint8 Y_SHORT_VECTOR_POSITIVE = 1 << 5; - -} - -class Table : Object { - - public string id = "NO_ID"; - - public uint32 checksum = 0; - public uint32 offset = 0; - public uint32 length = 0; - - protected FontData? font_data = null; - - public virtual string get_id () { - return id; - } - - public FontData get_font_data () { - if (font_data == null) { - warning (@"No font data for $(id)."); - font_data = new FontData (); - } - - return (!) font_data; - } - - public bool has_data () { - return length > 0; - } - - public virtual void parse (FontData dis) throws GLib.Error { - warning (@"Parse is not implemented for $(id)."); - } - - /** Validate table checksum. */ - public bool validate (FontData dis) { - bool valid; - - if (length == 0) { - stderr.printf (@"Table $id is of zero length.\n"); - valid = false; - } else { - valid = Table.validate_table (dis, checksum, offset, length, id); - } - - if (!valid) { - stderr.printf (@"Table $id is invalid.\n"); - } - - return valid; - } - - public static bool validate_table (FontData dis, uint32 checksum, uint32 offset, uint32 length, string name) { - uint32 ch = calculate_checksum (dis, offset, length, name); - bool c; - - c = (ch == checksum); - - if (!c) { - stderr.printf(@"Checksum does not match data for $(name).\n"); - stderr.printf(@"name: $name, checksum: $checksum, offset: $offset, length: $length\n"); - stderr.printf(@"calculated checksum $(ch)\n"); - } - - return c; - } - - public static uint32 calculate_checksum (FontData dis, uint32 offset, uint32 length, string name) { - uint32 checksum = 0; - uint32 l; - - dis.seek (offset); - - l = (length % 4 > 0) ? length / 4 + 1 : length / 4; - - for (uint32 i = 0; i < l; i++) { - checksum += dis.read_ulong (); - } - - return checksum; - } - - public static uint16 max_pow_2_less_than_i (uint16 ind) { - uint16 last = 0; - uint16 i = 1; - - while ((i <<= 1) < ind) { - last = i; - } - - return last; - } - - public static uint16 max_log_2_less_than_i (uint16 ind) { - return (uint16) (Math.log (ind) / Math.log (2)); - } -} - -class LocaTable : Table { - - uint32* glyph_offsets = null; - public uint32 size = 0; - - public LocaTable () { - id = "loca"; - } - - ~LocaTable () { - if (glyph_offsets != null) delete glyph_offsets; - } - - public uint32 get_offset (uint32 i) { - return_val_if_fail (glyph_offsets != null, 0); - - if (size == 0) { - warning ("No glyphs in loca table"); - } - - if (!(0 <= i < size + 1)) { - warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)"); - } - - return glyph_offsets [i]; - } - - /** Returns true if glyph at index i is empty and have no body to parse. */ - public bool is_empty (uint32 i) { - return_val_if_fail (glyph_offsets != null, true); - - if (size == 0) { - warning ("No glyphs in loca table"); - } - - if (!(0 <= i < size + 1)) { - warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)"); - } - - return glyph_offsets[i] == glyph_offsets[i + 1]; - } - - public new void parse (FontData dis, HeadTable head_table, MaxpTable maxp_table) throws GLib.Error { - size = maxp_table.num_glyphs; - glyph_offsets = new uint32[size + 1]; - - dis.seek (offset); - - printd (@"size: $size\n"); - printd (@"length: $length\n"); - printd (@"length/4-1: $(length / 4 - 1)\n"); - printd (@"length/2-1: $(length / 2 - 1)\n"); - printd (@"head_table.loca_offset_size: $(head_table.loca_offset_size)\n"); - - switch (head_table.loca_offset_size) { - case 0: - for (long i = 0; i < size + 1; i++) { - glyph_offsets[i] = 2 * dis.read_ushort (); - - if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) { - warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))"); - } - } - break; - - case 1: - for (long i = 0; i < size + 1; i++) { - glyph_offsets[i] = dis.read_ulong (); - - if (0 < i < size && glyph_offsets[i - 1] > glyph_offsets[i]) { - warning (@"Invalid loca table, it must be sorted. ($(glyph_offsets[i - 1]) > $(glyph_offsets[i]))"); - } - } - - break; - - default: - warning ("unknown size for offset in loca table"); - break; - } - } - - public void process (GlyfTable glyf_table, HeadTable head_table) { - FontData fd = new FontData (); - uint32 last = 0; - uint32 prev = 0; - int i = 0; - - foreach (uint32 o in glyf_table.location_offsets) { - if (i != 0 && (o - prev) % 4 != 0) { - warning (@"glyph length is not a multiple of four in gid $i"); - } - - if (o % 4 != 0) { - warning ("glyph is not on a four byte boundry"); - assert_not_reached (); - } - - prev = o; - i++; - } - - if (head_table.loca_offset_size == 0) { - foreach (uint32 o in glyf_table.location_offsets) { - fd.add_u16 ((uint16) (o / 2)); - - if (o < last) { - warning (@"Loca table must be sorted. ($o < $last)"); - } - - last = o; - } - - } else if (head_table.loca_offset_size == 1) { - foreach (uint32 o in glyf_table.location_offsets) { - fd.add_u32 (o); - - if (o < last) { - warning (@"Loca table must be sorted. ($o < $last)"); - } - - last = o; - } - - } else { - warn_if_reached (); - } - - if (!(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1)) { - warning (@"(glyf_table.location_offsets.length () == glyf_table.glyphs.length () + 1) ($(glyf_table.location_offsets.length ()) == $(glyf_table.glyphs.length () + 1))"); - } - - fd.pad (); - font_data = fd; - } -} - -class GlyfTable : Table { - - // Flags for composite glyph - static const uint16 BOTH_ARE_WORDS = 1 << 0; - static const uint16 BOTH_ARE_XY_VALUES = 1 << 1; - static const uint16 ROUND_TO_GRID = 1 << 2; - static const uint16 SCALE = 1 << 3; - static const uint16 RESERVED = 1 << 4; - static const uint16 MORE_COMPONENTS = 1 << 5; - static const uint16 SCALE_X_Y = 1 << 6; - static const uint16 SCALE_WITH_ROTATTION = 1 << 7; - static const uint16 INSTRUCTIONS = 1 << 8; - - public int16 xmin = int16.MAX; - public int16 ymin = int16.MAX; - public int16 xmax = int16.MIN; - public int16 ymax = int16.MIN; - - public FontData dis; - public HeadTable head_table; - public HmtxTable hmtx_table; - public LocaTable loca_table; - public CmapTable cmap_table; // cmap and post is null when inistialized and set in parse method - public PostTable post_table; - public KernTable kern_table; - - public List<uint32> location_offsets; - - // list of glyphs sorted in the order we expect to find them in a - // ttf font. notdef is the firs glyph followed by null and nonmarkingreturn. - // after that will all assigned glyphs appear in sorted order, all - // remaining unassigned glyphs is in the last part of the file. - public List<Glyph> glyphs; - - uint16 max_points = 0; - uint16 max_contours = 0; - - double total_width = 0; - int non_zero_glyphs = 0; - - public GlyfTable (LocaTable l) { - id = "glyf"; - loca_table = l; - location_offsets = new List<uint32> (); - glyphs = new List<Glyph> (); - } - - public int get_gid (string name) { - int i = 0; - foreach (Glyph g in glyphs) { - if (g.name == name) { - return i; - } - - i++; - } - - return -1; - } - - public int16 get_average_width () { - return (int16) (total_width / non_zero_glyphs); - } - - public uint16 get_max_contours () { - return max_contours; - } - - public uint16 get_max_points () { - return max_points; - } - - public uint16 get_first_char () { - return 32; // space - } - - public uint16 get_last_char () { - unowned List<Glyph> gl = glyphs.last (); - Glyph g = (!) gl.data; - - while (gl != glyphs.first ()) { - g = (!) gl.data; - - if (!g.is_unassigned ()) { - break; - } - - gl = gl.prev; - } - - return (uint16)g.unichar_code; - } - - public void process_glyph (Glyph g, FontData fd) throws GLib.Error { - uint16 end_point; - uint16 npoints; - int16 ncontours; - int16 nflags; - int glyph_offset; - uint len; - uint coordinate_length; - - fd.seek_end (); // append glyph - - glyph_offset = (int) fd.length (); - - printd (@"glyph_offset: $(glyph_offset)\n"); - - g.remove_empty_paths (); - if (g.path_list.length () == 0) { - // location_offsets == location_offset + 1 to tell parser that this glyf does not have a body - return; - } - - non_zero_glyphs++; - - ncontours = (int16) g.path_list.length (); - fd.add_short (ncontours); - - GlyfData glyf_data = new GlyfData (g); - - // bounding box - fd.add_16 (glyf_data.bounding_box_xmin); - fd.add_16 (glyf_data.bounding_box_ymin); - fd.add_16 (glyf_data.bounding_box_xmax); - fd.add_16 (glyf_data.bounding_box_ymax); - - // end points - foreach (uint16 end in glyf_data.end_points) { - fd.add_u16 (end); - } - - fd.add_u16 (0); // instruction length - - uint glyph_header = 12 + ncontours * 2; - - printd (@"next glyf: $(g.name) ($((uint32)g.unichar_code))\n"); - printd (@"glyf header length: $(glyph_header)\n"); - - end_point = glyf_data.get_end_point (); - ncontours = glyf_data.get_ncontours (); - npoints = (ncontours > 0) ? end_point : 0; // +1? - - if (npoints > max_points) { - max_points = npoints; - } - - if (ncontours > max_contours) { - max_contours = ncontours; - } - - // flags - nflags = glyf_data.get_nflags (); - if (nflags != npoints) { - warning (@"(nflags != npoints) ($nflags != $npoints) in glyph $(g.name). ncontours: $ncontours"); - } - assert (nflags == npoints); - - foreach (uint8 flag in glyf_data.flags) { - fd.add_byte (flag); - } - - printd (@"flags: $(nflags)\n"); - - // x coordinates - foreach (int16 x in glyf_data.coordinate_x) { - fd.add_16 (x); - } - - // y coordinates - foreach (int16 y in glyf_data.coordinate_y) { - fd.add_16 (y); - } - - len = fd.length (); - coordinate_length = fd.length () - nflags - glyph_header; - printd (@"coordinate_length: $(coordinate_length)\n"); - printd (@"fd.length (): $(fd.length ())\n"); - assert (fd.length () > nflags + glyph_header); - - printd (@"glyph_offset: $(glyph_offset)\n"); - printd (@"len: $(len)\n"); - - // save bounding box for head table - if (glyf_data.bounding_box_xmin < this.xmin) { - this.xmin = glyf_data.bounding_box_xmin; - } - - if (glyf_data.bounding_box_ymin < this.ymin) { - this.ymin = glyf_data.bounding_box_ymin; - } - - if (glyf_data.bounding_box_xmax > this.xmax) { - this.xmax = glyf_data.bounding_box_xmax; - } - - if (glyf_data.bounding_box_ymax > this.ymax) { - this.ymax = glyf_data.bounding_box_ymax; - } - - // part of average width calculation for OS/2 table - total_width += xmax - xmin; - - printd (@"length before padding: $(fd.length ())\n"); - - // all glyphs needs padding for loca table to be correct - while (fd.length () % 4 != 0) { - fd.add (0); - } - printd (@"length after padding: $(fd.length ())\n"); - } - - public Glyph? read_glyph (string name) throws GLib.Error { - Glyph? glyph; - Glyph g; - int i; - KernList kl; - string right; - double units_per_em; - double kern_val; - - i = post_table.get_gid (name); - - if (i == -1) { - return null; - } - - glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table); - - if (glyph != null) { - printd (@"kern: "); - units_per_em = head_table.get_units_per_em (); - kl = kern_table.get_all_pairs (i); - foreach (Kern k in kl.kernings) { - g = (!) glyph; - right = post_table.get_name (k.right); - kern_val = k.kerning * 1000.0 / units_per_em; - g.add_kerning (right, kern_val); - } - } - - return glyph; - } - - public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error { - this.cmap_table = cmap_table; - this.post_table = post_table; - this.loca_table = loca; - this.hmtx_table = hmtx_table; - this.head_table = head_table; - this.kern_table = kern_table; - this.dis = dis; - } - - Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error { - Glyph glyph = new Glyph (""); - double xmin, xmax; - double units_per_em = head_table.get_units_per_em (); - unichar character = 0; - string name; - - character = cmap_table.get_char (index); - name = post_table.get_name (index); - - if (name == "") { - StringBuilder name_c = new StringBuilder (); - name_c.append_unichar (character); - name = name_c.str; - } - - printd (@"name: $(name)\n"); - - if (!loca.is_empty (index)) { - glyph = parse_next_glyf (dis, character, index, out xmin, out xmax, units_per_em); - - glyph.left_limit = xmin - hmtx_table.get_lsb (index); - glyph.left_limit = 0; - glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (index); - } else { - // add empty glyph - glyph = new Glyph (name, character); - glyph.left_limit = -hmtx_table.get_lsb (index); - glyph.right_limit = hmtx_table.get_advance (index) - hmtx_table.get_lsb (index); - } - - glyph.name = name; - - if (character == 0) { - glyph.set_unassigned (true); - } - - if (character == 0 && name != "") { - stderr.printf (@"Got null character\n"); - stderr.printf (@"gid: $index\n"); - stderr.printf (@"char: $((uint) character)\n"); - stderr.printf (@"name: $(name)\n"); - } - - return glyph; - } - - Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error { - uint16 component_flags = 0; - uint16 glyph_index; - int16 arg1 = 0; - int16 arg2 = 0; - - F2Dot14 scale; - - F2Dot14 scalex; - F2Dot14 scaley; - - F2Dot14 scale01; - F2Dot14 scale10; - - Glyph glyph, linked_glyph; - List<int> x = new List<int> (); - List<int> y = new List<int> (); - List<int> gid = new List<int> (); - - double xmin, xmax; - double units_per_em = head_table.get_units_per_em (); - - int glid; - - StringBuilder name = new StringBuilder (); - name.append_unichar (character); - - glyph = new Glyph (name.str, character); - - do { - component_flags = dis.read_ushort (); - glyph_index = dis.read_ushort (); - - if ((component_flags & BOTH_ARE_WORDS) > 0) { - arg1 = dis.read_short (); - arg2 = dis.read_short (); - } else if ((component_flags & BOTH_ARE_XY_VALUES) > 0) { - arg1 = dis.read_byte (); - arg2 = dis.read_byte (); - } - - gid.append (glyph_index); - x.append (arg1); - y.append (arg2); - - // if ((component_flags & RESERVED) > 0) - - if ((component_flags & SCALE) > 0) { - scale = dis.read_f2dot14 (); - } else if ((component_flags & SCALE_X_Y) > 0) { - scalex = dis.read_f2dot14 (); - scaley = dis.read_f2dot14 (); - } else if ((component_flags & SCALE_WITH_ROTATTION) > 0) { - scalex = dis.read_f2dot14 (); - scale01 = dis.read_f2dot14 (); - scale10 = dis.read_f2dot14 (); - scaley = dis.read_f2dot14 (); - } - - } while ((component_flags & MORE_COMPONENTS) > 0); - - - for (int i = 0; i < gid.length (); i++) { - // compensate xmax ymax with coordinate - glid = gid.nth (i).data; - - if (glid == pgid) { - warning ("Cannot link a glyph to it self."); - continue; - } - - linked_glyph = parse_next_glyf (dis, character, glid, out xmin, out xmax, units_per_em); - } - - return glyph; - } - - Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error { - uint16* end_points = null; - uint8* instructions = null; - uint8* flags = null; - int16* xcoordinates = null; - int16* ycoordinates = null; - - int npoints = 0; - - int16 ncontours; - int16 ixmin; // set boundries - int16 iymin; - int16 ixmax; - int16 iymax; - uint16 ninstructions; - - int16 rxmin = int16.MAX; // real xmin - int16 rymin = int16.MAX;; - int16 rxmax = int16.MIN; - int16 rymax = int16.MIN; - - int nflags; - - Error? error = null; - - uint start, end, len; - - StringBuilder name = new StringBuilder (); - name.append_unichar (character); - - xmin = 0; - xmax = 0; - - start = loca_table.get_offset (gid); - end = loca_table.get_offset (gid + 1); - len = start - end; - - dis.seek (offset + start); - - ncontours = dis.read_short (); - - return_val_if_fail (start < end, new Glyph ("")); - - if (ncontours == 0) { - warning (@"Got zero contours in glyph $(name.str)."); - - // should skip body - } - - if (ncontours == -1) { - return parse_next_composite_glyf (dis, character, gid); - } - - return_val_if_fail (ncontours < len, new Glyph ("")); - - if (ncontours < -1) { - warning (@"Got $ncontours contours in glyf table."); - error = new BadFormat.PARSE ("Invalid glyf"); - throw error; - } - - ixmin = dis.read_short (); - iymin = dis.read_short (); - ixmax = dis.read_short (); - iymax = dis.read_short (); - - end_points = new uint16[ncontours + 1]; - for (int i = 0; i < ncontours; i++) { - end_points[i] = dis.read_ushort (); // FIXA: mind shot vector is negative - - if (i > 0 && end_points[i] < end_points[i -1]) { - warning (@"Next endpoint has bad value in $(name.str). (end_points[i] > end_points[i -1]) ($(end_points[i]) > $(end_points[i -1])) i: $i ncontours: $ncontours"); - } - } - - if (ncontours > 0) { - npoints = end_points[ncontours - 1] + 1; - } else { - npoints = 0; - } - - return_val_if_fail (npoints < len, new Glyph.no_lines ("")); - - ninstructions = dis.read_ushort (); - - return_val_if_fail (ninstructions < len, new Glyph.no_lines ("")); - - instructions = new uint8[ninstructions + 1]; - uint8 repeat; - for (int i = 0; i < ninstructions; i++) { - instructions[i] = dis.read_byte (); - } - - nflags = 0; - flags = new uint8[npoints + 1]; - for (int i = 0; i < npoints; i++) { - flags[i] = dis.read_byte (); - - if ((flags[i] & Coordinate.REPEAT) > 0) { - repeat = dis.read_byte (); - - if (i + repeat >= npoints) { - error = new BadFormat.PARSE ("Too many flags in glyf in glyph $(name.str). (i >= ninstructions)."); - break; - } - - for (int j = 0; j < repeat; j++) { - flags[j + i + 1] = flags[i]; - } - - nflags += repeat; - i += repeat; - } - - nflags++; - } - - if (nflags != npoints) { - warning (@"(nflags != npoints) ($nflags != $npoints) in $(name.str)"); - error = new BadFormat.PARSE (@"Wrong number of flags in glyph $(name.str). (nflags != npoints) ($nflags != $npoints)"); - } - - warn_if_fail (nflags == npoints); - - printd (@"npoints: $npoints\n"); - printd (@"ncontours: $ncontours\n"); - printd (@"ninstructions: $ninstructions\n"); - printd (@"nflags: $nflags\n"); - - int16 last = 0; - xcoordinates = new int16[npoints + 1]; - for (int i = 0; i < npoints; i++) { - if ((flags[i] & Coordinate.X_SHORT_VECTOR) > 0) { - if ((flags[i] & Coordinate.X_SHORT_VECTOR_POSITIVE) > 0) { - xcoordinates[i] = last + dis.read_byte (); - } else { - xcoordinates[i] = last - dis.read_byte (); - } - } else { - if ((flags[i] & Coordinate.X_IS_SAME) > 0) { - xcoordinates[i] = last; - } else { - xcoordinates[i] = last + dis.read_short (); - } - } - - last = xcoordinates[i]; - - if (last > rxmax) rxmax = last; - if (last < rxmin) rxmin = last; - - if (!(ixmin <= last <= ixmax)) { - stderr.printf (@"x is out of bounds in glyph $(name.str). ($ixmin <= $last <= $ixmax) char $((uint)character)\n"); - } - - if (!(head_table.xmin <= last <= head_table.xmax)) { - stderr.printf (@"x is outside of of font bounding box in glyph $(name.str). ($(head_table.xmin) <= $last <= $(head_table.xmax)) char $((uint)character)\n"); - } - } - - last = 0; - ycoordinates = new int16[npoints + 1]; - for (int i = 0; i < npoints; i++) { - if ((flags[i] & Coordinate.Y_SHORT_VECTOR) > 0) { - if ((flags[i] & Coordinate.Y_SHORT_VECTOR_POSITIVE) > 0) { - ycoordinates[i] = last + dis.read_byte (); - } else { - ycoordinates[i] = last - dis.read_byte (); - } - } else { - if ((flags[i] & Coordinate.Y_IS_SAME) > 0) { - ycoordinates[i] = last; - } else { - ycoordinates[i] = last + dis.read_short (); - } - } - - last = ycoordinates[i]; - - if (last > rymax) rymax = last; - if (last < rymin) rymin = last; - - if (!(iymin <= last <= iymax)) { - stderr.printf (@"y is out of bounds in glyph $(name.str). ($iymin <= $last <= $iymax) char $((uint)character)\n"); - } - - if (!(head_table.ymin <= last <= head_table.ymax)) { - stderr.printf (@"y is outside of of font bounding box in glyph $(name.str). ($(head_table.ymin) <= $last <= $(head_table.ymax)) char $((uint)character)\n"); - } - } - - if (rymin != iymin || rxmin != ixmin || rxmax != ixmax || rymax != iymax) { - warning (@"Warning real boundry for glyph does not match boundry set in glyph header for glyph $(name.str)."); - stderr.printf (@"ymin: $rymin header: $iymin\n"); - stderr.printf (@"xmin: $rxmin header: $ixmin\n"); - stderr.printf (@"ymax: $rymax header: $iymax\n"); - stderr.printf (@"xmax: $rxmax header: $ixmax\n"); - } - - int j = 0; - int first_point; - int last_point = 0; - Glyph glyph; - double x, y; - - glyph = new Glyph (name.str, character); - - xmin = ixmin * 1000.0 / units_per_em; - xmax = ixmax * 1000.0 / units_per_em; - - for (int i = 0; i < ncontours; i++) { - x = 0; - y = 0; - - Path path = new Path (); - EditPoint edit_point = new EditPoint (); - bool prev_is_curve = false; - - first_point = j; - last_point = end_points[i]; - for (; j <= end_points[i]; j++) { - - if (j >= npoints) { - warning (@"j >= npoints in glyph $(name.str). (j: $j, end_points[i]: $(end_points[i]), npoints: $npoints)"); - break; - } - - x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width - y = ycoordinates[j] * 1000.0 / units_per_em; - - if ((flags[j] & Coordinate.ON_PATH) > 0) { - // Point - edit_point = new EditPoint (); - edit_point.set_position (x, y); - path.add_point (edit_point); - - if (prev_is_curve) { - edit_point.get_left_handle ().set_point_type (PointType.NONE); - edit_point.get_left_handle ().length = 0; - } else { - edit_point.recalculate_linear_handles (); - } - - prev_is_curve = false; - } else { - - if (prev_is_curve) { - x = x - (x - edit_point.right_handle.x ()) / 2; - y = y - (y - edit_point.right_handle.y ()) / 2; - - edit_point = new EditPoint (); - edit_point.set_position (x, y); - path.add_point (edit_point); - } - - x = xcoordinates[j] * 1000.0 / units_per_em; // in proportion to em width - y = ycoordinates[j] * 1000.0 / units_per_em; - - edit_point.get_left_handle ().set_point_type (PointType.NONE); - edit_point.get_left_handle ().length = 0; - - edit_point.type = PointType.CUBIC; - edit_point.get_right_handle ().set_point_type (PointType.CUBIC); - edit_point.get_right_handle ().move_to_coordinate (x, y); - - prev_is_curve = true; - } - } - - // last to first point - if (prev_is_curve) { - x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width - y = ycoordinates[first_point] * 1000.0 / units_per_em; - - x = x - (x - edit_point.right_handle.x ()) / 2; - y = y - (y - edit_point.right_handle.y ()) / 2; - - edit_point = new EditPoint (); - edit_point.set_position (x, y); - path.add_point (edit_point); - - x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width - y = ycoordinates[first_point] * 1000.0 / units_per_em; - - edit_point.get_left_handle ().set_point_type (PointType.CUBIC); - edit_point.get_left_handle ().length = 0; - - edit_point.type = PointType.CUBIC; - edit_point.get_right_handle ().set_point_type (PointType.CUBIC); - edit_point.get_right_handle ().move_to_coordinate (x, y); - } - - // curve last to first - x = xcoordinates[first_point] * 1000.0 / units_per_em; // in proportion to em width - y = ycoordinates[first_point] * 1000.0 / units_per_em; - edit_point.type = PointType.CUBIC; - edit_point.get_right_handle ().set_point_type (PointType.CUBIC); - edit_point.get_right_handle ().move_to_coordinate (x, y); - - path.close (); - - glyph.add_path (path); - } - - // glyphs with no bounding boxes - if (ixmax <= ixmin) { - warning (@"Bounding box is bad. (xmax == xmin) ($xmax == $xmin)"); - - if (glyph.path_list.length () > 0) { - - Path ps = ((!) glyph.path_list.first ()).data; - - ps.update_region_boundries (); - xmin = ps.xmin; - xmax = ps.xmax; - - foreach (Path p in glyph.path_list) { - p.update_region_boundries (); - - if (p.xmin < xmin) { - xmin = p.xmin; - } - - if (p.xmax > xmax) { - xmax = p.xmax; - } - } - - } - } - - if (end_points != null) delete end_points; - if (instructions != null) delete instructions; - if (flags != null) delete flags; - if (xcoordinates != null) delete xcoordinates; - if (ycoordinates != null) delete ycoordinates; - - if (error != null) { - warning ("failed to parse glyph"); - throw (!) error; - } - - return glyph; - } - - /** Data for one entry in the glyf table. */ - class GlyfData : GLib.Object { - public List<Path> paths = new List<Path> (); - public List<uint16> end_points = new List<uint16> (); - public List<uint8> flags = new List<uint8> (); - public List<int16> coordinate_x = new List<int16> (); - public List<int16> coordinate_y = new List<int16> (); - - uint16 end_point = 0; - int16 nflags = 0; - - Glyph glyph; - - public int16 bounding_box_xmin = 0; - public int16 bounding_box_ymin = 0; - public int16 bounding_box_xmax = 0; - public int16 bounding_box_ymax = 0; - - public GlyfData (Glyph g) { - glyph = g; - - foreach (Path p in g.path_list) { - paths.append (p.get_quadratic_points ()); - } - - process_end_points (); - process_flags (); - process_x (); - process_y (); - process_bounding_box (); - } - - public uint16 get_end_point () { - return end_point; - } - - public int16 get_ncontours () { - return (int16) paths.length (); - } - - public int16 get_nflags () { - return nflags; - } - - void process_end_points () { - uint16 last_end_point = 0; - PointType type; - - end_point = 0; - - foreach (Path quadratic in paths) { - if (quadratic.points.length () == 0) { - warning (@"No points in path (before conversion $(quadratic.points.length ()))"); - continue; - } - - foreach (EditPoint e in quadratic.points) { - end_point++; - type = e.get_right_handle ().type; - - // off curve - end_point++; - - if (end_point == 0xFFFF) { - warning ("Too many points"); - } - } - end_points.append (end_point - 1); - - if (end_point - 1 < last_end_point) { - warning (@"Next endpoint has bad value. (end_point - 1 < last_end_point) ($(end_point - 1) < $last_end_point)"); - } - - last_end_point = end_point - 1; - } - - assert (end_point > 0); - } - - void process_flags () { - flags = new List<uint8> (); - nflags = 0; - - foreach (Path p in paths) { - p = p.get_quadratic_points (); - - foreach (EditPoint e in p.points) { - flags.append (Coordinate.ON_PATH); - nflags++; - - // off curve - flags.append (Coordinate.NONE); - nflags++; - } - } - } - - void process_x () { - double prev = 0; - double x; - - foreach (Path p in paths) { - foreach (EditPoint e in p.points) { - x = rint (e.x * UNITS - prev - glyph.left_limit * UNITS); - coordinate_x.append ((int16) x); - - prev = rint (e.x * UNITS - glyph.left_limit * UNITS); - - // off curve - x = rint (e.get_right_handle ().x () * UNITS - prev - glyph.left_limit * UNITS); - coordinate_x.append ((int16) x); - - prev = rint (e.get_right_handle ().x () * UNITS - glyph.left_limit * UNITS); - } - } - } - - void process_y () { - double prev = 0; - double y; - Font font = OpenFontFormatWriter.get_current_font (); - - foreach (Path p in paths) { - foreach (EditPoint e in p.points) { - y = rint (e.y * UNITS - prev + font.base_line * UNITS); - coordinate_y.append ((int16) y); - - prev = rint (e.y * UNITS + font.base_line * UNITS); - - // off curve - y = rint (e.get_right_handle ().y () * UNITS - prev + font.base_line * UNITS); - coordinate_y.append ((int16) y); - - prev = rint (e.get_right_handle ().y () * UNITS + font.base_line * UNITS); - } - } - } - - void process_bounding_box () { - int16 last = 0; - int i = 0; - - bounding_box_xmin = int16.MAX; - bounding_box_ymin = int16.MAX; - bounding_box_xmax = int16.MIN; - bounding_box_ymax = int16.MIN; - - if (coordinate_x.length () == 0) { - warning ("no points in coordinate_y"); - } - - foreach (int16 c in coordinate_x) { - c += last; - - // Only on curve points are good for calculating bounding box - if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) { - if (c < bounding_box_xmin) bounding_box_xmin = c; - if (c > bounding_box_xmax) bounding_box_xmax = c; - } - - last = c; - i++; - } - - if (coordinate_y.length () == 0) { - warning ("no points in coordinate_y"); - } - - last = 0; - i = 0; - foreach (int16 c in coordinate_y) { - c += last; - - if ((flags.nth (i).data & Coordinate.ON_PATH) > 0) { - if (c < bounding_box_ymin) bounding_box_ymin = c; - if (c > bounding_box_ymax) bounding_box_ymax = c; - } - - last = c; - i++; - } - - printd (@"Bounding box: $bounding_box_xmin,$bounding_box_ymin $bounding_box_xmax,$bounding_box_ymax\n"); - } - } - - // necessary in order to have glyphs sorted according to ttf specification - public void create_glyph_table () { - Glyph? gl; - Glyph g; - Font font = OpenFontFormatWriter.get_current_font (); - uint32 indice; - List<Glyph> unassigned_glyphs; - - // add notdef. character at index zero + other special chars first - glyphs.append (font.get_not_def_character ()); - glyphs.append (font.get_null_character ()); - glyphs.append (font.get_nonmarking_return ()); - glyphs.append (font.get_space ()); - - unassigned_glyphs = new List<Glyph> (); - - // add glyphs, first all assigned then the unassigned ones - for (indice = 0; (gl = font.get_glyph_indice (indice)) != null; indice++) { - g = (!) gl; - - if (g.unichar_code <= 27) { // skip control characters - continue; - } - - if (g.unichar_code == 32) { // skip space - continue; - } - - if (g.name == ".notdef") { - continue; - } - - g.remove_empty_paths (); - - if (!g.is_unassigned ()) { - glyphs.append (g); - } else { - unassigned_glyphs.append (g); - } - } - - foreach (Glyph ug in unassigned_glyphs) { - // FIXME: ligatures - // glyphs.append (ug); - } - - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - uint last_len = 0; - uint i = 0; - uint num_glyphs; - - create_glyph_table (); - - num_glyphs = glyphs.length (); - - foreach (Glyph g in glyphs) { - // set values for loca table - assert (fd.length () % 4 == 0); - location_offsets.append (fd.length ()); - process_glyph (g, fd); - - printd (@"adding glyph: $(g.name)\n"); - printd (@"glyf length: $(fd.length () - last_len)\n"); - printd (@"loca fd.length (): $(fd.length ())\n"); - - last_len = fd.length (); - - ProgressBar.set_progress ((double) (++i) / num_glyphs); - } - - location_offsets.append (fd.length ()); // last entry in loca table is special - - // every glyph is padded, no padding to be done here - assert (fd.length () % 4 == 0); - - font_data = fd; - } -} - -class CmapSubtable : Table { - - // Override these methods in subtables for each format. - - /** Obtain length of subtable in bytes. */ - public virtual uint get_length () { - warning ("Invalid CmapSubtable"); - return 0; - } - - /** Get char code for a glyph id. */ - public virtual unichar get_char (uint32 i) { - warning ("Invalid CmapSubtable"); - return 0; - } - - public void print_cmap () { - StringBuilder s; - unichar c; - for (uint32 i = 0; i < get_length (); i++) { - s = new StringBuilder (); - c = get_char (i); - s.append_unichar (c); - printd (@"Char: $(s.str) val ($((uint32)c))\tindice: $(i)\n"); - } - } -} - -/** Format 4 cmap subtable */ -class CmapSubtableWindowsUnicode : CmapSubtable { - uint16 format = 0; - HashTable <uint64?, unichar> table = new HashTable <uint64?, unichar> (int64_hash, int_equal); - - public CmapSubtableWindowsUnicode () { - } - - ~CmapSubtableWindowsUnicode () { - - } - - public override uint get_length () { - return table.size (); - } - - public override unichar get_char (uint32 indice) { - int64? c = table.lookup (indice); - - if (c == 0 && indice == 0) { - return 0; - } - - if (c == 0) { - while (table.lookup (--indice) == 0) { - if (indice == 0) { - return 0; - } - } - - warning (@"There is no char for glyph number $indice in cmap table. table.size: $(table.size ()))"); - return 0; - } - - return (unichar) c; - } - - public override void parse (FontData dis) throws GLib.Error { - dis.seek (offset); - - format = dis.read_ushort (); - - switch (format) { - case 4: - parse_format4 (dis); - break; - - default: - stderr.printf (@"CmapSubtable is in format $format, it is not supportet (yet).\n"); - break; - } - } - - public void parse_format4 (FontData dis) throws GLib.Error { - uint16 lang; - uint16 seg_count_x2; - uint16 seg_count; - uint16 search_range; - uint16 entry_selector; - uint16 range_shift; - - uint16* end_char = null; - uint16* start_char = null; - int16* id_delta = null; - uint16* id_range_offset = null; - uint16* glyph_id_array = null; - - uint32 gid_len; - - length = dis.read_ushort (); - lang = dis.read_ushort (); - seg_count_x2 = dis.read_ushort (); - search_range = dis.read_ushort (); - entry_selector = dis.read_ushort (); - range_shift = dis.read_ushort (); - - return_if_fail (seg_count_x2 % 2 == 0); - - seg_count = seg_count_x2 / 2; - - end_char = new uint16[seg_count]; - for (int i = 0; i < seg_count; i++) { - end_char[i] = dis.read_ushort (); - } - - if (end_char[seg_count - 1] != 0xFFFF) { - warning ("end_char is $(end_char[seg_count - 1]), expecting 0xFFFF."); - } - - dis.read_ushort (); // Reserved - - start_char = new uint16[seg_count]; - for (int i = 0; i < seg_count; i++) { - start_char[i] = dis.read_ushort (); - } - - id_delta = new int16[seg_count]; - for (int i = 0; i < seg_count; i++) { - id_delta[i] = dis.read_short (); - } - - id_range_offset = new uint16[seg_count]; - for (int i = 0; i < seg_count; i++) { - id_range_offset[i] = dis.read_ushort (); - } - - if (length == 0) { - warning ("cmap subtable version 4 has length 0."); - return; - } - - gid_len = (length - 16 - 8 * seg_count) / 2; - glyph_id_array = new uint16[gid_len]; - for (int i = 0; i < gid_len; i++) { - glyph_id_array[i] = dis.read_ushort (); - } - - // map all values in a hashtable - int indice = 0; - unichar character = 0; - uint32 id; - for (uint16 i = 0; i < seg_count && start_char[i] != 0xFFFF; i++) { - - // print_range (start_char[i], end_char[i], id_delta[i], id_range_offset[i]); - - uint16 j = 0; - do { - character = start_char[i] + j; - indice = start_char[i] + id_delta[i] + j; - - if (id_range_offset[i] == 0) { - table.insert (indice, character); - } else { - // the awkward indexing trick: - id = id_range_offset[i] / 2 + j + i - seg_count; - - if (!(0 <= id < gid_len)) { - warning (@"(0 <= id < gid_len) (0 <= $id < $gid_len)"); - break; - } - - indice = glyph_id_array [id] + id_delta[i]; - - StringBuilder s = new StringBuilder (); - s.append_unichar (character); - - table.insert (indice, character); - } - - j++; - } while (character != end_char[i]); - - } - - if (end_char != null) delete end_char; - if (start_char != null) delete start_char; - if (id_delta != null) delete id_delta; - if (id_range_offset != null) delete id_range_offset; - if (glyph_id_array != null) delete glyph_id_array; - - // it has a character for every glyph indice - // assert (validate_subtable ()); - } - - public void process (FontData fd, GlyfTable glyf_table) throws GLib.Error { - GlyphRange glyph_range = new GlyphRange (); - unowned List<UniRange> ranges; - - uint16 seg_count_2; - uint16 seg_count; - uint16 search_range; - uint16 entry_selector; - uint16 range_shift; - - uint16 gid_length = 0; - - uint32 indice; - uint32 first_assigned = 1; - - foreach (Glyph g in glyf_table.glyphs) { - if (!g.is_unassigned ()) { - glyph_range.add_single (g.unichar_code); - } - } - - // glyph_range.print_all (); - - ranges = glyph_range.get_ranges (); - seg_count = (uint16) ranges.length () + 1; - seg_count_2 = seg_count * 2; - search_range = 2 * largest_pow2 (seg_count); - entry_selector = largest_pow2_exponent (seg_count); - range_shift = seg_count_2 - search_range; - - // format - fd.add_ushort (4); - - // length of subtable - fd.add_ushort (16 + 8 * seg_count + gid_length); - - // language - fd.add_ushort (0); - - fd.add_ushort (seg_count_2); - fd.add_ushort (search_range); - fd.add_ushort (entry_selector); - fd.add_ushort (range_shift); - - // end codes - indice = first_assigned; - foreach (UniRange u in ranges) { - if (u.stop >= 0xFFFF) { - warning ("Not implemented yet."); - } - - fd.add_ushort ((uint16) u.stop); - indice += u.length (); - } - fd.add_ushort (0xFFFF); - - fd.add_ushort (0); // Reserved - - // start codes - indice = first_assigned; // since first glyph are notdef, null and nonmarkingreturn - foreach (UniRange u in ranges) { - if (u.start >= 0xFFFF) { - warning ("Not implemented yet."); - } - - fd.add_ushort ((uint16) u.start); - indice += u.length (); - } - fd.add_ushort (0xFFFF); - - // delta - indice = first_assigned; - foreach (UniRange u in ranges) { - - if ((u.start - indice) > 0xFFFF && u.start > indice) { - warning ("Need range offset."); - } - - fd.add_ushort ((uint16) (indice - u.start)); - indice += u.length (); - } - fd.add_ushort (1); - - // range offset - foreach (UniRange u in ranges) { - if (u.stop <= 0xFFFF) { - fd.add_ushort (0); - } else { - warning ("Not implemented yet."); - } - } - fd.add_ushort (0); - - // Fixa: implement rest of type 4 (mind gid_length in length field) - } -} - -class CmapTable : Table { - - GlyfTable glyf_table; - List<CmapSubtable> subtables; - - public CmapTable(GlyfTable gt) { - glyf_table = gt; - subtables = new List<CmapSubtable> (); - id = "cmap"; - } - - public unichar get_char (uint32 i) { - return get_prefered_table ().get_char (i) ; - } - - CmapSubtable get_prefered_table () { - if (subtables.length () == 0) { - warning ("No cmap table has been parsed."); - return new CmapSubtable (); - } - - return subtables.first ().data; - } - - public override string get_id () { - return "cmap"; - } - - public override void parse (FontData dis) throws GLib.Error { - uint16 version; - uint16 nsubtables; - - uint16 platform; - uint16 encoding; - uint32 sub_offset; - - CmapSubtable subtable; - - return_if_fail (offset > 0 && length > 0); - - dis.seek (offset); - - version = dis.read_ushort (); - nsubtables = dis.read_ushort (); - - printd (@"cmap version: $version\n"); - printd (@"cmap subtables: $nsubtables\n"); - - if (version != 0) { - warning (@"Bad version for cmap table: $version expecting 0. Number of subtables: $nsubtables"); - return; - } - - for (uint i = 0; i < nsubtables; i++) { - platform = dis.read_ushort (); - encoding = dis.read_ushort (); - sub_offset = dis.read_ulong (); - - if (platform == 3 && encoding == 1) { - printd (@"Parsing Unicode BMP (UCS-2) Platform: $platform Encoding: $encoding\n"); - subtable = new CmapSubtableWindowsUnicode (); - subtable.offset = offset + sub_offset; - subtables.append (subtable); - } else { - stderr.printf (@"Unknown cmap format. Platform: $platform Encoding: $encoding.\n"); - } - - if (encoding == 3) { - stderr.printf ("Font contains a cmap table with the obsolete encoding 3.\n"); - } - } - - if (subtables.length () == 0) { - warning ("No suitable cmap subtable found."); - } - - foreach (CmapSubtable t in subtables) { - t.parse (dis); - t.print_cmap (); - } - - } - - /** Character to glyph mapping */ - public void process (GlyfTable glyf_table) throws GLib.Error { - FontData fd = new FontData (); - CmapSubtableWindowsUnicode cmap = new CmapSubtableWindowsUnicode (); - uint16 n_encoding_tables; - uint32 subtable_offset = 0; - - n_encoding_tables = 1; - - fd.add_u16 (0); // table version - fd.add_u16 (n_encoding_tables); - - fd.add_u16 (3); // platform - fd.add_u16 (1); // encoding (Format Unicode UCS-4) - - subtable_offset = fd.length () + 4; - printd (@"subtable_offset: $(subtable_offset)\n"); - - fd.add_ulong (subtable_offset); - cmap.process (fd, glyf_table); - - // padding - fd.pad (); - - this.font_data = fd; - } -} - -class HeadTable : Table { - - public int16 xmin = int16.MIN; - public int16 ymin = int16.MIN; - public int16 xmax = int16.MAX; - public int16 ymax = int16.MAX; - - uint32 adjusted_checksum = 0; - - uint16 mac_style; - uint16 lowest_PPEM; - int16 font_direction_hint; - - public int16 loca_offset_size = 1; // 0 for int16 1 for int32 - int16 glyph_data_format; - - Fixed version; - Fixed font_revision; - - uint32 magic_number; - - uint16 flags; - - uint64 created; - uint64 modified; - - uint16 units_per_em = 2048; - - const uint8 BASELINE_AT_ZERO = 1 << 0; - const uint8 LSB_AT_ZERO = 1 << 1; - - GlyfTable glyf_table; - - public HeadTable (GlyfTable gt) { - glyf_table = gt; - id = "head"; - } - - public uint32 get_adjusted_checksum () { - return adjusted_checksum; - } - - public double get_units_per_em () { - return units_per_em * 10; // Fixa: we can refactor this number - } - - public override void parse (FontData dis) throws GLib.Error { - return_if_fail (offset > 0 && length > 0); - - dis.seek (offset); - - font_data = new FontData (); - - version = dis.read_fixed (); - - if (!version.equals (1, 0)) { - warning (@"Expecting head version 1.0 got $(version.get_string ())\n"); - } - - font_revision = dis.read_fixed (); - adjusted_checksum = dis.read_ulong (); - magic_number = dis.read_ulong (); - - if (magic_number != 0x5F0F3CF5) { - warning (@"Magic number is invalid. Got $(magic_number)."); - return; - } - - flags = dis.read_ushort (); - - if ((flags & BASELINE_AT_ZERO) > 0) { - printd ("Flag BASELINE_AT_ZERO has been set.\n"); - } - - if ((flags & LSB_AT_ZERO) > 0) { - printd ("Flags LSB_AT_ZERO has been set.\n"); - } - - units_per_em = dis.read_ushort (); - - created = dis.read_udate (); - modified = dis.read_udate (); - - xmin = dis.read_short (); - ymin = dis.read_short (); - - xmax = dis.read_short (); - ymax = dis.read_short (); - - printd (@"font boundries:\n"); - printd (@"xmin: $xmin\n"); - printd (@"ymin: $ymin\n"); - printd (@"xmax: $xmax\n"); - printd (@"ymax: $ymax\n"); - - mac_style = dis.read_ushort (); - lowest_PPEM = dis.read_ushort (); - font_direction_hint = dis.read_short (); - - loca_offset_size = dis.read_short (); - glyph_data_format = dis.read_short (); - - if (glyph_data_format != 0) { - warning (@"Unknown glyph data format. Expecting 0 got $glyph_data_format."); - } - - printd (@"Version: $(version.get_string ())\n"); - printd (@"flags: $flags\n"); - printd (@"font_revision: $(font_revision.get_string ())\n"); - printd (@"flags: $flags\n"); - printd (@"Units per em: $units_per_em\n"); - printd (@"lowest_PPEM: $lowest_PPEM\n"); - printd (@"font_direction_hint: $font_direction_hint\n"); - printd (@"loca_offset_size: $loca_offset_size\n"); - printd (@"glyph_data_format: $glyph_data_format\n"); - - // Some deprecated values follow here ... - } - - public uint32 get_font_checksum () { - return adjusted_checksum; - } - - public void set_check_sum_adjustment (uint32 csa) { - this.adjusted_checksum = csa; - } - - public uint32 get_checksum_position () { - return 8; - } - - public void process () throws GLib.Error { - FontData font_data = new FontData (); - Fixed version = 1 << 16; - Fixed font_revision = 1 << 16; - - font_data.add_fixed (version); - font_data.add_fixed (font_revision); - - // Zero on the first run and updated by directory tables checksum calculation - // for the entire font. - font_data.add_u32 (adjusted_checksum); - - font_data.add_u32 (0x5F0F3CF5); // magic number - - //font_data.add_u16 (BASELINE_AT_ZERO | LSB_AT_ZERO); - font_data.add_u16 (0); // flags - - font_data.add_u16 (1000); // units per em (should be a power of two for ttf fonts) - - font_data.add_64 (0); // creation time since 1904-01-01 - font_data.add_64 (0); // modified time since 1904-01-01 - - xmin = glyf_table.xmin; - ymin = glyf_table.ymin; - xmax = glyf_table.xmax; - ymax = glyf_table.ymax; - - printd (@"font boundries:\n"); - printd (@"xmin: $xmin\n"); - printd (@"ymin: $ymin\n"); - printd (@"xmax: $xmax\n"); - printd (@"ymax: $ymax\n"); - - font_data.add_short (xmin); - font_data.add_short (ymin); - font_data.add_short (xmax); - font_data.add_short (ymax); - - font_data.add_u16 (0); // mac style - font_data.add_u16 (2); // smallest recommended size in pixels, ppem - font_data.add_16 (2); // deprecated direction hint - font_data.add_16 (loca_offset_size); // long offset - font_data.add_16 (0); // Use current glyph data format - - font_data.pad (); - - this.font_data = font_data; - } -} - -class HheaTable : Table { - - Fixed version; - int16 ascender; - int16 descender; - int16 linegap; - uint16 max_advance; - int16 min_lsb; - int16 min_rsb; - int16 xmax_extent; - int16 carret_slope; - int16 carret_slope_run; - int16 carret_offset; - - int16 metric_format; - public int16 num_horizontal_metrics; - - GlyfTable glyf_table; - HeadTable head_table; - HmtxTable hmtx_table; - - public HheaTable (GlyfTable g, HeadTable h, HmtxTable hm) { - glyf_table = g; - head_table = h; - hmtx_table = hm; - id = "hhea"; - } - - public double get_ascender () { - return ascender * 1000 / head_table.get_units_per_em (); - } - - public double get_descender () { - return descender * 1000 / head_table.get_units_per_em (); - } - - public override void parse (FontData dis) throws GLib.Error { - dis.seek (offset); - - version = dis.read_fixed (); - - if (!version.equals (1, 0)) { - warning (@"wrong version in hhea table $(version.get_string ())"); - } - - ascender = dis.read_short (); - descender = dis.read_short (); - linegap = dis.read_short (); - max_advance = dis.read_ushort (); - min_lsb = dis.read_short (); - min_rsb = dis.read_short (); - xmax_extent = dis.read_short (); - carret_slope = dis.read_short (); - carret_slope_run = dis.read_short (); - carret_offset = dis.read_short (); - - // reserved x 4 - dis.read_short (); - dis.read_short (); - dis.read_short (); - dis.read_short (); - - metric_format = dis.read_short (); - num_horizontal_metrics = dis.read_short (); - } - - public void process () throws GLib.Error { - Font font = OpenFontFormatWriter.get_current_font (); - FontData fd = new FontData (); - Fixed version = 1 << 16; - - fd.add_fixed (version); // table version - - fd.add_16 ((int16) (-1 * (font.top_position - font.base_line) * UNITS)); // Ascender - fd.add_16 ((int16) (-1 * font.bottom_position * UNITS)); // Descender - fd.add_16 (0); // LineGap - - fd.add_u16 (hmtx_table.max_advance); // maximum advance width value in 'hmtx' table. - - fd.add_16 (hmtx_table.min_lsb); // min left side bearing - fd.add_16 (hmtx_table.min_rsb); // min right side bearing - fd.add_16 (hmtx_table.max_extent); // x max extent Max(lsb + (xMax - xMin)) - - fd.add_16 (1); // caretSlopeRise - fd.add_16 (0); // caretSlopeRun - fd.add_16 (0); // caretOffset - - // reserved - fd.add_16 (0); - fd.add_16 (0); - fd.add_16 (0); - fd.add_16 (0); - - fd.add_16 (0); // metricDataFormat 0 for current format. - - fd.add_u16 ((uint16) glyf_table.glyphs.length()); // numberOfHMetrics Number of hMetric entries in 'hmtx' table - - // padding - fd.pad (); - this.font_data = fd; - } -} - -class HmtxTable : Table { - - uint32 nmetrics; - uint32 nmonospaced; - - uint16* advance_width = null; - uint16* left_side_bearing = null; - uint16* left_side_bearing_monospaced = null; - - public int16 max_advance = 0; - public int16 max_extent = 0; - public int16 min_lsb = 0; - public int16 min_rsb = 0; - - HeadTable head_table; - GlyfTable glyf_table; - - public HmtxTable (HeadTable h, GlyfTable gt) { - head_table = h; - glyf_table = gt; - id = "hmtx"; - } - - ~HmtxTable () { - if (advance_width != null) delete advance_width; - if (left_side_bearing != null) delete left_side_bearing; - } - - public double get_advance (uint32 i) { - if (i >= nmetrics) { - warning (@"i >= nmetrics $i >= $nmetrics"); - return 0; - } - - return_val_if_fail (advance_width != null, 0.0); - - return advance_width[i] * 1000 / head_table.get_units_per_em (); - } - - /** Get left side bearing relative to xmin. */ - public double get_lsb (uint32 i) { - return_val_if_fail (i < nmetrics, 0.0); - return_val_if_fail (left_side_bearing != null, 0.0); - - return left_side_bearing[i] * 1000 / head_table.get_units_per_em (); - } - - public new void parse (FontData dis, HheaTable hhea_table, LocaTable loca_table) throws GLib.Error { - nmetrics = hhea_table.num_horizontal_metrics; - nmonospaced = loca_table.size - nmetrics; - - dis.seek (offset); - - if (nmetrics > loca_table.size) { - warning (@"(nmetrics > loca_table.size) ($nmetrics > $(loca_table.size))"); - return; - } - - printd (@"nmetrics: $nmetrics\n"); - printd (@"loca_table.size: $(loca_table.size)\n"); - - advance_width = new uint16[nmetrics]; - left_side_bearing = new uint16[nmetrics]; - left_side_bearing_monospaced = new uint16[nmonospaced]; - - for (int i = 0; i < nmetrics; i++) { - advance_width[i] = dis.read_ushort (); - left_side_bearing[i] = dis.read_short (); - } - - for (int i = 0; i < nmonospaced; i++) { - left_side_bearing_monospaced[i] = dis.read_short (); - } - } - - public void process () { - FontData fd = new FontData (); - - int16 advance; - int16 extent; - int16 rsb; - int16 lsb; - - double xmin; - double ymin; - double xmax; - double ymax; - - // advance and lsb - foreach (Glyph g in glyf_table.glyphs) { - g.boundries (out xmin, out ymin, out xmax, out ymax); - lsb = (int16) (-1 * (g.left_limit - xmin) * UNITS); - advance = (int16) ((g.right_limit - g.left_limit) * UNITS); - extent = (int16) (lsb + (xmax - xmin) * UNITS); - rsb = (int16) (advance - extent); - - fd.add_u16 (advance); - fd.add_16 (lsb); - - if (advance > max_advance) { - max_advance = advance; - } - - if (extent > max_extent) { - max_extent = extent; - } - - if (rsb < min_rsb) { - min_rsb = rsb; - } - - if (lsb < min_lsb) { - min_lsb = lsb; - } - } - - // monospaced lsb ... - - font_data = fd; - - warn_if_fail (max_advance != 0); - } -} - - -class MaxpTable : Table { - - GlyfTable glyf_table; - - public uint16 num_glyphs = 0; - - public MaxpTable (GlyfTable g) { - glyf_table = g; - id = "maxp"; - } - - public override void parse (FontData dis) - requires (offset > 0 && length > 0) { - Fixed format; - - dis.seek (offset); - - format = dis.read_fixed (); - printd (@"Maxp version: $(format.get_string ())\n"); - - num_glyphs = dis.read_ushort (); - - if (format == 0x00005000) { - return; - } - - // Format 1.0 continues here - } - - public void process () { - FontData fd = new FontData(); - - // Version 0.5 for fonts with cff data and 1.0 for ttf - fd.add_u32 (0x00010000); - - if (glyf_table.glyphs.length () == 0) { - warning ("Zero glyphs in maxp table."); - } - - fd.add_u16 ((uint16) glyf_table.glyphs.length ()); // numGlyphs in the font - - fd.add_u16 (glyf_table.get_max_points ()); // max points - fd.add_u16 (glyf_table.get_max_contours ()); // max contours - fd.add_u16 (0); // max composite points - fd.add_u16 (0); // max composite contours - fd.add_u16 (1); // max zones - fd.add_u16 (0); // twilight points - fd.add_u16 (0); // max storage - fd.add_u16 (0); // max function defs - fd.add_u16 (0); // max instruction defs - fd.add_u16 (0); // max stack elements - fd.add_u16 (0); // max size of instructions - fd.add_u16 (0); // max component elements - fd.add_u16 (0); // component depth - - fd.pad (); - - this.font_data = fd; - } -} - -class OffsetTable : Table { - DirectoryTable directory_table; - - public uint16 num_tables = 0; - uint16 search_range = 0; - uint16 entry_selector = 0; - uint16 range_shift = 0; - - public OffsetTable (DirectoryTable t) { - id = "Offset table"; - directory_table = t; - } - - public override void parse (FontData dis) throws Error { - Fixed version; - - dis.seek (offset); - - version = dis.read_fixed (); - num_tables = dis.read_ushort (); - search_range = dis.read_ushort (); - entry_selector = dis.read_ushort (); - range_shift = dis.read_ushort (); - - printd (@"Font file version $(version.get_string ())\n"); - printd (@"Number of tables $num_tables\n"); - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - Fixed version = 0x00010000; // sfnt version 1.0 for TTF CFF else use OTTO - - - num_tables = (uint16) directory_table.get_tables ().length () - 2; // number of tables, skip DirectoryTable and OffsetTable - - search_range = max_pow_2_less_than_i (num_tables) * 16; - entry_selector = max_log_2_less_than_i (num_tables); - range_shift = 16 * num_tables - search_range; - - fd.add_fixed (version); - fd.add_u16 (num_tables); - fd.add_u16 (search_range); - fd.add_u16 (entry_selector); - fd.add_u16 (range_shift); - - // skip padding for offset table - - this.font_data = fd; - } -} - -class NameTable : Table { - - public static const uint16 COPYRIGHT_NOTICE = 0; - public static const uint16 FONT_NAME = 1; - public static const uint16 SUBFAMILY_NAME = 2; - public static const uint16 UNIQUE_IDENTIFIER = 3; - public static const uint16 FULL_FONT_NAME = 4; // name + subfamily - public static const uint16 VERSION = 5; - public static const uint16 DESCRIPTION = 10; - public static const uint16 PREFERED_FAMILY = 16; - public static const uint16 PREFERED_SUB_FAMILY = 17; - - List<uint16> identifiers; - List<string> text; - - public NameTable () { - id = "name"; - text = new List<string> (); - identifiers = new List<uint16> (); - } - - public string get_name (uint16 identifier) { - int i = 0; - - foreach (uint16 n in identifiers) { - if (n == identifier) { - return text.nth (i).data; - } - i++; - } - - return ""; - } - - public override void parse (FontData dis) throws Error { - uint16 format; - - dis.seek (offset); - - format = dis.read_ushort (); - - switch (format) { - case 0: - parse_format0 (dis); - break; - - case 1: - warning ("name table format 1 is not implemented yet"); - break; - - default: - warning (@"unknown format $format in name table"); - break; - } - } - - public void parse_format0 (FontData dis) throws Error { - uint16 count; - uint16 storage_offset; - - List<uint16> strlen = new List<uint16> (); - List<uint16> off = new List<uint16> (); - List<uint16> name_id = new List<uint16> (); - List<uint16> encoding_id = new List<uint16> (); - List<uint16> platform = new List<uint16> (); - List<uint16> lang = new List<uint16> (); - - count = dis.read_ushort (); - storage_offset = dis.read_ushort (); - - for (int i = 0; i < count; i++) { - platform.append (dis.read_ushort ()); - encoding_id.append (dis.read_ushort ()); - lang.append (dis.read_ushort ()); - name_id.append (dis.read_ushort ()); - strlen.append (dis.read_ushort ()); - off.append (dis.read_ushort ()); - - identifiers.append (name_id.last ().data); - } - - int plat; - StringBuilder str; - for (int i = 0; i < count; i++) { - plat = platform.nth (i).data; - dis.seek (offset + storage_offset + off.nth (i).data); - str = new StringBuilder (); - - switch (plat) { - case 1: - for (int j = 0; j < strlen.nth (i).data; j++) { - char c = dis.read_char (); - str.append_c (c); - } - break; - - case 3: - for (int j = 0; j < strlen.nth (i).data; j += 2) { - unichar c; - char c0 = dis.read_char (); - char c1 = dis.read_char (); - - c = c0 << 8; - c += c1; - - str.append_unichar (c); - } - break; - - default: - break; - } - - text.append (str.str); - printd (@"Name id: $(name_id.nth (i).data) platform: $(platform.nth (i).data) enc: $(encoding_id.nth (i).data) lang: $(lang.nth (i).data) len: $(strlen.nth (i).data) str: \"$(str.str)\"\n"); - } - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - Font font = OpenFontFormatWriter.get_current_font (); - uint16 len = 0; - string t; - uint16 p; - uint16 l; - uint16 num_records; - - List<uint16> type = new List<uint16> (); - List<string> text = new List<string> (); - - text.append ("Copyright"); - type.append (COPYRIGHT_NOTICE); - - text.append (font.get_name ()); - type.append (FONT_NAME); - - text.append ("Regular"); - type.append (SUBFAMILY_NAME); - - text.append (font.get_name ()); // TODO: validate - type.append (UNIQUE_IDENTIFIER); - - text.append (font.get_name ()); - type.append (FULL_FONT_NAME); - - // This does for some reason cause an internal error in ms fontvalidatior utility. - // Head table can't parse integer from string. - text.append ("Version 1.0"); - type.append (VERSION); - - text.append (""); - type.append (DESCRIPTION); - - text.append (font.get_name ()); - type.append (PREFERED_FAMILY); - - text.append ("Regular"); - type.append (PREFERED_SUB_FAMILY); - - num_records = (uint16) text.length (); - - fd.add_ushort (0); // format 1 - fd.add_ushort (2 * num_records); // nplatforms * nrecords - fd.add_ushort (6 + 12 * 2 * num_records); // string storage offset - - for (int i = 0; i < num_records; i++) { - t = (!) text.nth (i).data; - p = (!) type.nth (i).data; - l = (uint16) t.length; - - fd.add_ushort (1); // platform - fd.add_ushort (0); // encoding id - fd.add_ushort (0); // language - fd.add_ushort (p); // name id - fd.add_ushort (l); // strlen - fd.add_ushort (len); // offset from begining of string storage - len += l; - } - - for (int i = 0; i < num_records; i++) { - t = (!) text.nth (i).data; - p = (!) type.nth (i).data; - l = (uint16) (2 * t.char_count ()); - - fd.add_ushort (3); // platform - fd.add_ushort (1); // encoding id - fd.add_ushort (0x0409); // language - fd.add_ushort (p); // name id - fd.add_ushort (l); // strlen - fd.add_ushort (len); // offset from begining of string storage - len += l; - } - - // platform 1 - foreach (string s in text) { - fd.add_str (s); - } - - // platform 3 - foreach (string s in text) { - fd.add_str_utf16 (s); - } - - fd.pad (); - - this.font_data = fd; - } -} - -class Os2Table : Table { - - public Os2Table () { - id = "OS/2"; - } - - public override void parse (FontData dis) throws Error { - - } - - public void process (GlyfTable glyf_table) { - FontData fd = new FontData (); - Font font = OpenFontFormatWriter.get_current_font (); - - int16 ascender; - int16 descender; - - fd.add_u16 (0x0002); // USHORT Version 0x0000, 0x0001, 0x0002, 0x0003, 0x0004 - - fd.add_16 (glyf_table.get_average_width ()); // SHORT xAvgCharWidth - - fd.add_u16 (400); // USHORT usWeightClass (400 is normal) - fd.add_u16 (5); // USHORT usWidthClass (5 is normal) - fd.add_u16 (0); // USHORT fsType - - fd.add_16 (40); // SHORT ySubscriptXSize - fd.add_16 (40); // SHORT ySubscriptYSize - fd.add_16 (40); // SHORT ySubscriptXOffset - fd.add_16 (40); // SHORT ySubscriptYOffset - fd.add_16 (40); // SHORT ySuperscriptXSize - fd.add_16 (40); // SHORT ySuperscriptYSize - fd.add_16 (40); // SHORT ySuperscriptXOffset - fd.add_16 (40); // SHORT ySuperscriptYOffset - fd.add_16 (40); // SHORT yStrikeoutSize - fd.add_16 (200); // SHORT yStrikeoutPosition - fd.add_16 (40); // SHORT sFamilyClass - - // FIXME: PANOSE - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - fd.add (0); - - // FIXME: - fd.add_u32 (0); // ulUnicodeRange1 Bits 0-31 - fd.add_u32 (0); // ULONG ulUnicodeRange2 Bits 32-63 - fd.add_u32 (0); // ULONG ulUnicodeRange3 Bits 64-95 - fd.add_u32 (0); // ULONG ulUnicodeRange4 Bits 96-127 - - fd.add_tag ("----"); // VendID - - fd.add_u16 (0); // USHORT fsSelection - - fd.add_u16 (glyf_table.get_first_char ()); // USHORT usFirstCharIndex - fd.add_u16 (glyf_table.get_last_char ()); // USHORT usLastCharIndex - - ascender = (int16) (glyf_table.xmax + font.base_line * UNITS); - descender = (int16) (-glyf_table.xmin + font.base_line * UNITS); - - fd.add_16 (ascender); // SHORT sTypoAscender - fd.add_16 (descender); // SHORT sTypoDescender - fd.add_16 (3); // SHORT sTypoLineGap - - fd.add_u16 (ascender); // USHORT usWinAscent - fd.add_u16 (descender); // USHORT usWinDescent - - // FIXA: - fd.add_u32 (0); // ULONG ulCodePageRange1 Bits 0-31 - fd.add_u32 (0); // ULONG ulCodePageRange2 Bits 32-63 - - fd.add_16 (ascender); // SHORT sxHeight version 0x0002 and later - fd.add_16 (ascender); // SHORT sCapHeight version 0x0002 and later - - fd.add_16 (0); // USHORT usDefaultChar version 0x0002 and later - fd.add_16 (0x0020); // USHORT usBreakChar version 0x0002 and later, also known as space - - // FIXA: calculate these values - fd.add_16 (1); // USHORT usMaxContext version 0x0002 and later - - // padding - fd.pad (); - - this.font_data = fd; - } - -} - -class PostTable : Table { - - GlyfTable glyf_table; - - List<uint16> index = new List<uint16> (); - List<string> names = new List<string> (); - - List<string> available_names = new List<string> (); - - public PostTable (GlyfTable g) { - id = "post"; - glyf_table = g; - } - - public int get_gid (string name) { // FIXME: do fast lookup - int i = 0; - int j = 0; - foreach (string n in names) { - if (n == name) { - j = 0; - foreach (uint16 k in index) { - if (k == i) { - return j; - } - j++; - } - - return i; - } - i++; - } - return -1; - } - - public string get_name (int gid) { - int k; - - if (!(0 <= gid < index.length ())) { - warning ("gid is out of range."); - return ""; - } - - k = (!) index.nth (gid).data; - - if (gid != 0 && k == 0) { - warning (@"Glyph $gid is assigned to name .notdef, only gid 0 can be .notdef character."); - return ""; - } - - if (!(0 <= k < names.length ())) { - warning ("k is out of range."); - return ""; - } - - return (!) names.nth (k).data; - } - - public override void parse (FontData dis) throws Error { - dis.seek (offset); - - Fixed format = dis.read_fixed (); - Fixed italic = dis.read_fixed (); - - int16 underlie_pos = dis.read_short (); - int16 underlie_thickness = dis.read_short (); - uint32 is_fixed_pitch = dis.read_ulong (); - - uint32 mem_min42 = dis.read_ulong (); - uint32 mem_max42 = dis.read_ulong (); - uint32 mem_min1 = dis.read_ulong (); - uint32 mem_max1 = dis.read_ulong (); - - uint16 nnames = dis.read_ushort (); - - if (format != 0x00020000) { - warning ("Only post tables of version 2 will parset got $(format.get_string ())"); - return; - } - - printd (@"format: $(format.get_string ())\n"); - printd (@"italic: $(italic.get_string ())\n"); - printd (@"underlie_pos: $(underlie_pos)\n"); - printd (@"underlie_thickness: $(underlie_thickness)\n"); - printd (@"is_fixed_pitch: $(is_fixed_pitch)\n"); - printd (@"mem_min42: $(mem_min42)\n"); - printd (@"mem_max42: $(mem_max42)\n"); - printd (@"mem_min1: $(mem_min1)\n"); - printd (@"mem_max1: $(mem_max1)\n"); - printd (@"\n"); - - printd (@"Num names: $(nnames)\n"); - - uint16 k; - int non_standard_names = 0; - for (uint16 i = 0; i < nnames; i++) { - k = dis.read_ushort (); - index.append (k); - - if (k >= 258) { - non_standard_names++; - } - } - - add_standard_names (); - - // read non standard names - for (int i = 0; i < non_standard_names; i++) { - uint8 len = dis.read_byte (); - StringBuilder name = new StringBuilder (); - - for (int j = 0; j < len; j++) { - name.append_c (dis.read_char ()); - } - - names.append (name.str); - } - - populate_available (); - } - - void populate_available () { - for (int i = 0; i < index.length (); i++) { - available_names.append (get_name (i)); - } - } - - // the Macintosh standard order - private void add_standard_names () { - names.append (".notdef"); - names.append (".null"); - names.append ("nonmarkingreturn"); - names.append ("space"); - names.append ("exclam"); - names.append ("quotedbl"); - names.append ("numbersign"); - names.append ("dollar"); - names.append ("percent"); - names.append ("ampersand"); - names.append ("quotesingle"); - names.append ("parenleft"); - names.append ("parenright"); - names.append ("asterisk"); - names.append ("plus"); - names.append ("comma"); - names.append ("hyphen"); - names.append ("period"); - names.append ("slash"); - names.append ("zero"); - names.append ("one"); - names.append ("two"); - names.append ("three"); - names.append ("four"); - names.append ("five"); - names.append ("six"); - names.append ("seven"); - names.append ("eight"); - names.append ("nine"); - names.append ("colon"); - names.append ("semicolon"); - names.append ("less"); - names.append ("equal"); - names.append ("greater"); - names.append ("question"); - names.append ("at"); - names.append ("A"); - names.append ("B"); - names.append ("C"); - names.append ("D"); - names.append ("E"); - names.append ("F"); - names.append ("G"); - names.append ("H"); - names.append ("I"); - names.append ("J"); - names.append ("K"); - names.append ("L"); - names.append ("M"); - names.append ("N"); - names.append ("O"); - names.append ("P"); - names.append ("Q"); - names.append ("R"); - names.append ("S"); - names.append ("T"); - names.append ("U"); - names.append ("V"); - names.append ("W"); - names.append ("X"); - names.append ("Y"); - names.append ("Z"); - names.append ("bracketleft"); - names.append ("backslash"); - names.append ("bracketright"); - names.append ("asciicircum"); - names.append ("underscore"); - names.append ("grave"); - names.append ("a"); - names.append ("b"); - names.append ("c"); - names.append ("d"); - names.append ("e"); - names.append ("f"); - names.append ("g"); - names.append ("h"); - names.append ("i"); - names.append ("j"); - names.append ("k"); - names.append ("l"); - names.append ("m"); - names.append ("n"); - names.append ("o"); - names.append ("p"); - names.append ("q"); - names.append ("r"); - names.append ("s"); - names.append ("t"); - names.append ("u"); - names.append ("v"); - names.append ("w"); - names.append ("x"); - names.append ("y"); - names.append ("z"); - names.append ("braceleft"); - names.append ("bar"); - names.append ("braceright"); - names.append ("asciitilde"); - names.append ("Adieresis"); - names.append ("Aring"); - names.append ("Ccedilla"); - names.append ("Eacute"); - names.append ("Ntilde"); - names.append ("Odieresis"); - names.append ("Udieresis"); - names.append ("aacute"); - names.append ("agrave"); - names.append ("acircumflex"); - names.append ("adieresis"); - names.append ("atilde"); - names.append ("aring"); - names.append ("ccedilla"); - names.append ("eacute"); - names.append ("egrave"); - names.append ("ecircumflex"); - names.append ("edieresis"); - names.append ("iacute"); - names.append ("igrave"); - names.append ("icircumflex"); - names.append ("idieresis"); - names.append ("ntilde"); - names.append ("oacute"); - names.append ("ograve"); - names.append ("ocircumflex"); - names.append ("odieresis"); - names.append ("otilde"); - names.append ("uacute"); - names.append ("ugrave"); - names.append ("ucircumflex"); - names.append ("udieresis"); - names.append ("dagger"); - names.append ("degree"); - names.append ("cent"); - names.append ("sterling"); - names.append ("section"); - names.append ("bullet"); - names.append ("paragraph"); - names.append ("germandbls"); - names.append ("registered"); - names.append ("copyright"); - names.append ("trademark"); - names.append ("acute"); - names.append ("dieresis"); - names.append ("notequal"); - names.append ("AE"); - names.append ("Oslash"); - names.append ("infinity"); - names.append ("plusminus"); - names.append ("lessequal"); - names.append ("greaterequal"); - names.append ("yen"); - names.append ("mu"); - names.append ("partialdiff"); - names.append ("summation"); - names.append ("product"); - names.append ("pi"); - names.append ("integral"); - names.append ("ordfeminine"); - names.append ("ordmasculine"); - names.append ("Omega"); - names.append ("ae"); - names.append ("oslash"); - names.append ("questiondown"); - names.append ("exclamdown"); - names.append ("logicalnot"); - names.append ("radical"); - names.append ("florin"); - names.append ("approxequal"); - names.append ("Delta"); - names.append ("guillemotleft"); - names.append ("guillemotright"); - names.append ("ellipsis"); - names.append ("nonbreakingspace"); - names.append ("Agrave"); - names.append ("Atilde"); - names.append ("Otilde"); - names.append ("OE"); - names.append ("oe"); - names.append ("endash"); - names.append ("emdash"); - names.append ("quotedblleft"); - names.append ("quotedblright"); - names.append ("quoteleft"); - names.append ("quoteright"); - names.append ("divide"); - names.append ("lozenge"); - names.append ("ydieresis"); - names.append ("Ydieresis"); - names.append ("fraction"); - names.append ("currency"); - names.append ("guilsinglleft"); - names.append ("guilsinglright"); - names.append ("fi"); - names.append ("fl"); - names.append ("daggerdbl"); - names.append ("periodcentered"); - names.append ("quotesinglbase"); - names.append ("quotedblbase"); - names.append ("perthousand"); - names.append ("Acircumflex"); - names.append ("Ecircumflex"); - names.append ("Aacute"); - names.append ("Edieresis"); - names.append ("Egrave"); - names.append ("Iacute"); - names.append ("Icircumflex"); - names.append ("Idieresis"); - names.append ("Igrave"); - names.append ("Oacute"); - names.append ("Ocircumflex"); - names.append ("apple"); - names.append ("Ograve"); - names.append ("Uacute"); - names.append ("Ucircumflex"); - names.append ("Ugrave"); - names.append ("dotlessi"); - names.append ("circumflex"); - names.append ("tilde"); - names.append ("macron"); - names.append ("breve"); - names.append ("dotaccent"); - names.append ("ring"); - names.append ("cedilla"); - names.append ("hungarumlaut"); - names.append ("ogonek"); - names.append ("caron"); - names.append ("Lslash"); - names.append ("lslash"); - names.append ("Scaron"); - names.append ("scaron"); - names.append ("Zcaron"); - names.append ("zcaron"); - names.append ("brokenbar"); - names.append ("Eth"); - names.append ("eth"); - names.append ("Yacute"); - names.append ("yacute"); - names.append ("Thorn"); - names.append ("thorn"); - names.append ("minus"); - names.append ("multiply"); - names.append ("onesuperior"); - names.append ("twosuperior"); - names.append ("threesuperior"); - names.append ("onehalf"); - names.append ("onequarter"); - names.append ("threequarters"); - names.append ("franc"); - names.append ("Gbreve"); - names.append ("gbreve"); - names.append ("Idotaccent"); - names.append ("Scedilla"); - names.append ("scedilla"); - names.append ("Cacute"); - names.append ("cacute"); - names.append ("Ccaron"); - names.append ("ccaron"); - names.append ("dcroat"); - } - - // mapping with char code to standard order - int get_standard_index (unichar c) { - switch (c) { - // entry 0 is the .notdef - - case '\0': - return 1; - - case '\r': - return 2; - - case ' ': // space - return 3; - - case '!': - return 4; - - case '"': - return 5; - - case '#': - return 6; - - case '$': - return 7; - - case '%': - return 8; - - case '&': - return 9; - - case '\'': - return 10; - - case '(': - return 11; - - case ')': - return 12; - - case '*': - return 13; - - case '+': - return 14; - - case ',': - return 15; - - case '-': - return 16; - - case '.': - return 17; - - case '/': - return 18; - - case '0': - return 19; - - case '1': - return 20; - - case '2': - return 21; - - case '3': - return 22; - - case '4': - return 23; - - case '5': - return 24; - - case '6': - return 25; - - case '7': - return 26; - - case '8': - return 27; - - case '9': - return 28; - - case ':': - return 29; - - case ';': - return 30; - - case '<': - return 31; - - case '=': - return 32; - - case '>': - return 33; - - case '?': - return 34; - - case '@': - return 35; - - case 'A': - return 36; - - case 'B': - return 37; - - case 'C': - return 38; - - case 'D': - return 39; - - case 'E': - return 40; - - case 'F': - return 41; - - case 'G': - return 42; - - case 'H': - return 43; - - case 'I': - return 44; - - case 'J': - return 45; - - case 'K': - return 46; - - case 'L': - return 47; - - case 'M': - return 48; - - case 'N': - return 49; - - case 'O': - return 50; - - case 'P': - return 51; - - case 'Q': - return 52; - - case 'R': - return 53; - - case 'S': - return 54; - - case 'T': - return 55; - - case 'U': - return 56; - - case 'V': - return 57; - - case 'W': - return 58; - - case 'X': - return 59; - - case 'Y': - return 60; - - case 'Z': - return 61; - - case '[': - return 62; - - case '\\': - return 63; - - case ']': - return 64; - - case '^': - return 65; - - case '_': - return 66; - - case '`': - return 67; - - case 'a': - return 68; - - case 'b': - return 69; - - case 'c': - return 70; - - case 'd': - return 71; - - case 'e': - return 72; - - case 'f': - return 73; - - case 'g': - return 74; - - case 'h': - return 75; - - case 'i': - return 76; - - case 'j': - return 77; - - case 'k': - return 78; - - case 'l': - return 79; - - case 'm': - return 80; - - case 'n': - return 81; - - case 'o': - return 82; - - case 'p': - return 83; - - case 'q': - return 84; - - case 'r': - return 85; - - case 's': - return 86; - - case 't': - return 87; - - case 'u': - return 88; - - case 'v': - return 89; - - case 'w': - return 90; - - case 'x': - return 91; - - case 'y': - return 92; - - case 'z': - return 93; - - case '{': - return 94; - - case '|': - return 95; - - case '}': - return 96; - - case '~': - return 97; - - case 'ร': - return 98; - - case 'ร ': - return 99; - - case 'ร': - return 100; - - case 'ร': - return 101; - - case 'ร': - return 102; - - case 'ร': - return 103; - - case 'ร': - return 104; - - case 'รก': - return 105; - - case 'ร ': - return 106; - - case 'รข': - return 107; - - case 'รค': - return 108; - - case 'รฃ': - return 109; - - case 'รฅ': - return 110; - - case 'รง': - return 111; - - case 'รฉ': - return 112; - - case 'รจ': - return 113; - - case 'รช': - return 114; - - case 'รซ': - return 115; - - case 'รญ': - return 116; - - case 'รฌ': - return 117; - - case 'รฎ': - return 118; - - case 'รฏ': - return 119; - - case 'รฑ': - return 120; - - case 'รณ': - return 121; - - case 'รฒ': - return 122; - - case 'รด': - return 123; - - case 'รถ': - return 124; - - case 'รต': - return 125; - - case 'รบ': - return 126; - - case 'รน': - return 127; - - case 'รป': - return 128; - - case 'รผ': - return 129; - - case 'โ ': - return 130; - - case 'ยฐ': - return 131; - - case 'ยข': - return 132; - - case 'ยฃ': - return 133; - - case 'ยง': - return 134; - - case 'โข': - return 135; - - case 'ยถ': - return 136; - - case 'ร': - return 137; - - case 'ยฎ': - return 138; - - case 'ยฉ': - return 139; - - case 'โข': - return 140; - - case 'ยด': - return 141; - - case 'ยจ': - return 142; - - case 'โ ': - return 143; - - case 'ร': - return 144; - - case 'ร': - return 145; - - case 'โ': - return 146; - - case 'ยฑ': - return 147; - - case 'โค': - return 148; - - case 'โฅ': - return 149; - - case 'ยฅ': - return 150; - - case 'ยต': - return 151; - - case 'โ': - return 152; - - case 'โ': - return 153; - - case 'โ': - return 154; - - case 'ฯ': - return 155; - - case 'โซ': - return 156; - - case 'ยช': - return 157; - - case 'ยบ': - return 158; - - case 'โฆ': - return 159; - - case 'รฆ': - return 160; - - case 'รธ': - return 161; - - case 'ยฟ': - return 162; - - case 'ยก': - return 163; - - case 'ยฌ': - return 164; - - case 'โ': - return 165; - - case 'ฦ': - return 166; - - case 'โ': - return 167; - - case 'โ': - return 168; - - case 'ยซ': - return 169; - - case 'ยป': - return 170; - - case 'โฆ': - return 171; - - case 'ย ': // non breaking space - return 172; - - case 'ร': - return 173; - - case 'ร': - return 174; - - case 'ร': - return 175; - - case 'ล': - return 176; - - case 'ล': - return 177; - - case 'โ': - return 178; - - case 'โ': - return 179; - - case 'โ': - return 180; - - case 'โ': - return 181; - - case 'โ': - return 182; - - case 'โ': - return 183; - - case 'รท': - return 184; - - case 'โ': - return 185; - - case 'รฟ': - return 186; - - case 'ลธ': - return 187; - - case 'โ': - return 188; - - case 'ยค': - return 189; - - case 'โน': - return 190; - - case 'โบ': - return 191; - - case '๏ฌ': - return 192; - - case '๏ฌ': - return 193; - - case 'โก': - return 194; - - case 'ยท': - return 195; - - case 'โ': - return 196; - - case 'โ': - return 197; - - case 'โฐ': - return 198; - - case 'ร': - return 199; - - case 'ร': - return 200; - - case 'ร': - return 201; - - case 'ร': - return 202; - - case 'ร': - return 203; - - case 'ร': - return 204; - - case 'ร': - return 205; - - case 'ร': - return 206; - - case 'ร': - return 207; - - case 'ร': - return 208; - - case 'ร': - return 209; - - // Machintosh apple goes here - // return 210; - - case 'ร': - return 211; - - case 'ร': - return 212; - - case 'ร': - return 213; - - case 'ร': - return 214; - - case 'ฤฑ': - return 215; - - case 'ห': - return 216; - - case 'ห': - return 217; - - case 'ยฏ': - return 218; - - case 'ห': - return 219; - - case 'ห': - return 220; - - case 'ห': - return 221; - - case 'ยธ': - return 222; - - case 'ห': - return 223; - - case 'ห': - return 224; - - case 'ห': - return 225; - - case 'ล': - return 226; - - case 'ล': - return 227; - - case 'ล ': - return 228; - - case 'ลก': - return 229; - - case 'ลฝ': - return 230; - - case 'ลพ': - return 231; - - case 'ยฆ': - return 232; - - case 'ร': - return 233; - - case 'รฐ': - return 234; - - case 'ร': - return 235; - - case 'รฝ': - return 236; - - case 'ร': - return 237; - - case 'รพ': - return 238; - - case 'โ': - return 239; - - case 'ร': - return 240; - - case 'ยน': - return 241; - - case 'ยฒ': - return 242; - - case 'ยณ': - return 243; - - case 'ยฝ': - return 244; - - case 'ยผ': - return 245; - - case 'ยพ': - return 246; - - case 'โฃ': - return 247; - - case 'ฤ': - return 248; - - case 'ฤ': - return 249; - - case 'ฤฐ': - return 250; - - case 'ล': - return 251; - - case 'ล': - return 252; - - case 'ฤ': - return 253; - - case 'ฤ': - return 254; - - case 'ฤ': - return 255; - - case 'ฤ': - return 256; - - case 'ฤ': - return 257; - } - - return 0; - } - - public void process () throws Error { - FontData fd = new FontData (); - string n; - - fd.add_fixed (0x00020000); // Version - fd.add_fixed (0x00000000); // italicAngle - - fd.add_short (-2); // underlinePosition - fd.add_short (1); // underlineThickness - - fd.add_ulong (0); // non zero for monospaced font - - // mem boundries may be omitted - fd.add_ulong (0); // min mem for type 42 - fd.add_ulong (0); // max mem for type 42 - - fd.add_ulong (0); // min mem for Type1 - fd.add_ulong (0); // max mem for Type1 - - fd.add_ushort ((uint16) glyf_table.glyphs.length ()); - - // this part of the spec is so weird - - fd.add_ushort ((uint16) 0); // first index is .notdef - index.append (0); - - assert (names.length () == 0); - add_standard_names (); - - int index; - Glyph g; - for (int i = 1; i < glyf_table.glyphs.length (); i++) { - g = (!) glyf_table.glyphs.nth (i).data; - index = get_standard_index (g.unichar_code); - - if (index != 0) { - fd.add_ushort ((uint16) index); // use standard name - } else { - printd (@"Adding non standard postscript name $(g.get_name ())\n"); - - index = (int) names.length (); // use font specific name - fd.add_ushort ((uint16) index); - names.append (g.get_name ()); - } - - this.index.append ((uint16) index); - } - - for (int i = 258; i < names.length (); i++) { - n = (!) names.nth (i).data; - - if (n.length > 0xFF) { - warning (@"too long name for glyph $n"); - } - - fd.add ((uint8) n.length); // length of string - fd.add_str (n); - } - - fd.pad (); - - this.font_data = fd; - } - -} - -/** Type2, PostScript outlines */ -class CffTable : Table { - - static const uint8 HSTEM = 1; - static const uint8 VSTEM = 3; - static const uint8 VMOVETO = 4; - static const uint8 HLINETO = 6; - static const uint8 VLINETO = 7; - static const uint8 ENDCHAR = 14; - static const uint8 HMOVETO = 22; - static const uint8 RMOVETO = 21; - - static const uint8 CHARSET = 15; - static const uint8 ENCODING = 16; - - static const uint8 VERSION = 0; - - public int version; - - FontData dis; - - public CffTable () { - id = "CFF "; - } - - uint32 read_offset (uint size) throws GLib.Error { - switch (size) { - case 0: - warning ("offset size is zero"); - return dis.read_byte (); - case 1: - return dis.read_byte (); - case 2: - return dis.read_ushort (); - case 4: - return dis.read_ulong (); - default: - warn_if_reached (); - break; - } - - warning ("offset size is zero"); - return 0; - } - - List<uint32> read_index () throws Error { - uint32 offset_size, off; - int entries; - List<uint32> offsets = new List<uint32> (); - - entries = dis.read_ushort (); - printd (@"number of entries $(entries)\n"); - - if (entries == 0) { - printd ("skip index"); - return offsets; - } - - offset_size = dis.read (); - printd (@"Offset size $(offset_size)\n"); - - // read the end offset as well - for (int i = 0; i <= entries; i++) { - off = read_offset (offset_size); - printd (@"offset $(off)\n"); - offsets.append (off); - } - - return offsets; - } - - public override void parse (FontData dis) throws Error { - uint v1, v2, offset_size, header_size, len; - string data; - List<uint32> offsets, dict_index; - int id, val; - int off; // offset relative to table position - - dis.seek (offset); - this.dis = dis; - - printd ("Parse CFF.\n"); - v1 = dis.read (); - v2 = dis.read (); - printd (@"Version $v1.$v2\n"); - header_size = dis.read (); - printd (@"Header size $(header_size)\n"); - offset_size = dis.read (); - printd (@"Offset size $(offset_size)\n"); - - // name index - offsets = read_index (); - - // name data - for (int i = 0; i < offsets.length () - 1; i++) { - off = (int) offsets.nth (i).data; - len = offsets.nth (i + 1).data - off; - //dis.seek (offset + off + header_size); - data = dis.read_string (len); - print (@"Found name $data\n"); - } - - // dict index - print (@"dict index\n"); - dict_index = read_index (); - - // dict data - id = 0; - val = 0; - for (int i = 0; i < dict_index.length () - 1; i++) { - off = (int) offsets.nth (i).data; - len = dict_index.nth (i + 1).data - dict_index.nth (i).data; - //dis.seek (offset + off + header_size); - - //for (int j = 0; j < len; j++) { - - if (dis.next_is_operator ()) { - id = dis.read (); - - if (id == 12) { - id = dis.read (); - } else { - switch (id) { - case 0: - version = val; - break; - default: - stderr.printf ("unknown operator"); - break; - } - } - } else { - val = dis.read_charstring_value (); - } - - printd (@"$i: id $(id)\n"); - printd (@"val $(val)\n"); - //printd (@"B $(dis.read ())\n"); - //} - } - - // string index - read_index (); - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - string name = "typeface"; - - // header - fd.add_byte (1); // format version (1.0) - fd.add_byte (0); - - fd.add_byte (4); // header size - fd.add_byte (2); // offset field size - ushort - - // name index: - fd.add_ushort (1); // number of entries - fd.add_byte (2); // offset field size - fd.add_ushort (1); // offset - fd.add ((uint8) name.length); // length of string - fd.add_str (name); - - // top dict index - fd.add_ushort (1); // number of entries - fd.add_byte (2); // offset field size - fd.add_ushort (1); // offset - fd.add_ushort (2); // offset - - fd.add_charstring_value (0); - fd.add_byte (CHARSET); - - // string index - fd.add_byte (0); - - // TODO: glyph gid to cid map - fd.add_byte (0); - - fd.pad (); - - this.font_data = fd; - } -} - -class GaspTable : Table { - - public GaspTable () { - id = "gasp"; - } - - public override void parse (FontData dis) throws Error { - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - - fd.add_ushort (0); - fd.add_ushort (0); - - fd.pad (); - - this.font_data = fd; - } - -} - -class GdefTable : Table { - - public GdefTable () { - id = "GDEF"; - } - - public override void parse (FontData dis) throws Error { - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - - fd.add_ulong (0x00010002); - fd.add_ushort (0); // class def - fd.add_ushort (0); // attach list - fd.add_ushort (0); // ligature carret - fd.add_ushort (0); // mark attach - fd.add_ushort (0); // mark glyf - fd.add_ushort (0); // mark glyf set def - - fd.pad (); - - this.font_data = fd; - } - -} - -class CvtTable : Table { - - public CvtTable () { - id = "cvt "; - } - - public override void parse (FontData dis) throws Error { - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - - fd.add_ushort (0); - fd.pad (); - - this.font_data = fd; - } - -} - -class Kern : GLib.Object { - public uint16 left; - public uint16 right; - public int16 kerning; - - public Kern (uint16 l, uint16 r, int16 k) { - left = l; - right = r; - kerning = k; - } -} - -class KernList : GLib.Object { - public List<Kern> kernings; - - public KernList () { - kernings = new List<Kern> (); - } -} - -class KernTable : Table { - - public static const uint16 HORIZONTAL = 1; - public static const uint16 MINIMUM = 1 << 1; - public static const uint16 CROSS_STREAM = 1 << 2; - public static const uint16 OVERRIDE = 1 << 3; - public static const uint16 FORMAT = 1 << 8; - - GlyfTable glyf_table; - - public List<Kern> kernings = new List<Kern> (); - public int kerning_pairs = 0; - - public KernTable (GlyfTable gt) { - glyf_table = gt; - id = "kern"; - } - - public KernList get_all_pairs (int gid) { - KernList kl = new KernList (); - - foreach (Kern k in kernings) { - if (k.left == gid) { - kl.kernings.append (k); - } - } - - return kl; - } - - public override void parse (FontData dis) throws GLib.Error { - uint16 version; - uint16 sub_tables; - - uint16 subtable_version; - uint16 subtable_length; - uint16 subtable_flags; - - uint16 search_range; - uint16 entry_selector; - uint16 range_shift; - - uint16 n_pairs; - - dis.seek (offset); - - version = dis.read_ushort (); - warn_if_fail (version == 0); - sub_tables = dis.read_ushort (); - - for (uint16 i = 0; i < sub_tables; i++) { - subtable_version = dis.read_ushort (); - subtable_length = dis.read_ushort (); - subtable_flags = dis.read_ushort (); - - n_pairs = dis.read_ushort (); - search_range = dis.read_ushort (); - entry_selector = dis.read_ushort (); - range_shift = dis.read_ushort (); - - // TODO: check more flags - if ((subtable_flags & HORIZONTAL) > 0 && (subtable_flags & CROSS_STREAM) == 0 && (subtable_flags & MINIMUM) == 0) { - parse_pairs (dis, n_pairs); - } - } - } - - public void parse_pairs (FontData dis, uint16 n_pairs) throws Error { - uint16 left; - uint16 right; - int16 kerning; - - for (int i = 0; i < n_pairs; i++) { - left = dis.read_ushort (); - right = dis.read_ushort (); - kerning = dis.read_short (); - - kernings.append (new Kern (left, right, kerning)); - } - } - - public void process () throws GLib.Error { - FontData fd = new FontData (); - uint16 n_pairs = 0; - - uint16 gid_left; - int gid_right; - - uint16 range_shift = 0; - uint16 entry_selector = 0; - uint16 search_range = 0; - - Kern kern; - int i; - - fd.add_ushort (0); // version - fd.add_ushort (1); // n subtables - - fd.add_ushort (0); // subtable version - - foreach (Glyph g in glyf_table.glyphs) { - foreach (Kerning k in g.kerning) { - n_pairs++; - } - } - - if (n_pairs > (uint16.MAX - 14) / 6.0) { - warning ("Too many kerning pairs!"); - n_pairs = (uint16) ((uint16.MAX - 14) / 6.0); - } - - this.kerning_pairs = n_pairs; - - fd.add_ushort (6 * n_pairs + 14); // subtable length - fd.add_ushort (HORIZONTAL); // subtable flags - - fd.add_ushort (n_pairs); - - search_range = 6 * largest_pow2 (n_pairs); - entry_selector = largest_pow2_exponent (n_pairs); - range_shift = 6 * n_pairs - search_range; - - fd.add_ushort (search_range); - fd.add_ushort (entry_selector); - fd.add_ushort (range_shift); - - gid_left = 0; - - i = 0; - foreach (Glyph g in glyf_table.glyphs) { - - foreach (Kerning k in g.kerning) { - // n_pairs is used to truncate this table to prevent buffer overflow - if (n_pairs == i++) { - break; - } - - gid_right = glyf_table.get_gid (k.glyph_right); - - if (gid_right == -1) { - warning ("right glyph not found in kerning table"); - } - - kern = new Kern (gid_left, (uint16)gid_right, (int16) (k.val * UNITS)); - - fd.add_ushort (kern.left); - fd.add_ushort (kern.right); - fd.add_short (kern.kerning); - - kernings.append (kern); - } - - gid_left++; - } - - fd.pad (); - this.font_data = fd; - } - -} - -class GposTable : Table { - - public GposTable () { - id = "GPOS"; - } - - public override void parse (FontData dis) throws Error { - warning ("Not implemented."); - } - - public void process (KernTable kern_table) throws GLib.Error { - FontData fd = new FontData (); - uint16 num_kerning_values = (uint16) kern_table.kernings.length (); - int i; - int size_of_pair; - - if (num_kerning_values == 0) { - warning ("No kerning to add to gpos table."); - } - - printd (@"Adding $num_kerning_values kerning pairs to gpos table.\n"); - - fd.add_ulong (0x00010000); // table version - fd.add_ushort (10); // offset to script list - fd.add_ushort (24); // offset to feature list - fd.add_ushort (38); // offset to lookup list - - // script list ? - fd.add_ushort (0); // number of items in script list - - // script table - fd.add_ushort (4); // offset to default language system - fd.add_ushort (0); // number of languages - - // script record, none right now - - // LangSys table - fd.add_ushort (0); // reserved - fd.add_ushort (0xFFFF); // required features (0xFFFF is none) - fd.add_ushort (1); // number of features - fd.add_ushort (0); // feature index - - // feature table - fd.add_ushort (1); // number of features - - fd.add_tag ("kern"); // feature tag - fd.add_ushort (8); // offset to feature - - fd.add_ushort (0); // feature prameters (null) - fd.add_ushort (1); // number of lookups - fd.add_ushort (0); // lookup indice - - // lookup table - fd.add_ushort (1); // number of lookups - fd.add_ushort (4); // offset to lookup 1 - - fd.add_ushort (2); // lookup type // FIXME - fd.add_ushort (0); // lookup flags - fd.add_ushort (1); // number of subtables - fd.add_ushort (8); // array of offsets to subtables - - // MarkFilteringSet - - // the kerning pair table - size_of_pair = 6; - fd.add_ushort (1); // position format - // offset to coverage table from beginning of kern pair table - fd.add_ushort (10 + 2 * num_kerning_values + size_of_pair * num_kerning_values); - fd.add_ushort (0x0004); // ValueFormat1 (0x0004 is x advance) - fd.add_ushort (0); // ValueFormat2 (0 is null) - fd.add_ushort (num_kerning_values); // n pairs - - // pair offsets orderd by coverage index - i = 0; - foreach (Kern k in kern_table.kernings) { - fd.add_ushort (10 + 2 * num_kerning_values + i * size_of_pair); - i++; - } - - // pair table - i = 0; - - foreach (Kern k in kern_table.kernings) { - // pair value record - fd.add_ushort (1); // n pair vaules - fd.add_ushort (k.right); // gid to second glyph - fd.add_short (k.kerning); // value of ValueFormat1, horizontal adjustment for advance - // value of ValueFormat2 is null - - ProgressBar.set_progress ((double) (num_kerning_values - ++i) / num_kerning_values); - } - - ProgressBar.set_progress (0); // reset progress bar - - // coverage - fd.add_ushort (1); // format - fd.add_ushort (num_kerning_values); // number of gid - foreach (Kern k in kern_table.kernings) { - fd.add_ushort (k.left); // gid - } - - fd.pad (); - this.font_data = fd; - } - -} - -/** Table with list of tables sorted by table tag. */ -class DirectoryTable : Table { - - public CmapTable cmap_table; - public CvtTable cvt_table; - public GaspTable gasp_table; - public GdefTable gdef_table; - public GlyfTable glyf_table; - public GposTable gpos_table; - public HeadTable head_table; - public HheaTable hhea_table; - public HmtxTable hmtx_table; - public KernTable kern_table; - public MaxpTable maxp_table; - public NameTable name_table; - public Os2Table os_2_table; - public PostTable post_table; - public LocaTable loca_table; - - public OffsetTable offset_table; - - List<Table> tables; - - public DirectoryTable () { - offset_table = new OffsetTable (this); - - loca_table = new LocaTable (); - gasp_table = new GaspTable (); - gdef_table = new GdefTable (); - glyf_table = new GlyfTable (loca_table); - cmap_table = new CmapTable (glyf_table); - cvt_table = new CvtTable (); - head_table = new HeadTable (glyf_table); - hmtx_table = new HmtxTable (head_table, glyf_table); - hhea_table = new HheaTable (glyf_table, head_table, hmtx_table); - kern_table = new KernTable (glyf_table); - gpos_table = new GposTable (); - maxp_table = new MaxpTable (glyf_table); - name_table = new NameTable (); - os_2_table = new Os2Table (); - post_table = new PostTable (glyf_table); - - id = "Directory table"; - } - - public void process () throws GLib.Error { - // generate font data - glyf_table.process (); - gasp_table.process (); - gdef_table.process (); - cmap_table.process (glyf_table); - cvt_table.process (); - hmtx_table.process (); - hhea_table.process (); - maxp_table.process (); - name_table.process (); - os_2_table.process (glyf_table); - head_table.process (); - loca_table.process (glyf_table, head_table); - post_table.process (); - kern_table.process (); - gpos_table.process (kern_table); - - offset_table.process (); - process_directory (); // this table - } - - public unowned List<Table> get_tables () { - if (tables.length () == 0) { - tables.append (offset_table); - tables.append (this); - - if (kern_table.kerning_pairs > 0) { - // FIXME: Firefox will ignore the Kern table of GPOS is present - // but not adjust the values according to the GPOS table. - // - // Maybe there is a problem with gpos_table that I am not aware of. - tables.append (gpos_table); - } - - // tables.append (gdef_table); // invalid table - tables.append (os_2_table); - tables.append (cmap_table); - // tables.append (cvt_table); - // tables.append (gasp_table); - tables.append (glyf_table); - tables.append (head_table); - tables.append (hhea_table); - tables.append (hmtx_table); - - if (kern_table.kerning_pairs > 0) { - tables.append (kern_table); - } - - tables.append (loca_table); - tables.append (maxp_table); - tables.append (name_table); - tables.append (post_table); - } - - return tables; - } - - public void set_offset_table (OffsetTable ot) { - offset_table = ot; - } - - public new void parse (FontData dis, File file, OpenFontFormatReader reader_callback) throws Error { - StringBuilder tag = new StringBuilder (); - uint32 checksum; - uint32 offset; - uint32 length; - - return_if_fail (offset_table.num_tables > 0); - - for (uint i = 0; i < offset_table.num_tables; i++) { - tag.erase (); - - tag.append_unichar ((unichar) dis.read_byte ()); - tag.append_unichar ((unichar) dis.read_byte ()); - tag.append_unichar ((unichar) dis.read_byte ()); - tag.append_unichar ((unichar) dis.read_byte ()); - - checksum = dis.read_ulong (); - offset = dis.read_ulong (); - length = dis.read_ulong (); - - printd (@"$(tag.str) \toffset: $offset \tlength: $length \tchecksum: $checksum.\n"); - - if (tag.str == "cvt") { - cvt_table.id = tag.str; - cvt_table.checksum = checksum; - cvt_table.offset = offset; - cvt_table.length = length; - } else if (tag.str == "hmtx") { - hmtx_table.id = tag.str; - hmtx_table.checksum = checksum; - hmtx_table.offset = offset; - hmtx_table.length = length; - } else if (tag.str == "hhea") { - hhea_table.id = tag.str; - hhea_table.checksum = checksum; - hhea_table.offset = offset; - hhea_table.length = length; - } else if (tag.str == "loca") { - loca_table.id = tag.str; - loca_table.checksum = checksum; - loca_table.offset = offset; - loca_table.length = length; - } else if (tag.str == "cmap") { - cmap_table.id = tag.str; - cmap_table.checksum = checksum; - cmap_table.offset = offset; - cmap_table.length = length; - } else if (tag.str == "maxp") { - maxp_table.id = tag.str; - maxp_table.checksum = checksum; - maxp_table.offset = offset; - maxp_table.length = length; - } else if (tag.str == "gasp") { - gasp_table.id = tag.str; - gasp_table.checksum = checksum; - gasp_table.offset = offset; - gasp_table.length = length; - } else if (tag.str == "gdef") { - gdef_table.id = tag.str; - gdef_table.checksum = checksum; - gdef_table.offset = offset; - gdef_table.length = length; - } else if (tag.str == "glyf") { - glyf_table.id = tag.str; - glyf_table.checksum = checksum; - glyf_table.offset = offset; - glyf_table.length = length; - } else if (tag.str == "head") { - head_table.id = tag.str; - head_table.checksum = checksum; - head_table.offset = offset; - head_table.length = length; - } else if (tag.str == "name") { - name_table.id = tag.str; - name_table.checksum = checksum; - name_table.offset = offset; - name_table.length = length; - } else if (tag.str == "OS/2") { - os_2_table.id = tag.str; - os_2_table.checksum = checksum; - os_2_table.offset = offset; - os_2_table.length = length; - } else if (tag.str == "post") { - post_table.id = tag.str; - post_table.checksum = checksum; - post_table.offset = offset; - post_table.length = length; - } else if (tag.str == "kern") { - kern_table.id = tag.str; - kern_table.checksum = checksum; - kern_table.offset = offset; - kern_table.length = length; - } else if (tag.str == "GPOS") { - gpos_table.id = tag.str; - gpos_table.checksum = checksum; - gpos_table.offset = offset; - gpos_table.length = length; - } - } - - head_table.parse (dis); - - if (!validate_tables (dis, file)) { - warning ("Missing required table or bad checksum."); - } - - hhea_table.parse (dis); - reader_callback.set_limits (); - - name_table.parse (dis); - post_table.parse (dis); - os_2_table.parse (dis); - maxp_table.parse (dis); - loca_table.parse (dis, head_table, maxp_table); - hmtx_table.parse (dis, hhea_table, loca_table); - cmap_table.parse (dis); - gpos_table.parse (dis); - - if (kern_table.has_data ()) { - kern_table.parse (dis); - } - - glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table, kern_table); - - if (kern_table.has_data ()) { - gasp_table.parse (dis); - } - - if (kern_table.has_data ()) { - cvt_table.parse (dis); - } - } - - public bool validate_tables (FontData dis, File file) { - bool valid = true; - - try { - dis.seek (0); - - if (!validate_checksum_for_entire_font (dis, file)) { - warning ("file has invalid checksum"); - } else { - printd ("Font file has valid checksum.\n"); - } - - // Skip validation of head table for now it should be simple but it seems to - // be broken in some funny way. - - if (!glyf_table.validate (dis)) { - warning ("glyf_table has invalid checksum"); - valid = false; - } - - if (!maxp_table.validate (dis)) { - warning ("maxp_table has is invalid checksum"); - valid = false; - } - - if (!loca_table.validate (dis)) { - warning ("loca_table has invalid checksum"); - valid = false; - } - - if (!cmap_table.validate (dis)) { - warning ("cmap_table has invalid checksum"); - valid = false; - } - - if (!hhea_table.validate (dis)) { - warning ("hhea_table has invalid checksum"); - valid = false; - } - - if (!hmtx_table.validate (dis)) { - warning ("hmtx_table has invalid checksum"); - valid = false; - } - - if (!name_table.validate (dis)) { - warning ("name_table has invalid checksum"); - valid = false; - } - - if (!os_2_table.validate (dis)) { - warning ("os_2_table has invalid checksum"); - valid = false; - } - - if (!post_table.validate (dis)) { - warning ("post_table has invalid checksum"); - valid = false; - } - - if (kern_table.has_data () && !kern_table.validate (dis)) { - warning ("kern_table has invalid checksum"); - valid = false; - } - - if (!gpos_table.validate (dis)) { - warning ("gpos_table has invalid checksum"); - valid = false; - } - } catch (GLib.Error e) { - warning (e.message); - valid = false; - } - - return valid; - } - - bool validate_checksum_for_entire_font (FontData dis, File f) throws GLib.Error { - uint p = head_table.offset + head_table.get_checksum_position (); - uint32 checksum_font, checksum_head; - - checksum_head = head_table.get_font_checksum (); - - dis.seek (0); - - // zero out checksum entry in head table before validating it - dis.write_at (p + 0, 0); - dis.write_at (p + 1, 0); - dis.write_at (p + 2, 0); - dis.write_at (p + 3, 0); - - checksum_font = (uint32) (0xB1B0AFBA - dis.check_sum ()); - - if (checksum_font != checksum_head) { - warning (@"Fontfile checksum in head table does not match calculated checksum. checksum_font: $checksum_font checksum_head: $checksum_head"); - return false; - } - - return true; - } - - public long get_font_file_size () { - long length = 0; - - foreach (Table t in tables) { - length += t.get_font_data ().length_with_padding (); - } - - return length; - } - - public void process_directory () throws GLib.Error { - create_directory (); // create directory without offsets to calculate length of offset table and checksum for entre file - create_directory (); // generate a valid directory - } - - // Check sum adjustment for the entire font - public uint32 get_font_file_checksum () { - uint32 check_sum = 0; - foreach (Table t in tables) { - t.get_font_data ().continous_check_sum (ref check_sum); - } - return check_sum; - } - - public void create_directory () throws GLib.Error { - FontData fd; - - uint32 table_offset = 0; - uint32 table_length = 0; - - uint32 check_sum = 0; - - fd = new FontData (); - - return_if_fail (offset_table.num_tables > 0); - - table_offset += offset_table.get_font_data ().length_with_padding (); - - if (this.font_data != null) { - table_offset += this.get_font_data ().length_with_padding (); - } - - head_table.set_check_sum_adjustment (0); // Set this to zero, calculate checksums and update the value - head_table.process (); - - // write the directory - foreach (Table t in tables) { - - if (t is DirectoryTable || t is OffsetTable) { - continue; - } - - printd (@"c $(t.id) offset: $(table_offset) len with pad $(t.get_font_data ().length_with_padding ())\n"); - - table_length = t.get_font_data ().length (); // without padding - - fd.add_tag (t.get_id ()); // name of table - fd.add_u32 (t.get_font_data ().check_sum ()); - fd.add_u32 (table_offset); - fd.add_u32 (table_length); - - table_offset += t.get_font_data ().length_with_padding (); - } - - // padding - fd.pad (); - - this.font_data = fd; - - check_sum = get_font_file_checksum (); - head_table.set_check_sum_adjustment ((uint32)(0xB1B0AFBA - check_sum)); - head_table.process (); // update the value - } - -} - -/** Largest power of two less than max. */ -internal static uint16 largest_pow2 (uint16 max) { - uint16 x = 1; - uint16 l = 0; - - while (x <= max) { - l = x; - x = x << 1; - } - - return l; -} - -internal static uint16 largest_pow2_exponent (uint16 max) { - uint16 exp = 0; - uint16 l = 0; - uint16 x = 0; - - while (x <= max) { - l = exp; - exp++; - x = 1 << exp; - } - - return l; -} - -void printd (string s) { - //print (s); -} - -} @@ -6,6 +6,7 @@ import sys from os.path import join from doit.action import CmdAction import config+import fnmatch def cmd(name, *args):@@ -24,6 +25,23 @@ def cmd(name, *args): return ' '.join(parts) +def get_sources_path (folder, pattern):+ files = []+ for root, dirnames, filenames in os.walk(folder):+ for filename in fnmatch.filter(filenames, pattern):+ files.append(os.path.join(root, filename))+ return files++++def get_sources_name (folder, pattern):+ files = []+ for root, dirnames, filenames in os.walk(folder):+ for filename in fnmatch.filter(filenames, pattern):+ files.append(filename)+ return files++ class Vala(object): """helper to generate tasks to compile vala code"""@@ -35,19 +53,18 @@ class Vala(object): self.pkg_libs = pkg_libs self.vala_deps = vala_deps or [] - self.vala = list(glob.glob(src + '/*.vala'))- self.c = list(glob.glob(src + '/*.c')) # copy regular c sources- self.cc = [join(build, f) for f in self.c]- self.cc += [join(build, f.replace('.vala', '.c')) for f in self.vala]- self.obj = [f.replace('.c', '.o') for f in self.cc]- self.obj += [join(build, f.replace('.vala', '.o')) for f in self.vala]+ self.vala = get_sources_path (src, '*.vala')+ self.c = get_sources_path (src, '*.c') # copy regular c sources+ self.cc = [join(build + '/' + src, f) for f in get_sources_name (src, '*.c') ]+ self.cc += [join(build + '/' + src, f.replace('.vala', '.c')) for f in get_sources_name (src, '*.vala')]+ self.obj = [f.replace('.c', '.o') for f in get_sources_name (src, '*.c')]+ self.obj += [join(build, f.replace('.vala', '.o')) for f in get_sources_name (src, '*.vala')] if library: self.header = join(build, library) + '.h' self.vapi = join(build, library) + '.vapi' self.so = join(build, src) + '.so' - def gen_c(self, opts): """translate code from vala to C and create .vapi""" options = ['--ccode', '--save-temps']