michael@0: /* michael@0: * Copyright © 2007,2008,2009,2010 Red Hat, Inc. michael@0: * Copyright © 2010,2012,2013 Google, Inc. michael@0: * michael@0: * This is part of HarfBuzz, a text shaping library. michael@0: * michael@0: * Permission is hereby granted, without written agreement and without michael@0: * license or royalty fees, to use, copy, modify, and distribute this michael@0: * software and its documentation for any purpose, provided that the michael@0: * above copyright notice and the following two paragraphs appear in michael@0: * all copies of this software. michael@0: * michael@0: * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR michael@0: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES michael@0: * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN michael@0: * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH michael@0: * DAMAGE. michael@0: * michael@0: * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, michael@0: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND michael@0: * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS michael@0: * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO michael@0: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. michael@0: * michael@0: * Red Hat Author(s): Behdad Esfahbod michael@0: * Google Author(s): Behdad Esfahbod michael@0: */ michael@0: michael@0: #ifndef HB_OT_LAYOUT_GPOS_TABLE_HH michael@0: #define HB_OT_LAYOUT_GPOS_TABLE_HH michael@0: michael@0: #include "hb-ot-layout-gsubgpos-private.hh" michael@0: michael@0: michael@0: namespace OT { michael@0: michael@0: michael@0: /* buffer **position** var allocations */ michael@0: #define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */ michael@0: #define cursive_chain() var.i16[1] /* character to which this connects, may be positive or negative */ michael@0: michael@0: michael@0: /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */ michael@0: michael@0: typedef USHORT Value; michael@0: michael@0: typedef Value ValueRecord[VAR]; michael@0: michael@0: struct ValueFormat : USHORT michael@0: { michael@0: enum Flags { michael@0: xPlacement = 0x0001, /* Includes horizontal adjustment for placement */ michael@0: yPlacement = 0x0002, /* Includes vertical adjustment for placement */ michael@0: xAdvance = 0x0004, /* Includes horizontal adjustment for advance */ michael@0: yAdvance = 0x0008, /* Includes vertical adjustment for advance */ michael@0: xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */ michael@0: yPlaDevice = 0x0020, /* Includes vertical Device table for placement */ michael@0: xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */ michael@0: yAdvDevice = 0x0080, /* Includes vertical Device table for advance */ michael@0: ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */ michael@0: reserved = 0xF000, /* For future use */ michael@0: michael@0: devices = 0x00F0 /* Mask for having any Device table */ michael@0: }; michael@0: michael@0: /* All fields are options. Only those available advance the value pointer. */ michael@0: #if 0 michael@0: SHORT xPlacement; /* Horizontal adjustment for michael@0: * placement--in design units */ michael@0: SHORT yPlacement; /* Vertical adjustment for michael@0: * placement--in design units */ michael@0: SHORT xAdvance; /* Horizontal adjustment for michael@0: * advance--in design units (only used michael@0: * for horizontal writing) */ michael@0: SHORT yAdvance; /* Vertical adjustment for advance--in michael@0: * design units (only used for vertical michael@0: * writing) */ michael@0: Offset xPlaDevice; /* Offset to Device table for michael@0: * horizontal placement--measured from michael@0: * beginning of PosTable (may be NULL) */ michael@0: Offset yPlaDevice; /* Offset to Device table for vertical michael@0: * placement--measured from beginning michael@0: * of PosTable (may be NULL) */ michael@0: Offset xAdvDevice; /* Offset to Device table for michael@0: * horizontal advance--measured from michael@0: * beginning of PosTable (may be NULL) */ michael@0: Offset yAdvDevice; /* Offset to Device table for vertical michael@0: * advance--measured from beginning of michael@0: * PosTable (may be NULL) */ michael@0: #endif michael@0: michael@0: inline unsigned int get_len (void) const michael@0: { return _hb_popcount32 ((unsigned int) *this); } michael@0: inline unsigned int get_size (void) const michael@0: { return get_len () * Value::static_size; } michael@0: michael@0: void apply_value (hb_font_t *font, michael@0: hb_direction_t direction, michael@0: const void *base, michael@0: const Value *values, michael@0: hb_glyph_position_t &glyph_pos) const michael@0: { michael@0: unsigned int x_ppem, y_ppem; michael@0: unsigned int format = *this; michael@0: hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (direction); michael@0: michael@0: if (!format) return; michael@0: michael@0: if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++)); michael@0: if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++)); michael@0: if (format & xAdvance) { michael@0: if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values)); michael@0: values++; michael@0: } michael@0: /* y_advance values grow downward but font-space grows upward, hence negation */ michael@0: if (format & yAdvance) { michael@0: if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values)); michael@0: values++; michael@0: } michael@0: michael@0: if (!has_device ()) return; michael@0: michael@0: x_ppem = font->x_ppem; michael@0: y_ppem = font->y_ppem; michael@0: michael@0: if (!x_ppem && !y_ppem) return; michael@0: michael@0: /* pixel -> fractional pixel */ michael@0: if (format & xPlaDevice) { michael@0: if (x_ppem) glyph_pos.x_offset += (base + get_device (values)).get_x_delta (font); michael@0: values++; michael@0: } michael@0: if (format & yPlaDevice) { michael@0: if (y_ppem) glyph_pos.y_offset += (base + get_device (values)).get_y_delta (font); michael@0: values++; michael@0: } michael@0: if (format & xAdvDevice) { michael@0: if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font); michael@0: values++; michael@0: } michael@0: if (format & yAdvDevice) { michael@0: /* y_advance values grow downward but font-space grows upward, hence negation */ michael@0: if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font); michael@0: values++; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) { michael@0: unsigned int format = *this; michael@0: michael@0: if (format & xPlacement) values++; michael@0: if (format & yPlacement) values++; michael@0: if (format & xAdvance) values++; michael@0: if (format & yAdvance) values++; michael@0: michael@0: if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false; michael@0: if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false; michael@0: if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false; michael@0: if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static inline OffsetTo& get_device (Value* value) michael@0: { return *CastP > (value); } michael@0: static inline const OffsetTo& get_device (const Value* value) michael@0: { return *CastP > (value); } michael@0: michael@0: static inline const SHORT& get_short (const Value* value) michael@0: { return *CastP (value); } michael@0: michael@0: public: michael@0: michael@0: inline bool has_device (void) const { michael@0: unsigned int format = *this; michael@0: return (format & devices) != 0; michael@0: } michael@0: michael@0: inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); michael@0: } michael@0: michael@0: inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) { michael@0: TRACE_SANITIZE (this); michael@0: unsigned int len = get_len (); michael@0: michael@0: if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false); michael@0: michael@0: if (!has_device ()) return TRACE_RETURN (true); michael@0: michael@0: for (unsigned int i = 0; i < count; i++) { michael@0: if (!sanitize_value_devices (c, base, values)) michael@0: return TRACE_RETURN (false); michael@0: values += len; michael@0: } michael@0: michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ michael@0: inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) { michael@0: TRACE_SANITIZE (this); michael@0: michael@0: if (!has_device ()) return TRACE_RETURN (true); michael@0: michael@0: for (unsigned int i = 0; i < count; i++) { michael@0: if (!sanitize_value_devices (c, base, values)) michael@0: return TRACE_RETURN (false); michael@0: values += stride; michael@0: } michael@0: michael@0: return TRACE_RETURN (true); michael@0: } michael@0: }; michael@0: michael@0: michael@0: struct AnchorFormat1 michael@0: { michael@0: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED, michael@0: hb_position_t *x, hb_position_t *y) const michael@0: { michael@0: *x = font->em_scale_x (xCoordinate); michael@0: *y = font->em_scale_y (yCoordinate); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: SHORT xCoordinate; /* Horizontal value--in design units */ michael@0: SHORT yCoordinate; /* Vertical value--in design units */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (6); michael@0: }; michael@0: michael@0: struct AnchorFormat2 michael@0: { michael@0: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id, michael@0: hb_position_t *x, hb_position_t *y) const michael@0: { michael@0: unsigned int x_ppem = font->x_ppem; michael@0: unsigned int y_ppem = font->y_ppem; michael@0: hb_position_t cx, cy; michael@0: hb_bool_t ret; michael@0: michael@0: ret = (x_ppem || y_ppem) && michael@0: font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); michael@0: *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate); michael@0: *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 2 */ michael@0: SHORT xCoordinate; /* Horizontal value--in design units */ michael@0: SHORT yCoordinate; /* Vertical value--in design units */ michael@0: USHORT anchorPoint; /* Index to glyph contour point */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (8); michael@0: }; michael@0: michael@0: struct AnchorFormat3 michael@0: { michael@0: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED, michael@0: hb_position_t *x, hb_position_t *y) const michael@0: { michael@0: *x = font->em_scale_x (xCoordinate); michael@0: *y = font->em_scale_y (yCoordinate); michael@0: michael@0: if (font->x_ppem) michael@0: *x += (this+xDeviceTable).get_x_delta (font); michael@0: if (font->y_ppem) michael@0: *y += (this+yDeviceTable).get_x_delta (font); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 3 */ michael@0: SHORT xCoordinate; /* Horizontal value--in design units */ michael@0: SHORT yCoordinate; /* Vertical value--in design units */ michael@0: OffsetTo michael@0: xDeviceTable; /* Offset to Device table for X michael@0: * coordinate-- from beginning of michael@0: * Anchor table (may be NULL) */ michael@0: OffsetTo michael@0: yDeviceTable; /* Offset to Device table for Y michael@0: * coordinate-- from beginning of michael@0: * Anchor table (may be NULL) */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (10); michael@0: }; michael@0: michael@0: struct Anchor michael@0: { michael@0: inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id, michael@0: hb_position_t *x, hb_position_t *y) const michael@0: { michael@0: *x = *y = 0; michael@0: switch (u.format) { michael@0: case 1: u.format1.get_anchor (font, glyph_id, x, y); return; michael@0: case 2: u.format2.get_anchor (font, glyph_id, x, y); return; michael@0: case 3: u.format3.get_anchor (font, glyph_id, x, y); return; michael@0: default: return; michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: case 2: return TRACE_RETURN (u.format2.sanitize (c)); michael@0: case 3: return TRACE_RETURN (u.format3.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: AnchorFormat1 format1; michael@0: AnchorFormat2 format2; michael@0: AnchorFormat3 format3; michael@0: } u; michael@0: public: michael@0: DEFINE_SIZE_UNION (2, format); michael@0: }; michael@0: michael@0: michael@0: struct AnchorMatrix michael@0: { michael@0: inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const { michael@0: *found = false; michael@0: if (unlikely (row >= rows || col >= cols)) return Null(Anchor); michael@0: *found = !matrix[row * cols + col].is_null (); michael@0: return this+matrix[row * cols + col]; michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) { michael@0: TRACE_SANITIZE (this); michael@0: if (!c->check_struct (this)) return TRACE_RETURN (false); michael@0: if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false); michael@0: unsigned int count = rows * cols; michael@0: if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false); michael@0: for (unsigned int i = 0; i < count; i++) michael@0: if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false); michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: USHORT rows; /* Number of rows */ michael@0: protected: michael@0: OffsetTo michael@0: matrix[VAR]; /* Matrix of offsets to Anchor tables-- michael@0: * from beginning of AnchorMatrix table */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (2, matrix); michael@0: }; michael@0: michael@0: michael@0: struct MarkRecord michael@0: { michael@0: friend struct MarkArray; michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c, void *base) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT klass; /* Class defined for this mark */ michael@0: OffsetTo michael@0: markAnchor; /* Offset to Anchor table--from michael@0: * beginning of MarkArray table */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (4); michael@0: }; michael@0: michael@0: struct MarkArray : ArrayOf /* Array of MarkRecords--in Coverage order */ michael@0: { michael@0: inline bool apply (hb_apply_context_t *c, michael@0: unsigned int mark_index, unsigned int glyph_index, michael@0: const AnchorMatrix &anchors, unsigned int class_count, michael@0: unsigned int glyph_pos) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: const MarkRecord &record = ArrayOf::operator[](mark_index); michael@0: unsigned int mark_class = record.klass; michael@0: michael@0: const Anchor& mark_anchor = this + record.markAnchor; michael@0: bool found; michael@0: const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found); michael@0: /* If this subtable doesn't have an anchor for this base and this class, michael@0: * return false such that the subsequent subtables have a chance at it. */ michael@0: if (unlikely (!found)) return TRACE_RETURN (false); michael@0: michael@0: hb_position_t mark_x, mark_y, base_x, base_y; michael@0: michael@0: mark_anchor.get_anchor (c->font, buffer->cur().codepoint, &mark_x, &mark_y); michael@0: glyph_anchor.get_anchor (c->font, buffer->info[glyph_pos].codepoint, &base_x, &base_y); michael@0: michael@0: hb_glyph_position_t &o = buffer->cur_pos(); michael@0: o.x_offset = base_x - mark_x; michael@0: o.y_offset = base_y - mark_y; michael@0: o.attach_lookback() = buffer->idx - glyph_pos; michael@0: michael@0: buffer->idx++; michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (ArrayOf::sanitize (c, this)); michael@0: } michael@0: }; michael@0: michael@0: michael@0: /* Lookups */ michael@0: michael@0: struct SinglePosFormat1 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+coverage).add_coverage (c->input); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+coverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: valueFormat.apply_value (c->font, c->direction, this, michael@0: values, buffer->cur_pos()); michael@0: michael@0: buffer->idx++; michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: OffsetTo michael@0: coverage; /* Offset to Coverage table--from michael@0: * beginning of subtable */ michael@0: ValueFormat valueFormat; /* Defines the types of data in the michael@0: * ValueRecord */ michael@0: ValueRecord values; /* Defines positioning michael@0: * value(s)--applied to all glyphs in michael@0: * the Coverage table */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (6, values); michael@0: }; michael@0: michael@0: struct SinglePosFormat2 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+coverage).add_coverage (c->input); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+coverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: if (likely (index >= valueCount)) return TRACE_RETURN (false); michael@0: michael@0: valueFormat.apply_value (c->font, c->direction, this, michael@0: &values[index * valueFormat.get_len ()], michael@0: buffer->cur_pos()); michael@0: michael@0: buffer->idx++; michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 2 */ michael@0: OffsetTo michael@0: coverage; /* Offset to Coverage table--from michael@0: * beginning of subtable */ michael@0: ValueFormat valueFormat; /* Defines the types of data in the michael@0: * ValueRecord */ michael@0: USHORT valueCount; /* Number of ValueRecords */ michael@0: ValueRecord values; /* Array of ValueRecords--positioning michael@0: * values applied to glyphs */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (8, values); michael@0: }; michael@0: michael@0: struct SinglePos michael@0: { michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (c->dispatch (u.format1)); michael@0: case 2: return TRACE_RETURN (c->dispatch (u.format2)); michael@0: default:return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: case 2: return TRACE_RETURN (u.format2.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: SinglePosFormat1 format1; michael@0: SinglePosFormat2 format2; michael@0: } u; michael@0: }; michael@0: michael@0: michael@0: struct PairValueRecord michael@0: { michael@0: friend struct PairSet; michael@0: michael@0: protected: michael@0: GlyphID secondGlyph; /* GlyphID of second glyph in the michael@0: * pair--first glyph is listed in the michael@0: * Coverage table */ michael@0: ValueRecord values; /* Positioning data for the first glyph michael@0: * followed by for second glyph */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (2, values); michael@0: }; michael@0: michael@0: struct PairSet michael@0: { michael@0: friend struct PairPosFormat1; michael@0: michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c, michael@0: const ValueFormat *valueFormats) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: unsigned int len1 = valueFormats[0].get_len (); michael@0: unsigned int len2 = valueFormats[1].get_len (); michael@0: unsigned int record_size = USHORT::static_size * (1 + len1 + len2); michael@0: michael@0: const PairValueRecord *record = CastP (array); michael@0: unsigned int count = len; michael@0: for (unsigned int i = 0; i < count; i++) michael@0: { michael@0: c->input->add (record->secondGlyph); michael@0: record = &StructAtOffset (record, record_size); michael@0: } michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c, michael@0: const ValueFormat *valueFormats, michael@0: unsigned int pos) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: unsigned int len1 = valueFormats[0].get_len (); michael@0: unsigned int len2 = valueFormats[1].get_len (); michael@0: unsigned int record_size = USHORT::static_size * (1 + len1 + len2); michael@0: michael@0: const PairValueRecord *record = CastP (array); michael@0: unsigned int count = len; michael@0: for (unsigned int i = 0; i < count; i++) michael@0: { michael@0: /* TODO bsearch */ michael@0: if (buffer->info[pos].codepoint == record->secondGlyph) michael@0: { michael@0: valueFormats[0].apply_value (c->font, c->direction, this, michael@0: &record->values[0], buffer->cur_pos()); michael@0: valueFormats[1].apply_value (c->font, c->direction, this, michael@0: &record->values[len1], buffer->pos[pos]); michael@0: if (len2) michael@0: pos++; michael@0: buffer->idx = pos; michael@0: return TRACE_RETURN (true); michael@0: } michael@0: record = &StructAtOffset (record, record_size); michael@0: } michael@0: michael@0: return TRACE_RETURN (false); michael@0: } michael@0: michael@0: struct sanitize_closure_t { michael@0: void *base; michael@0: ValueFormat *valueFormats; michael@0: unsigned int len1; /* valueFormats[0].get_len() */ michael@0: unsigned int stride; /* 1 + len1 + len2 */ michael@0: }; michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) { michael@0: TRACE_SANITIZE (this); michael@0: if (!(c->check_struct (this) michael@0: && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false); michael@0: michael@0: unsigned int count = len; michael@0: PairValueRecord *record = CastP (array); michael@0: return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) michael@0: && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT len; /* Number of PairValueRecords */ michael@0: USHORT array[VAR]; /* Array of PairValueRecords--ordered michael@0: * by GlyphID of the second glyph */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (2, array); michael@0: }; michael@0: michael@0: struct PairPosFormat1 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+coverage).add_coverage (c->input); michael@0: unsigned int count = pairSet.len; michael@0: for (unsigned int i = 0; i < count; i++) michael@0: (this+pairSet[i]).collect_glyphs (c, &valueFormat1); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+coverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, 1); michael@0: if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); michael@0: michael@0: unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: if (!skippy_iter.next ()) return TRACE_RETURN (false); michael@0: michael@0: return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx)); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: michael@0: unsigned int len1 = valueFormat1.get_len (); michael@0: unsigned int len2 = valueFormat2.get_len (); michael@0: PairSet::sanitize_closure_t closure = { michael@0: this, michael@0: &valueFormat1, michael@0: len1, michael@0: 1 + len1 + len2 michael@0: }; michael@0: michael@0: return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: OffsetTo michael@0: coverage; /* Offset to Coverage table--from michael@0: * beginning of subtable */ michael@0: ValueFormat valueFormat1; /* Defines the types of data in michael@0: * ValueRecord1--for the first glyph michael@0: * in the pair--may be zero (0) */ michael@0: ValueFormat valueFormat2; /* Defines the types of data in michael@0: * ValueRecord2--for the second glyph michael@0: * in the pair--may be zero (0) */ michael@0: OffsetArrayOf michael@0: pairSet; /* Array of PairSet tables michael@0: * ordered by Coverage Index */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (10, pairSet); michael@0: }; michael@0: michael@0: struct PairPosFormat2 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: /* (this+coverage).add_coverage (c->input); // Don't need this. */ michael@0: michael@0: unsigned int count1 = class1Count; michael@0: const ClassDef &klass1 = this+classDef1; michael@0: for (unsigned int i = 0; i < count1; i++) michael@0: klass1.add_class (c->input, i); michael@0: michael@0: unsigned int count2 = class2Count; michael@0: const ClassDef &klass2 = this+classDef2; michael@0: for (unsigned int i = 0; i < count2; i++) michael@0: klass2.add_class (c->input, i); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+coverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, 1); michael@0: if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); michael@0: michael@0: unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: if (!skippy_iter.next ()) return TRACE_RETURN (false); michael@0: michael@0: unsigned int len1 = valueFormat1.get_len (); michael@0: unsigned int len2 = valueFormat2.get_len (); michael@0: unsigned int record_len = len1 + len2; michael@0: michael@0: unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); michael@0: unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); michael@0: if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false); michael@0: michael@0: const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; michael@0: valueFormat1.apply_value (c->font, c->direction, this, michael@0: v, buffer->cur_pos()); michael@0: valueFormat2.apply_value (c->font, c->direction, this, michael@0: v + len1, buffer->pos[skippy_iter.idx]); michael@0: michael@0: buffer->idx = skippy_iter.idx; michael@0: if (len2) michael@0: buffer->idx++; michael@0: michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!(c->check_struct (this) michael@0: && coverage.sanitize (c, this) michael@0: && classDef1.sanitize (c, this) michael@0: && classDef2.sanitize (c, this))) return TRACE_RETURN (false); michael@0: michael@0: unsigned int len1 = valueFormat1.get_len (); michael@0: unsigned int len2 = valueFormat2.get_len (); michael@0: unsigned int stride = len1 + len2; michael@0: unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size (); michael@0: unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; michael@0: return TRACE_RETURN (c->check_array (values, record_size, count) && michael@0: valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && michael@0: valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 2 */ michael@0: OffsetTo michael@0: coverage; /* Offset to Coverage table--from michael@0: * beginning of subtable */ michael@0: ValueFormat valueFormat1; /* ValueRecord definition--for the michael@0: * first glyph of the pair--may be zero michael@0: * (0) */ michael@0: ValueFormat valueFormat2; /* ValueRecord definition--for the michael@0: * second glyph of the pair--may be michael@0: * zero (0) */ michael@0: OffsetTo michael@0: classDef1; /* Offset to ClassDef table--from michael@0: * beginning of PairPos subtable--for michael@0: * the first glyph of the pair */ michael@0: OffsetTo michael@0: classDef2; /* Offset to ClassDef table--from michael@0: * beginning of PairPos subtable--for michael@0: * the second glyph of the pair */ michael@0: USHORT class1Count; /* Number of classes in ClassDef1 michael@0: * table--includes Class0 */ michael@0: USHORT class2Count; /* Number of classes in ClassDef2 michael@0: * table--includes Class0 */ michael@0: ValueRecord values; /* Matrix of value pairs: michael@0: * class1-major, class2-minor, michael@0: * Each entry has value1 and value2 */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (16, values); michael@0: }; michael@0: michael@0: struct PairPos michael@0: { michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (c->dispatch (u.format1)); michael@0: case 2: return TRACE_RETURN (c->dispatch (u.format2)); michael@0: default:return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: case 2: return TRACE_RETURN (u.format2.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: PairPosFormat1 format1; michael@0: PairPosFormat2 format2; michael@0: } u; michael@0: }; michael@0: michael@0: michael@0: struct EntryExitRecord michael@0: { michael@0: friend struct CursivePosFormat1; michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c, void *base) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); michael@0: } michael@0: michael@0: protected: michael@0: OffsetTo michael@0: entryAnchor; /* Offset to EntryAnchor table--from michael@0: * beginning of CursivePos michael@0: * subtable--may be NULL */ michael@0: OffsetTo michael@0: exitAnchor; /* Offset to ExitAnchor table--from michael@0: * beginning of CursivePos michael@0: * subtable--may be NULL */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (4); michael@0: }; michael@0: michael@0: struct CursivePosFormat1 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+coverage).add_coverage (c->input); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+coverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: michael@0: /* We don't handle mark glyphs here. */ michael@0: if (unlikely (_hb_glyph_info_is_mark (&buffer->cur()))) return TRACE_RETURN (false); michael@0: michael@0: hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, 1); michael@0: if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false); michael@0: michael@0: const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; michael@0: if (!this_record.exitAnchor) return TRACE_RETURN (false); michael@0: michael@0: if (!skippy_iter.next ()) return TRACE_RETURN (false); michael@0: michael@0: const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; michael@0: if (!next_record.entryAnchor) return TRACE_RETURN (false); michael@0: michael@0: unsigned int i = buffer->idx; michael@0: unsigned int j = skippy_iter.idx; michael@0: michael@0: hb_position_t entry_x, entry_y, exit_x, exit_y; michael@0: (this+this_record.exitAnchor).get_anchor (c->font, buffer->info[i].codepoint, &exit_x, &exit_y); michael@0: (this+next_record.entryAnchor).get_anchor (c->font, buffer->info[j].codepoint, &entry_x, &entry_y); michael@0: michael@0: hb_glyph_position_t *pos = buffer->pos; michael@0: michael@0: hb_position_t d; michael@0: /* Main-direction adjustment */ michael@0: switch (c->direction) { michael@0: case HB_DIRECTION_LTR: michael@0: pos[i].x_advance = exit_x + pos[i].x_offset; michael@0: michael@0: d = entry_x + pos[j].x_offset; michael@0: pos[j].x_advance -= d; michael@0: pos[j].x_offset -= d; michael@0: break; michael@0: case HB_DIRECTION_RTL: michael@0: d = exit_x + pos[i].x_offset; michael@0: pos[i].x_advance -= d; michael@0: pos[i].x_offset -= d; michael@0: michael@0: pos[j].x_advance = entry_x + pos[j].x_offset; michael@0: break; michael@0: case HB_DIRECTION_TTB: michael@0: pos[i].y_advance = exit_y + pos[i].y_offset; michael@0: michael@0: d = entry_y + pos[j].y_offset; michael@0: pos[j].y_advance -= d; michael@0: pos[j].y_offset -= d; michael@0: break; michael@0: case HB_DIRECTION_BTT: michael@0: d = exit_y + pos[i].y_offset; michael@0: pos[i].y_advance -= d; michael@0: pos[i].y_offset -= d; michael@0: michael@0: pos[j].y_advance = entry_y; michael@0: break; michael@0: case HB_DIRECTION_INVALID: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: /* Cross-direction adjustment */ michael@0: if (c->lookup_props & LookupFlag::RightToLeft) { michael@0: pos[i].cursive_chain() = j - i; michael@0: if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) michael@0: pos[i].y_offset = entry_y - exit_y; michael@0: else michael@0: pos[i].x_offset = entry_x - exit_x; michael@0: } else { michael@0: pos[j].cursive_chain() = i - j; michael@0: if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) michael@0: pos[j].y_offset = exit_y - entry_y; michael@0: else michael@0: pos[j].x_offset = exit_x - entry_x; michael@0: } michael@0: michael@0: buffer->idx = j; michael@0: return TRACE_RETURN (true); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: OffsetTo michael@0: coverage; /* Offset to Coverage table--from michael@0: * beginning of subtable */ michael@0: ArrayOf michael@0: entryExitRecord; /* Array of EntryExit records--in michael@0: * Coverage Index order */ michael@0: public: michael@0: DEFINE_SIZE_ARRAY (6, entryExitRecord); michael@0: }; michael@0: michael@0: struct CursivePos michael@0: { michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (c->dispatch (u.format1)); michael@0: default:return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: CursivePosFormat1 format1; michael@0: } u; michael@0: }; michael@0: michael@0: michael@0: typedef AnchorMatrix BaseArray; /* base-major-- michael@0: * in order of BaseCoverage Index--, michael@0: * mark-minor-- michael@0: * ordered by class--zero-based. */ michael@0: michael@0: struct MarkBasePosFormat1 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+markCoverage).add_coverage (c->input); michael@0: (this+baseCoverage).add_coverage (c->input); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+markCoverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: /* now we search backwards for a non-mark glyph */ michael@0: hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, buffer->idx, 1); michael@0: skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); michael@0: do { michael@0: if (!skippy_iter.prev ()) return TRACE_RETURN (false); michael@0: /* We only want to attach to the first of a MultipleSubst sequence. Reject others. */ michael@0: if (0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx])) break; michael@0: skippy_iter.reject (); michael@0: } while (1); michael@0: michael@0: /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ michael@0: if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { /*return TRACE_RETURN (false);*/ } michael@0: michael@0: unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); michael@0: if (base_index == NOT_COVERED) return TRACE_RETURN (false); michael@0: michael@0: return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) && michael@0: markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: OffsetTo michael@0: markCoverage; /* Offset to MarkCoverage table--from michael@0: * beginning of MarkBasePos subtable */ michael@0: OffsetTo michael@0: baseCoverage; /* Offset to BaseCoverage table--from michael@0: * beginning of MarkBasePos subtable */ michael@0: USHORT classCount; /* Number of classes defined for marks */ michael@0: OffsetTo michael@0: markArray; /* Offset to MarkArray table--from michael@0: * beginning of MarkBasePos subtable */ michael@0: OffsetTo michael@0: baseArray; /* Offset to BaseArray table--from michael@0: * beginning of MarkBasePos subtable */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (12); michael@0: }; michael@0: michael@0: struct MarkBasePos michael@0: { michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (c->dispatch (u.format1)); michael@0: default:return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: MarkBasePosFormat1 format1; michael@0: } u; michael@0: }; michael@0: michael@0: michael@0: typedef AnchorMatrix LigatureAttach; /* component-major-- michael@0: * in order of writing direction--, michael@0: * mark-minor-- michael@0: * ordered by class--zero-based. */ michael@0: michael@0: typedef OffsetListOf LigatureArray; michael@0: /* Array of LigatureAttach michael@0: * tables ordered by michael@0: * LigatureCoverage Index */ michael@0: michael@0: struct MarkLigPosFormat1 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+markCoverage).add_coverage (c->input); michael@0: (this+ligatureCoverage).add_coverage (c->input); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+markCoverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: /* now we search backwards for a non-mark glyph */ michael@0: hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, buffer->idx, 1); michael@0: skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); michael@0: if (!skippy_iter.prev ()) return TRACE_RETURN (false); michael@0: michael@0: /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ michael@0: if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { /*return TRACE_RETURN (false);*/ } michael@0: michael@0: unsigned int j = skippy_iter.idx; michael@0: unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); michael@0: if (lig_index == NOT_COVERED) return TRACE_RETURN (false); michael@0: michael@0: const LigatureArray& lig_array = this+ligatureArray; michael@0: const LigatureAttach& lig_attach = lig_array[lig_index]; michael@0: michael@0: /* Find component to attach to */ michael@0: unsigned int comp_count = lig_attach.rows; michael@0: if (unlikely (!comp_count)) return TRACE_RETURN (false); michael@0: michael@0: /* We must now check whether the ligature ID of the current mark glyph michael@0: * is identical to the ligature ID of the found ligature. If yes, we michael@0: * can directly use the component index. If not, we attach the mark michael@0: * glyph to the last component of the ligature. */ michael@0: unsigned int comp_index; michael@0: unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]); michael@0: unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); michael@0: unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); michael@0: if (lig_id && lig_id == mark_id && mark_comp > 0) michael@0: comp_index = MIN (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; michael@0: else michael@0: comp_index = comp_count - 1; michael@0: michael@0: return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j)); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) && michael@0: markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: OffsetTo michael@0: markCoverage; /* Offset to Mark Coverage table--from michael@0: * beginning of MarkLigPos subtable */ michael@0: OffsetTo michael@0: ligatureCoverage; /* Offset to Ligature Coverage michael@0: * table--from beginning of MarkLigPos michael@0: * subtable */ michael@0: USHORT classCount; /* Number of defined mark classes */ michael@0: OffsetTo michael@0: markArray; /* Offset to MarkArray table--from michael@0: * beginning of MarkLigPos subtable */ michael@0: OffsetTo michael@0: ligatureArray; /* Offset to LigatureArray table--from michael@0: * beginning of MarkLigPos subtable */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (12); michael@0: }; michael@0: michael@0: struct MarkLigPos michael@0: { michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (c->dispatch (u.format1)); michael@0: default:return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: MarkLigPosFormat1 format1; michael@0: } u; michael@0: }; michael@0: michael@0: michael@0: typedef AnchorMatrix Mark2Array; /* mark2-major-- michael@0: * in order of Mark2Coverage Index--, michael@0: * mark1-minor-- michael@0: * ordered by class--zero-based. */ michael@0: michael@0: struct MarkMarkPosFormat1 michael@0: { michael@0: inline void collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: (this+mark1Coverage).add_coverage (c->input); michael@0: (this+mark2Coverage).add_coverage (c->input); michael@0: } michael@0: michael@0: inline const Coverage &get_coverage (void) const michael@0: { michael@0: return this+mark1Coverage; michael@0: } michael@0: michael@0: inline bool apply (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: hb_buffer_t *buffer = c->buffer; michael@0: unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint); michael@0: if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false); michael@0: michael@0: /* now we search backwards for a suitable mark glyph until a non-mark glyph */ michael@0: hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, buffer->idx, 1); michael@0: skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags); michael@0: if (!skippy_iter.prev ()) return TRACE_RETURN (false); michael@0: michael@0: if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return TRACE_RETURN (false); } michael@0: michael@0: unsigned int j = skippy_iter.idx; michael@0: michael@0: unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur()); michael@0: unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]); michael@0: unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); michael@0: unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); michael@0: michael@0: if (likely (id1 == id2)) { michael@0: if (id1 == 0) /* Marks belonging to the same base. */ michael@0: goto good; michael@0: else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ michael@0: goto good; michael@0: } else { michael@0: /* If ligature ids don't match, it may be the case that one of the marks michael@0: * itself is a ligature. In which case match. */ michael@0: if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) michael@0: goto good; michael@0: } michael@0: michael@0: /* Didn't match. */ michael@0: return TRACE_RETURN (false); michael@0: michael@0: good: michael@0: unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); michael@0: if (mark2_index == NOT_COVERED) return TRACE_RETURN (false); michael@0: michael@0: return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) && michael@0: mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this) michael@0: && mark2Array.sanitize (c, this, (unsigned int) classCount)); michael@0: } michael@0: michael@0: protected: michael@0: USHORT format; /* Format identifier--format = 1 */ michael@0: OffsetTo michael@0: mark1Coverage; /* Offset to Combining Mark1 Coverage michael@0: * table--from beginning of MarkMarkPos michael@0: * subtable */ michael@0: OffsetTo michael@0: mark2Coverage; /* Offset to Combining Mark2 Coverage michael@0: * table--from beginning of MarkMarkPos michael@0: * subtable */ michael@0: USHORT classCount; /* Number of defined mark classes */ michael@0: OffsetTo michael@0: mark1Array; /* Offset to Mark1Array table--from michael@0: * beginning of MarkMarkPos subtable */ michael@0: OffsetTo michael@0: mark2Array; /* Offset to Mark2Array table--from michael@0: * beginning of MarkMarkPos subtable */ michael@0: public: michael@0: DEFINE_SIZE_STATIC (12); michael@0: }; michael@0: michael@0: struct MarkMarkPos michael@0: { michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (c->dispatch (u.format1)); michael@0: default:return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.format.sanitize (c)) return TRACE_RETURN (false); michael@0: switch (u.format) { michael@0: case 1: return TRACE_RETURN (u.format1.sanitize (c)); michael@0: default:return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: USHORT format; /* Format identifier */ michael@0: MarkMarkPosFormat1 format1; michael@0: } u; michael@0: }; michael@0: michael@0: michael@0: struct ContextPos : Context {}; michael@0: michael@0: struct ChainContextPos : ChainContext {}; michael@0: michael@0: struct ExtensionPos : Extension michael@0: { michael@0: typedef struct PosLookupSubTable LookupSubTable; michael@0: }; michael@0: michael@0: michael@0: michael@0: /* michael@0: * PosLookup michael@0: */ michael@0: michael@0: michael@0: struct PosLookupSubTable michael@0: { michael@0: friend struct PosLookup; michael@0: michael@0: enum Type { michael@0: Single = 1, michael@0: Pair = 2, michael@0: Cursive = 3, michael@0: MarkBase = 4, michael@0: MarkLig = 5, michael@0: MarkMark = 6, michael@0: Context = 7, michael@0: ChainContext = 8, michael@0: Extension = 9 michael@0: }; michael@0: michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: switch (lookup_type) { michael@0: case Single: return TRACE_RETURN (u.single.dispatch (c)); michael@0: case Pair: return TRACE_RETURN (u.pair.dispatch (c)); michael@0: case Cursive: return TRACE_RETURN (u.cursive.dispatch (c)); michael@0: case MarkBase: return TRACE_RETURN (u.markBase.dispatch (c)); michael@0: case MarkLig: return TRACE_RETURN (u.markLig.dispatch (c)); michael@0: case MarkMark: return TRACE_RETURN (u.markMark.dispatch (c)); michael@0: case Context: return TRACE_RETURN (u.context.dispatch (c)); michael@0: case ChainContext: return TRACE_RETURN (u.chainContext.dispatch (c)); michael@0: case Extension: return TRACE_RETURN (u.extension.dispatch (c)); michael@0: default: return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { michael@0: TRACE_SANITIZE (this); michael@0: if (!u.header.sub_format.sanitize (c)) michael@0: return TRACE_RETURN (false); michael@0: switch (lookup_type) { michael@0: case Single: return TRACE_RETURN (u.single.sanitize (c)); michael@0: case Pair: return TRACE_RETURN (u.pair.sanitize (c)); michael@0: case Cursive: return TRACE_RETURN (u.cursive.sanitize (c)); michael@0: case MarkBase: return TRACE_RETURN (u.markBase.sanitize (c)); michael@0: case MarkLig: return TRACE_RETURN (u.markLig.sanitize (c)); michael@0: case MarkMark: return TRACE_RETURN (u.markMark.sanitize (c)); michael@0: case Context: return TRACE_RETURN (u.context.sanitize (c)); michael@0: case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); michael@0: case Extension: return TRACE_RETURN (u.extension.sanitize (c)); michael@0: default: return TRACE_RETURN (true); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: union { michael@0: struct { michael@0: USHORT sub_format; michael@0: } header; michael@0: SinglePos single; michael@0: PairPos pair; michael@0: CursivePos cursive; michael@0: MarkBasePos markBase; michael@0: MarkLigPos markLig; michael@0: MarkMarkPos markMark; michael@0: ContextPos context; michael@0: ChainContextPos chainContext; michael@0: ExtensionPos extension; michael@0: } u; michael@0: public: michael@0: DEFINE_SIZE_UNION (2, header.sub_format); michael@0: }; michael@0: michael@0: michael@0: struct PosLookup : Lookup michael@0: { michael@0: inline const PosLookupSubTable& get_subtable (unsigned int i) const michael@0: { return this+CastR > (subTable)[i]; } michael@0: michael@0: inline bool is_reverse (void) const michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const michael@0: { michael@0: TRACE_COLLECT_GLYPHS (this); michael@0: c->set_recurse_func (NULL); michael@0: return TRACE_RETURN (dispatch (c)); michael@0: } michael@0: michael@0: template michael@0: inline void add_coverage (set_t *glyphs) const michael@0: { michael@0: hb_get_coverage_context_t c; michael@0: const Coverage *last = NULL; michael@0: unsigned int count = get_subtable_count (); michael@0: for (unsigned int i = 0; i < count; i++) { michael@0: const Coverage *coverage = &get_subtable (i).dispatch (&c, get_type ()); michael@0: if (coverage != last) { michael@0: coverage->add_coverage (glyphs); michael@0: last = coverage; michael@0: } michael@0: } michael@0: } michael@0: michael@0: inline bool apply_once (hb_apply_context_t *c) const michael@0: { michael@0: TRACE_APPLY (this); michael@0: if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props)) michael@0: return TRACE_RETURN (false); michael@0: return TRACE_RETURN (dispatch (c)); michael@0: } michael@0: michael@0: static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index); michael@0: michael@0: template michael@0: static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); michael@0: michael@0: template michael@0: inline typename context_t::return_t dispatch (context_t *c) const michael@0: { michael@0: TRACE_DISPATCH (this); michael@0: unsigned int lookup_type = get_type (); michael@0: unsigned int count = get_subtable_count (); michael@0: for (unsigned int i = 0; i < count; i++) { michael@0: typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type); michael@0: if (c->stop_sublookup_iteration (r)) michael@0: return TRACE_RETURN (r); michael@0: } michael@0: return TRACE_RETURN (c->default_return_value ()); michael@0: } michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); michael@0: OffsetArrayOf &list = CastR > (subTable); michael@0: return TRACE_RETURN (list.sanitize (c, this, get_type ())); michael@0: } michael@0: }; michael@0: michael@0: typedef OffsetListOf PosLookupList; michael@0: michael@0: /* michael@0: * GPOS -- The Glyph Positioning Table michael@0: */ michael@0: michael@0: struct GPOS : GSUBGPOS michael@0: { michael@0: static const hb_tag_t tableTag = HB_OT_TAG_GPOS; michael@0: michael@0: inline const PosLookup& get_lookup (unsigned int i) const michael@0: { return CastR (GSUBGPOS::get_lookup (i)); } michael@0: michael@0: static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); michael@0: static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer); michael@0: michael@0: inline bool sanitize (hb_sanitize_context_t *c) { michael@0: TRACE_SANITIZE (this); michael@0: if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); michael@0: OffsetTo &list = CastR > (lookupList); michael@0: return TRACE_RETURN (list.sanitize (c, this)); michael@0: } michael@0: public: michael@0: DEFINE_SIZE_STATIC (10); michael@0: }; michael@0: michael@0: michael@0: static void michael@0: fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction) michael@0: { michael@0: unsigned int j = pos[i].cursive_chain(); michael@0: if (likely (!j)) michael@0: return; michael@0: michael@0: j += i; michael@0: michael@0: pos[i].cursive_chain() = 0; michael@0: michael@0: fix_cursive_minor_offset (pos, j, direction); michael@0: michael@0: if (HB_DIRECTION_IS_HORIZONTAL (direction)) michael@0: pos[i].y_offset += pos[j].y_offset; michael@0: else michael@0: pos[i].x_offset += pos[j].x_offset; michael@0: } michael@0: michael@0: static void michael@0: fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction) michael@0: { michael@0: if (likely (!(pos[i].attach_lookback()))) michael@0: return; michael@0: michael@0: unsigned int j = i - pos[i].attach_lookback(); michael@0: michael@0: pos[i].x_offset += pos[j].x_offset; michael@0: pos[i].y_offset += pos[j].y_offset; michael@0: michael@0: if (HB_DIRECTION_IS_FORWARD (direction)) michael@0: for (unsigned int k = j; k < i; k++) { michael@0: pos[i].x_offset -= pos[k].x_advance; michael@0: pos[i].y_offset -= pos[k].y_advance; michael@0: } michael@0: else michael@0: for (unsigned int k = j + 1; k < i + 1; k++) { michael@0: pos[i].x_offset += pos[k].x_advance; michael@0: pos[i].y_offset += pos[k].y_advance; michael@0: } michael@0: } michael@0: michael@0: void michael@0: GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) michael@0: { michael@0: buffer->clear_positions (); michael@0: michael@0: unsigned int count = buffer->len; michael@0: for (unsigned int i = 0; i < count; i++) michael@0: buffer->pos[i].attach_lookback() = buffer->pos[i].cursive_chain() = 0; michael@0: } michael@0: michael@0: void michael@0: GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) michael@0: { michael@0: unsigned int len; michael@0: hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); michael@0: hb_direction_t direction = buffer->props.direction; michael@0: michael@0: /* Handle cursive connections */ michael@0: for (unsigned int i = 0; i < len; i++) michael@0: fix_cursive_minor_offset (pos, i, direction); michael@0: michael@0: /* Handle attachments */ michael@0: for (unsigned int i = 0; i < len; i++) michael@0: fix_mark_attachment (pos, i, direction); michael@0: michael@0: _hb_buffer_deallocate_gsubgpos_vars (buffer); michael@0: } michael@0: michael@0: michael@0: /* Out-of-class implementation for methods recursing */ michael@0: michael@0: template michael@0: inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) michael@0: { michael@0: const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos); michael@0: const PosLookup &l = gpos.get_lookup (lookup_index); michael@0: return l.dispatch (c); michael@0: } michael@0: michael@0: inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) michael@0: { michael@0: const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos); michael@0: const PosLookup &l = gpos.get_lookup (lookup_index); michael@0: unsigned int saved_lookup_props = c->lookup_props; michael@0: c->set_lookup (l); michael@0: bool ret = l.apply_once (c); michael@0: c->lookup_props = saved_lookup_props; michael@0: return ret; michael@0: } michael@0: michael@0: michael@0: #undef attach_lookback michael@0: #undef cursive_chain michael@0: michael@0: michael@0: } /* namespace OT */ michael@0: michael@0: michael@0: #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */