diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh index a197cec81..3eac473a4 100644 --- a/src/hb-aat-layout-ankr-table.hh +++ b/src/hb-aat-layout-ankr-table.hh @@ -45,32 +45,47 @@ struct Anchor return_trace (c->check_struct (this)); } + public: FWORD xCoordinate; FWORD yCoordinate; public: DEFINE_SIZE_STATIC (4); }; +typedef LArrayOf GlyphAnchors; + struct ankr { static const hb_tag_t tableTag = HB_AAT_TAG_ankr; + inline const Anchor &get_anchor (hb_codepoint_t glyph_id, + unsigned int i, + unsigned int num_glyphs, + const char *end) const + { + unsigned int offset = (this+lookupTable).get_value_or_null (glyph_id, num_glyphs); + const GlyphAnchors &anchors = StructAtOffset (&(this+anchorData), offset); + /* TODO Use sanitizer; to avoid overflows and more. */ + if (unlikely ((const char *) &anchors + anchors.get_size () > end)) + return Null(Anchor); + return anchors[i]; + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && version == 0 && - lookupTable.sanitize (c, this) && - anchors.sanitize (c, this))); + lookupTable.sanitize (c, this))); } protected: HBUINT16 version; /* Version number (set to zero) */ HBUINT16 flags; /* Flags (currently unused; set to zero) */ - LOffsetTo > + LOffsetTo > > lookupTable; /* Offset to the table's lookup table */ - LOffsetTo > - anchors; /* Offset to the glyph data table */ + LOffsetTo + anchorData; /* Offset to the glyph data table */ public: DEFINE_SIZE_STATIC (12); diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh index 37ab53539..78a27a74c 100644 --- a/src/hb-aat-layout-common.hh +++ b/src/hb-aat-layout-common.hh @@ -514,6 +514,7 @@ struct StateTableDriver }; +struct ankr; struct hb_aat_apply_context_t : hb_dispatch_context_t @@ -529,6 +530,8 @@ struct hb_aat_apply_context_t : hb_face_t *face; hb_buffer_t *buffer; hb_sanitize_context_t sanitizer; + const ankr &ankr_table; + const char *ankr_end; /* Unused. For debug tracing only. */ unsigned int lookup_index; @@ -537,11 +540,15 @@ struct hb_aat_apply_context_t : inline hb_aat_apply_context_t (hb_ot_shape_plan_t *plan_, hb_font_t *font_, hb_buffer_t *buffer_, - hb_blob_t *table) : + hb_blob_t *blob = const_cast (&Null(hb_blob_t)), + const ankr &ankr_table_ = Null(ankr), + const char *ankr_end_ = nullptr) : plan (plan_), font (font_), face (font->face), buffer (buffer_), - sanitizer (), lookup_index (0), debug_depth (0) + sanitizer (), + ankr_table (ankr_table_), ankr_end (ankr_end_), + lookup_index (0), debug_depth (0) { - sanitizer.init (table); + sanitizer.init (blob); sanitizer.set_num_glyphs (face->get_num_glyphs ()); sanitizer.start_processing (); sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh index 3cff334a9..cd1129128 100644 --- a/src/hb-aat-layout-kerx-table.hh +++ b/src/hb-aat-layout-kerx-table.hh @@ -30,6 +30,7 @@ #include "hb-open-type.hh" #include "hb-aat-layout-common.hh" +#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-kern-table.hh" /* @@ -99,6 +100,100 @@ struct KerxSubTableFormat0 struct KerxSubTableFormat1 { + struct EntryData + { + HBUINT16 kernActionIndex;/* Index into the kerning value array. If + * this index is 0xFFFF, then no kerning + * is to be performed. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static const bool in_place = true; + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */ + Reserved = 0x1FFF, /* Not used; set to 0. */ + }; + + inline driver_context_t (const KerxSubTableFormat1 *table, + hb_aat_apply_context_t *c_) : + c (c_), + /* Apparently the offset kernAction is from the beginning of the state-machine, + * similar to offsets in morx table, NOT from beginning of this table, like + * other subtables in kerx. Discovered via testing. */ + kernAction (&table->machine + table->kernAction), + depth (0) {} + + inline bool is_actionable (StateTableDriver *driver, + const Entry *entry) + { + return entry->data.kernActionIndex != 0xFFFF; + } + inline bool transition (StateTableDriver *driver, + const Entry *entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry->flags; + + if (flags & Reset) + { + depth = 0; + } + + if (flags & Push) + { + if (likely (depth < ARRAY_LENGTH (stack))) + stack[depth++] = buffer->idx; + else + depth = 0; /* Probably not what CoreText does, but better? */ + } + + if (entry->data.kernActionIndex != 0xFFFF) + { + const FWORD *actions = &kernAction[entry->data.kernActionIndex]; + if (!c->sanitizer.check_array (actions, depth)) + { + depth = 0; + return false; + } + + hb_mask_t kern_mask = c->plan->kern_mask; + for (unsigned int i = 0; i < depth; i++) + { + /* Apparently, when spec says "Each pops one glyph from the kerning stack + * and applies the kerning value to it.", it doesn't mean it in that order. + * The deepest item in the stack corresponds to the first item in the action + * list. Discovered by testing. */ + unsigned int idx = stack[i]; + int v = *actions++; + if (buffer->info[idx].mask & kern_mask) + { + /* XXX Non-forward direction... */ + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + buffer->pos[idx].x_advance += c->font->em_scale_x (v); + else + buffer->pos[idx].y_advance += c->font->em_scale_y (v); + } + } + depth = 0; + } + + return true; + } + + private: + hb_aat_apply_context_t *c; + const UnsizedArrayOf &kernAction; + unsigned int stack[8]; + unsigned int depth; + }; + inline bool apply (hb_aat_apply_context_t *c) const { TRACE_APPLY (this); @@ -106,7 +201,10 @@ struct KerxSubTableFormat1 if (!c->plan->requested_kerning) return false; - /* TODO */ + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc); return_trace (true); } @@ -114,14 +212,13 @@ struct KerxSubTableFormat1 inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this) && - stateHeader.sanitize (c))); + return_trace (likely (machine.sanitize (c))); } protected: - KerxSubTableHeader header; - StateTable stateHeader; - LOffsetTo > valueTable; + KerxSubTableHeader header; + StateTable machine; + LOffsetTo, false> kernAction; public: DEFINE_SIZE_STATIC (32); }; @@ -136,7 +233,7 @@ struct KerxSubTableFormat2 unsigned int offset = l + r; const FWORD *v = &StructAtOffset (&(this+array), offset); if (unlikely ((const char *) v < (const char *) &array || - (const char *) v + v->static_size - (const char *) this <= header.length)) + (const char *) v + v->static_size - (const char *) this > header.length)) return 0; return *v; } @@ -159,11 +256,9 @@ struct KerxSubTableFormat2 inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this) && - rowWidth.sanitize (c) && + return_trace (likely (rowWidth.sanitize (c) && leftClassTable.sanitize (c, this) && - rightClassTable.sanitize (c, this) && - array.sanitize (c, this))); + rightClassTable.sanitize (c, this))); } struct accelerator_t @@ -190,7 +285,8 @@ struct KerxSubTableFormat2 LOffsetTo > rightClassTable;/* Offset from beginning of this subtable to * right-hand class table. */ - LOffsetTo array; /* Offset from beginning of this subtable to + LOffsetTo, false> + array; /* Offset from beginning of this subtable to * the start of the kerning array. */ public: DEFINE_SIZE_STATIC (28); @@ -198,11 +294,152 @@ struct KerxSubTableFormat2 struct KerxSubTableFormat4 { + struct EntryData + { + HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of + * the action to perform. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static const bool in_place = true; + enum Flags + { + Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* Not used; set to 0. */ + }; + + enum SubTableFlags + { + ActionType = 0xC0000000, /* A two-bit field containing the action type. */ + Unused = 0x3F000000, /* Unused - must be zero. */ + Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning + * of the subtable to the beginning of the control + * point table. */ + }; + + inline driver_context_t (const KerxSubTableFormat4 *table, + hb_aat_apply_context_t *c_) : + c (c_), + action_type ((table->flags & ActionType) >> 30), + ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))), + mark_set (false), + mark (0) {} + + inline bool is_actionable (StateTableDriver *driver, + const Entry *entry) + { + return entry->data.ankrActionIndex != 0xFFFF; + } + inline bool transition (StateTableDriver *driver, + const Entry *entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry->flags; + + if (mark_set && entry->data.ankrActionIndex != 0xFFFF) + { + hb_glyph_position_t &o = buffer->cur_pos(); + switch (action_type) + { + case 0: /* Control Point Actions.*/ + { + /* indexed into glyph outline. */ + const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex]; + if (!c->sanitizer.check_array (data, 2)) + return false; + HB_UNUSED unsigned int markControlPoint = *data++; + HB_UNUSED unsigned int currControlPoint = *data++; + hb_position_t markX = 0; + hb_position_t markY = 0; + hb_position_t currX = 0; + hb_position_t currY = 0; + if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint, + markControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &markX, &markY) || + !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint, + currControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &currX, &currY)) + return true; /* True, such that the machine continues. */ + + o.x_offset = markX - currX; + o.y_offset = markY - currY; + } + break; + + case 1: /* Anchor Point Actions. */ + { + /* Indexed into 'ankr' table. */ + const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex]; + if (!c->sanitizer.check_array (data, 2)) + return false; + unsigned int markAnchorPoint = *data++; + unsigned int currAnchorPoint = *data++; + const Anchor markAnchor = c->ankr_table.get_anchor (c->buffer->info[mark].codepoint, + markAnchorPoint, + c->face->get_num_glyphs (), + c->ankr_end); + const Anchor currAnchor = c->ankr_table.get_anchor (c->buffer->cur ().codepoint, + currAnchorPoint, + c->face->get_num_glyphs (), + c->ankr_end); + + o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate); + o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate); + } + break; + + case 2: /* Control Point Coordinate Actions. */ + { + const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex]; + if (!c->sanitizer.check_array (data, 4)) + return false; + int markX = *data++; + int markY = *data++; + int currX = *data++; + int currY = *data++; + + o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX); + o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY); + } + break; + } + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) mark - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + + if (flags & Mark) + { + mark_set = true; + mark = buffer->idx; + } + + return true; + } + + private: + hb_aat_apply_context_t *c; + unsigned int action_type; + const HBUINT16 *ankrData; + bool mark_set; + unsigned int mark; + }; + inline bool apply (hb_aat_apply_context_t *c) const { TRACE_APPLY (this); - /* TODO */ + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc); return_trace (true); } @@ -211,14 +448,18 @@ struct KerxSubTableFormat4 { TRACE_SANITIZE (this); - /* TODO */ - return_trace (likely (c->check_struct (this))); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && + machine.sanitize (c) && + flags.sanitize (c)); } protected: KerxSubTableHeader header; + StateTable machine; + HBUINT32 flags; public: - DEFINE_SIZE_STATIC (12); + DEFINE_SIZE_STATIC (32); }; struct KerxSubTableFormat6 @@ -241,7 +482,7 @@ struct KerxSubTableFormat6 unsigned int offset = l + r; const FWORD32 *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD32)); if (unlikely ((const char *) v < (const char *) &t.array || - (const char *) v + v->static_size - (const char *) this <= header.length)) + (const char *) v + v->static_size - (const char *) this > header.length)) return 0; return *v; } @@ -253,7 +494,7 @@ struct KerxSubTableFormat6 unsigned int offset = l + r; const FWORD *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD)); if (unlikely ((const char *) v < (const char *) &t.array || - (const char *) v + v->static_size - (const char *) this <= header.length)) + (const char *) v + v->static_size - (const char *) this > header.length)) return 0; return *v; } @@ -281,12 +522,10 @@ struct KerxSubTableFormat6 is_long () ? ( u.l.rowIndexTable.sanitize (c, this) && - u.l.columnIndexTable.sanitize (c, this) && - u.l.array.sanitize (c, this) + u.l.columnIndexTable.sanitize (c, this) ) : ( u.s.rowIndexTable.sanitize (c, this) && - u.s.columnIndexTable.sanitize (c, this) && - u.s.array.sanitize (c, this) + u.s.columnIndexTable.sanitize (c, this) ))); } @@ -316,13 +555,15 @@ struct KerxSubTableFormat6 { LOffsetTo > rowIndexTable; LOffsetTo > columnIndexTable; - LOffsetTo array; + LOffsetTo, false> + array; } l; struct Short { LOffsetTo > rowIndexTable; LOffsetTo > columnIndexTable; - LOffsetTo array; + LOffsetTo, false> + array; } s; } u; public: diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh index 0020750ce..b902fd79b 100644 --- a/src/hb-aat-layout-morx-table.hh +++ b/src/hb-aat-layout-morx-table.hh @@ -164,7 +164,7 @@ struct RearrangementSubtable driver_context_t dc (this); - StateTableDriver driver (machine, c->buffer, c->face); + StateTableDriver driver (machine, c->buffer, c->face); driver.drive (&dc); return_trace (dc.ret); @@ -365,7 +365,7 @@ struct LigatureSubtable inline bool is_actionable (StateTableDriver *driver, const Entry *entry) { - return !!(entry->flags & PerformAction); + return entry->flags & PerformAction; } inline bool transition (StateTableDriver *driver, const Entry *entry) diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh index 3b7d43881..e02721750 100644 --- a/src/hb-aat-layout-trak-table.hh +++ b/src/hb-aat-layout-trak-table.hh @@ -46,28 +46,32 @@ struct TrackTableEntry { friend struct TrackData; - inline bool sanitize (hb_sanitize_context_t *c, const void *base, - unsigned int size) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this) && - (valuesZ.sanitize (c, base, size)))); - } - - private: inline float get_track_value () const { return track.to_float (); } - inline int get_value (const void *base, unsigned int index) const + inline int get_value (const void *base, + unsigned int index, + unsigned int nSizes) const { - return (base+valuesZ)[index]; + return hb_array_t ((base+valuesZ).arrayZ, nSizes)[index]; + } + + public: + inline bool sanitize (hb_sanitize_context_t *c, const void *base, + unsigned int nSizes) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (valuesZ.sanitize (c, base, nSizes)))); } protected: Fixed track; /* Track value for this record. */ - NameID trackNameID; /* The 'name' table index for this track */ + NameID trackNameID; /* The 'name' table index for this track. + * (a short word or phrase like "loose" + * or "very tight") */ OffsetTo, HBUINT16, false> valuesZ; /* Offset from start of tracking table to * per-size tracking values for this track. */ @@ -78,12 +82,20 @@ struct TrackTableEntry struct TrackData { - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + inline float interpolate_at (unsigned int idx, + float target_size, + const TrackTableEntry &trackTableEntry, + const void *base) const { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - sizeTable.sanitize (c, base, nSizes) && - trackTable.sanitize (c, nTracks, base, nSizes)); + unsigned int sizes = nSizes; + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + + float s0 = size_table[idx].to_float (); + float s1 = size_table[idx + 1].to_float (); + float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0); + return (float) t * trackTableEntry.get_value (base, idx + 1, sizes) + + ((float) 1.0 - t) * trackTableEntry.get_value (base, idx, sizes); + return 0; } inline float get_tracking (const void *base, float ptem) const @@ -94,48 +106,59 @@ struct TrackData * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html */ float csspx = ptem * 96.f / 72.f; - Fixed fixed_size; - fixed_size.set_float (csspx); - - /* XXX Clean this up. Make it work with nSizes==1 and 0. */ - - unsigned int sizes = nSizes; + /* + * Choose track. + */ const TrackTableEntry *trackTableEntry = nullptr; - for (unsigned int i = 0; i < sizes; ++i) - // For now we only seek for track entries with zero tracking value - if (trackTable[i].get_track_value () == 0.f) - trackTableEntry = &trackTable[0]; + unsigned int count = nTracks; + for (unsigned int i = 0; i < count; i++) + { + /* Note: Seems like the track entries are sorted by values. But the + * spec doesn't explicitly say that. It just mentions it in the example. */ - // We couldn't match any, exit + /* For now we only seek for track entries with zero tracking value */ + + if (trackTable[i].get_track_value () == 0.f) + { + trackTableEntry = &trackTable[0]; + break; + } + } if (!trackTableEntry) return 0.; + /* + * Choose size. + */ + unsigned int sizes = nSizes; + if (!sizes) return 0.; + if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); + /* TODO bfind() */ + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); unsigned int size_index; - UnsizedArrayOf size_table = base+sizeTable; - for (size_index = 0; size_index < sizes; ++size_index) - if (size_table[size_index] >= fixed_size) + for (size_index = 0; size_index < sizes; size_index++) + if (size_table[size_index].to_float () >= csspx) break; - // TODO(ebraminio): We don't attempt to extrapolate to larger or - // smaller values for now but we should do, per spec - if (size_index == sizes) - return trackTableEntry->get_value (base, sizes - 1); - if (size_index == 0 || size_table[size_index] == fixed_size) - return trackTableEntry->get_value (base, size_index); + return interpolate_at (size_index ? size_index - 1 : 0, csspx, + *trackTableEntry, base); + } - float s0 = size_table[size_index - 1].to_float (); - float s1 = size_table[size_index].to_float (); - float t = (csspx - s0) / (s1 - s0); - return (float) t * trackTableEntry->get_value (base, size_index) + - ((float) 1.0 - t) * trackTableEntry->get_value (base, size_index - 1); + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + sizeTable.sanitize (c, base, nSizes) && + trackTable.sanitize (c, nTracks, base, nSizes)); } protected: HBUINT16 nTracks; /* Number of separate tracks included in this table. */ HBUINT16 nSizes; /* Number of point sizes included in this table. */ LOffsetTo, false> - sizeTable; /* Offset to array[nSizes] of size values. */ + sizeTable; /* Offset from start of the tracking table to + * Array[nSizes] of size values.. */ UnsizedArrayOf trackTable; /* Array[nTracks] of TrackTableEntry records. */ @@ -147,6 +170,8 @@ struct trak { static const hb_tag_t tableTag = HB_AAT_TAG_trak; + inline bool has_data (void) const { return version.to_int () != 0; } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -169,24 +194,24 @@ struct trak { const TrackData &trackData = this+horizData; float tracking = trackData.get_tracking (this, ptem); - hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2); + hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_x (tracking); foreach_grapheme (buffer, start, end) { - buffer->pos[start].x_offset += advance_to_add; buffer->pos[start].x_advance += advance_to_add; - buffer->pos[end].x_advance += advance_to_add; + buffer->pos[start].x_offset += offset_to_add; } } else { const TrackData &trackData = this+vertData; float tracking = trackData.get_tracking (this, ptem); - hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2); + hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_y (tracking); foreach_grapheme (buffer, start, end) { - buffer->pos[start].y_offset += advance_to_add; buffer->pos[start].y_advance += advance_to_add; - buffer->pos[end].y_advance += advance_to_add; + buffer->pos[start].y_offset += offset_to_add; } } @@ -194,15 +219,17 @@ struct trak } protected: - FixedVersion<> version; /* Version of the tracking table--currently - * 0x00010000u for version 1.0. */ - HBUINT16 format; /* Format of the tracking table */ - OffsetTo horizData; /* TrackData for horizontal text */ - OffsetTo vertData; /* TrackData for vertical text */ + FixedVersion<> version; /* Version of the tracking table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the tracking table (set to 0). */ + OffsetTo horizData; /* Offset from start of tracking table to TrackData + * for horizontal text (or 0 if none). */ + OffsetTo vertData; /* Offset from start of tracking table to TrackData + * for vertical text (or 0 if none). */ HBUINT16 reserved; /* Reserved. Set to 0. */ public: - DEFINE_SIZE_MIN (12); + DEFINE_SIZE_STATIC (12); }; } /* namespace AAT */ diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc index 3fd4f9f89..2b86ba8c7 100644 --- a/src/hb-aat-layout.cc +++ b/src/hb-aat-layout.cc @@ -37,7 +37,7 @@ #include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise. /* - * morx/kerx/trak/ankr + * morx/kerx/trak */ static inline const AAT::morx& @@ -68,6 +68,26 @@ _get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr) *blob = hb_ot_face_data (face)->kerx.get_blob (); return kerx; } +static inline const AAT::ankr& +_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) + { + if (blob) + *blob = hb_blob_get_empty (); + return Null(AAT::ankr); + } + const AAT::ankr& ankr = *(hb_ot_face_data (face)->ankr.get ()); + if (blob) + *blob = hb_ot_face_data (face)->ankr.get_blob (); + return ankr; +} +static inline const AAT::trak& +_get_trak (hb_face_t *face) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(AAT::trak); + return *(hb_ot_face_data (face)->trak.get ()); +} hb_bool_t @@ -103,6 +123,27 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan, hb_blob_t *blob; const AAT::kerx& kerx = _get_kerx (font->face, &blob); - AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); + hb_blob_t *ankr_blob; + const AAT::ankr& ankr = _get_ankr (font->face, &ankr_blob); + + AAT::hb_aat_apply_context_t c (plan, font, buffer, blob, + ankr, ankr_blob->data + ankr_blob->length); kerx.apply (&c); } + +hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face) +{ + return _get_trak (face).has_data (); +} + +void +hb_aat_layout_track (hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const AAT::trak& trak = _get_trak (font->face); + + AAT::hb_aat_apply_context_t c (plan, font, buffer); + trak.apply (&c); +} diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh index aafc3278f..897c26aa0 100644 --- a/src/hb-aat-layout.hh +++ b/src/hb-aat-layout.hh @@ -47,4 +47,12 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); +HB_INTERNAL hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face); + +HB_INTERNAL void +hb_aat_layout_track (hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + #endif /* HB_AAT_LAYOUT_HH */ diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh index 1a96609d0..746c24621 100644 --- a/src/hb-ot-layout-gpos-table.hh +++ b/src/hb-ot-layout-gpos-table.hh @@ -1755,10 +1755,6 @@ template struct GPOS_accelerator_t : GPOS::accelerator_t {}; -#undef attach_chain -#undef attach_type - - } /* namespace OT */ diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 3b1c93db4..a5538871b 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -100,22 +100,23 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, else if (hb_aat_layout_has_positioning (face)) plan.apply_kerx = true; - if (plan.requested_kerning) + if (plan.requested_kerning && !plan.apply_kerx && !has_gpos_kern) { - if (plan.apply_kerx) - ;/* kerx supercedes kern. */ - else if (!has_gpos_kern) - { - if (hb_ot_layout_has_kerning (face)) - plan.apply_kern = true; - else - plan.fallback_kerning = true; - } + /* Apparently Apple applies kerx if GPOS kern was not applied. */ + if (hb_aat_layout_has_positioning (face)) + plan.apply_kerx = true; + if (hb_ot_layout_has_kerning (face)) + plan.apply_kern = true; + else + plan.fallback_kerning = true; } plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); - if (!plan.apply_gpos) + if (!plan.apply_gpos && !plan.apply_kerx) plan.fallback_mark_positioning = true; + + /* Currently we always apply trak. */ + plan.apply_trak = hb_aat_layout_has_tracking (face); } @@ -820,34 +821,39 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c) hb_ot_layout_position_start (c->font, c->buffer); - switch (c->plan->shaper->zero_width_marks) - { - case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: - zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); - break; + if (!c->plan->apply_kerx) + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; - default: - case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: - case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: - break; - } + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + break; + } if (c->plan->apply_gpos) c->plan->position (c->font, c->buffer); else if (c->plan->apply_kerx) hb_aat_layout_position (c->plan, c->font, c->buffer); - switch (c->plan->shaper->zero_width_marks) - { - case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: - zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); - break; + if (c->plan->apply_trak) + hb_aat_layout_track (c->plan, c->font, c->buffer); - default: - case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: - case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: - break; - } + if (!c->plan->apply_kerx) + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + break; + } /* Finishing off GPOS has to follow a certain order. */ hb_ot_layout_position_finish_advances (c->font, c->buffer); diff --git a/src/hb-ot-shape.hh b/src/hb-ot-shape.hh index 4943c5155..c9c0d3e09 100644 --- a/src/hb-ot-shape.hh +++ b/src/hb-ot-shape.hh @@ -50,10 +50,11 @@ struct hb_ot_shape_plan_t bool fallback_kerning : 1; bool fallback_mark_positioning : 1; - bool apply_morx : 1; + bool apply_gpos : 1; bool apply_kerx : 1; bool apply_kern : 1; - bool apply_gpos : 1; + bool apply_morx : 1; + bool apply_trak : 1; inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources index 1f7eff202..f2402740f 100644 --- a/test/shaping/data/in-house/Makefile.sources +++ b/test/shaping/data/in-house/Makefile.sources @@ -1,4 +1,5 @@ TESTS = \ + tests/aat-trak.tests \ tests/arabic-fallback-shaping.tests \ tests/arabic-feature-order.tests \ tests/arabic-like-joining.tests \ diff --git a/test/shaping/data/in-house/fonts/TestTRAK.ttf b/test/shaping/data/in-house/fonts/TestTRAK.ttf new file mode 100644 index 000000000..07ae3afd8 Binary files /dev/null and b/test/shaping/data/in-house/fonts/TestTRAK.ttf differ diff --git a/test/shaping/data/in-house/tests/aat-trak.tests b/test/shaping/data/in-house/tests/aat-trak.tests new file mode 100644 index 000000000..9e6505585 --- /dev/null +++ b/test/shaping/data/in-house/tests/aat-trak.tests @@ -0,0 +1,8 @@ +../fonts/TestTRAK.ttf::U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000] +../fonts/TestTRAK.ttf:--font-ptem=.5:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200] +../fonts/TestTRAK.ttf:--font-ptem=1:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200] +../fonts/TestTRAK.ttf:--font-ptem=2:U+0041,U+0042,U+0043:[A.alt=0@93,0+1187|B=1@93,0+1187|C.alt=2@93,0+1187] +../fonts/TestTRAK.ttf:--font-ptem=9:U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000] +../fonts/TestTRAK.ttf:--font-ptem=24:U+0041,U+0042,U+0043:[A.alt=0@-12,0+976|B=1@-12,0+976|C.alt=2@-12,0+976] +../fonts/TestTRAK.ttf:--font-ptem=72:U+0041,U+0042,U+0043:[A.alt=0@-50,0+900|B=1@-50,0+900|C.alt=2@-50,0+900] +../fonts/TestTRAK.ttf:--font-ptem=144:U+0041,U+0042,U+0043:[A.alt=0@-100,0+800|B=1@-100,0+800|C.alt=2@-100,0+800]