| 1 | /* |
| 2 | * Copyright © 2015 Google, Inc. |
| 3 | * Copyright © 2019 Adobe Inc. |
| 4 | * Copyright © 2019 Ebrahim Byagowi |
| 5 | * |
| 6 | * This is part of HarfBuzz, a text shaping library. |
| 7 | * |
| 8 | * Permission is hereby granted, without written agreement and without |
| 9 | * license or royalty fees, to use, copy, modify, and distribute this |
| 10 | * software and its documentation for any purpose, provided that the |
| 11 | * above copyright notice and the following two paragraphs appear in |
| 12 | * all copies of this software. |
| 13 | * |
| 14 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| 15 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| 16 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| 17 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| 18 | * DAMAGE. |
| 19 | * |
| 20 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| 21 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| 22 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| 23 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| 24 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| 25 | * |
| 26 | * Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter |
| 27 | * Adobe Author(s): Michiharu Ariza |
| 28 | */ |
| 29 | |
| 30 | #ifndef HB_OT_GLYF_TABLE_HH |
| 31 | #define HB_OT_GLYF_TABLE_HH |
| 32 | |
| 33 | #include "hb-open-type.hh" |
| 34 | #include "hb-ot-head-table.hh" |
| 35 | #include "hb-ot-hmtx-table.hh" |
| 36 | #include "hb-ot-var-gvar-table.hh" |
| 37 | #include "hb-draw.hh" |
| 38 | |
| 39 | namespace OT { |
| 40 | |
| 41 | |
| 42 | /* |
| 43 | * loca -- Index to Location |
| 44 | * https://docs.microsoft.com/en-us/typography/opentype/spec/loca |
| 45 | */ |
| 46 | #define HB_OT_TAG_loca HB_TAG('l','o','c','a') |
| 47 | |
| 48 | |
| 49 | struct loca |
| 50 | { |
| 51 | friend struct glyf; |
| 52 | |
| 53 | static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; |
| 54 | |
| 55 | bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const |
| 56 | { |
| 57 | TRACE_SANITIZE (this); |
| 58 | return_trace (true); |
| 59 | } |
| 60 | |
| 61 | protected: |
| 62 | UnsizedArrayOf<HBUINT8> |
| 63 | dataZ; /* Location data. */ |
| 64 | public: |
| 65 | DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always |
| 66 | * check the size externally, allow Null() object of it by |
| 67 | * defining it _MIN instead. */ |
| 68 | }; |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | * glyf -- TrueType Glyph Data |
| 73 | * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf |
| 74 | */ |
| 75 | #define HB_OT_TAG_glyf HB_TAG('g','l','y','f') |
| 76 | |
| 77 | |
| 78 | struct glyf |
| 79 | { |
| 80 | static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; |
| 81 | |
| 82 | bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const |
| 83 | { |
| 84 | TRACE_SANITIZE (this); |
| 85 | /* Runtime checks as eager sanitizing each glyph is costy */ |
| 86 | return_trace (true); |
| 87 | } |
| 88 | |
| 89 | template<typename Iterator, |
| 90 | hb_requires (hb_is_source_of (Iterator, unsigned int))> |
| 91 | static bool |
| 92 | _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets) |
| 93 | { |
| 94 | unsigned max_offset = + padded_offsets | hb_reduce(hb_add, 0); |
| 95 | unsigned num_offsets = padded_offsets.len () + 1; |
| 96 | bool use_short_loca = max_offset < 0x1FFFF; |
| 97 | unsigned entry_size = use_short_loca ? 2 : 4; |
| 98 | char *loca_prime_data = (char *) calloc (entry_size, num_offsets); |
| 99 | |
| 100 | if (unlikely (!loca_prime_data)) return false; |
| 101 | |
| 102 | DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d " |
| 103 | "max_offset %d size %d" , |
| 104 | entry_size, num_offsets, max_offset, entry_size * num_offsets); |
| 105 | |
| 106 | if (use_short_loca) |
| 107 | _write_loca (padded_offsets, 1, hb_array ((HBUINT16*) loca_prime_data, num_offsets)); |
| 108 | else |
| 109 | _write_loca (padded_offsets, 0, hb_array ((HBUINT32*) loca_prime_data, num_offsets)); |
| 110 | |
| 111 | hb_blob_t * loca_blob = hb_blob_create (loca_prime_data, |
| 112 | entry_size * num_offsets, |
| 113 | HB_MEMORY_MODE_WRITABLE, |
| 114 | loca_prime_data, |
| 115 | free); |
| 116 | |
| 117 | bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) |
| 118 | && _add_head_and_set_loca_version (plan, use_short_loca); |
| 119 | |
| 120 | hb_blob_destroy (loca_blob); |
| 121 | return result; |
| 122 | } |
| 123 | |
| 124 | template<typename IteratorIn, typename IteratorOut, |
| 125 | hb_requires (hb_is_source_of (IteratorIn, unsigned int)), |
| 126 | hb_requires (hb_is_sink_of (IteratorOut, unsigned))> |
| 127 | static void |
| 128 | _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest) |
| 129 | { |
| 130 | unsigned int offset = 0; |
| 131 | dest << 0; |
| 132 | + it |
| 133 | | hb_map ([=, &offset] (unsigned int padded_size) |
| 134 | { |
| 135 | offset += padded_size; |
| 136 | DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d" , offset); |
| 137 | return offset >> right_shift; |
| 138 | }) |
| 139 | | hb_sink (dest) |
| 140 | ; |
| 141 | } |
| 142 | |
| 143 | /* requires source of SubsetGlyph complains the identifier isn't declared */ |
| 144 | template <typename Iterator> |
| 145 | bool serialize (hb_serialize_context_t *c, |
| 146 | Iterator it, |
| 147 | const hb_subset_plan_t *plan) |
| 148 | { |
| 149 | TRACE_SERIALIZE (this); |
| 150 | unsigned init_len = c->length (); |
| 151 | for (const auto &_ : it) _.serialize (c, plan); |
| 152 | |
| 153 | /* As a special case when all glyph in the font are empty, add a zero byte |
| 154 | * to the table, so that OTS doesn’t reject it, and to make the table work |
| 155 | * on Windows as well. |
| 156 | * See https://github.com/khaledhosny/ots/issues/52 */ |
| 157 | if (init_len == c->length ()) |
| 158 | { |
| 159 | HBUINT8 empty_byte; |
| 160 | empty_byte = 0; |
| 161 | c->copy (empty_byte); |
| 162 | } |
| 163 | return_trace (true); |
| 164 | } |
| 165 | |
| 166 | /* Byte region(s) per glyph to output |
| 167 | unpadded, hints removed if so requested |
| 168 | If we fail to process a glyph we produce an empty (0-length) glyph */ |
| 169 | bool subset (hb_subset_context_t *c) const |
| 170 | { |
| 171 | TRACE_SUBSET (this); |
| 172 | |
| 173 | glyf *glyf_prime = c->serializer->start_embed <glyf> (); |
| 174 | if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); |
| 175 | |
| 176 | hb_vector_t<SubsetGlyph> glyphs; |
| 177 | _populate_subset_glyphs (c->plan, &glyphs); |
| 178 | |
| 179 | glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan); |
| 180 | |
| 181 | auto padded_offsets = |
| 182 | + hb_iter (glyphs) |
| 183 | | hb_map (&SubsetGlyph::padded_size) |
| 184 | ; |
| 185 | |
| 186 | if (c->serializer->in_error ()) return_trace (false); |
| 187 | return_trace (c->serializer->check_success (_add_loca_and_head (c->plan, |
| 188 | padded_offsets))); |
| 189 | } |
| 190 | |
| 191 | template <typename SubsetGlyph> |
| 192 | void |
| 193 | _populate_subset_glyphs (const hb_subset_plan_t *plan, |
| 194 | hb_vector_t<SubsetGlyph> *glyphs /* OUT */) const |
| 195 | { |
| 196 | OT::glyf::accelerator_t glyf; |
| 197 | glyf.init (plan->source); |
| 198 | |
| 199 | + hb_range (plan->num_output_glyphs ()) |
| 200 | | hb_map ([&] (hb_codepoint_t new_gid) |
| 201 | { |
| 202 | SubsetGlyph subset_glyph = {0}; |
| 203 | subset_glyph.new_gid = new_gid; |
| 204 | |
| 205 | /* should never fail: all old gids should be mapped */ |
| 206 | if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) |
| 207 | return subset_glyph; |
| 208 | |
| 209 | subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); |
| 210 | if (plan->drop_hints) subset_glyph.drop_hints_bytes (); |
| 211 | else subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); |
| 212 | |
| 213 | return subset_glyph; |
| 214 | }) |
| 215 | | hb_sink (glyphs) |
| 216 | ; |
| 217 | |
| 218 | glyf.fini (); |
| 219 | } |
| 220 | |
| 221 | static bool |
| 222 | _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) |
| 223 | { |
| 224 | hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source); |
| 225 | hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); |
| 226 | hb_blob_destroy (head_blob); |
| 227 | |
| 228 | if (unlikely (!head_prime_blob)) |
| 229 | return false; |
| 230 | |
| 231 | head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); |
| 232 | head_prime->indexToLocFormat = use_short_loca ? 0 : 1; |
| 233 | bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); |
| 234 | |
| 235 | hb_blob_destroy (head_prime_blob); |
| 236 | return success; |
| 237 | } |
| 238 | |
| 239 | struct CompositeGlyphChain |
| 240 | { |
| 241 | enum composite_glyph_flag_t |
| 242 | { |
| 243 | ARG_1_AND_2_ARE_WORDS = 0x0001, |
| 244 | ARGS_ARE_XY_VALUES = 0x0002, |
| 245 | ROUND_XY_TO_GRID = 0x0004, |
| 246 | WE_HAVE_A_SCALE = 0x0008, |
| 247 | MORE_COMPONENTS = 0x0020, |
| 248 | WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, |
| 249 | WE_HAVE_A_TWO_BY_TWO = 0x0080, |
| 250 | WE_HAVE_INSTRUCTIONS = 0x0100, |
| 251 | USE_MY_METRICS = 0x0200, |
| 252 | OVERLAP_COMPOUND = 0x0400, |
| 253 | SCALED_COMPONENT_OFFSET = 0x0800, |
| 254 | UNSCALED_COMPONENT_OFFSET = 0x1000 |
| 255 | }; |
| 256 | |
| 257 | unsigned int get_size () const |
| 258 | { |
| 259 | unsigned int size = min_size; |
| 260 | /* arg1 and 2 are int16 */ |
| 261 | if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; |
| 262 | /* arg1 and 2 are int8 */ |
| 263 | else size += 2; |
| 264 | |
| 265 | /* One x 16 bit (scale) */ |
| 266 | if (flags & WE_HAVE_A_SCALE) size += 2; |
| 267 | /* Two x 16 bit (xscale, yscale) */ |
| 268 | else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; |
| 269 | /* Four x 16 bit (xscale, scale01, scale10, yscale) */ |
| 270 | else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; |
| 271 | |
| 272 | return size; |
| 273 | } |
| 274 | |
| 275 | bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } |
| 276 | bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } |
| 277 | void get_anchor_points (unsigned int &point1, unsigned int &point2) const |
| 278 | { |
| 279 | const HBUINT8 *p = &StructAfter<const HBUINT8> (glyphIndex); |
| 280 | if (flags & ARG_1_AND_2_ARE_WORDS) |
| 281 | { |
| 282 | point1 = ((const HBUINT16 *) p)[0]; |
| 283 | point2 = ((const HBUINT16 *) p)[1]; |
| 284 | } |
| 285 | else |
| 286 | { |
| 287 | point1 = p[0]; |
| 288 | point2 = p[1]; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | void transform_points (contour_point_vector_t &points) const |
| 293 | { |
| 294 | float matrix[4]; |
| 295 | contour_point_t trans; |
| 296 | if (get_transformation (matrix, trans)) |
| 297 | { |
| 298 | if (scaled_offsets ()) |
| 299 | { |
| 300 | points.translate (trans); |
| 301 | points.transform (matrix); |
| 302 | } |
| 303 | else |
| 304 | { |
| 305 | points.transform (matrix); |
| 306 | points.translate (trans); |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | protected: |
| 312 | bool scaled_offsets () const |
| 313 | { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } |
| 314 | |
| 315 | bool get_transformation (float (&matrix)[4], contour_point_t &trans) const |
| 316 | { |
| 317 | matrix[0] = matrix[3] = 1.f; |
| 318 | matrix[1] = matrix[2] = 0.f; |
| 319 | |
| 320 | int tx, ty; |
| 321 | const HBINT8 *p = &StructAfter<const HBINT8> (glyphIndex); |
| 322 | if (flags & ARG_1_AND_2_ARE_WORDS) |
| 323 | { |
| 324 | tx = *(const HBINT16 *) p; |
| 325 | p += HBINT16::static_size; |
| 326 | ty = *(const HBINT16 *) p; |
| 327 | p += HBINT16::static_size; |
| 328 | } |
| 329 | else |
| 330 | { |
| 331 | tx = *p++; |
| 332 | ty = *p++; |
| 333 | } |
| 334 | if (is_anchored ()) tx = ty = 0; |
| 335 | |
| 336 | trans.init ((float) tx, (float) ty); |
| 337 | |
| 338 | { |
| 339 | const F2DOT14 *points = (const F2DOT14 *) p; |
| 340 | if (flags & WE_HAVE_A_SCALE) |
| 341 | { |
| 342 | matrix[0] = matrix[3] = points[0].to_float (); |
| 343 | return true; |
| 344 | } |
| 345 | else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) |
| 346 | { |
| 347 | matrix[0] = points[0].to_float (); |
| 348 | matrix[3] = points[1].to_float (); |
| 349 | return true; |
| 350 | } |
| 351 | else if (flags & WE_HAVE_A_TWO_BY_TWO) |
| 352 | { |
| 353 | matrix[0] = points[0].to_float (); |
| 354 | matrix[1] = points[1].to_float (); |
| 355 | matrix[2] = points[2].to_float (); |
| 356 | matrix[3] = points[3].to_float (); |
| 357 | return true; |
| 358 | } |
| 359 | } |
| 360 | return tx || ty; |
| 361 | } |
| 362 | |
| 363 | public: |
| 364 | HBUINT16 flags; |
| 365 | HBGlyphID glyphIndex; |
| 366 | public: |
| 367 | DEFINE_SIZE_MIN (4); |
| 368 | }; |
| 369 | |
| 370 | struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphChain &> |
| 371 | { |
| 372 | typedef const CompositeGlyphChain *__item_t__; |
| 373 | composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : |
| 374 | glyph (glyph_), current (current_) |
| 375 | { if (!check_range (current)) current = nullptr; } |
| 376 | composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {} |
| 377 | |
| 378 | const CompositeGlyphChain &__item__ () const { return *current; } |
| 379 | bool __more__ () const { return current; } |
| 380 | void __next__ () |
| 381 | { |
| 382 | if (!(current->flags & CompositeGlyphChain::MORE_COMPONENTS)) { current = nullptr; return; } |
| 383 | |
| 384 | const CompositeGlyphChain *possible = &StructAfter<CompositeGlyphChain, |
| 385 | CompositeGlyphChain> (*current); |
| 386 | if (!check_range (possible)) { current = nullptr; return; } |
| 387 | current = possible; |
| 388 | } |
| 389 | bool operator != (const composite_iter_t& o) const |
| 390 | { return glyph != o.glyph || current != o.current; } |
| 391 | |
| 392 | bool check_range (const CompositeGlyphChain *composite) const |
| 393 | { |
| 394 | return glyph.check_range (composite, CompositeGlyphChain::min_size) |
| 395 | && glyph.check_range (composite, composite->get_size ()); |
| 396 | } |
| 397 | |
| 398 | private: |
| 399 | hb_bytes_t glyph; |
| 400 | __item_t__ current; |
| 401 | }; |
| 402 | |
| 403 | enum phantom_point_index_t |
| 404 | { |
| 405 | PHANTOM_LEFT = 0, |
| 406 | PHANTOM_RIGHT = 1, |
| 407 | PHANTOM_TOP = 2, |
| 408 | PHANTOM_BOTTOM = 3, |
| 409 | PHANTOM_COUNT = 4 |
| 410 | }; |
| 411 | |
| 412 | struct Glyph |
| 413 | { |
| 414 | enum simple_glyph_flag_t |
| 415 | { |
| 416 | FLAG_ON_CURVE = 0x01, |
| 417 | FLAG_X_SHORT = 0x02, |
| 418 | FLAG_Y_SHORT = 0x04, |
| 419 | FLAG_REPEAT = 0x08, |
| 420 | FLAG_X_SAME = 0x10, |
| 421 | FLAG_Y_SAME = 0x20, |
| 422 | FLAG_RESERVED1 = 0x40, |
| 423 | FLAG_RESERVED2 = 0x80 |
| 424 | }; |
| 425 | |
| 426 | private: |
| 427 | struct |
| 428 | { |
| 429 | bool () const { return numberOfContours; } |
| 430 | |
| 431 | bool (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const |
| 432 | { |
| 433 | /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ |
| 434 | /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ |
| 435 | extents->x_bearing = font->em_scale_x (font->face->table.hmtx->get_side_bearing (gid)); |
| 436 | extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); |
| 437 | extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); |
| 438 | extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); |
| 439 | |
| 440 | return true; |
| 441 | } |
| 442 | |
| 443 | HBINT16 ; |
| 444 | /* If the number of contours is |
| 445 | * greater than or equal to zero, |
| 446 | * this is a simple glyph; if negative, |
| 447 | * this is a composite glyph. */ |
| 448 | FWORD ; /* Minimum x for coordinate data. */ |
| 449 | FWORD ; /* Minimum y for coordinate data. */ |
| 450 | FWORD ; /* Maximum x for coordinate data. */ |
| 451 | FWORD ; /* Maximum y for coordinate data. */ |
| 452 | public: |
| 453 | DEFINE_SIZE_STATIC (10); |
| 454 | }; |
| 455 | |
| 456 | struct SimpleGlyph |
| 457 | { |
| 458 | const GlyphHeader &; |
| 459 | hb_bytes_t bytes; |
| 460 | (const GlyphHeader &, hb_bytes_t bytes_) : |
| 461 | header (header_), bytes (bytes_) {} |
| 462 | |
| 463 | unsigned int instruction_len_offset () const |
| 464 | { return GlyphHeader::static_size + 2 * header.numberOfContours; } |
| 465 | |
| 466 | unsigned int length (unsigned int instruction_len) const |
| 467 | { return instruction_len_offset () + 2 + instruction_len; } |
| 468 | |
| 469 | unsigned int instructions_length () const |
| 470 | { |
| 471 | unsigned int instruction_length_offset = instruction_len_offset (); |
| 472 | if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; |
| 473 | |
| 474 | const HBUINT16 &instructionLength = StructAtOffset<HBUINT16> (&bytes, instruction_length_offset); |
| 475 | /* Out of bounds of the current glyph */ |
| 476 | if (unlikely (length (instructionLength) > bytes.length)) return 0; |
| 477 | return instructionLength; |
| 478 | } |
| 479 | |
| 480 | const Glyph trim_padding () const |
| 481 | { |
| 482 | /* based on FontTools _g_l_y_f.py::trim */ |
| 483 | const char *glyph = bytes.arrayZ; |
| 484 | const char *glyph_end = glyph + bytes.length; |
| 485 | /* simple glyph w/contours, possibly trimmable */ |
| 486 | glyph += instruction_len_offset (); |
| 487 | |
| 488 | if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); |
| 489 | unsigned int num_coordinates = StructAtOffset<HBUINT16> (glyph - 2, 0) + 1; |
| 490 | unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0); |
| 491 | |
| 492 | glyph += 2 + num_instructions; |
| 493 | |
| 494 | unsigned int coord_bytes = 0; |
| 495 | unsigned int coords_with_flags = 0; |
| 496 | while (glyph < glyph_end) |
| 497 | { |
| 498 | uint8_t flag = *glyph; |
| 499 | glyph++; |
| 500 | |
| 501 | unsigned int repeat = 1; |
| 502 | if (flag & FLAG_REPEAT) |
| 503 | { |
| 504 | if (unlikely (glyph >= glyph_end)) return Glyph (); |
| 505 | repeat = *glyph + 1; |
| 506 | glyph++; |
| 507 | } |
| 508 | |
| 509 | unsigned int xBytes, yBytes; |
| 510 | xBytes = yBytes = 0; |
| 511 | if (flag & FLAG_X_SHORT) xBytes = 1; |
| 512 | else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; |
| 513 | |
| 514 | if (flag & FLAG_Y_SHORT) yBytes = 1; |
| 515 | else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; |
| 516 | |
| 517 | coord_bytes += (xBytes + yBytes) * repeat; |
| 518 | coords_with_flags += repeat; |
| 519 | if (coords_with_flags >= num_coordinates) break; |
| 520 | } |
| 521 | |
| 522 | if (unlikely (coords_with_flags != num_coordinates)) return Glyph (); |
| 523 | return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph))); |
| 524 | } |
| 525 | |
| 526 | /* zero instruction length */ |
| 527 | void drop_hints () |
| 528 | { |
| 529 | GlyphHeader & = const_cast<GlyphHeader &> (header); |
| 530 | (HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0; |
| 531 | } |
| 532 | |
| 533 | void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const |
| 534 | { |
| 535 | unsigned int instructions_len = instructions_length (); |
| 536 | unsigned int glyph_length = length (instructions_len); |
| 537 | dest_start = bytes.sub_array (0, glyph_length - instructions_len); |
| 538 | dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); |
| 539 | } |
| 540 | |
| 541 | static bool read_points (const HBUINT8 *&p /* IN/OUT */, |
| 542 | contour_point_vector_t &points_ /* IN/OUT */, |
| 543 | const hb_bytes_t &bytes, |
| 544 | void (* setter) (contour_point_t &_, float v), |
| 545 | const simple_glyph_flag_t short_flag, |
| 546 | const simple_glyph_flag_t same_flag) |
| 547 | { |
| 548 | float v = 0; |
| 549 | for (unsigned i = 0; i < points_.length; i++) |
| 550 | { |
| 551 | uint8_t flag = points_[i].flag; |
| 552 | if (flag & short_flag) |
| 553 | { |
| 554 | if (unlikely (!bytes.check_range (p))) return false; |
| 555 | if (flag & same_flag) |
| 556 | v += *p++; |
| 557 | else |
| 558 | v -= *p++; |
| 559 | } |
| 560 | else |
| 561 | { |
| 562 | if (!(flag & same_flag)) |
| 563 | { |
| 564 | if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; |
| 565 | v += *(const HBINT16 *) p; |
| 566 | p += HBINT16::static_size; |
| 567 | } |
| 568 | } |
| 569 | setter (points_[i], v); |
| 570 | } |
| 571 | return true; |
| 572 | } |
| 573 | |
| 574 | bool get_contour_points (contour_point_vector_t &points_ /* OUT */, |
| 575 | bool phantom_only = false) const |
| 576 | { |
| 577 | const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header); |
| 578 | int num_contours = header.numberOfContours; |
| 579 | if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; |
| 580 | unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; |
| 581 | |
| 582 | points_.resize (num_points); |
| 583 | for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); |
| 584 | if (phantom_only) return true; |
| 585 | |
| 586 | for (int i = 0; i < num_contours; i++) |
| 587 | points_[endPtsOfContours[i]].is_end_point = true; |
| 588 | |
| 589 | /* Skip instructions */ |
| 590 | const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1], |
| 591 | endPtsOfContours[num_contours]); |
| 592 | |
| 593 | /* Read flags */ |
| 594 | for (unsigned int i = 0; i < num_points; i++) |
| 595 | { |
| 596 | if (unlikely (!bytes.check_range (p))) return false; |
| 597 | uint8_t flag = *p++; |
| 598 | points_[i].flag = flag; |
| 599 | if (flag & FLAG_REPEAT) |
| 600 | { |
| 601 | if (unlikely (!bytes.check_range (p))) return false; |
| 602 | unsigned int repeat_count = *p++; |
| 603 | while ((repeat_count-- > 0) && (++i < num_points)) |
| 604 | points_[i].flag = flag; |
| 605 | } |
| 606 | } |
| 607 | |
| 608 | /* Read x & y coordinates */ |
| 609 | return (read_points (p, points_, bytes, |
| 610 | [] (contour_point_t &p, float v) { p.x = v; }, FLAG_X_SHORT, FLAG_X_SAME) && |
| 611 | read_points (p, points_, bytes, |
| 612 | [] (contour_point_t &p, float v) { p.y = v; }, FLAG_Y_SHORT, FLAG_Y_SAME)); |
| 613 | } |
| 614 | }; |
| 615 | |
| 616 | struct CompositeGlyph |
| 617 | { |
| 618 | const GlyphHeader &; |
| 619 | hb_bytes_t bytes; |
| 620 | (const GlyphHeader &, hb_bytes_t bytes_) : |
| 621 | header (header_), bytes (bytes_) {} |
| 622 | |
| 623 | composite_iter_t get_iterator () const |
| 624 | { return composite_iter_t (bytes, &StructAfter<CompositeGlyphChain, GlyphHeader> (header)); } |
| 625 | |
| 626 | unsigned int instructions_length (hb_bytes_t bytes) const |
| 627 | { |
| 628 | unsigned int start = bytes.length; |
| 629 | unsigned int end = bytes.length; |
| 630 | const CompositeGlyphChain *last = nullptr; |
| 631 | for (auto &item : get_iterator ()) |
| 632 | last = &item; |
| 633 | if (unlikely (!last)) return 0; |
| 634 | |
| 635 | if ((uint16_t) last->flags & CompositeGlyphChain::WE_HAVE_INSTRUCTIONS) |
| 636 | start = (char *) last - &bytes + last->get_size (); |
| 637 | if (unlikely (start > end)) return 0; |
| 638 | return end - start; |
| 639 | } |
| 640 | |
| 641 | /* Trimming for composites not implemented. |
| 642 | * If removing hints it falls out of that. */ |
| 643 | const Glyph trim_padding () const { return Glyph (bytes); } |
| 644 | |
| 645 | /* remove WE_HAVE_INSTRUCTIONS flag from composite glyph */ |
| 646 | void drop_hints () |
| 647 | { |
| 648 | for (const auto &_ : get_iterator ()) |
| 649 | *const_cast<OT::HBUINT16 *> (&_.flags) = (uint16_t) _.flags & ~OT::glyf::CompositeGlyphChain::WE_HAVE_INSTRUCTIONS; |
| 650 | } |
| 651 | |
| 652 | /* Chop instructions off the end */ |
| 653 | void drop_hints_bytes (hb_bytes_t &dest_start) const |
| 654 | { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } |
| 655 | }; |
| 656 | |
| 657 | enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; |
| 658 | |
| 659 | public: |
| 660 | composite_iter_t get_composite_iterator () const |
| 661 | { |
| 662 | if (type != COMPOSITE) return composite_iter_t (); |
| 663 | return CompositeGlyph (*header, bytes).get_iterator (); |
| 664 | } |
| 665 | |
| 666 | const Glyph trim_padding () const |
| 667 | { |
| 668 | switch (type) { |
| 669 | case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); |
| 670 | case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); |
| 671 | default: return bytes; |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | void drop_hints () |
| 676 | { |
| 677 | switch (type) { |
| 678 | case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; |
| 679 | case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; |
| 680 | default: return; |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const |
| 685 | { |
| 686 | switch (type) { |
| 687 | case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; |
| 688 | case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; |
| 689 | default: return; |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | /* Note: Recursively calls itself. |
| 694 | * all_points includes phantom points |
| 695 | */ |
| 696 | template<typename T> |
| 697 | bool get_points (T glyph_for_gid, hb_font_t *font, |
| 698 | contour_point_vector_t &all_points /* OUT */, |
| 699 | bool phantom_only = false, |
| 700 | unsigned int depth = 0) const |
| 701 | { |
| 702 | if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; |
| 703 | contour_point_vector_t points; |
| 704 | |
| 705 | switch (type) { |
| 706 | case COMPOSITE: |
| 707 | { |
| 708 | /* pseudo component points for each component in composite glyph */ |
| 709 | unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ()); |
| 710 | if (unlikely (!points.resize (num_points))) return false; |
| 711 | for (unsigned i = 0; i < points.length; i++) |
| 712 | points[i].init (); |
| 713 | break; |
| 714 | } |
| 715 | case SIMPLE: |
| 716 | if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) |
| 717 | return false; |
| 718 | break; |
| 719 | default: return false; /* empty glyph */ |
| 720 | } |
| 721 | |
| 722 | hb_face_t *face = font->face; |
| 723 | |
| 724 | /* Init phantom points */ |
| 725 | if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; |
| 726 | hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); |
| 727 | { |
| 728 | for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init (); |
| 729 | int h_delta = (int) header->xMin - face->table.hmtx->get_side_bearing (gid); |
| 730 | int v_orig = (int) header->yMax + face->table.vmtx->get_side_bearing (gid); |
| 731 | unsigned h_adv = face->table.hmtx->get_advance (gid); |
| 732 | unsigned v_adv = face->table.vmtx->get_advance (gid); |
| 733 | phantoms[PHANTOM_LEFT].x = h_delta; |
| 734 | phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; |
| 735 | phantoms[PHANTOM_TOP].y = v_orig; |
| 736 | phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; |
| 737 | } |
| 738 | |
| 739 | #ifndef HB_NO_VAR |
| 740 | if (unlikely (!face->table.gvar->apply_deltas_to_points (gid, font, points.as_array ()))) |
| 741 | return false; |
| 742 | #endif |
| 743 | |
| 744 | switch (type) { |
| 745 | case SIMPLE: |
| 746 | all_points.extend (points.as_array ()); |
| 747 | break; |
| 748 | case COMPOSITE: |
| 749 | { |
| 750 | unsigned int comp_index = 0; |
| 751 | for (auto &item : get_composite_iterator ()) |
| 752 | { |
| 753 | contour_point_vector_t comp_points; |
| 754 | if (unlikely (!glyph_for_gid (item.glyphIndex).get_points (glyph_for_gid, font, comp_points, phantom_only, depth + 1)) |
| 755 | || comp_points.length < PHANTOM_COUNT) |
| 756 | return false; |
| 757 | |
| 758 | /* Copy phantom points from component if USE_MY_METRICS flag set */ |
| 759 | if (item.is_use_my_metrics ()) |
| 760 | for (unsigned int i = 0; i < PHANTOM_COUNT; i++) |
| 761 | phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; |
| 762 | |
| 763 | /* Apply component transformation & translation */ |
| 764 | item.transform_points (comp_points); |
| 765 | |
| 766 | /* Apply translation from gvar */ |
| 767 | comp_points.translate (points[comp_index]); |
| 768 | |
| 769 | if (item.is_anchored ()) |
| 770 | { |
| 771 | unsigned int p1, p2; |
| 772 | item.get_anchor_points (p1, p2); |
| 773 | if (likely (p1 < all_points.length && p2 < comp_points.length)) |
| 774 | { |
| 775 | contour_point_t delta; |
| 776 | delta.init (all_points[p1].x - comp_points[p2].x, |
| 777 | all_points[p1].y - comp_points[p2].y); |
| 778 | |
| 779 | comp_points.translate (delta); |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); |
| 784 | |
| 785 | comp_index++; |
| 786 | } |
| 787 | |
| 788 | all_points.extend (phantoms); |
| 789 | } break; |
| 790 | default: return false; |
| 791 | } |
| 792 | |
| 793 | if (depth == 0) /* Apply at top level */ |
| 794 | { |
| 795 | /* Undocumented rasterizer behavior: |
| 796 | * Shift points horizontally by the updated left side bearing |
| 797 | */ |
| 798 | contour_point_t delta; |
| 799 | delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); |
| 800 | if (delta.x) all_points.translate (delta); |
| 801 | } |
| 802 | |
| 803 | return true; |
| 804 | } |
| 805 | |
| 806 | bool get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const |
| 807 | { |
| 808 | if (type == EMPTY) return true; /* Empty glyph; zero extents. */ |
| 809 | return header->get_extents (font, gid, extents); |
| 810 | } |
| 811 | |
| 812 | hb_bytes_t get_bytes () const { return bytes; } |
| 813 | |
| 814 | Glyph (hb_bytes_t bytes_ = hb_bytes_t (), |
| 815 | hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), |
| 816 | header (bytes.as<GlyphHeader> ()) |
| 817 | { |
| 818 | int num_contours = header->numberOfContours; |
| 819 | if (unlikely (num_contours == 0)) type = EMPTY; |
| 820 | else if (num_contours > 0) type = SIMPLE; |
| 821 | else type = COMPOSITE; /* negative numbers */ |
| 822 | } |
| 823 | |
| 824 | protected: |
| 825 | hb_bytes_t bytes; |
| 826 | hb_codepoint_t gid; |
| 827 | const GlyphHeader *; |
| 828 | unsigned type; |
| 829 | }; |
| 830 | |
| 831 | struct accelerator_t |
| 832 | { |
| 833 | void init (hb_face_t *face_) |
| 834 | { |
| 835 | short_offset = false; |
| 836 | num_glyphs = 0; |
| 837 | loca_table = nullptr; |
| 838 | glyf_table = nullptr; |
| 839 | face = face_; |
| 840 | const OT::head &head = *face->table.head; |
| 841 | if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) |
| 842 | /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ |
| 843 | return; |
| 844 | short_offset = 0 == head.indexToLocFormat; |
| 845 | |
| 846 | loca_table = hb_sanitize_context_t ().reference_table<loca> (face); |
| 847 | glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face); |
| 848 | |
| 849 | num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; |
| 850 | num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); |
| 851 | } |
| 852 | |
| 853 | void fini () |
| 854 | { |
| 855 | loca_table.destroy (); |
| 856 | glyf_table.destroy (); |
| 857 | } |
| 858 | |
| 859 | protected: |
| 860 | template<typename T> |
| 861 | bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const |
| 862 | { |
| 863 | /* Making this alloc free is not that easy |
| 864 | https://github.com/harfbuzz/harfbuzz/issues/2095 |
| 865 | mostly because of gvar handling in VF fonts, |
| 866 | perhaps a separate path for non-VF fonts can be considered */ |
| 867 | contour_point_vector_t all_points; |
| 868 | |
| 869 | bool phantom_only = !consumer.is_consuming_contour_points (); |
| 870 | if (unlikely (!glyph_for_gid (gid).get_points ([this] (hb_codepoint_t gid) -> const Glyph { return this->glyph_for_gid (gid); }, |
| 871 | font, all_points, phantom_only))) |
| 872 | return false; |
| 873 | |
| 874 | if (consumer.is_consuming_contour_points ()) |
| 875 | { |
| 876 | for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) |
| 877 | consumer.consume_point (all_points[point_index]); |
| 878 | consumer.points_end (); |
| 879 | } |
| 880 | |
| 881 | /* Where to write phantoms, nullptr if not requested */ |
| 882 | contour_point_t *phantoms = consumer.get_phantoms_sink (); |
| 883 | if (phantoms) |
| 884 | for (unsigned i = 0; i < PHANTOM_COUNT; ++i) |
| 885 | phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i]; |
| 886 | |
| 887 | return true; |
| 888 | } |
| 889 | |
| 890 | public: |
| 891 | #ifndef HB_NO_VAR |
| 892 | struct points_aggregator_t |
| 893 | { |
| 894 | hb_font_t *font; |
| 895 | hb_glyph_extents_t *extents; |
| 896 | contour_point_t *phantoms; |
| 897 | |
| 898 | struct contour_bounds_t |
| 899 | { |
| 900 | contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } |
| 901 | |
| 902 | void add (const contour_point_t &p) |
| 903 | { |
| 904 | min_x = hb_min (min_x, p.x); |
| 905 | min_y = hb_min (min_y, p.y); |
| 906 | max_x = hb_max (max_x, p.x); |
| 907 | max_y = hb_max (max_y, p.y); |
| 908 | } |
| 909 | |
| 910 | bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } |
| 911 | |
| 912 | void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) |
| 913 | { |
| 914 | if (unlikely (empty ())) |
| 915 | { |
| 916 | extents->width = 0; |
| 917 | extents->x_bearing = 0; |
| 918 | extents->height = 0; |
| 919 | extents->y_bearing = 0; |
| 920 | return; |
| 921 | } |
| 922 | extents->x_bearing = font->em_scalef_x (min_x); |
| 923 | extents->width = font->em_scalef_x (max_x - min_x); |
| 924 | extents->y_bearing = font->em_scalef_y (max_y); |
| 925 | extents->height = font->em_scalef_y (min_y - max_y); |
| 926 | } |
| 927 | |
| 928 | protected: |
| 929 | float min_x, min_y, max_x, max_y; |
| 930 | } bounds; |
| 931 | |
| 932 | points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) |
| 933 | { |
| 934 | font = font_; |
| 935 | extents = extents_; |
| 936 | phantoms = phantoms_; |
| 937 | if (extents) bounds = contour_bounds_t (); |
| 938 | } |
| 939 | |
| 940 | void consume_point (const contour_point_t &point) { bounds.add (point); } |
| 941 | void points_end () { bounds.get_extents (font, extents); } |
| 942 | |
| 943 | bool is_consuming_contour_points () { return extents; } |
| 944 | contour_point_t *get_phantoms_sink () { return phantoms; } |
| 945 | }; |
| 946 | |
| 947 | unsigned int |
| 948 | get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const |
| 949 | { |
| 950 | bool success = false; |
| 951 | |
| 952 | contour_point_t phantoms[PHANTOM_COUNT]; |
| 953 | if (likely (font->num_coords == face->table.gvar->get_axis_count ())) |
| 954 | success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); |
| 955 | |
| 956 | if (unlikely (!success)) |
| 957 | return is_vertical |
| 958 | ? face->table.vmtx->get_advance (gid) |
| 959 | : face->table.hmtx->get_advance (gid); |
| 960 | |
| 961 | return is_vertical |
| 962 | ? roundf (phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y) |
| 963 | : roundf (phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x); |
| 964 | } |
| 965 | |
| 966 | int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const |
| 967 | { |
| 968 | hb_glyph_extents_t extents; |
| 969 | |
| 970 | contour_point_t phantoms[PHANTOM_COUNT]; |
| 971 | if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) |
| 972 | return is_vertical |
| 973 | ? face->table.vmtx->get_side_bearing (gid) |
| 974 | : face->table.hmtx->get_side_bearing (gid); |
| 975 | |
| 976 | return is_vertical |
| 977 | ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing |
| 978 | : floorf (phantoms[PHANTOM_LEFT].x); |
| 979 | } |
| 980 | #endif |
| 981 | |
| 982 | bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const |
| 983 | { |
| 984 | if (unlikely (gid >= num_glyphs)) return false; |
| 985 | #ifndef HB_NO_VAR |
| 986 | if (font->num_coords && font->num_coords == face->table.gvar->get_axis_count ()) |
| 987 | return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); |
| 988 | #endif |
| 989 | return glyph_for_gid (gid).get_extents (font, extents); |
| 990 | } |
| 991 | |
| 992 | const Glyph |
| 993 | glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const |
| 994 | { |
| 995 | unsigned int start_offset, end_offset; |
| 996 | if (unlikely (gid >= num_glyphs)) return Glyph (); |
| 997 | |
| 998 | if (short_offset) |
| 999 | { |
| 1000 | const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; |
| 1001 | start_offset = 2 * offsets[gid]; |
| 1002 | end_offset = 2 * offsets[gid + 1]; |
| 1003 | } |
| 1004 | else |
| 1005 | { |
| 1006 | const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; |
| 1007 | start_offset = offsets[gid]; |
| 1008 | end_offset = offsets[gid + 1]; |
| 1009 | } |
| 1010 | |
| 1011 | if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) |
| 1012 | return Glyph (); |
| 1013 | |
| 1014 | Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, |
| 1015 | end_offset - start_offset), gid); |
| 1016 | return needs_padding_removal ? glyph.trim_padding () : glyph; |
| 1017 | } |
| 1018 | |
| 1019 | void |
| 1020 | add_gid_and_children (hb_codepoint_t gid, hb_set_t *gids_to_retain, |
| 1021 | unsigned int depth = 0) const |
| 1022 | { |
| 1023 | if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return; |
| 1024 | /* Check if is already visited */ |
| 1025 | if (gids_to_retain->has (gid)) return; |
| 1026 | |
| 1027 | gids_to_retain->add (gid); |
| 1028 | |
| 1029 | for (auto &item : glyph_for_gid (gid).get_composite_iterator ()) |
| 1030 | add_gid_and_children (item.glyphIndex, gids_to_retain, depth); |
| 1031 | } |
| 1032 | |
| 1033 | #ifdef HB_EXPERIMENTAL_API |
| 1034 | struct path_builder_t |
| 1035 | { |
| 1036 | hb_font_t *font; |
| 1037 | draw_helper_t *draw_helper; |
| 1038 | |
| 1039 | struct optional_point_t |
| 1040 | { |
| 1041 | optional_point_t () { has_data = false; } |
| 1042 | optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; } |
| 1043 | |
| 1044 | bool has_data; |
| 1045 | float x; |
| 1046 | float y; |
| 1047 | |
| 1048 | optional_point_t lerp (optional_point_t p, float t) |
| 1049 | { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } |
| 1050 | } first_oncurve, first_offcurve, last_offcurve; |
| 1051 | |
| 1052 | path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_) |
| 1053 | { |
| 1054 | font = font_; |
| 1055 | draw_helper = &draw_helper_; |
| 1056 | first_oncurve = first_offcurve = last_offcurve = optional_point_t (); |
| 1057 | } |
| 1058 | |
| 1059 | /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 |
| 1060 | See also: |
| 1061 | * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html |
| 1062 | * https://stackoverflow.com/a/20772557 */ |
| 1063 | void consume_point (const contour_point_t &point) |
| 1064 | { |
| 1065 | /* Skip empty contours */ |
| 1066 | if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data)) |
| 1067 | return; |
| 1068 | |
| 1069 | bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE; |
| 1070 | optional_point_t p (point.x, point.y); |
| 1071 | if (!first_oncurve.has_data) |
| 1072 | { |
| 1073 | if (is_on_curve) |
| 1074 | { |
| 1075 | first_oncurve = p; |
| 1076 | draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); |
| 1077 | } |
| 1078 | else |
| 1079 | { |
| 1080 | if (first_offcurve.has_data) |
| 1081 | { |
| 1082 | optional_point_t mid = first_offcurve.lerp (p, .5f); |
| 1083 | first_oncurve = mid; |
| 1084 | last_offcurve = p; |
| 1085 | draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); |
| 1086 | } |
| 1087 | else |
| 1088 | first_offcurve = p; |
| 1089 | } |
| 1090 | } |
| 1091 | else |
| 1092 | { |
| 1093 | if (last_offcurve.has_data) |
| 1094 | { |
| 1095 | if (is_on_curve) |
| 1096 | { |
| 1097 | draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), |
| 1098 | font->em_scalef_x (p.x), font->em_scalef_y (p.y)); |
| 1099 | last_offcurve = optional_point_t (); |
| 1100 | } |
| 1101 | else |
| 1102 | { |
| 1103 | optional_point_t mid = last_offcurve.lerp (p, .5f); |
| 1104 | draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), |
| 1105 | font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); |
| 1106 | last_offcurve = p; |
| 1107 | } |
| 1108 | } |
| 1109 | else |
| 1110 | { |
| 1111 | if (is_on_curve) |
| 1112 | draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); |
| 1113 | else |
| 1114 | last_offcurve = p; |
| 1115 | } |
| 1116 | } |
| 1117 | |
| 1118 | if (point.is_end_point) |
| 1119 | { |
| 1120 | if (first_offcurve.has_data && last_offcurve.has_data) |
| 1121 | { |
| 1122 | optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); |
| 1123 | draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), |
| 1124 | font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); |
| 1125 | last_offcurve = optional_point_t (); |
| 1126 | /* now check the rest */ |
| 1127 | } |
| 1128 | |
| 1129 | if (first_offcurve.has_data && first_oncurve.has_data) |
| 1130 | draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y), |
| 1131 | font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); |
| 1132 | else if (last_offcurve.has_data && first_oncurve.has_data) |
| 1133 | draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), |
| 1134 | font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); |
| 1135 | else if (first_oncurve.has_data) |
| 1136 | draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); |
| 1137 | |
| 1138 | /* Getting ready for the next contour */ |
| 1139 | first_oncurve = first_offcurve = last_offcurve = optional_point_t (); |
| 1140 | draw_helper->end_path (); |
| 1141 | } |
| 1142 | } |
| 1143 | void points_end () {} |
| 1144 | |
| 1145 | bool is_consuming_contour_points () { return true; } |
| 1146 | contour_point_t *get_phantoms_sink () { return nullptr; } |
| 1147 | }; |
| 1148 | |
| 1149 | bool |
| 1150 | get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const |
| 1151 | { return get_points (font, gid, path_builder_t (font, draw_helper)); } |
| 1152 | #endif |
| 1153 | |
| 1154 | private: |
| 1155 | bool short_offset; |
| 1156 | unsigned int num_glyphs; |
| 1157 | hb_blob_ptr_t<loca> loca_table; |
| 1158 | hb_blob_ptr_t<glyf> glyf_table; |
| 1159 | hb_face_t *face; |
| 1160 | }; |
| 1161 | |
| 1162 | struct SubsetGlyph |
| 1163 | { |
| 1164 | hb_codepoint_t new_gid; |
| 1165 | hb_codepoint_t old_gid; |
| 1166 | Glyph source_glyph; |
| 1167 | hb_bytes_t dest_start; /* region of source_glyph to copy first */ |
| 1168 | hb_bytes_t dest_end; /* region of source_glyph to copy second */ |
| 1169 | |
| 1170 | bool serialize (hb_serialize_context_t *c, |
| 1171 | const hb_subset_plan_t *plan) const |
| 1172 | { |
| 1173 | TRACE_SERIALIZE (this); |
| 1174 | |
| 1175 | hb_bytes_t dest_glyph = dest_start.copy (c); |
| 1176 | dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); |
| 1177 | unsigned int pad_length = padding (); |
| 1178 | DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d" , dest_glyph.length, dest_glyph.length + pad_length, pad_length); |
| 1179 | |
| 1180 | HBUINT8 pad; |
| 1181 | pad = 0; |
| 1182 | while (pad_length > 0) |
| 1183 | { |
| 1184 | c->embed (pad); |
| 1185 | pad_length--; |
| 1186 | } |
| 1187 | |
| 1188 | if (unlikely (!dest_glyph.length)) return_trace (true); |
| 1189 | |
| 1190 | /* update components gids */ |
| 1191 | for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) |
| 1192 | { |
| 1193 | hb_codepoint_t new_gid; |
| 1194 | if (plan->new_gid_for_old_gid (_.glyphIndex, &new_gid)) |
| 1195 | ((OT::glyf::CompositeGlyphChain *) &_)->glyphIndex = new_gid; |
| 1196 | } |
| 1197 | |
| 1198 | if (plan->drop_hints) Glyph (dest_glyph).drop_hints (); |
| 1199 | |
| 1200 | return_trace (true); |
| 1201 | } |
| 1202 | |
| 1203 | void drop_hints_bytes () |
| 1204 | { source_glyph.drop_hints_bytes (dest_start, dest_end); } |
| 1205 | |
| 1206 | unsigned int length () const { return dest_start.length + dest_end.length; } |
| 1207 | /* pad to 2 to ensure 2-byte loca will be ok */ |
| 1208 | unsigned int padding () const { return length () % 2; } |
| 1209 | unsigned int padded_size () const { return length () + padding (); } |
| 1210 | }; |
| 1211 | |
| 1212 | protected: |
| 1213 | UnsizedArrayOf<HBUINT8> |
| 1214 | dataZ; /* Glyphs data. */ |
| 1215 | public: |
| 1216 | DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always |
| 1217 | * check the size externally, allow Null() object of it by |
| 1218 | * defining it _MIN instead. */ |
| 1219 | }; |
| 1220 | |
| 1221 | struct glyf_accelerator_t : glyf::accelerator_t {}; |
| 1222 | |
| 1223 | } /* namespace OT */ |
| 1224 | |
| 1225 | |
| 1226 | #endif /* HB_OT_GLYF_TABLE_HH */ |
| 1227 | |