Clone in a terminal:
PostTable.vala
/*
	Copyright (C) 2012 2013 2015 Johan Mattsson

	This library is free software; you can redistribute it and/or modify 
	it under the terms of the GNU Lesser General Public License as 
	published by the Free Software Foundation; either version 3 of the 
	License, or (at your option) any later version.

	This library 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 
	Lesser General Public License for more details.
*/

namespace BirdFont {

public class PostTable : OtfTable {
	
	GlyfTable glyf_table;
	
	Gee.ArrayList<uint16> index = new Gee.ArrayList<uint16> ();
	Gee.ArrayList<string> names = new Gee.ArrayList<string> ();
	
	public PostTable (GlyfTable g) {
		id = "post";
		glyf_table = g;
	}
	
	// the Macintosh standard order
	private void add_standard_names () {
		names.add (".notdef");
		names.add (".null");
		names.add ("nonmarkingreturn");
		names.add ("space");
		names.add ("exclam");
		names.add ("quotedbl");
		names.add ("numbersign");
		names.add ("dollar");
		names.add ("percent");
		names.add ("ampersand");
		names.add ("quotesingle");
		names.add ("parenleft");
		names.add ("parenright");
		names.add ("asterisk");
		names.add ("plus");
		names.add ("comma");
		names.add ("hyphen");
		names.add ("period");
		names.add ("slash");
		names.add ("zero");
		names.add ("one");
		names.add ("two");
		names.add ("three");
		names.add ("four");
		names.add ("five");
		names.add ("six");
		names.add ("seven");
		names.add ("eight");
		names.add ("nine");
		names.add ("colon");
		names.add ("semicolon");
		names.add ("less");
		names.add ("equal");
		names.add ("greater");
		names.add ("question");
		names.add ("at");
		names.add ("A");
		names.add ("B");
		names.add ("C");
		names.add ("D");
		names.add ("E");
		names.add ("F");
		names.add ("G");
		names.add ("H");
		names.add ("I");
		names.add ("J");
		names.add ("K");
		names.add ("L");
		names.add ("M");
		names.add ("N");
		names.add ("O");
		names.add ("P");
		names.add ("Q");
		names.add ("R");
		names.add ("S");
		names.add ("T");
		names.add ("U");
		names.add ("V");
		names.add ("W");
		names.add ("X");
		names.add ("Y");
		names.add ("Z");
		names.add ("bracketleft");
		names.add ("backslash");
		names.add ("bracketright");
		names.add ("asciicircum");
		names.add ("underscore");
		names.add ("grave");
		names.add ("a");
		names.add ("b");
		names.add ("c");
		names.add ("d");
		names.add ("e");
		names.add ("f");
		names.add ("g");
		names.add ("h");
		names.add ("i");
		names.add ("j");
		names.add ("k");
		names.add ("l");
		names.add ("m");
		names.add ("n");
		names.add ("o");
		names.add ("p");
		names.add ("q");
		names.add ("r");
		names.add ("s");
		names.add ("t");
		names.add ("u");
		names.add ("v");
		names.add ("w");
		names.add ("x");
		names.add ("y");
		names.add ("z");
		names.add ("braceleft");
		names.add ("bar");
		names.add ("braceright");
		names.add ("asciitilde");
		names.add ("Adieresis");
		names.add ("Aring");
		names.add ("Ccedilla");
		names.add ("Eacute");
		names.add ("Ntilde");
		names.add ("Odieresis");
		names.add ("Udieresis");
		names.add ("aacute");
		names.add ("agrave");
		names.add ("acircumflex");
		names.add ("adieresis");
		names.add ("atilde");
		names.add ("aring");
		names.add ("ccedilla");
		names.add ("eacute");
		names.add ("egrave");
		names.add ("ecircumflex");
		names.add ("edieresis");
		names.add ("iacute");
		names.add ("igrave");
		names.add ("icircumflex");
		names.add ("idieresis");
		names.add ("ntilde");
		names.add ("oacute");
		names.add ("ograve");
		names.add ("ocircumflex");
		names.add ("odieresis");
		names.add ("otilde");
		names.add ("uacute");
		names.add ("ugrave");
		names.add ("ucircumflex");
		names.add ("udieresis");
		names.add ("dagger");
		names.add ("degree");
		names.add ("cent");
		names.add ("sterling");
		names.add ("section");
		names.add ("bullet");
		names.add ("paragraph");
		names.add ("germandbls");
		names.add ("registered");
		names.add ("copyright");
		names.add ("trademark");
		names.add ("acute");
		names.add ("dieresis");
		names.add ("notequal");
		names.add ("AE");
		names.add ("Oslash");
		names.add ("infinity");
		names.add ("plusminus");
		names.add ("lessequal");
		names.add ("greaterequal");
		names.add ("yen");
		names.add ("mu");
		names.add ("partialdiff");
		names.add ("summation");
		names.add ("product");
		names.add ("pi");
		names.add ("integral");
		names.add ("ordfeminine");
		names.add ("ordmasculine");
		names.add ("Omega");
		names.add ("ae");
		names.add ("oslash");
		names.add ("questiondown");
		names.add ("exclamdown");
		names.add ("logicalnot");
		names.add ("radical");
		names.add ("florin");
		names.add ("approxequal");
		names.add ("Delta");
		names.add ("guillemotleft");
		names.add ("guillemotright");
		names.add ("ellipsis");
		names.add ("nonbreakingspace");
		names.add ("Agrave");
		names.add ("Atilde");
		names.add ("Otilde");
		names.add ("OE");
		names.add ("oe");
		names.add ("endash");
		names.add ("emdash");
		names.add ("quotedblleft");
		names.add ("quotedblright");
		names.add ("quoteleft");
		names.add ("quoteright");
		names.add ("divide");
		names.add ("lozenge");
		names.add ("ydieresis");
		names.add ("Ydieresis");
		names.add ("fraction");
		names.add ("currency");
		names.add ("guilsinglleft");
		names.add ("guilsinglright");
		names.add ("fi");
		names.add ("fl");
		names.add ("daggerdbl");
		names.add ("periodcentered");
		names.add ("quotesinglbase");
		names.add ("quotedblbase");
		names.add ("perthousand");
		names.add ("Acircumflex");
		names.add ("Ecircumflex");
		names.add ("Aacute");
		names.add ("Edieresis");
		names.add ("Egrave");
		names.add ("Iacute");
		names.add ("Icircumflex");
		names.add ("Idieresis");
		names.add ("Igrave");
		names.add ("Oacute");
		names.add ("Ocircumflex");
		names.add ("apple");
		names.add ("Ograve");
		names.add ("Uacute");
		names.add ("Ucircumflex");
		names.add ("Ugrave");
		names.add ("dotlessi");
		names.add ("circumflex");
		names.add ("tilde");
		names.add ("macron");
		names.add ("breve");
		names.add ("dotaccent");
		names.add ("ring");
		names.add ("cedilla");
		names.add ("hungarumlaut");
		names.add ("ogonek");
		names.add ("caron");
		names.add ("Lslash");
		names.add ("lslash");
		names.add ("Scaron");
		names.add ("scaron");
		names.add ("Zcaron");
		names.add ("zcaron");
		names.add ("brokenbar");
		names.add ("Eth");
		names.add ("eth");
		names.add ("Yacute");
		names.add ("yacute");
		names.add ("Thorn");
		names.add ("thorn");
		names.add ("minus");
		names.add ("multiply");
		names.add ("onesuperior");
		names.add ("twosuperior");
		names.add ("threesuperior");
		names.add ("onehalf");
		names.add ("onequarter");
		names.add ("threequarters");
		names.add ("franc");
		names.add ("Gbreve");
		names.add ("gbreve");
		names.add ("Idotaccent");
		names.add ("Scedilla");
		names.add ("scedilla");
		names.add ("Cacute");
		names.add ("cacute");
		names.add ("Ccaron");
		names.add ("ccaron");
		names.add ("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 'fi':
				return 192;

			case 'fl':
				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;
		int name_index;
		GlyphCollection gc;
		Glyph g;
		string ps_name;
		StringBuilder name;
		
		fd.add_fixed (0x00020000); // Version

 		Font font = OpenFontFormatWriter.get_current_font ();
 		
		if (!font.italic) {
			if (font.italic_angle != 0) {
				printd ("Italic angle set to $(font.italic_angle) but the italic checkbox is not ticked.");
			}
			
			fd.add_fixed (0x00000000); // italicAngle
		} else {
			int angle = FontData.to_fixed (font.italic_angle);
			fd.add_32 (angle);
		}

		
		fd.add_short (-2); // underlinePosition
		fd.add_short (1); // underlineThickness

		fd.add_ulong (0); // non zero for monospaced font
		
		// mem boundaries 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.size);

		// this part of the spec is so weird
		
		fd.add_ushort ((uint16) 0); // first index is .notdef
		index.add (0);
		
		assert (names.size == 0);
		add_standard_names ();

		for (int i = 1; i < glyf_table.glyphs.size; i++) {
			gc = glyf_table.glyphs.get (i);
			g = gc.get_current ();
			name_index = get_standard_index (g.unichar_code);
			
			if (name_index != 0 && !gc.is_unassigned ()) {
				fd.add_ushort ((uint16) name_index);  // use standard name
			} else {
				printd (@"Adding non standard postscript name $(gc.get_name ())\n");
				
				name_index = (int) names.size; // use font specific name
				fd.add_ushort ((uint16) name_index);
				
				name = new StringBuilder ();
				if (gc.is_unassigned ()) {
					name.append (g.get_name ());
				} else {
					unichar c = gc.get_unicode_character ();
					if (c < 0xFFFF) {
						name.printf ("uni%04x", c);
					} else {
						name.printf ("u%05x", c);
					}
				}
				
				ps_name = create_ps_name (name.str);
				names.add (ps_name);
			}
			
			this.index.add ((uint16) name_index);
		}

		for (int i = 258; i < names.size; i++) {
			n = (!) names.get (i);
			
			if (n.length > 0xFF) {
				warning (@"too long name for glyph $n");
				continue;
			}
			
			fd.add ((uint8) n.length);
			fd.add_str (n);
		}		

		fd.pad ();
		
		this.font_data = fd;
	}

	string create_ps_name (string name) {
		string valid_name = NameTable.name_validation (name, false, 0xFF);
		
		if (valid_name.char_count () == 1) {
			warning (@"Too short name: $valid_name generated from $name");
			valid_name = add_suffix (valid_name);
		}
		
		if (names.index_of (valid_name) > -1) {
			valid_name = add_suffix (valid_name);
		} 
		
		return valid_name;
	}
	
	string add_suffix (string valid_name) {
		int i = 2;
		string s;
		
		s = valid_name + @"_$i";
		while (names.index_of (s) > -1) {
			i++;
			s = valid_name + @"_$i";
		}
		
		return s;
	}
}

}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112