The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

GlyfData.vala in libbirdfont/OpenFontFormat

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/OpenFontFormat/GlyfData.vala.
Merge ../birdfont-2.x
1 /* 2 Copyright (C) 2012, 2013, 2014 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Math; 16 17 namespace BirdFont { 18 19 public class CoordinateFlags : GLib.Object { 20 /** TTF coordinate flags. */ 21 22 public static const uint8 NONE = 0; 23 public static const uint8 ON_PATH = 1 << 0; 24 public static const uint8 X_SHORT_VECTOR = 1 << 1; 25 public static const uint8 Y_SHORT_VECTOR = 1 << 2; 26 public static const uint8 REPEAT = 1 << 3; 27 28 // same flag or short vector sign flag 29 public static const uint8 X_IS_SAME = 1 << 4; 30 public static const uint8 Y_IS_SAME = 1 << 5; 31 public static const uint8 X_SHORT_VECTOR_POSITIVE = 1 << 4; 32 public static const uint8 Y_SHORT_VECTOR_POSITIVE = 1 << 5; 33 34 } 35 36 /** Data for one entry in the glyf table. */ 37 public class GlyfData : GLib.Object { 38 public Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> (); 39 public Gee.ArrayList<EditPoint> points = new Gee.ArrayList<EditPoint> (); 40 public Gee.ArrayList<uint16> end_points = new Gee.ArrayList<uint16> (); 41 public Gee.ArrayList<uint8> flags = new Gee.ArrayList<uint8> (); 42 public Gee.ArrayList<int16> coordinate_x = new Gee.ArrayList<int16> (); 43 public Gee.ArrayList<int16> coordinate_y = new Gee.ArrayList<int16> (); 44 45 uint16 end_point = 0; 46 int16 nflags = 0; 47 48 Glyph glyph; 49 50 public int16 bounding_box_xmin = 0; 51 public int16 bounding_box_ymin = 0; 52 public int16 bounding_box_xmax = 0; 53 public int16 bounding_box_ymax = 0; 54 55 private static double UNITS { 56 get { return HeadTable.UNITS; } 57 } 58 59 public GlyfData (Glyph g) { 60 bool process; 61 PathList qp = g.get_quadratic_paths (); 62 63 glyph = g; 64 65 foreach (Path p in qp.paths) { 66 if (p.points.size > 0) { 67 if (!is_empty (p)) { 68 // Add points at extrema 69 p.add_extrema (); 70 } 71 } 72 } 73 74 process = true; 75 76 while (process) { 77 points.clear (); 78 paths.clear (); 79 foreach (Path p in qp.paths) { 80 if (!is_empty (p)) { 81 paths.add (p); 82 foreach (EditPoint ep in p.points) { 83 points.add (ep); 84 } 85 } 86 } 87 88 if (paths.size == 0) { 89 break; 90 } 91 92 process_end_points (); 93 process_flags (); 94 process_x (); 95 96 // error checking is done here 97 process = !process_y (); 98 99 process_bounding_box (); 100 } 101 } 102 103 bool is_empty (Path p) { 104 EditPoint? last = null; 105 106 if (unlikely (p.points.size < 2)) { 107 warning (@"A path in $(glyph.get_name ()) has less than three points, it will not be exported."); 108 return true; 109 } 110 111 foreach (EditPoint ep in p.points) { 112 if (last != null && !ep.equals ((!) last)) { 113 return false; 114 } 115 last = ep; 116 } 117 118 warning (@"A path in $(glyph.get_name ()) ($(glyph.get_hex ())) has no area but $(p.points.size) points at $(p.get_first_point ().x),$(p.get_first_point ().y)."); 119 return true; 120 } 121 122 public uint16 get_end_point () { 123 return end_point; 124 } 125 126 public int16 get_ncontours () { 127 return (int16) paths.size; 128 } 129 130 public int16 get_nflags () { 131 return nflags; 132 } 133 134 void process_end_points () { 135 uint16 last_end_point = 0; 136 PointType type; 137 138 end_points.clear (); 139 end_point = 0; 140 141 foreach (Path quadratic in paths) { 142 if (unlikely (quadratic.points.size == 0)) { 143 warning (@"No points in path (before conversion $(quadratic.points.size))"); 144 continue; 145 } 146 147 if (unlikely (quadratic.points.size < 2)) { 148 warning ("A path contains less than three points, it will not be exported."); 149 continue; 150 } 151 152 foreach (EditPoint e in quadratic.points) { 153 end_point++; 154 type = e.get_right_handle ().type; 155 156 // off curve 157 end_point++; 158 159 if (end_point >= 0xFFFF) { 160 warning ("Too many points"); 161 } 162 } 163 end_points.add (end_point - 1); 164 165 if (unlikely (end_point - 1 < last_end_point)) { 166 warning (@"Next endpoint has bad value. (end_point - 1 < last_end_point) ($(end_point - 1) < $last_end_point)"); 167 } 168 169 last_end_point = end_point - 1; 170 } 171 172 if (unlikely (end_point == 0)) { 173 warning (@"End point is zero for glyph $(glyph.get_name ())"); 174 } 175 } 176 177 void process_flags () { 178 PointType type; 179 180 flags = new Gee.ArrayList<uint8> (); 181 nflags = 0; 182 183 foreach (Path p in paths) { 184 foreach (EditPoint e in p.points) { 185 flags.add (CoordinateFlags.ON_PATH); 186 nflags++; 187 188 type = e.get_right_handle ().type; 189 190 // off curve 191 flags.add (CoordinateFlags.NONE); 192 nflags++; 193 } 194 } 195 } 196 197 public static double tie_to_ttf_grid_x (Glyph glyph, double x) { 198 double ttf_x; 199 ttf_x = rint (x * UNITS - glyph.left_limit * UNITS); 200 return (ttf_x / UNITS) + glyph.left_limit; 201 } 202 203 public static double tie_to_ttf_grid_y (Font font, double y) { 204 double ttf_y; 205 ttf_y = rint (y * UNITS - font.base_line * UNITS); 206 return (ttf_y / UNITS) + font.base_line; 207 } 208 209 void process_x () { 210 double prev = 0; 211 double x; 212 PointType type; 213 214 coordinate_x.clear (); 215 foreach (Path p in paths) { 216 foreach (EditPoint e in p.points) { 217 x = rint (e.x * UNITS - prev - glyph.left_limit * UNITS); 218 coordinate_x.add ((int16) x); 219 220 prev = rint (e.x * UNITS - glyph.left_limit * UNITS); 221 222 type = e.get_right_handle ().type; 223 224 // off curve 225 x = rint (e.get_right_handle ().x * UNITS - prev - glyph.left_limit * UNITS); 226 coordinate_x.add ((int16) x); 227 228 prev = rint (e.get_right_handle ().x * UNITS - glyph.left_limit * UNITS); 229 } 230 } 231 } 232 233 bool process_y () { 234 double prev = 0; 235 double y; 236 Font font = OpenFontFormatWriter.get_current_font (); 237 PointType type; 238 int epi = 0; 239 240 coordinate_y.clear (); 241 242 foreach (Path p in paths) { 243 foreach (EditPoint e in p.points) { 244 y = rint (e.y * UNITS - prev - font.base_line * UNITS); 245 coordinate_y.add ((int16) y); 246 247 if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { 248 warning (@"Point on point in TTF. Index $(coordinate_y.size - 1)"); 249 250 // FIXME: distorted shape 251 /* 252 if (BirdFont.has_argument ("--test")) { 253 print (glyph.get_name () + "\n"); 254 print (points.get (epi).to_string ()); 255 PenTool.remove_point_simplify (new PointSelection (points.get (epi), p)); 256 return false; 257 } 258 */ 259 } 260 261 prev = rint (e.y * UNITS - font.base_line * UNITS); 262 263 type = e.get_right_handle ().type; 264 265 // off curve 266 y = rint (e.get_right_handle ().y * UNITS - prev - font.base_line * UNITS); 267 coordinate_y.add ((int16) y); 268 269 if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { 270 warning (@"Point on point in TTF (off curve) Index: $(coordinate_y.size - 1) "); 271 if (BirdFont.has_argument ("--test")) { 272 print (glyph.get_name () + "\n"); 273 print (points.get (epi).to_string ()); 274 275 // FIXME: distorted shape 276 PenTool.remove_point_simplify (new PointSelection (points.get (epi), p)); 277 return false; 278 } 279 } 280 281 prev = rint (e.get_right_handle ().y * UNITS - font.base_line * UNITS); 282 epi++; 283 } 284 } 285 286 return true; 287 } 288 289 void process_bounding_box () { 290 int16 last = 0; 291 int i = 0; 292 293 bounding_box_xmin = int16.MAX; 294 bounding_box_ymin = int16.MAX; 295 bounding_box_xmax = int16.MIN; 296 bounding_box_ymax = int16.MIN; 297 298 if (coordinate_x.size == 0) { 299 warning ("no points in coordinate_y"); 300 } 301 302 foreach (int16 c in coordinate_x) { 303 c += last; 304 305 if (c < bounding_box_xmin) { 306 bounding_box_xmin = c; 307 } 308 309 if (c > bounding_box_xmax) { 310 bounding_box_xmax = c; 311 } 312 313 last = c; 314 i++; 315 } 316 317 if (coordinate_y.size == 0) { 318 warning ("no points in coordinate_y"); 319 } 320 321 last = 0; 322 i = 0; 323 foreach (int16 c in coordinate_y) { 324 c += last; 325 326 if (c < bounding_box_ymin) { 327 bounding_box_ymin = c; 328 } 329 330 if (c > bounding_box_ymax) { 331 bounding_box_ymax = c; 332 } 333 334 last = c; 335 i++; 336 } 337 338 printd (@"Bounding box: $bounding_box_xmin,$bounding_box_ymin $bounding_box_xmax,$bounding_box_ymax\n"); 339 } 340 } 341 342 } 343 344