Commit ef90c26
test case improvments
Changed files (10)
addedsrc/Argument.valamodifiedsrc/Font.valamodifiedsrc/GlyphRange.valamodifiedsrc/GlyphTable.valamodifiedsrc/OpenFontFormatWriter.valamodifiedsrc/OverView.valamodifiedsrc/Supplement.valaaddedsrc/TestCases.valamodifiedsrc/TestSupplement.valamodifiedsrc/wscript
Diff
new file mode 100644@@ -0,0 +1,217 @@+/*+ 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/>.+*/++namespace Supplement {+ +class Argument : GLib.Object {+ + List<string> args;+ + public Argument (string line) {+ args = new List<string> ();+ set_argument (line);+ }+ + public Argument.command_line (string[] arg) { + args = new List<string> ();++ foreach (string a in arg) {+ args.append (a);+ }+ }+ + /** returns 0 if valid or index the bad parameter. */ + public int validate () {+ string prev = "";+ int i = 0;+ foreach (string a in args) {+ + // program name+ if (i == 0) {+ prev = a;+ i++;+ continue;+ }++ // a single character, like -t+ if (!a.has_prefix ("--") && a.has_prefix ("-")) {+ a = expand_param (a);+ }+ + print (@"\"$a\"\n");+ + // valid parameter+ if (a == "--autosave" || a == "--exit" || a == "--slow" || a == "--help" || a == "--test") {+ prev = a;+ i++;+ continue;+ } else if (a.has_prefix ("--")) {+ return i;+ }+ + // not argument to test+ if (prev != "--test") {+ return i;+ }+ + prev = a;+ i++;+ }+ + return 0;+ }+ + public void print_all () {+ print (@"$(args.length ()) arguments:\n");+ + foreach (string p in args) {+ print (@"$p\n");+ }+ }+ + public bool has_argument (string param) {+ return (get_argument (param) != null);+ }+ + /** Get commandline argument. */+ public string? get_argument (string param) {+ int i = 0;+ string? n;+ string p;++ if (param.substring (0, 1) != "-") {+ warning (@"parameters must begin with \"-\" got $param");+ return null;+ }++ foreach (string s in args) {++ // this is content not a parameter + if (s.substring (0, 1) != "-") continue;++ // we might need to expand -t to test fo instance+ if (s.substring (0, 2) != "--") {+ p = expand_param (s);+ } else { + p = s;+ }+ + if (param == p) {+ if (i + 2 >= args.length ()) {+ return "";+ }+ + n = args.nth (i + 2).data;+ if (n == null) {+ return "";+ }+ + if (args.nth (i + 2).data.substring (0, 1) == "-") {+ return "";+ }+ + return args.nth (i + 2).data;+ }+ + i++;+ }+ + return null;+ }++ private void print_padded (string cmd, string desc) {+ int l = 25 - cmd.char_count ();++ stdout.printf (cmd);+ + for (int i = 0; i < l; i++) {+ stdout.printf (" ");+ }+ + stdout.printf (desc);+ stdout.printf ("\n");+ }++ /** Return full command line parameter for an abbrevation.+ * -t becomes --test.+ */+ private string expand_param (string? param) {+ if (param == null) return "";+ var p = (!) param;+ + if (p.length == 0) return "";+ if (p.get_char (0) != '-') return "";+ if (p.char_count () != 2) return "";+ + switch (p.get_char (1)) {+ case 'a': + return "--autosave";+ case 'e': + return "--exit";+ case 's': + return "--slow";+ case 'h': + return "--help";+ case 't': + return "--test";+ }+ + return "";+ }++ private void set_argument (string arg) {+ int i = 0;+ int a;+ string n;+ + if (arg.char_count () <= 1) {+ return;+ }+ + do {+ a = arg.index_of (" ", i + 1);+ n = arg.substring (i, a - i);+ + if (n.index_of ("\"") == 0) {+ a = arg.index_of ("\"", i + 1);+ n = arg.substring (i, a - i + 1);+ }+ + args.append (n);+ + i += n.char_count () + 1;+ } while (i < arg.char_count ());+ }++ public void print_help () + requires (args.length () > 0)+ {+ stdout.printf ("Usage: ");+ stdout.printf (args.nth (0).data);+ stdout.printf (" [OPTION ...]\n");++ print_padded ("-e, --exit", "exit if a testcase failes");+ print_padded ("-h, --help", "show this message");+ print_padded ("-s, --slow", "sleep between each command in test suite");+ print_padded ("-t, --test [TEST]", "run test case");+ + stdout.printf ("\n");+ }++}+ +}@@ -280,40 +280,71 @@ class Font : GLib.Object { return get_glyph (n) != null; } - public void create_not_def () {+ public Glyph get_nonmarking_return () {+ Glyph ret;+ + if (has_glyph ("nonmarkingreturn")) {+ return (!) get_glyph ("nonmarkingreturn");+ }+ + ret = new Glyph ("nonmarkingreturn");+ ret.set_unassigned (true);+ ret.left_limit = 0;+ ret.right_limit = 0;+ + return ret;+ }+ + public Glyph get_null_character () {+ Glyph n;+ + if (has_glyph ("null")) {+ return (!) get_glyph ("null");+ }+ + n = new Glyph ("null");+ n.set_unassigned (true);+ n.left_limit = 0;+ n.right_limit = 0;+ + return n;+ }+ + public Glyph get_not_def_character () { Glyph g;+ Path p; Path i; - return_if_fail (!has_glyph ("notdef."));+ if (has_glyph ("notdef")) {+ return (!) get_glyph ("notdef");+ } - g = new Glyph ("notdef.", 0);+ g = new Glyph ("notdef", 0); p = new Path (); i = new Path (); g.set_unassigned (true);- g.left_limit = -120;- g.right_limit = 120;+ g.left_limit = -33;+ g.right_limit = 33; - p.add (-100, 100);- p.add (100, 100);- p.add (100, -100);- p.add (-100, -100);+ p.add (-30, 30);+ p.add (30, 30);+ p.add (30, -30);+ p.add (-30, -30); p.close (); - i.add (-80, 80);- i.add (80, 80);- i.add (80, -80);- i.add (-80, -80);+ i.add (-25, 25);+ i.add (25, 25);+ i.add (25, -25);+ i.add (-25, -25); i.reverse (); i.close (); g.add_path (i); g.add_path (p);- - add_glyph (g);- - assert (has_glyph ("notdef."));++ return g; } public void add_glyph (Glyph glyph) {@@ -327,6 +358,11 @@ class Font : GLib.Object { public void add_glyph_collection (GlyphCollection glyph_collection) { GlyphCollection? gc = get_glyph_collection (glyph_collection.get_name ()); + if (glyph_collection.get_name () == "") {+ warning ("Refusing to insert glyph with name \"\", null character should be named null.");+ return;+ }+ if (gc != null) { warning ("glyph has been inserted"); }@@ -651,7 +687,7 @@ class Font : GLib.Object { gcl = get_glyph_collection (g.get_name ()); if (gcl != null) {- warning (@"glyph \"$(g.get_name ())\"does already exist");+ warning (@"glyph \"$(g.get_name ())\" does already exist"); } if (g.is_unassigned ()) {@@ -661,15 +697,15 @@ class Font : GLib.Object { g.name = @"($(++next_unindexed))"; } - unassigned_glyphs.insert ((!) gc);+ add_glyph_collection ((!) gc); } else if (gcl == null) { gc = new GlyphCollection (g);- glyph_cache.insert ((!) gc);+ add_glyph_collection ((!) gc); } else { stderr.printf (@"Glyph collection does already have an entry for $(g.get_name ()) char $((uint64) g.unichar_code).\n"); gc = new GlyphCollection (g); g.name = @"($(++next_unindexed))";- unassigned_glyphs.insert ((!) gc);+ add_glyph_collection ((!) gc); } // take xheight from appropriate lower case letter@@ -951,7 +987,7 @@ class Font : GLib.Object { if (gc == null) { gc = new GlyphCollection (g);- glyph_cache.insert ((!) gc);+ add_glyph_collection ((!) gc); } else { ((!)gc).insert_glyph (g, selected); }@@ -95,7 +95,7 @@ class GlyphRange { add_range (c, c); } - public unichar get_length () {+ public uint32 get_length () { unichar l = len; l += unassigned.length (); return l;@@ -196,8 +196,7 @@ class GlyphRange { } r = ranges.first ().data;-- ti = (index < r.length ()) ? index - 1: index;+ ti = index; foreach (UniRange u in ranges) { ti -= u.length ();@@ -215,7 +214,7 @@ class GlyphRange { return sb.str; } - public unichar length () {+ public uint32 length () { return len; } @@ -91,6 +91,7 @@ class GlyphTable : GLib.Object { if (g.get_name () == "") { warning (@"No proper name for glyph collection. g.get_name (): \"$(g.get_name ())\"");+ return false; } if (find (g.get_name ()) != null) {@@ -45,13 +45,7 @@ class OpenFontFormatWriter : Object { Glyph? g; unowned List<Table> tables; unichar indice = 0; - - if (!font.has_glyph ("notdef.")) { - font.create_not_def (); - } - - assert (font.has_glyph ("notdef.")); - + directory_table.process (); tables = directory_table.get_tables (); @@ -436,16 +430,6 @@ class LocaTable : Table { if (glyph_offsets != null) delete glyph_offsets; } - public uint32 get_last_glyph_length () { - return_if_fail (glyph_offsets != null); - - if (size == 0) { - warning ("No glyphs in loca table"); - } - - return glyph_offsets[size + 1]; - } - public uint32 get_offset (uint32 i) { return_if_fail (glyph_offsets != null); @@ -472,10 +456,6 @@ class LocaTable : Table { warning (@"No offset for glyph $i. Requires (0 <= $i < $(size + 1)"); } - if (i == size -1) { - return get_last_glyph_length () == 0; - } - return glyph_offsets[i] == glyph_offsets[i + 1]; } @@ -526,8 +506,6 @@ class LocaTable : Table { for (int i = 0; i < size; i++) { print (@"get_offset ($i): $(get_offset (i))\n"); } - - print (@"get_last_glyph_length (): $(get_last_glyph_length ())\n"); } public void process (GlyfTable glyf_table, HeadTable head_table) { @@ -569,8 +547,9 @@ class LocaTable : Table { warn_if_reached (); } - if (!(glyf_table.location_offsets.length () == font.length ())) { - warning (@"(glyf_table.location_offsets.length () == font.length ()) ($(glyf_table.location_offsets.length ()) == $(font.length ()))"); + // err maybe not +1 + 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))"); } font_data = fd; @@ -594,12 +573,18 @@ class GlyfTable : Table { public int16 ymax = 0; public LocaTable loca_table; - public List<uint32> location_offsets; + + 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. + public List<Glyph> glyphs; public GlyfTable (LocaTable l) { id = "glyf"; loca_table = l; location_offsets = new List<uint32> (); + glyphs = new List<Glyph> (); } /** Add this glyph from thread safe callback to the running gui. */ @@ -625,17 +610,17 @@ class GlyfTable : Table { print (@"loca.size: $(loca.size)\n"); - // notdef. character: + // notdef character: character = cmap.get_char (0); if (character != 0) { - warning ("notdef. has a another value in cmap table"); + warning ("notdef has a another value in cmap table"); } glyph = parse_next_glyf (dis, 0, 0, out xmin, out xmax, units_per_em); glyph.left_limit = 0; glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (0); - glyph.name = "notdef."; + glyph.name = "notdef"; glyph.set_unassigned (true); add_glyph (glyph); @@ -657,10 +642,12 @@ class GlyfTable : Table { glyph = parse_next_glyf (dis, character, glyph_offset, out xmin, out xmax, units_per_em); - //glyph.left_limit = xmin - hmtx_table.get_lsb (i); + glyph.left_limit = xmin - hmtx_table.get_lsb (i); glyph.left_limit = 0; glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (i); + print (@"$(glyph.right_limit) = $(glyph.left_limit) + $(hmtx_table.get_advance (i))\n"); + if (xmin > glyph.right_limit || xmax < glyph.left_limit) { warning (@"Glyph $(name.str) is outside of it's box."); glyph.left_limit = xmin; @@ -1081,14 +1068,12 @@ class GlyfTable : Table { Font font = Supplement.get_current_font (); - // glyph need padding too, but to two byte boundries - if (fd.length () % 2 != 0) { - fd.add (0); + g.remove_empty_paths (); + if (g.path_list.length () == 0) { + // ensure that location_offsets == location_offset + 1 + return; } - // set values for loca table - location_offsets.append (fd.length ()); - g.remove_empty_paths (); ncontours = (int16) g.path_list.length (); @@ -1167,35 +1152,51 @@ class GlyfTable : Table { prev = e.y + font.base_line; } } + + // glyph need padding too, but to two byte boundries + if (fd.length () % 2 != 0) { + fd.add (0); + } } - public void process () { - FontData fd = new FontData (); - uint32 indice; + // necessary in order to have glyphs sorted according to ttf specification + public void create_glyph_table () { Glyph? gl; Glyph g; Font font = Supplement.get_current_font (); - - assert (font.has_glyph ("notdef.")); - - // add notdef. character at index zero - gl = font.get_glyph ("notdef."); - process_glyph ((!)gl, fd); - - - // add glyphs + uint32 indice; + + // 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 ()); + + // add glyphs, first all assigned then the unassigned ones for (indice = 0; (gl = font.get_glyph_indice (indice)) != null; indice++) { g = (!) gl; - if (g.name == "notdef.") { + if (g.name == "notdef" || g.name == "null" || g.name == "nonmarkingreturn") { continue; } + glyphs.append (g); + } + + } + + public void process () { + FontData fd = new FontData (); + + create_glyph_table (); + + foreach (Glyph g in glyphs) { + // set values for loca table + location_offsets.append (fd.length ()); + process_glyph (g, fd); } - // last entry in loca table - location_offsets.append (fd.length ()); + location_offsets.append (fd.length ()); // last entry in loca table is special fd.pad (); @@ -1409,14 +1410,12 @@ class CmapSubtableWindowsUnicode : CmapSubtable { // print (@"New range $(s.str) - $(e.str) delta: $delta_offset, range: $range_offset\n"); } - public void process (FontData fd) { + public void process (FontData fd, GlyfTable glyf_table) { GlyphRange glyph_range = new GlyphRange (); - Font font = Supplement.get_current_font (); unowned List<UniRange> ranges; unichar i = 0; Glyph? gl; - Glyph g; uint16 seg_count_2; uint16 seg_count; @@ -1427,10 +1426,18 @@ class CmapSubtableWindowsUnicode : CmapSubtable { uint16 gid_length = 0; uint32 indice; - - for (i = 0; (gl = font.get_glyph_indice (i)) != null; i++) { - g = (!) gl; - + uint32 first_assigned = 0; + + foreach (Glyph g in glyf_table.glyphs) { + if (!g.is_unassigned ()) { + first_assigned++; + } else { + break; + } + } + print (@"first_assigned $first_assigned\n"); + first_assigned = 1; + foreach (Glyph g in glyf_table.glyphs) { if (!g.is_unassigned ()) { glyph_range.add_single (g.unichar_code); } @@ -1460,7 +1467,7 @@ class CmapSubtableWindowsUnicode : CmapSubtable { fd.add_ushort (range_shift); // end codes - indice = 0; + indice = first_assigned; foreach (UniRange u in ranges) { if (u.stop >= 0xFFFF) { warning ("Not implemented yet."); @@ -1474,7 +1481,7 @@ class CmapSubtableWindowsUnicode : CmapSubtable { fd.add_ushort (0); // Reserved // start codes - indice = 1; // one since first glyph is notdef. + indice = first_assigned; // since first glyph are notdef, null and nonmarkingreturn foreach (UniRange u in ranges) { if (u.start >= 0xFFFF) { warning ("Not implemented yet."); @@ -1486,7 +1493,7 @@ class CmapSubtableWindowsUnicode : CmapSubtable { fd.add_ushort (0xFFFF); // Last segment // delta - indice = 1; + indice = first_assigned; foreach (UniRange u in ranges) { if ((u.start - indice) > 0xFFFF && u.start > indice) { @@ -1654,7 +1661,7 @@ class CmapTable : Table { print (@"subtable_offset: $(subtable_offset)\n"); fd.add_ulong (subtable_offset); - cmap.process (fd); + cmap.process (fd, glyf_table); // padding fd.pad (); @@ -1846,10 +1853,12 @@ class HheaTable : Table { GlyfTable glyf_table; HeadTable head_table; + HmtxTable hmtx_table; - public HheaTable (GlyfTable g, HeadTable h) { + public HheaTable (GlyfTable g, HeadTable h, HmtxTable hm) { glyf_table = g; head_table = h; + hmtx_table = hm; id = "hhea"; } @@ -1902,14 +1911,14 @@ class HheaTable : Table { fd.add_16 ((int16) (-1 * (font.top_position - font.base_line))); // Ascender fd.add_16 ((int16) (-1 * font.bottom_position)); // Descender fd.add_16 (0); // LineGap + + fd.add_u16 (hmtx_table.max_advance); // maximum advance width value in 'hmtx' table. - fd.add_u16 (0); // advanceWidthMax Maximum advance width value in 'hmtx' table. - - fd.add_16 (0); // minLeftSideBearing - fd.add_16 (0); // minRightSideBearing - fd.add_16 (0); // xMaxExtent Max(lsb + (xMax - xMin)) + 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 (0); // caretSlopeRise + fd.add_16 (1); // caretSlopeRise fd.add_16 (0); // caretSlopeRun fd.add_16 (0); // caretOffset @@ -1937,11 +1946,18 @@ class HmtxTable : Table { 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) { + public HmtxTable (HeadTable h, GlyfTable gt) { head_table = h; + glyf_table = gt; id = "hmtx"; } @@ -1951,16 +1967,16 @@ class HmtxTable : Table { } public double get_advance (uint32 i) { - return_if_fail (i < nmetrics); - return_if_fail (advance_width != null); + return_val_if_fail (i < nmetrics, 0.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_if_fail (i < nmetrics); - return_if_fail (left_side_bearing != null); + 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 (); } @@ -1989,6 +2005,8 @@ class HmtxTable : Table { for (int i = 0; i < nmetrics; i++) { advance_width[i] = dis.read_ushort (); left_side_bearing[i] = dis.read_short (); + + print (@"advance_width[i] $(advance_width[i]) $(left_side_bearing[i]) \n"); } for (int i = 0; i < nmonospaced; i++) { @@ -1999,18 +2017,53 @@ class HmtxTable : Table { public void process () { FontData fd = new FontData (); Font font = Supplement.get_current_font (); - Glyph g; + + int16 advance; + int16 extent; + int16 rsb; + int16 lsb; + + double xmin; + double ymin; + double xmax; + double ymax; // advance and lsb - for (uint i = 0; i < font.length (); i++) { - g = (!) font.get_glyph_indice (i); - fd.add_u16 ((uint16) (g.right_limit - g.left_limit)); - fd.add_16 (0); + foreach (Glyph g in glyf_table.glyphs) { + g.boundries (out xmin, out ymin, out xmax, out ymax); + + lsb = (int16) (xmin - g.left_limit); + advance = (int16) (g.right_limit - g.left_limit); + extent = (int16) (lsb + (xmax - xmin)); + rsb = (int16) (advance - extent); + + print (@"$(g.name) advance: $advance = $((int16) (g.right_limit)) - $((int16) (g.left_limit))\n"); + + 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); } } @@ -2046,18 +2099,17 @@ class MaxpTable : Table { public void process () { FontData fd = new FontData(); - Font font = Supplement.get_current_font (); uint16 max_points, max_contours; // Version 0.5 for fonts with cff data and 1.0 for ttf fd.add_u16 (0); fd.add_u16 (5); - if (font.length () == 0) { + if (glyf_table.glyphs.length () == 0) { warning ("Zero glyphs in maxp table."); } - fd.add_u16 ((uint16) font.length ()); // numGlyphs in the font + fd.add_u16 ((uint16) glyf_table.glyphs.length ()); // numGlyphs in the font fd.pad (); @@ -2271,8 +2323,8 @@ class DirectoryTable : Table { glyf_table = new GlyfTable (loca_table); cmap_table = new CmapTable (glyf_table); head_table = new HeadTable (glyf_table); - hhea_table = new HheaTable (glyf_table, head_table); - hmtx_table = new HmtxTable (head_table); + hmtx_table = new HmtxTable (head_table, glyf_table); + hhea_table = new HheaTable (glyf_table, head_table, hmtx_table); maxp_table = new MaxpTable (glyf_table); name_table = new NameTable (); os_2_table = new Os2Table (); @@ -2283,11 +2335,11 @@ class DirectoryTable : Table { public void process () { // generate font data - head_table.process (); glyf_table.process (); + head_table.process (); cmap_table.process (glyf_table); - hhea_table.process (); hmtx_table.process (); + hhea_table.process (); maxp_table.process (); name_table.process (); os_2_table.process (); @@ -2307,8 +2359,8 @@ class DirectoryTable : Table { tables.append (glyf_table); tables.append (loca_table); tables.append (cmap_table); // The other required tables - tables.append (hhea_table); tables.append (hmtx_table); + tables.append (hhea_table); tables.append (maxp_table); //tables.append (os_2_table); //tables.append (name_table); @@ -2486,18 +2538,21 @@ class DirectoryTable : Table { table_offset = 0; table_offset += offset_table.get_font_data ().length_with_padding (); - table_offset += this.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 the sum and update the value foreach (Table t in tables) { - - print (@"c $(t.id) offset: $(table_offset) len with pad $( t.get_font_data ().length_with_padding ())\n"); - + if (t is DirectoryTable || t is OffsetTable) { continue; } + print (@"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 @@ -27,9 +27,9 @@ class OverView : FontDisplay { int rows = 0; int items_per_row = 0;- unichar first_character = 1;- unichar first_visible = 1;- unichar selected = 1;+ unichar first_character = 0;+ unichar first_visible = 0;+ unichar selected = 0; double view_offset_y = 0; double view_offset_x = 0;@@ -226,10 +226,10 @@ class OverView : FontDisplay { return visible_characters; } - public void draw_caption (int row, double width, Context cr, double y, unichar index_begin) {+ public void draw_caption (int row, double width, Context cr, double y, uint64 index_begin) { string character_string; double left_margin, x, caption_y; // for glyph caption- unichar index = index_begin;+ uint64 index = index_begin; int i = row * items_per_row; cr.save (); cr.set_line_width (1);@@ -252,11 +252,16 @@ class OverView : FontDisplay { warning ("Max zoom level reached"); while (x < width) {- if (index > glyph_range.get_length ()) {+ if (! (0 <= index < glyph_range.get_length ())) { break; } - character_string = glyph_range.get_char (index);+ character_string = glyph_range.get_char ((uint32) index);+ + if (character_string == "") {+ warning ("Got null character as name for glyph.");+ break;+ } cr.save (); draw_thumbnail (cr, character_string, x, y);@@ -353,7 +358,7 @@ class OverView : FontDisplay { public override void draw (Allocation allocation, Context cr) { double width; double y;- unichar t;+ uint64 t; int i; while (visible_characters.length () > 9) {@@ -23,7 +23,7 @@ class Supplement { static Font current_font; - static string[]? args = null; + public static Argument args; public static Font get_current_font () { return current_font; @@ -54,142 +54,30 @@ class Supplement { return backup; } - + public static bool has_argument (string param) { - return (get_argument (param) != null); + return args.has_argument (param); } - /** Get commandline argument. */ - public static string? get_argument (string param) - requires (args != null) - { - var a = (!) args; - int i = 0; - string? n; - - foreach (string s in a) { - string p; - - if (s.substring (0, 1) != "-") continue; - - if (s.substring (0, 2) != "--") { - p = expand_param (s); - } else { - p = s; - } - - if (param == p) { - if (i + 2 >= a.length) { - return ""; - } - - n = a[i + 2]; - if (n == null || ((!)n).length == 0) { - return ""; - } - - if (a[i + 2].substring (0, 1) == "-") { - return ""; - } - - return a[i + 2]; - } - - i++; - } - - return null; - } - - private static void print_padded (string cmd, string desc) { - int l = 25 - cmd.char_count (); - - stdout.printf (cmd); - - for (int i = 0; i < l; i++) { - stdout.printf (" "); - } - - stdout.printf (desc); - stdout.printf ("\n"); - } - - /** Return full command line parameter for an abbrevation. - * -t becomes --test. - */ - private static string expand_param (string? param) { - if (param == null) return ""; - var p = (!) param; - - if (p.length == 0) return ""; - if (p.get_char (0) != '-') return ""; - if (p.char_count () != 2) return ""; - - switch (p.get_char (1)) { - case 'a': - return "--autosave"; - case 'e': - return "--exit"; - case 's': - return "--slow"; - case 'h': - return "--help"; - case 't': - return "--test"; - } - - return ""; + public static string? get_argument (string param) { + return args.get_argument (param); } - - // FIXME: It seems broken: - public static string[] split_compound_parameters (string[] a) { - int ti = 0; - string[] na = new string [a.length]; - - foreach (string s in a) { - if (s.substring (0, 2) != "--" && s.length > 2) { - unichar c; - int t = 1; - - while (s.get_next_char (ref t, out c)) { - if (c != ' ' && c != '-') { - StringBuilder b = new StringBuilder (); - b.append_unichar (c); - na += @"-$(b.str)"; - } - } - } - na[ti] = a[ti]; - ti++; - } + public static void main(string[] arg) { + int err_arg; - return na; - } - - public static void print_help (string[] args) - requires (args.length > 0) - { - - stdout.printf ("Usage: "); - stdout.printf (args[0]); - stdout.printf (" [OPTION ...]\n"); - - print_padded ("-e, --exit", "exit if a testcase failes"); - print_padded ("-h, --help", "show this message"); - print_padded ("-s, --slow", "sleep between each command in test suite"); - print_padded ("-t, --test[=TEST]", "run test case"); + args = new Argument.command_line (arg); - stdout.printf ("\n"); - } + if (args.has_argument ("--help")) { + args.print_help (); + Process.exit (0); + } - public static void main(string[] args) { - Supplement.args = split_compound_parameters (args); - Supplement.args = args; - - if (has_argument ("--help")) { - print_help (args); - Process.exit(0); + err_arg = args.validate (); + if (err_arg != 0) { + stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n"); + args.print_help (); + Process.exit (0); } Preferences preferences = new Preferences (); @@ -197,7 +85,7 @@ class Supplement { current_font = new Font (); - Gtk.init (ref args); + Gtk.init (ref arg); var window = new MainWindow ("Supplement"); window.show_all (); @@ -205,7 +93,7 @@ class Supplement { idle.set_callback(() => { preferences.load (); return false; - }); + }); idle.attach (null); new file mode 100644@@ -0,0 +1,224 @@+/*+ 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/>.+*/++namespace Supplement {++/** All the things we want to test listed is here. */+class TestCases {+ + public List<Test> test_cases;++ public TestCases () {+ add (test_argument, "Argument list");+ add (test_glyph_ranges, "Glyph ranges");+ add (test_glyph_table, "Glyph table");+ add (test_active_edit_point, "Active edit point");+ add (test_hex, "Unicode hex values");+ add (test_reverse_path, "Reverse path");+ add (test_reverse_random_paths, "Reverse random paths");+ add (test_coordinates, "Coordinates");+ add (test_drawing, "Pen tool");+ add (test_delete_points, "Delete edit points");+ add (test_view_result, "View result in web browser");+ add (test_save_backup, "Save backup");+ }++ public static void test_argument () {+ Argument arg = new Argument ("supplement -t \"Argument list\" --unknown -unknown --help -s");+ + return_if_fail (arg.has_argument ("--test"));+ return_if_fail ((!) arg.get_argument ("--test") == "\"Argument list\"" );+ return_if_fail (arg.has_argument ("--unknown"));+ return_if_fail (arg.has_argument ("--help"));+ return_if_fail (arg.has_argument ("--slow"));+ return_if_fail (arg.validate () != 0);++ arg = new Argument ("supplement --test \"Argument list\"");+ return_if_fail ((!) arg.get_argument ("--test") == "\"Argument list\"" );+ return_if_fail (!arg.has_argument ("--help"));+ return_if_fail (!arg.has_argument ("--slow"));+ return_if_fail (arg.validate () == 0);+ + }++ public static void test_glyph_ranges () {+ GlyphRange gr = new GlyphRange ();+ + gr.add_range ('b', 'c');+ gr.add_single ('d');+ gr.add_range ('e', 'h');+ gr.add_range ('k', 'm');+ gr.add_range ('o', 'u');+ gr.add_range ('a', 'd');+ gr.add_range ('f', 'z');+ gr.add_range ('b', 'd');+ + return_if_fail (gr.length () == 'z' - 'a' + 1);+ return_if_fail (gr.get_ranges ().length () == 1);+ return_if_fail (gr.get_ranges ().first ().data.length () == 'z' - 'a' + 1);+ + for (unichar i = 'a'; i <= 'z'; i++) {+ uint index = i - 'a';+ string c = gr.get_char (index);+ StringBuilder s = new StringBuilder ();+ s.append_unichar (i);+ + if (c != s.str) {+ warning (@"wrong glyph in glyph range got \"$c\" expected \"$(s.str)\" for index $(index).");+ }+ }+ + gr = new GlyphRange ();+ gr.add_single ('a');+ gr.add_range ('c', 'e');+ gr.add_single ('◊');+ return_if_fail (gr.get_char (0) == "a");+ return_if_fail (gr.get_char (1) == "c");+ return_if_fail (gr.get_char (2) == "d");+ return_if_fail (gr.get_char (3) == "e");+ return_if_fail (gr.get_char (4) == "◊");+ }++ public static void test_glyph_table () {+ GlyphTable table = new GlyphTable ();+ List<GlyphCollection> gc = new List<GlyphCollection> ();+ List<unowned GlyphCollection> gc_copy;+ GlyphCollection g;+ + return_if_fail (table.length () == 0);+ return_if_fail (table.get ("Some glyph") == null);+ + // generate test data+ for (uint i = 0; i < 1000; i++) {+ gc.append (new GlyphCollection (new Glyph (@"TEST $i", i + 'a')));+ }++ // insert in random order+ gc_copy = gc.copy ();+ for (uint i = 0; gc_copy.length () > 0; i++) {+ int t = (int) ((gc_copy.length () - 1) * Random.next_double ());+ g = gc_copy.nth (t).data;+ + if (!table.insert (g)) {+ warning (@"Failed to insert $(g.get_name ())");+ return;+ }+ + gc_copy.remove_all (g);+ + if (!table.validate_index ()) {+ table.print_all ();+ warning ("index is invalid");+ return;+ }+ }+ + return_if_fail (table.length () == gc.length ());+ + // validate table+ for (uint i = 0; i > 1000; i++) {+ g = (!) table.get (gc.nth (i).data.get_name ());+ return_if_fail (gc.nth (i).data == g);+ }+ + // search + for (int i = 0; i < 2000; i++) {+ int t = (int) (999 * Random.next_double ());+ if (table.get (@"TEST $t") == null) {+ table.print_all ();+ warning (@"Did't find TEST $t in glyph table.");+ return;+ }+ }++ // remove+ table.remove ("TEST 0");+ table.remove ("TEST 53");+ return_if_fail (table.get ("TEST 0") == null);+ return_if_fail (table.get ("TEST 53") == null);+ + // search + return_if_fail (table.get ("TEST 52") != null);+ return_if_fail (table.get ("TEST 54") != null);+ }++ public static void test_delete_points () {+ PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+ tool.test_delete_points ();+ }++ public static void test_active_edit_point () {+ PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+ tool.test_active_edit_point ();+ }+ + public static void test_save_backup () {+ // TODO draw various things and assert that they are restored correctly+ Supplement.get_current_font ().save_backup ();+ Supplement.get_current_font ().restore_backup ();+ }++ public static void test_hex () {+ test_hex_conv ('H', "U+48", 72);+ test_hex_conv ('1', "U+31", 49);+ test_hex_conv ('å', "U+e5", 229);+ test_hex_conv ('◊', "U+25ca", 9674);+ }++ private static void test_hex_conv (unichar h, string sr, int r) {+ string s = Font.to_hex (h);+ unichar t = Font.to_unichar (sr);+ + if (s != sr) warning (@"($s != \"$sr\")");+ if ((int)t != r || t != h) warning (@"$((int)t) != $r || $t != '$h'");+ }++ public static void test_coordinates () {+ PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+ tool.test_coordinates (); + }++ public static void test_view_result () {+ ExportTool tool = (ExportTool) MainWindow.get_toolbox ().get_tool ("export");+ tool.test_view_result ();+ }++ public static void test_reverse_random_paths () {+ PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+ tool.test_reverse_random_triangles (); + }++ public static void test_reverse_path () {+ PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool");+ tool.test_reverse_path ();+ }++ public static void test_drawing () {+ Tool tool = MainWindow.get_toolbox ().get_tool ("pen_tool");+ tool.test ();+ }+ + private void add (Callback callback, string name) {+ test_cases.append (new Test (callback, name));+ }+ + public unowned List<Test> get_test_functions () {+ return test_cases;+ }+} + +}@@ -27,170 +27,6 @@ class Test : Object { } } -/** All the things we might want to test is here. */ -class TestCases { - - public List<Test> test_cases; - - public TestCases () { - add (test_glyph_ranges, "Glyph ranges"); - add (test_glyph_table, "Glyph table"); - add (test_active_edit_point, "Active edit point"); - add (test_hex, "Unicode hex values"); - add (test_reverse_path, "Reverse path"); - add (test_reverse_random_paths, "Reverse random paths"); - add (test_coordinates, "Coordinates"); - add (test_drawing, "Pen tool"); - add (test_delete_points, "Delete edit points"); - add (test_view_result, "View result in web browser"); - add (test_save_backup, "Save backup"); - } - - public static void test_glyph_ranges () { - GlyphRange gr = new GlyphRange (); - - gr.add_range ('b', 'c'); - gr.add_single ('d'); - gr.add_range ('e', 'h'); - gr.add_range ('k', 'm'); - gr.add_range ('o', 'u'); - gr.add_range ('a', 'd'); - gr.add_range ('f', 'z'); - gr.add_range ('b', 'd'); - - return_if_fail (gr.length () == 'z' - 'a' + 1); - return_if_fail (gr.get_ranges ().length () == 1); - return_if_fail (gr.get_ranges ().first ().data.length () == 'z' - 'a' + 1); - } - - public static void test_glyph_table () { - GlyphTable table = new GlyphTable (); - List<GlyphCollection> gc = new List<GlyphCollection> (); - List<unowned GlyphCollection> gc_copy; - GlyphCollection g; - - return_if_fail (table.length () == 0); - return_if_fail (table.get ("Some glyph") == null); - - // generate test data - for (uint i = 0; i < 1000; i++) { - gc.append (new GlyphCollection (new Glyph (@"TEST $i", i + 'a'))); - } - - // insert in random order - gc_copy = gc.copy (); - for (uint i = 0; gc_copy.length () > 0; i++) { - int t = (int) ((gc_copy.length () - 1) * Random.next_double ()); - g = gc_copy.nth (t).data; - - if (!table.insert (g)) { - warning (@"Failed to insert $(g.get_name ())"); - return; - } - - gc_copy.remove_all (g); - - if (!table.validate_index ()) { - table.print_all (); - warning ("index is invalid"); - return; - } - } - - return_if_fail (table.length () == gc.length ()); - - // validate table - for (uint i = 0; i > 1000; i++) { - g = (!) table.get (gc.nth (i).data.get_name ()); - return_if_fail (gc.nth (i).data == g); - } - - // search - for (int i = 0; i < 2000; i++) { - int t = (int) (999 * Random.next_double ()); - if (table.get (@"TEST $t") == null) { - table.print_all (); - warning (@"Did't find TEST $t in glyph table."); - return; - } - } - - // remove - table.remove ("TEST 0"); - table.remove ("TEST 53"); - return_if_fail (table.get ("TEST 0") == null); - return_if_fail (table.get ("TEST 53") == null); - - // search - return_if_fail (table.get ("TEST 52") != null); - return_if_fail (table.get ("TEST 54") != null); - } - - public static void test_delete_points () { - PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool"); - tool.test_delete_points (); - } - - public static void test_active_edit_point () { - PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool"); - tool.test_active_edit_point (); - } - - public static void test_save_backup () { - // TODO draw various things and assert that they are restored correctly - Supplement.get_current_font ().save_backup (); - Supplement.get_current_font ().restore_backup (); - } - - public static void test_hex () { - test_hex_conv ('H', "U+48", 72); - test_hex_conv ('1', "U+31", 49); - test_hex_conv ('å', "U+e5", 229); - test_hex_conv ('◊', "U+25ca", 9674); - } - - private static void test_hex_conv (unichar h, string sr, int r) { - string s = Font.to_hex (h); - unichar t = Font.to_unichar (sr); - - if (s != sr) warning (@"($s != \"$sr\")"); - if ((int)t != r || t != h) warning (@"$((int)t) != $r || $t != '$h'"); - } - - public static void test_coordinates () { - PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool"); - tool.test_coordinates (); - } - - public static void test_view_result () { - ExportTool tool = (ExportTool) MainWindow.get_toolbox ().get_tool ("export"); - tool.test_view_result (); - } - - public static void test_reverse_random_paths () { - PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool"); - tool.test_reverse_random_triangles (); - } - - public static void test_reverse_path () { - PenTool tool = (PenTool) MainWindow.get_toolbox ().get_tool ("pen_tool"); - tool.test_reverse_path (); - } - - public static void test_drawing () { - Tool tool = MainWindow.get_toolbox ().get_tool ("pen_tool"); - tool.test (); - } - - private void add (Callback callback, string name) { - test_cases.append (new Test (callback, name)); - } - - public unowned List<Test> get_test_functions () { - return test_cases; - } -} - /** Class for executing tests cases. */ class TestSupplement : GLib.Object { @@ -208,18 +44,23 @@ class TestSupplement : GLib.Object { unowned List<Test> passed; unowned List<Test> failed; + unowned List<Test> skipped; static TestSupplement? singleton = null; bool has_failed = false; - + bool has_skipped = false; + static bool slow_test = false; + string test_cases_to_run; // name of specific test case or all to run all test cases + public TestSupplement () { assert (singleton == null); tests = new TestCases (); test_cases = tests.get_test_functions (); current_case = test_cases.first (); + test_cases_to_run = "All"; from_command_line (); } @@ -245,7 +86,7 @@ class TestSupplement : GLib.Object { if (t.name == s) return true; } - if (s == "") { + if (s == "" || s == "All") { print ("No speceific tescase given run all test cases.\n"); return true; } @@ -258,17 +99,18 @@ class TestSupplement : GLib.Object { string? stn = Supplement.get_argument ("--test"); if (stn != null) { - string st = (!)stn; + string st = (!) stn; if (!has_test_case (st)) { - stderr.printf (@"Test case $st does not exist.\n"); - stderr.printf ("\nAvaliable cases:\n"); + stderr.printf (@"Test case \"$st\" does not exist.\n"); + stderr.printf ("\nAvaliable test cases:\n"); + foreach (var t in test_cases) { stderr.printf (t.name); stderr.printf ("\n"); } - assert (false); + Process.exit(1); } if (st == "All" || st == "") { @@ -277,6 +119,7 @@ class TestSupplement : GLib.Object { stderr.printf (@"Run only test case \"$st\" \n"); } + test_cases_to_run = st; } } @@ -326,6 +169,16 @@ class TestSupplement : GLib.Object { stdout.printf ("\n"); stdout.printf ("Testcase results:\n"); + foreach (var t in skipped) { + stdout.printf ("%s", t.name); + pad (40 - t.name.char_count()); + stdout.printf ("Skipped\n"); + } + + if (skipped.length () > 0) { + stdout.printf ("\n"); + } + foreach (var t in passed) { stdout.printf ("%s", t.name); pad (40 - t.name.char_count()); @@ -355,31 +208,42 @@ class TestSupplement : GLib.Object { } Test test = (!)((!) current_case).data; - + has_failed = false; - - test.callback (); + has_skipped = false; + if (test_cases_to_run != "All" && test_cases_to_run != test.name) { + has_skipped = true; + } else { + test.callback (); + } + if (has_failed) { - failed.append ((!)test); + failed.append ((!) test); if (Supplement.has_argument ("--exit")) { print_result (); - - bool test_case_will_not_fail = false; - assert (test_case_will_not_fail); + Process.exit (1); } + } else if (has_skipped) { + skipped.append ((!) test); } else { - passed.append ((!)test); + passed.append ((!) test); } if (unlikely (current_case == test_cases.last ())) { - stderr.printf ("Finished running test suite.\n"); + stdout.printf ("Finished running test suite.\n"); + AtomicInt.set (ref state, DONE); Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler); print_result (); + + if (Supplement.has_argument ("--exit")) { + print_result (); + Process.exit ((failed.length () == 0) ? 0 : 1); + } return false; } @@ -1,4 +1,5 @@ SUPPLEMENT_MAIN = [ + 'Argument.vala', 'Font.vala', 'BackgroundTool.vala', 'BackgroundSelection.vala', @@ -43,6 +44,7 @@ SUPPLEMENT_MAIN = [ 'SvgFontFormatWriter.vala', 'Preferences.vala', 'TabBar.vala', + 'TestCases.vala', 'TestSupplement.vala', 'Tool.vala', 'Toolbox.vala',