diff options
Diffstat (limited to 'thirdparty/harfbuzz/src/OT')
40 files changed, 4959 insertions, 7 deletions
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS.hh new file mode 100644 index 0000000000..224d6b746b --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS.hh @@ -0,0 +1,165 @@ +#ifndef OT_LAYOUT_GPOS_HH +#define OT_LAYOUT_GPOS_HH + +#include "../../hb-ot-layout-common.hh" +#include "../../hb-ot-layout-gsubgpos.hh" +#include "GPOS/Common.hh" +#include "GPOS/PosLookup.hh" + +namespace OT { +namespace Layout { + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level = HB_MAX_NESTING_LEVEL); + +/* + * GPOS -- Glyph Positioning + * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos + */ + +struct GPOS : GSUBGPOS +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; + + using Lookup = GPOS_impl::PosLookup; + + const GPOS_impl::PosLookup& get_lookup (unsigned int i) const + { return static_cast<const GPOS_impl::PosLookup &> (GSUBGPOS::get_lookup (i)); } + + static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features); + return GSUBGPOS::subset<GPOS_impl::PosLookup> (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { return GSUBGPOS::sanitize<GPOS_impl::PosLookup> (c); } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) + { + if (!c->gpos_lookups->has (i)) continue; + const GPOS_impl::PosLookup &l = get_lookup (i); + l.dispatch (c); + } + } + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups<GPOS_impl::PosLookup> (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t; +}; + + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level) +{ + /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate + * offset of glyph they are attached to. */ + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain)) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + if (unlikely (j >= len)) + return; + + if (unlikely (!nesting_level)) + return; + + propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); + + assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); + + if (type & GPOS_impl::ATTACH_TYPE_CURSIVE) + { + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[i].y_offset += pos[j].y_offset; + else + pos[i].x_offset += pos[j].x_offset; + } + else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/ + { + pos[i].x_offset += pos[j].x_offset; + pos[i].y_offset += pos[j].y_offset; + + assert (j < i); + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } +} + +void +GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; +} + +void +GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) +{ + //_hb_buffer_assert_gsubgpos_vars (buffer); +} + +void +GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int len; + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); + hb_direction_t direction = buffer->props.direction; + + /* Handle attachments */ + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) + for (unsigned i = 0; i < len; i++) + propagate_attachment_offsets (pos, len, i, direction); + + if (unlikely (font->slant)) + { + for (unsigned i = 0; i < len; i++) + if (unlikely (pos[i].y_offset)) + pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset); + } +} + +} + +struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { + GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {} +}; + +} + +#endif /* OT_LAYOUT_GPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/Anchor.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/Anchor.hh new file mode 100644 index 0000000000..bfe6b36afd --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/Anchor.hh @@ -0,0 +1,84 @@ +#ifndef OT_LAYOUT_GPOS_ANCHOR_HH +#define OT_LAYOUT_GPOS_ANCHOR_HH + +#include "AnchorFormat1.hh" +#include "AnchorFormat2.hh" +#include "AnchorFormat3.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct Anchor +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AnchorFormat1 format1; + AnchorFormat2 format2; + AnchorFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + *x = *y = 0; + switch (u.format) { + case 1: u.format1.get_anchor (c, glyph_id, x, y); return; + case 2: u.format2.get_anchor (c, glyph_id, x, y); return; + case 3: u.format3.get_anchor (c, glyph_id, x, y); return; + default: return; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer)))); + case 2: + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + // AnchorFormat 2 just containins extra hinting information, so + // if hints are being dropped convert to format 1. + return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer)))); + } + return_trace (bool (reinterpret_cast<Anchor *> (u.format2.copy (c->serializer)))); + case 3: return_trace (bool (reinterpret_cast<Anchor *> (u.format3.copy (c->serializer, + c->plan->layout_variation_idx_map)))); + default:return_trace (false); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHOR_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh new file mode 100644 index 0000000000..738cc31bbf --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT1_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT1_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + public: + DEFINE_SIZE_STATIC (6); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + } + + AnchorFormat1* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + AnchorFormat1* out = c->embed<AnchorFormat1> (this); + if (!out) return_trace (out); + out->format = 1; + return_trace (out); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh new file mode 100644 index 0000000000..70b4d19f53 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh @@ -0,0 +1,58 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT2_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT2_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat2 +{ + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + HBUINT16 anchorPoint; /* Index to glyph contour point */ + public: + DEFINE_SIZE_STATIC (8); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + hb_font_t *font = c->font; + +#ifdef HB_NO_HINTING + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + return; +#endif + + unsigned int x_ppem = font->x_ppem; + unsigned int y_ppem = font->y_ppem; + hb_position_t cx = 0, cy = 0; + bool ret; + + ret = (x_ppem || y_ppem) && + font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); + *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate); + *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate); + } + + AnchorFormat2* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed<AnchorFormat2> (this)); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh new file mode 100644 index 0000000000..d77b4699be --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -0,0 +1,70 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + Offset16To<Device> + xDeviceTable; /* Offset to Device table for X + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + Offset16To<Device> + yDeviceTable; /* Offset to Device table for Y + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + + if (font->x_ppem || font->num_coords) + *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache); + if (font->y_ppem || font->num_coords) + *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); + } + + AnchorFormat3* copy (hb_serialize_context_t *c, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + if (!layout_variation_idx_map) return_trace (nullptr); + + auto *out = c->embed<AnchorFormat3> (this); + if (unlikely (!out)) return_trace (nullptr); + + out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices); + (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh new file mode 100644 index 0000000000..c442efa1ea --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh @@ -0,0 +1,77 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH +#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorMatrix +{ + HBUINT16 rows; /* Number of rows */ + UnsizedArrayOf<Offset16To<Anchor>> + matrixZ; /* Matrix of offsets to Anchor tables-- + * from beginning of AnchorMatrix table */ + public: + DEFINE_SIZE_ARRAY (2, matrixZ); + + bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const + { + TRACE_SANITIZE (this); + if (!c->check_struct (this)) return_trace (false); + if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false); + unsigned int count = rows * cols; + if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (!matrixZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + const Anchor& get_anchor (unsigned int row, unsigned int col, + unsigned int cols, bool *found) const + { + *found = false; + if (unlikely (row >= rows || col >= cols)) return Null (Anchor); + *found = !matrixZ[row * cols + col].is_null (); + return this+matrixZ[row * cols + col]; + } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + Iterator index_iter) const + { + for (unsigned i : index_iter) + (this+matrixZ[i]).collect_variation_indices (c); + } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool subset (hb_subset_context_t *c, + unsigned num_rows, + Iterator index_iter) const + { + TRACE_SUBSET (this); + + auto *out = c->serializer->start_embed (this); + + if (!index_iter) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->rows = num_rows; + for (const unsigned i : index_iter) + { + auto *offset = c->serializer->embed (matrixZ[i]); + if (!offset) return_trace (false); + offset->serialize_subset (c, matrixZ[i], this); + } + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh new file mode 100644 index 0000000000..d551ac2a2b --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ChainContextPos : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/Common.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/Common.hh new file mode 100644 index 0000000000..e16c06729d --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/Common.hh @@ -0,0 +1,32 @@ +#ifndef OT_LAYOUT_GPOS_COMMON_HH +#define OT_LAYOUT_GPOS_COMMON_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +enum attach_type_t { + ATTACH_TYPE_NONE = 0X00, + + /* Each attachment should be either a mark or a cursive; can't be both. */ + ATTACH_TYPE_MARK = 0X01, + ATTACH_TYPE_CURSIVE = 0X02, +}; + +/* buffer **position** var allocations */ +#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ +#define attach_type() var.u8[2] /* attachment type */ +/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */ + +template<typename Iterator, typename SrcLookup> +static void SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_map_t *layout_variation_idx_map); + + +} +} +} + +#endif // OT_LAYOUT_GPOS_COMMON_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh new file mode 100644 index 0000000000..2a01eaa3a6 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ContextPos : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CONTEXTPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh new file mode 100644 index 0000000000..c105cfb091 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh @@ -0,0 +1,35 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH +#define OT_LAYOUT_GPOS_CURSIVEPOS_HH + +#include "CursivePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct CursivePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + CursivePosFormat1 format1; + } u; + + public: + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh new file mode 100644 index 0000000000..e212fab976 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -0,0 +1,281 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH + +#include "Anchor.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct EntryExitRecord +{ + friend struct CursivePosFormat1; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+entryAnchor).collect_variation_indices (c); + (src_base+exitAnchor).collect_variation_indices (c); + } + + EntryExitRecord* subset (hb_subset_context_t *c, + const void *src_base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->entryAnchor.serialize_subset (c, entryAnchor, src_base); + out->exitAnchor.serialize_subset (c, exitAnchor, src_base); + return_trace (out); + } + + protected: + Offset16To<Anchor> + entryAnchor; /* Offset to EntryAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + Offset16To<Anchor> + exitAnchor; /* Offset to ExitAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + public: + DEFINE_SIZE_STATIC (4); +}; + +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + /* Stop if we see new parent in the chain. */ + if (j == new_parent) + return; + + reverse_cursive_minor_offset (pos, j, direction, new_parent); + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[j].y_offset = -pos[i].y_offset; + else + pos[j].x_offset = -pos[i].x_offset; + + pos[j].attach_chain() = -chain; + pos[j].attach_type() = type; +} + + +struct CursivePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + Array16Of<EntryExitRecord> + entryExitRecord; /* Array of EntryExit records--in + * Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (6, entryExitRecord); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + + const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; + if (!this_record.entryAnchor) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; + if (!prev_record.exitAnchor) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int i = skippy_iter.idx; + unsigned int j = buffer->idx; + + buffer->unsafe_to_break (i, j); + float entry_x, entry_y, exit_x, exit_y; + (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y); + (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); + + hb_glyph_position_t *pos = buffer->pos; + + hb_position_t d; + /* Main-direction adjustment */ + switch (c->direction) { + case HB_DIRECTION_LTR: + pos[i].x_advance = roundf (exit_x) + pos[i].x_offset; + + d = roundf (entry_x) + pos[j].x_offset; + pos[j].x_advance -= d; + pos[j].x_offset -= d; + break; + case HB_DIRECTION_RTL: + d = roundf (exit_x) + pos[i].x_offset; + pos[i].x_advance -= d; + pos[i].x_offset -= d; + + pos[j].x_advance = roundf (entry_x) + pos[j].x_offset; + break; + case HB_DIRECTION_TTB: + pos[i].y_advance = roundf (exit_y) + pos[i].y_offset; + + d = roundf (entry_y) + pos[j].y_offset; + pos[j].y_advance -= d; + pos[j].y_offset -= d; + break; + case HB_DIRECTION_BTT: + d = roundf (exit_y) + pos[i].y_offset; + pos[i].y_advance -= d; + pos[i].y_offset -= d; + + pos[j].y_advance = roundf (entry_y); + break; + case HB_DIRECTION_INVALID: + default: + break; + } + + /* Cross-direction adjustment */ + + /* We attach child to parent (think graph theory and rooted trees whereas + * the root stays on baseline and each node aligns itself against its + * parent. + * + * Optimize things for the case of RightToLeft, as that's most common in + * Arabic. */ + unsigned int child = i; + unsigned int parent = j; + hb_position_t x_offset = entry_x - exit_x; + hb_position_t y_offset = entry_y - exit_y; + if (!(c->lookup_props & LookupFlag::RightToLeft)) + { + unsigned int k = child; + child = parent; + parent = k; + x_offset = -x_offset; + y_offset = -y_offset; + } + + /* If child was already connected to someone else, walk through its old + * chain and reverse the link direction, such that the whole tree of its + * previous connection now attaches to new parent. Watch out for case + * where new parent is on the path from old chain... + */ + reverse_cursive_minor_offset (pos, child, c->direction, parent); + + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; + pos[child].attach_chain() = (int) parent - (int) child; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[child].y_offset = y_offset; + else + pos[child].x_offset = x_offset; + + /* If parent was attached to child, separate them. + * https://github.com/harfbuzz/harfbuzz/issues/2469 + */ + if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + pos[parent].attach_chain() = 0; + + buffer->idx++; + return_trace (true); + } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_subset_context_t *c, + Iterator it, + const void *src_base) + { + if (unlikely (!c->serializer->extend_min ((*this)))) return; + this->format = 1; + this->entryExitRecord.len = it.len (); + + for (const EntryExitRecord& entry_record : + it + | hb_map (hb_second)) + entry_record.subset (c, src_base); + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c->serializer, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + auto it = + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&> + { return hb_pair (glyph_map[p.first], p.second);}) + ; + + bool ret = bool (it); + out->serialize (c, it, this); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh new file mode 100644 index 0000000000..d1808adab4 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh @@ -0,0 +1,17 @@ +#ifndef OT_LAYOUT_GPOS_EXTENSIONPOS_HH +#define OT_LAYOUT_GPOS_EXTENSIONPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ExtensionPos : Extension<ExtensionPos> +{ + typedef struct PosLookupSubTable SubTable; +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh new file mode 100644 index 0000000000..f8cddd1991 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh @@ -0,0 +1,113 @@ +#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH +#define OT_LAYOUT_GPOS_MARKARRAY_HH + +#include "AnchorMatrix.hh" +#include "MarkRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Coverage order */ +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array16Of<MarkRecord>::sanitize (c, this)); + } + + bool apply (hb_ot_apply_context_t *c, + unsigned int mark_index, unsigned int glyph_index, + const AnchorMatrix &anchors, unsigned int class_count, + unsigned int glyph_pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + const MarkRecord &record = Array16Of<MarkRecord>::operator[](mark_index); + unsigned int mark_class = record.klass; + + const Anchor& mark_anchor = this + record.markAnchor; + bool found; + const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found); + /* If this subtable doesn't have an anchor for this base and this class, + * return false such that the subsequent subtables have a chance at it. */ + if (unlikely (!found)) return_trace (false); + + float mark_x, mark_y, base_x, base_y; + + buffer->unsafe_to_break (glyph_pos, buffer->idx + 1); + mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y); + glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); + + hb_glyph_position_t &o = buffer->cur_pos(); + o.x_offset = roundf (base_x - mark_x); + o.y_offset = roundf (base_y - mark_y); + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + + buffer->idx++; + return_trace (true); + } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool subset (hb_subset_context_t *c, + Iterator coverage, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto* out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + auto mark_iter = + + hb_zip (coverage, this->iter ()) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ; + + unsigned new_length = 0; + for (const auto& mark_record : mark_iter) { + if (unlikely (!mark_record.subset (c, this, klass_mapping))) + return_trace (false); + new_length++; + } + + if (unlikely (!c->serializer->check_assign (out->len, new_length, + HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) + return_trace (false); + + return_trace (true); + } +}; + +static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) +{ + hb_set_t orig_classes; + + + hb_zip (mark_coverage, mark_array) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + | hb_map (&MarkRecord::get_class) + | hb_sink (orig_classes) + ; + + unsigned idx = 0; + for (auto klass : orig_classes.iter ()) + { + if (klass_mapping->has (klass)) continue; + klass_mapping->set (klass, idx); + idx++; + } +} + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh new file mode 100644 index 0000000000..e99e13ff84 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh @@ -0,0 +1,35 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH +#define OT_LAYOUT_GPOS_MARKBASEPOS_HH + +#include "MarkBasePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkBasePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkBasePosFormat1 format1; + } u; + + public: + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh new file mode 100644 index 0000000000..a10b806fe5 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh @@ -0,0 +1,217 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH + +#include "MarkArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix BaseArray; /* base-major-- + * in order of BaseCoverage Index--, + * mark-minor-- + * ordered by class--zero-based. */ + +struct MarkBasePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + markCoverage; /* Offset to MarkCoverage table--from + * beginning of MarkBasePos subtable */ + Offset16To<Coverage> + baseCoverage; /* Offset to BaseCoverage table--from + * beginning of MarkBasePos subtable */ + HBUINT16 classCount; /* Number of classes defined for marks */ + Offset16To<MarkArray> + markArray; /* Offset to MarkArray table--from + * beginning of MarkBasePos subtable */ + Offset16To<BaseArray> + baseArray; /* Offset to BaseArray table--from + * beginning of MarkBasePos subtable */ + + public: + DEFINE_SIZE_STATIC (12); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + baseCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + baseArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+baseCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t<unsigned> base_indexes; + for (const unsigned row : base_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + (this+baseArray).collect_variation_indices (c, base_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + do { + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + /* We only want to attach to the first of a MultipleSubst sequence. + * https://github.com/harfbuzz/harfbuzz/issues/740 + * Reject others... + * ...but stop if we find a mark in the MultipleSubst sequence: + * https://github.com/harfbuzz/harfbuzz/issues/1020 */ + if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) || + 0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) || + (skippy_iter.idx == 0 || + _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) || + _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) != + _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) || + _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) != + _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1 + )) + break; + skippy_iter.reject (); + } while (true); + + /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); + if (base_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + mark_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + base_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t<unsigned> base_indexes; + for (const unsigned row : + base_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + + out->baseArray.serialize_subset (c, baseArray, this, + base_iter.len (), + base_indexes.iter ()); + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh new file mode 100644 index 0000000000..7e74aa73e0 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh @@ -0,0 +1,35 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH +#define OT_LAYOUT_GPOS_MARKLIGPOS_HH + +#include "MarkLigPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkLigPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkLigPosFormat1 format1; + } u; + + public: + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh new file mode 100644 index 0000000000..4382aa6c6c --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -0,0 +1,244 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix LigatureAttach; /* component-major-- + * in order of writing direction--, + * mark-minor-- + * ordered by class--zero-based. */ + +/* Array of LigatureAttach tables ordered by LigatureCoverage Index */ +struct LigatureArray : List16OfOffset16To<LigatureAttach> +{ + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool subset (hb_subset_context_t *c, + Iterator coverage, + unsigned class_count, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto _ : + hb_zip (coverage, *this) + | hb_filter (glyphset, hb_first)) + { + auto *matrix = out->serialize_append (c->serializer); + if (unlikely (!matrix)) return_trace (false); + + const LigatureAttach& src = (this + _.second); + auto indexes = + + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + ; + matrix->serialize_subset (c, + _.second, + this, + src.rows, + indexes); + } + return_trace (this->len); + } +}; + +struct MarkLigPosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + markCoverage; /* Offset to Mark Coverage table--from + * beginning of MarkLigPos subtable */ + Offset16To<Coverage> + ligatureCoverage; /* Offset to Ligature Coverage + * table--from beginning of MarkLigPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + Offset16To<MarkArray> + markArray; /* Offset to MarkArray table--from + * beginning of MarkLigPos subtable */ + Offset16To<LigatureArray> + ligatureArray; /* Offset to LigatureArray table--from + * beginning of MarkLigPos subtable */ + public: + DEFINE_SIZE_STATIC (12); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + ligatureCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + ligatureArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+ligatureCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned ligcount = (this+ligatureArray).len; + auto lig_iter = + + hb_zip (this+ligatureCoverage, hb_range (ligcount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + const LigatureArray& lig_array = this+ligatureArray; + for (const unsigned i : lig_iter) + { + hb_sorted_vector_t<unsigned> lig_indexes; + unsigned row_count = lig_array[i].rows; + for (unsigned row : + hb_range (row_count)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (lig_indexes) + ; + } + + lig_array[i].collect_variation_indices (c, lig_indexes.iter ()); + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int j = skippy_iter.idx; + unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); + if (lig_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + const LigatureArray& lig_array = this+ligatureArray; + const LigatureAttach& lig_attach = lig_array[lig_index]; + + /* Find component to attach to */ + unsigned int comp_count = lig_attach.rows; + if (unlikely (!comp_count)) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + /* We must now check whether the ligature ID of the current mark glyph + * is identical to the ligature ID of the found ligature. If yes, we + * can directly use the component index. If not, we attach the mark + * glyph to the last component of the ligature. */ + unsigned int comp_index; + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (lig_id && lig_id == mark_id && mark_comp > 0) + comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + else + comp_index = comp_count - 1; + + return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + auto new_mark_coverage = + + mark_iter + | hb_map_retains_sorting (hb_first) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage)) + return_trace (false); + + out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping); + + auto new_ligature_coverage = + + hb_iter (this + ligatureCoverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) + return_trace (false); + + out->ligatureArray.serialize_subset (c, ligatureArray, this, + hb_iter (this+ligatureCoverage), classCount, &klass_mapping); + + return_trace (true); + } + +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh new file mode 100644 index 0000000000..c0eee6d54c --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh @@ -0,0 +1,36 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH +#define OT_LAYOUT_GPOS_MARKMARKPOS_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkMarkPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkMarkPosFormat1 format1; + } u; + + public: + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh new file mode 100644 index 0000000000..c48a74f773 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -0,0 +1,227 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix Mark2Array; /* mark2-major-- + * in order of Mark2Coverage Index--, + * mark1-minor-- + * ordered by class--zero-based. */ + +struct MarkMarkPosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + mark1Coverage; /* Offset to Combining Mark1 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + Offset16To<Coverage> + mark2Coverage; /* Offset to Combining Mark2 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + Offset16To<MarkArray> + mark1Array; /* Offset to Mark1Array table--from + * beginning of MarkMarkPos subtable */ + Offset16To<Mark2Array> + mark2Array; /* Offset to Mark2Array table--from + * beginning of MarkMarkPos subtable */ + public: + DEFINE_SIZE_STATIC (12); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mark1Coverage.sanitize (c, this) && + mark2Coverage.sanitize (c, this) && + mark1Array.sanitize (c, this) && + mark2Array.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+mark1Coverage).intersects (glyphs) && + (this+mark2Coverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping); + + unsigned mark2_count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2_count)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t<unsigned> mark2_indexes; + for (const unsigned row : mark2_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+mark1Coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint); + if (likely (mark1_index == NOT_COVERED)) return_trace (false); + + /* now we search backwards for a suitable mark glyph until a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int j = skippy_iter.idx; + + unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); + unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); + + if (likely (id1 == id2)) + { + if (id1 == 0) /* Marks belonging to the same base. */ + goto good; + else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ + goto good; + } + else + { + /* If ligature ids don't match, it may be the case that one of the marks + * itself is a ligature. In which case match. */ + if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) + goto good; + } + + /* Didn't match. */ + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + + good: + unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); + if (mark2_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark1_iter = + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + mark1_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->mark1Array.serialize_subset (c, mark1Array, this, + (this+mark1Coverage).iter (), + &klass_mapping); + + unsigned mark2count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2count)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + mark2_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t<unsigned> mark2_indexes; + for (const unsigned row : + mark2_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + + out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ()); + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh new file mode 100644 index 0000000000..7a514453ae --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh @@ -0,0 +1,52 @@ +#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH +#define OT_LAYOUT_GPOS_MARKRECORD_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkRecord +{ + friend struct MarkArray; + + protected: + HBUINT16 klass; /* Class defined for this mark */ + Offset16To<Anchor> + markAnchor; /* Offset to Anchor table--from + * beginning of MarkArray table */ + public: + DEFINE_SIZE_STATIC (4); + + unsigned get_class () const { return (unsigned) klass; } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); + } + + MarkRecord *subset (hb_subset_context_t *c, + const void *src_base, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->klass = klass_mapping->get (klass); + out->markAnchor.serialize_subset (c, markAnchor, src_base); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+markAnchor).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh new file mode 100644 index 0000000000..8479178d38 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh @@ -0,0 +1,38 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH +#define OT_LAYOUT_GPOS_PAIRPOS_HH + +#include "PairPosFormat1.hh" +#include "PairPosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PairPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + PairPosFormat1 format1; + PairPosFormat2 format2; + } u; + + public: + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOS_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh new file mode 100644 index 0000000000..35a2db2d45 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -0,0 +1,420 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PairValueRecord +{ + friend struct PairSet; + + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct context_t + { + const void *base; + const ValueFormat *valueFormats; + const ValueFormat *newFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_map_t *layout_variation_idx_map; + }; + + bool subset (hb_subset_context_t *c, + context_t *closure) const + { + TRACE_SERIALIZE (this); + auto *s = c->serializer; + auto *out = s->start_embed (*this); + if (unlikely (!s->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].copy_values (s, + closure->newFormats[0], + closure->base, &values[0], + closure->layout_variation_idx_map); + closure->valueFormats[1].copy_values (s, + closure->newFormats[1], + closure->base, + &values[closure->len1], + closure->layout_variation_idx_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + + bool intersects (const hb_set_t& glyphset) const + { + return glyphset.has(secondGlyph); + } + + const Value* get_values_1 () const + { + return &values[0]; + } + + const Value* get_values_2 (ValueFormat format1) const + { + return &values[format1.get_len ()]; + } + + protected: + HBGlyphID16 secondGlyph; /* GlyphID of second glyph in the + * pair--first glyph is listed in the + * Coverage table */ + ValueRecord values; /* Positioning data for the first glyph + * followed by for second glyph */ + public: + DEFINE_SIZE_ARRAY (2, values); +}; + +struct PairSet +{ + friend struct PairPosFormat1; + + bool intersects (const hb_set_t *glyphs, + const ValueFormat *valueFormats) const + { + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (glyphs->has (record->secondGlyph)) + return true; + record = &StructAtOffset<const PairValueRecord> (record, record_size); + } + return false; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + c->input->add_array (&record->secondGlyph, len, record_size); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset<const PairValueRecord> (record, record_size); + } + } + + bool apply (hb_ot_apply_context_t *c, + const ValueFormat *valueFormats, + unsigned int pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); + bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, pos + 1); + if (len2) + pos++; + buffer->idx = pos; + return_trace (true); + } + buffer->unsafe_to_concat (buffer->idx, pos + 1); + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2], + const ValueFormat newFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + + PairValueRecord::context_t context = + { + this, + valueFormats, + newFormats, + len1, + &glyph_map, + c->plan->layout_variation_idx_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->subset (c, &context)) num++; + record = &StructAtOffset<const PairValueRecord> (record, record_size); + } + + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); + } + + struct sanitize_closure_t + { + const ValueFormat *valueFormats; + unsigned int len1; /* valueFormats[0].get_len() */ + unsigned int stride; /* 1 + len1 + len2 */ + }; + + bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && c->check_range (&firstPairValueRecord, + len, + HBUINT16::static_size, + closure->stride))) return_trace (false); + + unsigned int count = len; + const PairValueRecord *record = &firstPairValueRecord; + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); + } + + protected: + HBUINT16 len; /* Number of PairValueRecords */ + PairValueRecord firstPairValueRecord; + /* Array of PairValueRecords--ordered + * by GlyphID of the second glyph */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct PairPosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat[2]; /* [0] Defines the types of data in + * ValueRecord1--for the first glyph + * in the pair--may be zero (0) */ + /* [1] Defines the types of data in + * ValueRecord2--for the second glyph + * in the pair--may be zero (0) */ + Array16OfOffset16To<PairSet> + pairSet; /* Array of PairSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (10, pairSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + if (!c->check_struct (this)) return_trace (false); + + unsigned int len1 = valueFormat[0].get_len (); + unsigned int len2 = valueFormat[1].get_len (); + PairSet::sanitize_closure_t closure = + { + valueFormat, + len1, + 1 + len1 + len2 + }; + + return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); + } + + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_zip (this+coverage, pairSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([glyphs, this] (const Offset16To<PairSet> &_) + { return (this+_).intersects (glyphs, valueFormat); }) + | hb_any + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return; + + auto it = + + hb_zip (this+coverage, pairSet) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + if (!it) return; + + it + | hb_map (hb_add (this)) + | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + unsigned int count = pairSet.len; + for (unsigned int i = 0; i < count; i++) + (this+pairSet[i]).collect_glyphs (c, valueFormat); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat[0] = valueFormat[0]; + out->valueFormat[1] = valueFormat[1]; + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_pair_t<unsigned, unsigned> newFormats = compute_effective_value_formats (glyphset); + out->valueFormat[0] = newFormats.first; + out->valueFormat[1] = newFormats.second; + } + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const Offset16To<PairSet>& _) + { + auto snap = c->serializer->snapshot (); + auto *o = out->pairSet.serialize_append (c->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat); + if (!ret) + { + out->pairSet.pop (); + c->serializer->revert (snap); + } + return ret; + }, + hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + + return_trace (bool (new_coverage)); + } + + + hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset) const + { + unsigned len1 = valueFormat[0].get_len (); + unsigned len2 = valueFormat[1].get_len (); + unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + + unsigned format1 = 0; + unsigned format2 = 0; + for (const Offset16To<PairSet>& _ : + + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second)) + { + const PairSet& set = (this + _); + const PairValueRecord *record = &set.firstPairValueRecord; + + for (unsigned i = 0; i < set.len; i++) + { + if (record->intersects (glyphset)) + { + format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ()); + format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0])); + } + record = &StructAtOffset<const PairValueRecord> (record, record_size); + } + } + + return hb_pair (format1, format2); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh new file mode 100644 index 0000000000..3f5f9959c4 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -0,0 +1,314 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PairPosFormat2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + Offset16To<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat1; /* ValueRecord definition--for the + * first glyph of the pair--may be zero + * (0) */ + ValueFormat valueFormat2; /* ValueRecord definition--for the + * second glyph of the pair--may be + * zero (0) */ + Offset16To<ClassDef> + classDef1; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the first glyph of the pair */ + Offset16To<ClassDef> + classDef2; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the second glyph of the pair */ + HBUINT16 class1Count; /* Number of classes in ClassDef1 + * table--includes Class0 */ + HBUINT16 class2Count; /* Number of classes in ClassDef2 + * table--includes Class0 */ + ValueRecord values; /* Matrix of value pairs: + * class1-major, class2-minor, + * Each entry has value1 and value2 */ + public: + DEFINE_SIZE_ARRAY (16, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && coverage.sanitize (c, this) + && classDef1.sanitize (c, this) + && classDef2.sanitize (c, this))) return_trace (false); + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int stride = len1 + len2; + unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size (); + unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; + return_trace (c->check_range ((const void *) values, + count, + record_size) && + valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && + valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+coverage).intersects (glyphs) && + (this+classDef2).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!intersects (c->glyph_set)) return; + if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return; + + hb_set_t klass1_glyphs, klass2_glyphs; + if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return; + if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return; + + hb_set_t class1_set, class2_set; + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage)) + { + if (!klass1_glyphs.has (cp)) class1_set.add (0); + else + { + unsigned klass1 = (this+classDef1).get (cp); + class1_set.add (klass1); + } + } + + class2_set.add (0); + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs)) + { + unsigned klass2 = (this+classDef2).get (cp); + class2_set.add (klass2); + } + + if (class1_set.is_empty () + || class2_set.is_empty () + || (class2_set.get_population() == 1 && class2_set.has(0))) + return; + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + const hb_array_t<const Value> values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2)); + for (const unsigned class1_idx : class1_set.iter ()) + { + for (const unsigned class2_idx : class2_set.iter ()) + { + unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + if (valueFormat1.has_device ()) + valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1)); + + if (valueFormat2.has_device ()) + valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2)); + } + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+classDef2).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int record_len = len1 + len2; + + unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); + unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); + if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) + { + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + return_trace (false); + } + + const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; + + bool applied_first = false, applied_second = false; + + + /* Isolate simple kerning and apply it half to each side. + * Results in better cursor positinoing / underline drawing. + * + * Disabled, because causes issues... :-( + * https://github.com/harfbuzz/harfbuzz/issues/3408 + * https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978 + */ +#ifndef HB_SPLIT_KERN + if (0) +#endif + { + if (!len2) + { + const hb_direction_t dir = buffer->props.direction; + const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir); + const bool backward = HB_DIRECTION_IS_BACKWARD (dir); + unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance; + if (backward) + mask |= mask >> 2; /* Add eg. xPlacement in RTL. */ + /* Add Devices. */ + mask |= mask << 4; + + if (valueFormat1 & ~mask) + goto bail; + + /* Is simple kern. Apply value on an empty position slot, + * then split it between sides. */ + + hb_glyph_position_t pos{}; + if (valueFormat1.apply_value (c, this, v, pos)) + { + hb_position_t *src = &pos.x_advance; + hb_position_t *dst1 = &buffer->cur_pos().x_advance; + hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance; + unsigned i = horizontal ? 0 : 1; + + hb_position_t kern = src[i]; + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + + if (!backward) + { + dst1[i] += kern1; + dst2[i] += kern2; + dst2[i + 2] += kern2; + } + else + { + dst1[i] += kern1; + dst1[i + 2] += src[i + 2] - kern2; + dst2[i] += kern2; + } + + applied_first = applied_second = kern != 0; + goto success; + } + goto boring; + } + } + bail: + + + applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos()); + applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + + success: + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + else + boring: + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + + + buffer->idx = skippy_iter.idx; + if (len2) + buffer->idx++; + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass1_map; + out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage)); + out->class1Count = klass1_map.get_population (); + + hb_map_t klass2_map; + out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false); + out->class2Count = klass2_map.get_population (); + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat1, valueFormat2); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + newFormats = compute_effective_value_formats (klass1_map, klass2_map); + + out->valueFormat1 = newFormats.first; + out->valueFormat2 = newFormats.second; + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + valueFormat1.copy_values (c->serializer, newFormats.first, this, &values[idx], c->plan->layout_variation_idx_map); + valueFormat2.copy_values (c->serializer, newFormats.second, this, &values[idx + len1], c->plan->layout_variation_idx_map); + } + } + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->coverage.serialize_serialize (c->serializer, it); + return_trace (out->class1Count && out->class2Count && bool (it)); + } + + + hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map, + const hb_map_t& klass2_map) const + { + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + unsigned format1 = 0; + unsigned format2 = 0; + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + format1 = format1 | valueFormat1.get_effective_format (&values[idx]); + format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]); + } + } + + return hb_pair (format1, format2); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh new file mode 100644 index 0000000000..c4e57bb543 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUP_HH +#define OT_LAYOUT_GPOS_POSLOOKUP_HH + +#include "PosLookupSubTable.hh" +#include "../../../hb-ot-layout-common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookup : Lookup +{ + using SubTable = PosLookupSubTable; + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable<SubTable> (i); } + + bool is_reverse () const + { + return false; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { return dispatch (c); } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + template <typename set_t> + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t<set_t> c (glyphs); + dispatch (&c); + } + + template <typename context_t> + static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset<SubTable> (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize<SubTable> (c); } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_POSLOOKUP_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh new file mode 100644 index 0000000000..c19fbc323f --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH + +#include "SinglePos.hh" +#include "PairPos.hh" +#include "CursivePos.hh" +#include "MarkBasePos.hh" +#include "MarkLigPos.hh" +#include "MarkMarkPos.hh" +#include "ContextPos.hh" +#include "ChainContextPos.hh" +#include "ExtensionPos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct PosLookup; + + enum Type { + Single = 1, + Pair = 2, + Cursive = 3, + MarkBase = 4, + MarkLig = 5, + MarkMark = 6, + Context = 7, + ChainContext = 8, + Extension = 9 + }; + + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, std::forward<Ts> (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, std::forward<Ts> (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, std::forward<Ts> (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, std::forward<Ts> (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, std::forward<Ts> (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + + protected: + union { + SinglePos single; + PairPos pair; + CursivePos cursive; + MarkBasePos markBase; + MarkLigPos markLig; + MarkMarkPos markMark; + ContextPos context; + ChainContextPos chainContext; + ExtensionPos extension; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh new file mode 100644 index 0000000000..57e146befd --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh @@ -0,0 +1,98 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH +#define OT_LAYOUT_GPOS_SINGLEPOS_HH + +#include "SinglePosFormat1.hh" +#include "SinglePosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SinglePosFormat1 format1; + SinglePosFormat2 format2; + } u; + + public: + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t<const Value> first_val_iter = hb_second (*glyph_val_iter_pairs); + + for (const auto iter : glyph_val_iter_pairs) + for (const auto _ : hb_zip (iter.second, first_val_iter)) + if (_.first != _.second) + return 2; + + return 1; + } + + template<typename Iterator, + typename SrcLookup, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + const SrcLookup* src, + Iterator glyph_val_iter_pairs, + const hb_map_t *layout_variation_idx_map) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + ValueFormat new_format = src->get_value_format (); + + if (glyph_val_iter_pairs) + { + format = get_format (glyph_val_iter_pairs); + new_format = src->get_value_format ().get_effective_format (+ glyph_val_iter_pairs | hb_map (hb_second)); + } + + u.format = format; + switch (u.format) { + case 1: u.format1.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_map); + return; + case 2: u.format2.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_map); + return; + default:return; + } + } + + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + + +template<typename Iterator, typename SrcLookup> +static void +SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_map_t *layout_variation_idx_map) +{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_map); } + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh new file mode 100644 index 0000000000..8b7840ed0e --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -0,0 +1,124 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH + +#include "Common.hh" +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + ValueRecord values; /* Defines positioning + * value(s)--applied to all glyphs in + * the Coverage table */ + public: + DEFINE_SIZE_ARRAY (6, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_value (c, this, values)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_iter (this+coverage) + | hb_filter (c->glyph_set) + ; + + if (!it) return; + valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + valueFormat.apply_value (c, this, values, buffer->cur_pos()); + + buffer->idx++; + return_trace (true); + } + + template<typename Iterator, + typename SrcLookup, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_map_t *layout_variation_idx_map) + { + if (unlikely (!c->extend_min (this))) return; + if (unlikely (!c->check_assign (valueFormat, + newFormat, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + + for (const hb_array_t<const Value>& _ : + it | hb_map (hb_second)) + { + src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); + // Only serialize the first entry in the iterator, the rest are assumed to + // be the same. + break; + } + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh new file mode 100644 index 0000000000..0d038b4422 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -0,0 +1,140 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + Offset16To<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + HBUINT16 valueCount; /* Number of ValueRecords */ + ValueRecord values; /* Array of ValueRecords--positioning + * values applied to glyphs */ + public: + DEFINE_SIZE_ARRAY (8, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_values (c, this, values, valueCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (c->glyph_set, hb_first) + ; + + if (!it) return; + + unsigned sub_length = valueFormat.get_len (); + const hb_array_t<const Value> values_array = values.as_array (valueCount * sub_length); + + for (unsigned i : + it + | hb_map (hb_second)) + valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length)); + + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (likely (index >= valueCount)) return_trace (false); + + valueFormat.apply_value (c, this, + &values[index * valueFormat.get_len ()], + buffer->cur_pos()); + + buffer->idx++; + return_trace (true); + } + + template<typename Iterator, + typename SrcLookup, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_map_t *layout_variation_idx_map) + { + auto out = c->extend_min (this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t<const Value> _) + { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned sub_length = valueFormat.get_len (); + auto values_array = values.as_array (valueCount * sub_length); + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (const hb_pair_t<hb_codepoint_t, unsigned>& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */ diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh new file mode 100644 index 0000000000..b29f287bce --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh @@ -0,0 +1,329 @@ +#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH +#define OT_LAYOUT_GPOS_VALUEFORMAT_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef HBUINT16 Value; + +typedef UnsizedArrayOf<Value> ValueRecord; + +struct ValueFormat : HBUINT16 +{ + enum Flags { + xPlacement = 0x0001u, /* Includes horizontal adjustment for placement */ + yPlacement = 0x0002u, /* Includes vertical adjustment for placement */ + xAdvance = 0x0004u, /* Includes horizontal adjustment for advance */ + yAdvance = 0x0008u, /* Includes vertical adjustment for advance */ + xPlaDevice = 0x0010u, /* Includes horizontal Device table for placement */ + yPlaDevice = 0x0020u, /* Includes vertical Device table for placement */ + xAdvDevice = 0x0040u, /* Includes horizontal Device table for advance */ + yAdvDevice = 0x0080u, /* Includes vertical Device table for advance */ + ignored = 0x0F00u, /* Was used in TrueType Open for MM fonts */ + reserved = 0xF000u, /* For future use */ + + devices = 0x00F0u /* Mask for having any Device table */ + }; + +/* All fields are options. Only those available advance the value pointer. */ +#if 0 + HBINT16 xPlacement; /* Horizontal adjustment for + * placement--in design units */ + HBINT16 yPlacement; /* Vertical adjustment for + * placement--in design units */ + HBINT16 xAdvance; /* Horizontal adjustment for + * advance--in design units (only used + * for horizontal writing) */ + HBINT16 yAdvance; /* Vertical adjustment for advance--in + * design units (only used for vertical + * writing) */ + Offset16To<Device> xPlaDevice; /* Offset to Device table for + * horizontal placement--measured from + * beginning of PosTable (may be NULL) */ + Offset16To<Device> yPlaDevice; /* Offset to Device table for vertical + * placement--measured from beginning + * of PosTable (may be NULL) */ + Offset16To<Device> xAdvDevice; /* Offset to Device table for + * horizontal advance--measured from + * beginning of PosTable (may be NULL) */ + Offset16To<Device> yAdvDevice; /* Offset to Device table for vertical + * advance--measured from beginning of + * PosTable (may be NULL) */ +#endif + + IntType& operator = (uint16_t i) { v = i; return *this; } + + unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } + unsigned int get_size () const { return get_len () * Value::static_size; } + + bool apply_value (hb_ot_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const + { + bool ret = false; + unsigned int format = *this; + if (!format) return ret; + + hb_font_t *font = c->font; + bool horizontal = +#ifndef HB_NO_VERTICAL + HB_DIRECTION_IS_HORIZONTAL (c->direction) +#else + true +#endif + ; + + if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++, &ret)); + if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++, &ret)); + if (format & xAdvance) { + if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret)); + values++; + } + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (format & yAdvance) { + if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret)); + values++; + } + + if (!has_device ()) return ret; + + bool use_x_device = font->x_ppem || font->num_coords; + bool use_y_device = font->y_ppem || font->num_coords; + + if (!use_x_device && !use_y_device) return ret; + + const VariationStore &store = c->var_store; + auto *cache = c->var_store_cache; + + /* pixel -> fractional pixel */ + if (format & xPlaDevice) { + if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store, cache); + values++; + } + if (format & yPlaDevice) { + if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store, cache); + values++; + } + if (format & xAdvDevice) { + if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache); + values++; + } + if (format & yAdvDevice) { + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache); + values++; + } + return ret; + } + + unsigned int get_effective_format (const Value *values) const + { + unsigned int format = *this; + for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) { + if (format & flag) should_drop (*values++, (Flags) flag, &format); + } + + return format; + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + unsigned int get_effective_format (Iterator it) const { + unsigned int new_format = 0; + + for (const hb_array_t<const Value>& values : it) + new_format = new_format | get_effective_format (&values); + + return new_format; + } + + void copy_values (hb_serialize_context_t *c, + unsigned int new_format, + const void *base, + const Value *values, + const hb_map_t *layout_variation_idx_map) const + { + unsigned int format = *this; + if (!format) return; + + if (format & xPlacement) copy_value (c, new_format, xPlacement, *values++); + if (format & yPlacement) copy_value (c, new_format, yPlacement, *values++); + if (format & xAdvance) copy_value (c, new_format, xAdvance, *values++); + if (format & yAdvance) copy_value (c, new_format, yAdvance, *values++); + + if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + } + + void copy_value (hb_serialize_context_t *c, + unsigned int new_format, + Flags flag, + Value value) const + { + // Filter by new format. + if (!(new_format & flag)) return; + c->copy (value); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *base, + const hb_array_t<const Value>& values) const + { + unsigned format = *this; + unsigned i = 0; + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + if (format & xPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::yPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::xAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::yAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + } + + private: + bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + unsigned int format = *this; + + if (format & xPlacement) values++; + if (format & yPlacement) values++; + if (format & xAdvance) values++; + if (format & yAdvance) values++; + + if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + + return true; + } + + static inline Offset16To<Device>& get_device (Value* value) + { + return *static_cast<Offset16To<Device> *> (value); + } + static inline const Offset16To<Device>& get_device (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *static_cast<const Offset16To<Device> *> (value); + } + + bool copy_device (hb_serialize_context_t *c, const void *base, + const Value *src_value, const hb_map_t *layout_variation_idx_map) const + { + Value *dst_value = c->copy (*src_value); + + if (!dst_value) return false; + if (*dst_value == 0) return true; + + *dst_value = 0; + c->push (); + if ((base + get_device (src_value)).copy (c, layout_variation_idx_map)) + { + c->add_link (*dst_value, c->pop_pack ()); + return true; + } + else + { + c->pop_discard (); + return false; + } + } + + static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *reinterpret_cast<const HBINT16 *> (value); + } + + public: + + bool has_device () const + { + unsigned int format = *this; + return (format & devices) != 0; + } + + bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + TRACE_SANITIZE (this); + return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); + } + + bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const + { + TRACE_SANITIZE (this); + unsigned int len = get_len (); + + if (!c->check_range (values, count, get_size ())) return_trace (false); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += len; + } + + return_trace (true); + } + + /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ + bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const + { + TRACE_SANITIZE (this); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += stride; + } + + return_trace (true); + } + + private: + + void should_drop (Value value, Flags flag, unsigned int* format) const + { + if (value) return; + *format = *format & ~flag; + } + +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/GSUB.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/GSUB.hh index ad153ce8d7..3f0c4b2ad9 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/GSUB.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/GSUB.hh @@ -19,6 +19,8 @@ namespace GSUB { struct GSUB : GSUBGPOS { + using Lookup = SubstLookup; + static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB; const SubstLookup& get_lookup (unsigned int i) const diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh index 3419b5a734..8fb3b55097 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh @@ -10,7 +10,7 @@ namespace GSUB { struct SubstLookup : Lookup { - typedef SubstLookupSubTable SubTable; + using SubTable = SubstLookupSubTable; bool sanitize (hb_sanitize_context_t *c) const { return Lookup::sanitize<SubTable> (c); } @@ -73,8 +73,6 @@ struct SubstLookup : Lookup return hb_closure_lookups_context_t::default_return_value (); } - c->set_recurse_func (dispatch_closure_lookups_recurse_func); - hb_closure_lookups_context_t::return_t ret = dispatch (c); return ret; } @@ -100,8 +98,6 @@ struct SubstLookup : Lookup return dispatch (c); } - static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); - bool serialize_single (hb_serialize_context_t *c, uint32_t lookup_props, hb_sorted_array_t<const HBGlyphID16> glyphs, @@ -206,8 +202,6 @@ struct SubstLookup : Lookup return ret; } - HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index); - template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); } diff --git a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh new file mode 100644 index 0000000000..c145beaa49 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh @@ -0,0 +1,258 @@ +#ifndef OT_GLYF_COMPOSITEGLYPH_HH +#define OT_GLYF_COMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct CompositeGlyphRecord +{ + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000 + }; + + public: + unsigned int get_size () const + { + unsigned int size = min_size; + /* arg1 and 2 are int16 */ + if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; + /* arg1 and 2 are int8 */ + else size += 2; + + /* One x 16 bit (scale) */ + if (flags & WE_HAVE_A_SCALE) size += 2; + /* Two x 16 bit (xscale, yscale) */ + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ + else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; + + return size; + } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + void set_overlaps_flag () + { + flags = (uint16_t) flags | OVERLAP_COMPOUND; + } + + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const + { + const HBUINT8 *p = &StructAfter<const HBUINT8> (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + + int tx, ty; + const HBINT8 *p = &StructAfter<const HBINT8> (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; + + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; + } + + public: + HBUINT16 flags; + HBGlyphID16 glyphIndex; + public: + DEFINE_SIZE_MIN (4); +}; + +struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphRecord &> +{ + typedef const CompositeGlyphRecord *__item_t__; + composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_current (current_); + } + + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + item_t __item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_current (&StructAtOffset<CompositeGlyphRecord> (current, current_size)); + } + composite_iter_t __end__ () const { return composite_iter_t (); } + bool operator != (const composite_iter_t& o) const + { return current != o.current; } + + + void set_current (__item_t__ current_) + { + if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = current_->get_size (); + if (!glyph.check_range (current_, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = current_; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; +}; + +struct CompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + composite_iter_t iter () const + { return composite_iter_t (bytes, &StructAfter<CompositeGlyphRecord, GlyphHeader> (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const + { + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphRecord *last = nullptr; + for (auto &item : iter ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; + } + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const hb_bytes_t trim_padding () const { return bytes; } + + void drop_hints () + { + for (const auto &_ : iter ()) + const_cast<CompositeGlyphRecord &> (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + + void set_overlaps_flag () + { + CompositeGlyphRecord& glyph_chain = const_cast<CompositeGlyphRecord &> ( + StructAfter<CompositeGlyphRecord, GlyphHeader> (header)); + if (!bytes.check_range(&glyph_chain, CompositeGlyphRecord::min_size)) + return; + glyph_chain.set_overlaps_flag (); + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_COMPOSITEGLYPH_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh new file mode 100644 index 0000000000..2199d2c48b --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh @@ -0,0 +1,233 @@ +#ifndef OT_GLYF_GLYPH_HH +#define OT_GLYF_GLYPH_HH + + +#include "../../hb-open-type.hh" + +#include "GlyphHeader.hh" +#include "SimpleGlyph.hh" +#include "CompositeGlyph.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +enum phantom_point_index_t +{ + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 +}; + +struct Glyph +{ + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).iter (); + } + + const hb_bytes_t trim_padding () const + { + switch (type) { + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + default: return bytes; + } + } + + void drop_hints () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + default: return; + } + } + + void set_overlaps_flag () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; + case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; + default: return; + } + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + default: return; + } + } + + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + template <typename accelerator_t> + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + bool phantom_only = false, + unsigned int depth = 0) const + { + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + contour_point_vector_t stack_points; + bool inplace = type == SIMPLE && all_points.length == 0; + /* Load into all_points if it's empty, as an optimization. */ + contour_point_vector_t &points = inplace ? all_points : stack_points; + + switch (type) { + case COMPOSITE: + { + /* pseudo component points for each component in composite glyph */ + unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ()); + if (unlikely (!points.resize (num_points))) return false; + break; + } + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + int h_delta = (int) header->xMin - + glyf_accelerator.hmtx->get_side_bearing (gid); + int v_orig = (int) header->yMax + +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_side_bearing (gid) +#else + 0 +#endif + ; + unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); + unsigned v_adv = +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_advance (gid) +#else + - font->face->get_upem () +#endif + ; + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()); +#endif + + switch (type) { + case SIMPLE: + if (!inplace) + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + contour_point_vector_t comp_points; + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) + { + comp_points.reset (); + if (unlikely (!glyf_accelerator.glyph_for_gid (item.glyphIndex) + .get_points (font, glyf_accelerator, comp_points, + phantom_only, depth + 1))) + return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + /* Apply component transformation & translation */ + item.transform_points (comp_points); + + /* Apply translation from gvar */ + comp_points.translate (points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + + comp_index++; + } + + all_points.extend (phantoms); + } break; + default: + all_points.extend (phantoms); + } + + if (depth == 0) /* Apply at top level */ + { + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return !all_points.in_error (); + } + + bool get_extents (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + + Glyph (hb_bytes_t bytes_ = hb_bytes_t (), + hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), + header (bytes.as<GlyphHeader> ()), + gid (gid_) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + const GlyphHeader *header; + hb_codepoint_t gid; + unsigned type; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPH_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/GlyphHeader.hh b/thirdparty/harfbuzz/src/OT/glyf/GlyphHeader.hh new file mode 100644 index 0000000000..792bd5478f --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/GlyphHeader.hh @@ -0,0 +1,48 @@ +#ifndef OT_GLYF_GLYPHHEADER_HH +#define OT_GLYF_GLYPHHEADER_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct GlyphHeader +{ + bool has_data () const { return numberOfContours; } + + template <typename accelerator_t> + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); + extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); + extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); + extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); + + return true; + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPHHEADER_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh new file mode 100644 index 0000000000..6df978cf13 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh @@ -0,0 +1,216 @@ +#ifndef OT_GLYF_SIMPLEGLYPH_HH +#define OT_GLYF_SIMPLEGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct SimpleGlyph +{ + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_OVERLAP_SIMPLE = 0x40, + FLAG_RESERVED2 = 0x80 + }; + + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + unsigned int instructions_length () const + { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset<HBUINT16> (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const hb_bytes_t trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const uint8_t *glyph = (uint8_t*) bytes.arrayZ; + const uint8_t *glyph_end = glyph + bytes.length; + /* simple glyph w/contours, possibly trimmable */ + glyph += instruction_len_offset (); + + if (unlikely (glyph + 2 >= glyph_end)) return hb_bytes_t (); + unsigned int num_coordinates = StructAtOffset<HBUINT16> (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0); + + glyph += 2 + num_instructions; + + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; + while (glyph < glyph_end) + { + uint8_t flag = *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (unlikely (glyph >= glyph_end)) return hb_bytes_t (); + repeat = *glyph + 1; + glyph++; + } + + unsigned int xBytes, yBytes; + xBytes = yBytes = 0; + if (flag & FLAG_X_SHORT) xBytes = 1; + else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; + + if (flag & FLAG_Y_SHORT) yBytes = 1; + else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; + + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; + } + + if (unlikely (coords_with_flags != num_coordinates)) return hb_bytes_t (); + return bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)); + } + + /* zero instruction length */ + void drop_hints () + { + GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header); + (HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + void set_overlaps_flag () + { + if (unlikely (!header.numberOfContours)) return; + + unsigned flags_offset = length (instructions_length ()); + if (unlikely (flags_offset + 1 > bytes.length)) return; + + HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset<HBUINT16> (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + + static bool read_flags (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const HBUINT8 *end) + { + unsigned count = points_.length; + for (unsigned int i = 0; i < count;) + { + if (unlikely (p + 1 > end)) return false; + uint8_t flag = *p++; + points_.arrayZ[i++].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (p + 1 > end)) return false; + unsigned int repeat_count = *p++; + unsigned stop = hb_min (i + repeat_count, count); + for (; i < stop;) + points_.arrayZ[i++].flag = flag; + } + } + return true; + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const HBUINT8 *end, + float contour_point_t::*m, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + int v = 0; + + unsigned count = points_.length; + for (unsigned i = 0; i < count; i++) + { + unsigned flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (p + 1 > end)) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (p + HBINT16::static_size > end)) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + points_.arrayZ[i].*m = v; + } + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header); + int num_contours = header.numberOfContours; + assert (num_contours); + /* One extra item at the end, for the instruction-count below. */ + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy + if (!points_.resize (num_points)) return false; + if (phantom_only) return true; + + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + if (unlikely ((const char *) p < bytes.arrayZ)) return false; /* Unlikely overflow */ + const HBUINT8 *end = (const HBUINT8 *) (bytes.arrayZ + bytes.length); + if (unlikely (p >= end)) return false; + + /* Read x & y coordinates */ + return read_flags (p, points_, end) + && read_points (p, points_, end, &contour_point_t::x, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, end, &contour_point_t::y, + FLAG_Y_SHORT, FLAG_Y_SAME); + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SIMPLEGLYPH_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh new file mode 100644 index 0000000000..ebe4372047 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh @@ -0,0 +1,72 @@ +#ifndef OT_GLYF_SUBSETGLYPH_HH +#define OT_GLYF_SUBSETGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct SubsetGlyph +{ + hb_codepoint_t new_gid; + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + + bool serialize (hb_serialize_context_t *c, + bool use_short_loca, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); + unsigned int pad_length = use_short_loca ? padding () : 0; + DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.glyphIndex, &new_gid)) + const_cast<CompositeGlyphRecord &> (_).glyphIndex = new_gid; + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + Glyph (dest_glyph).drop_hints (); + + if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) + Glyph (dest_glyph).set_overlaps_flag (); + + return_trace (true); + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SUBSETGLYPH_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh b/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh new file mode 100644 index 0000000000..f51f7a81fc --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh @@ -0,0 +1,90 @@ +#ifndef OT_GLYF_GLYF_HELPERS_HH +#define OT_GLYF_GLYF_HELPERS_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-subset-plan.hh" + +#include "loca.hh" + + +namespace OT { +namespace glyf_impl { + + +template<typename IteratorIn, typename IteratorOut, + hb_requires (hb_is_source_of (IteratorIn, unsigned int)), + hb_requires (hb_is_sink_of (IteratorOut, unsigned))> +static void +_write_loca (IteratorIn it, bool short_offsets, IteratorOut dest) +{ + unsigned right_shift = short_offsets ? 1 : 0; + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; +} + +static bool +_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) +{ + hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source); + hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); + hb_blob_destroy (head_blob); + + if (unlikely (!head_prime_blob)) + return false; + + head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; +} + +template<typename Iterator, + hb_requires (hb_is_source_of (Iterator, unsigned int))> +static bool +_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca) +{ + unsigned num_offsets = padded_offsets.len () + 1; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d", + entry_size, num_offsets, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + hb_free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; +} + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HELPERS_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/glyf.hh b/thirdparty/harfbuzz/src/OT/glyf/glyf.hh new file mode 100644 index 0000000000..f74513cb96 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/glyf.hh @@ -0,0 +1,388 @@ +#ifndef OT_GLYF_GLYF_HH +#define OT_GLYF_GLYF_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-ot-head-table.hh" +#include "../../hb-ot-hmtx-table.hh" +#include "../../hb-ot-var-gvar-table.hh" +#include "../../hb-draw.hh" + +#include "glyf-helpers.hh" +#include "Glyph.hh" +#include "SubsetGlyph.hh" +#include "loca.hh" +#include "path-builder.hh" + + +namespace OT { + + +/* + * glyf -- TrueType Glyph Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf + */ +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + + +struct glyf +{ + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* Runtime checks as eager sanitizing each glyph is costy */ + return_trace (true); + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template <typename Iterator> + bool serialize (hb_serialize_context_t *c, + Iterator it, + bool use_short_loca, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + unsigned init_len = c->length (); + for (const auto &_ : it) _.serialize (c, use_short_loca, plan); + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); + } + return_trace (true); + } + + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + glyf *glyf_prime = c->serializer->start_embed <glyf> (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_vector_t<glyf_impl::SubsetGlyph> glyphs; + _populate_subset_glyphs (c->plan, &glyphs); + + auto padded_offsets = + + hb_iter (glyphs) + | hb_map (&glyf_impl::SubsetGlyph::padded_size) + ; + + unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); + bool use_short_loca = max_offset < 0x1FFFF; + + + glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); + if (!use_short_loca) { + padded_offsets = + + hb_iter (glyphs) + | hb_map (&glyf_impl::SubsetGlyph::length) + ; + } + + + if (unlikely (c->serializer->in_error ())) return_trace (false); + return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan, + padded_offsets, + use_short_loca))); + } + + void + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const; + + protected: + UnsizedArrayOf<HBUINT8> + dataZ; /* Glyphs data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + +struct glyf_accelerator_t +{ + glyf_accelerator_t (hb_face_t *face) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; +#ifndef HB_NO_VERTICAL + vmtx = nullptr; +#endif + const OT::head &head = *face->table.head; + if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = face->table.loca.get_blob (); // Needs no destruct! + glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; +#ifndef HB_NO_VERTICAL + vmtx = face->table.vmtx; +#endif + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + ~glyf_accelerator_t () + { + glyf_table.destroy (); + } + + bool has_data () const { return num_glyphs; } + + protected: + template<typename T> + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this allocfree is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + unsigned count = all_points.length; + assert (count >= glyf_impl::PHANTOM_COUNT); + count -= glyf_impl::PHANTOM_COUNT; + for (unsigned point_index = 0; point_index < count; point_index++) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i]; + + return true; + } + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) + { + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); + } + + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) + { + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + extents->x_bearing = font->em_scalef_x (min_x); + extents->width = font->em_scalef_x (max_x) - extents->x_bearing; + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y) - extents->y_bearing; + } + + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + if (extents) bounds = contour_bounds_t (); + } + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + public: + unsigned + get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (likely (font->num_coords == gvar->get_axis_count ())) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); + + if (unlikely (!success)) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_advance (gid) : +#endif + hmtx->get_advance (gid); + + float result = is_vertical + ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y + : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); + } + + int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_side_bearing (gid) : +#endif + hmtx->get_side_bearing (gid); + + return is_vertical + ? ceilf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing + : floorf (phantoms[glyf_impl::PHANTOM_LEFT].x); + } +#endif + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); +#endif + return glyph_for_gid (gid).get_extents (font, *this, extents); + } + + const glyf_impl::Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph (); + + unsigned int start_offset, end_offset; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return glyf_impl::Glyph (); + + glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph; + } + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const + { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); } + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; +#ifndef HB_NO_VERTICAL + const vmtx_accelerator_t *vmtx; +#endif + + private: + bool short_offset; + unsigned int num_glyphs; + hb_blob_ptr_t<loca> loca_table; + hb_blob_ptr_t<glyf> glyf_table; +}; + + +inline void +glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const +{ + OT::glyf_accelerator_t glyf (plan->source); + + + hb_range (plan->num_output_glyphs ()) + | hb_map ([&] (hb_codepoint_t new_gid) + { + glyf_impl::SubsetGlyph subset_glyph = {0}; + subset_glyph.new_gid = new_gid; + + /* should never fail: all old gids should be mapped */ + if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) + return subset_glyph; + + if (new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + subset_glyph.source_glyph = glyf_impl::Glyph (); + else + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + return subset_glyph; + }) + | hb_sink (glyphs) + ; +} + + + +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/loca.hh b/thirdparty/harfbuzz/src/OT/glyf/loca.hh new file mode 100644 index 0000000000..4481cba8ed --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/loca.hh @@ -0,0 +1,43 @@ +#ifndef OT_GLYF_LOCA_HH +#define OT_GLYF_LOCA_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/loca + */ +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + +struct loca +{ + friend struct glyf; + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + protected: + UnsizedArrayOf<HBUINT8> + dataZ; /* Location data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + + +} /* namespace OT */ + + +#endif /* OT_GLYF_LOCA_HH */ diff --git a/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh b/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh new file mode 100644 index 0000000000..9bfc45a1a6 --- /dev/null +++ b/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh @@ -0,0 +1,135 @@ +#ifndef OT_GLYF_PATH_BUILDER_HH +#define OT_GLYF_PATH_BUILDER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct path_builder_t +{ + hb_font_t *font; + hb_draw_session_t *draw_session; + + struct optional_point_t + { + optional_point_t () {} + optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {} + operator bool () const { return has_data; } + + bool has_data = false; + float x = 0.; + float y = 0.; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, last_offcurve; + + path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) + { + font = font_; + draw_session = &draw_session_; + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 */ + void consume_point (const contour_point_t &point) + { + bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE; + optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y)); + if (!first_oncurve) + { + if (is_on_curve) + { + first_oncurve = p; + draw_session->move_to (p.x, p.y); + } + else + { + if (first_offcurve) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_session->move_to (mid.x, mid.y); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve) + { + if (is_on_curve) + { + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_session->line_to (p.x, p.y); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve && last_offcurve) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve && first_oncurve) + draw_session->quadratic_to (first_offcurve.x, first_offcurve.y, + first_oncurve.x, first_oncurve.y); + else if (last_offcurve && first_oncurve) + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + else if (first_oncurve) + draw_session->line_to (first_oncurve.x, first_oncurve.y); + else if (first_offcurve) + { + float x = first_offcurve.x, y = first_offcurve.y; + draw_session->move_to (x, y); + draw_session->quadratic_to (x, y, x, y); + } + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_session->close_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_PATH_BUILDER_HH */ |