blob: e2057c25610b23b87a8066f03c37e27a070208c7 [file] [log] [blame]
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001/*
2 * Copyright (C) 2007,2008,2009 Red Hat, Inc.
3 *
4 * This is part of HarfBuzz, an OpenType Layout engine library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Red Hat Author(s): Behdad Esfahbod
25 */
26
27#ifndef HB_OT_LAYOUT_GPOS_PRIVATE_H
28#define HB_OT_LAYOUT_GPOS_PRIVATE_H
29
30#include "hb-ot-layout-gsubgpos-private.h"
31
Behdad Esfahbodc968fc22009-05-25 04:04:24 -040032#define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -040033
34/* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
35
36typedef SHORT Value;
37typedef Value ValueRecord[];
38
Behdad Esfahbodfca6a0d2009-05-21 04:49:04 -040039struct ValueFormat : USHORT
40{
41 enum
42 {
43 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
44 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
45 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
46 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
47 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
48 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
49 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
50 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
Behdad Esfahbode4b92b82009-05-26 15:38:53 -040051 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
Behdad Esfahbodfca6a0d2009-05-21 04:49:04 -040052 reserved = 0xF000, /* For future use */
53 };
54
55 inline unsigned int get_len () const
Behdad Esfahbod79420ad2009-05-26 12:24:16 -040056 { return _hb_popcount32 ((unsigned int) *this); }
Behdad Esfahbodfca6a0d2009-05-21 04:49:04 -040057
Behdad Esfahbodc968fc22009-05-25 04:04:24 -040058 const void apply_value (hb_ot_layout_t *layout,
59 const char *base,
60 const Value *values,
61 hb_glyph_position_t *glyph_pos) const
Behdad Esfahbodfca6a0d2009-05-21 04:49:04 -040062 {
63 unsigned int x_ppem, y_ppem;
64 hb_16dot16_t x_scale, y_scale;
65 unsigned int pixel_value;
66 unsigned int format = *this;
67
68 if (!format)
69 return;
70
71 /* All fields are options. Only those available advance the value
72 * pointer. */
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -040073#if 0
74struct ValueRecord {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -040075 SHORT xPlacement; /* Horizontal adjustment for
76 * placement--in design units */
77 SHORT yPlacement; /* Vertical adjustment for
78 * placement--in design units */
79 SHORT xAdvance; /* Horizontal adjustment for
80 * advance--in design units (only used
81 * for horizontal writing) */
82 SHORT yAdvance; /* Vertical adjustment for advance--in
83 * design units (only used for vertical
84 * writing) */
85 Offset xPlaDevice; /* Offset to Device table for
86 * horizontal placement--measured from
87 * beginning of PosTable (may be NULL) */
88 Offset yPlaDevice; /* Offset to Device table for vertical
89 * placement--measured from beginning
90 * of PosTable (may be NULL) */
91 Offset xAdvDevice; /* Offset to Device table for
92 * horizontal advance--measured from
93 * beginning of PosTable (may be NULL) */
94 Offset yAdvDevice; /* Offset to Device table for vertical
95 * advance--measured from beginning of
96 * PosTable (may be NULL) */
97};
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -040098#endif
99
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400100 x_scale = layout->gpos_info.x_scale;
101 y_scale = layout->gpos_info.y_scale;
102 /* design units -> fractional pixel */
103 if (format & xPlacement)
104 glyph_pos->x_pos += x_scale * *(USHORT*)values++ / 0x10000;
105 if (format & yPlacement)
106 glyph_pos->y_pos += y_scale * *(USHORT*)values++ / 0x10000;
107 if (format & xAdvance)
108 glyph_pos->x_advance += x_scale * *(USHORT*)values++ / 0x10000;
109 if (format & yAdvance)
110 glyph_pos->y_advance += y_scale * *(USHORT*)values++ / 0x10000;
111
112 if (HB_LIKELY (!layout->gpos_info.dvi))
113 {
114 x_ppem = layout->gpos_info.x_ppem;
115 y_ppem = layout->gpos_info.y_ppem;
116 /* pixel -> fractional pixel */
117 if (format & xPlaDevice)
118 glyph_pos->x_pos += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 6;
119 if (format & yPlaDevice)
120 glyph_pos->y_pos += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 6;
121 if (format & xAdvDevice)
122 glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 6;
123 if (format & yAdvDevice)
124 glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 6;
125 }
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400126 }
127};
128ASSERT_SIZE (ValueFormat, 2);
129
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400130
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400131struct AnchorFormat1
132{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400133 friend struct Anchor;
134
135 private:
136 inline void get_anchor (hb_ot_layout_t *layout, hb_codepoint_t glyph_id,
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400137 hb_position_t *x, hb_position_t *y) const
138 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400139 *x = layout->gpos_info.x_scale * xCoordinate / 0x10000;
140 *y = layout->gpos_info.y_scale * yCoordinate / 0x10000;
141 }
142
143 private:
144 USHORT format; /* Format identifier--format = 1 */
145 SHORT xCoordinate; /* Horizontal value--in design units */
146 SHORT yCoordinate; /* Vertical value--in design units */
147};
148ASSERT_SIZE (AnchorFormat1, 6);
149
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400150struct AnchorFormat2
151{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400152 friend struct Anchor;
153
154 private:
155 inline void get_anchor (hb_ot_layout_t *layout, hb_codepoint_t glyph_id,
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400156 hb_position_t *x, hb_position_t *y) const
157 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400158 /* TODO Contour */
159 *x = layout->gpos_info.x_scale * xCoordinate / 0x10000;
160 *y = layout->gpos_info.y_scale * yCoordinate / 0x10000;
161 }
162
163 private:
164 USHORT format; /* Format identifier--format = 2 */
165 SHORT xCoordinate; /* Horizontal value--in design units */
166 SHORT yCoordinate; /* Vertical value--in design units */
167 USHORT anchorPoint; /* Index to glyph contour point */
168};
169ASSERT_SIZE (AnchorFormat2, 8);
170
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400171struct AnchorFormat3
172{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400173 friend struct Anchor;
174
175 private:
176 inline void get_anchor (hb_ot_layout_t *layout, hb_codepoint_t glyph_id,
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400177 hb_position_t *x, hb_position_t *y) const
178 {
Behdad Esfahbodc18ec2b2009-05-21 04:54:01 -0400179 *x = layout->gpos_info.x_scale * xCoordinate / 0x10000;
180 *y = layout->gpos_info.y_scale * yCoordinate / 0x10000;
181
182 if (!layout->gpos_info.dvi)
183 {
184 *x += (this+xDeviceTable).get_delta (layout->gpos_info.x_ppem) << 6;
185 *y += (this+yDeviceTable).get_delta (layout->gpos_info.y_ppem) << 6;
186 }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400187 }
188
189 private:
190 USHORT format; /* Format identifier--format = 3 */
191 SHORT xCoordinate; /* Horizontal value--in design units */
192 SHORT yCoordinate; /* Vertical value--in design units */
193 OffsetTo<Device>
194 xDeviceTable; /* Offset to Device table for X
195 * coordinate-- from beginning of
196 * Anchor table (may be NULL) */
197 OffsetTo<Device>
198 yDeviceTable; /* Offset to Device table for Y
199 * coordinate-- from beginning of
200 * Anchor table (may be NULL) */
201};
202ASSERT_SIZE (AnchorFormat3, 10);
203
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400204struct Anchor
205{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400206 inline void get_anchor (hb_ot_layout_t *layout, hb_codepoint_t glyph_id,
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400207 hb_position_t *x, hb_position_t *y) const
208 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400209 *x = *y = 0;
210 switch (u.format) {
211 case 1: u.format1->get_anchor (layout, glyph_id, x, y); return;
212 case 2: u.format2->get_anchor (layout, glyph_id, x, y); return;
213 case 3: u.format3->get_anchor (layout, glyph_id, x, y); return;
214 default: return;
215 }
216 }
217
218 private:
219 union {
220 USHORT format; /* Format identifier */
221 AnchorFormat1 format1[];
222 AnchorFormat2 format2[];
223 AnchorFormat3 format3[];
224 } u;
225};
226ASSERT_SIZE (Anchor, 2);
227
228
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400229struct MarkRecord
230{
Behdad Esfahbod377bfc52009-05-21 04:58:24 -0400231 friend struct MarkArray;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400232
233 private:
234 USHORT klass; /* Class defined for this mark */
235 OffsetTo<Anchor>
236 markAnchor; /* Offset to Anchor table--from
237 * beginning of MarkArray table */
238};
239ASSERT_SIZE (MarkRecord, 4);
240
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400241struct MarkArray
242{
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400243 inline unsigned int get_class (unsigned int index) const { return markRecord[index].klass; }
244 inline const Anchor& get_anchor (unsigned int index) const { return this+markRecord[index].markAnchor; }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400245
246 private:
247 ArrayOf<MarkRecord>
248 markRecord; /* Array of MarkRecords--in Coverage order */
249};
250ASSERT_SIZE (MarkArray, 2);
251
252
253/* Lookups */
254
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400255struct SinglePosFormat1
256{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400257 friend struct SinglePos;
258
259 private:
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400260 inline bool apply (APPLY_ARG_DEF) const
261 {
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400262 unsigned int index = (this+coverage) (IN_CURGLYPH ());
263 if (HB_LIKELY (index == NOT_COVERED))
264 return false;
265
266 valueFormat.apply_value (layout, (const char *) this, values, CURPOSITION ());
267 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400268 }
269
270 private:
271 USHORT format; /* Format identifier--format = 1 */
272 OffsetTo<Coverage>
273 coverage; /* Offset to Coverage table--from
274 * beginning of subtable */
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400275 ValueFormat valueFormat; /* Defines the types of data in the
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400276 * ValueRecord */
277 ValueRecord values; /* Defines positioning
278 * value(s)--applied to all glyphs in
279 * the Coverage table */
280};
281ASSERT_SIZE (SinglePosFormat1, 6);
282
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400283struct SinglePosFormat2
284{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400285 friend struct SinglePos;
286
287 private:
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400288 inline bool apply (APPLY_ARG_DEF) const
289 {
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400290 unsigned int index = (this+coverage) (IN_CURGLYPH ());
291 if (HB_LIKELY (index == NOT_COVERED))
292 return false;
293
294 if (HB_LIKELY (index >= valueCount))
295 return false;
296
297 valueFormat.apply_value (layout, (const char *) this,
298 values + index * valueFormat.get_len (),
299 CURPOSITION ());
300 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400301 }
302
303 private:
304 USHORT format; /* Format identifier--format = 2 */
305 OffsetTo<Coverage>
306 coverage; /* Offset to Coverage table--from
307 * beginning of subtable */
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400308 ValueFormat valueFormat; /* Defines the types of data in the
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400309 * ValueRecord */
310 USHORT valueCount; /* Number of ValueRecords */
311 ValueRecord values; /* Array of ValueRecords--positioning
312 * values applied to glyphs */
313};
314ASSERT_SIZE (SinglePosFormat2, 8);
315
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400316struct SinglePos
317{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400318 friend struct PosLookupSubTable;
319
320 private:
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400321 inline bool apply (APPLY_ARG_DEF) const
322 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400323 switch (u.format) {
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -0400324 case 1: return u.format1->apply (APPLY_ARG);
325 case 2: return u.format2->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400326 default:return false;
327 }
328 }
329
330 private:
331 union {
332 USHORT format; /* Format identifier */
333 SinglePosFormat1 format1[];
334 SinglePosFormat2 format2[];
335 } u;
336};
337ASSERT_SIZE (SinglePos, 2);
338
339
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400340struct PairValueRecord
341{
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400342 friend struct PairPosFormat1;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400343
344 private:
345 GlyphID secondGlyph; /* GlyphID of second glyph in the
346 * pair--first glyph is listed in the
347 * Coverage table */
348 ValueRecord values; /* Positioning data for the first glyph
349 * followed by for second glyph */
350};
351ASSERT_SIZE (PairValueRecord, 2);
352
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400353struct PairSet
354{
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400355 friend struct PairPosFormat1;
356
357 private:
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400358 USHORT len; /* Number of PairValueRecords */
359 /* XXX */
360 PairValueRecord
361 array[]; /* Array of PairValueRecords--ordered
362 * by GlyphID of the second glyph */
363};
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400364ASSERT_SIZE (PairSet, 2);
365
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400366struct PairPosFormat1
367{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400368 friend struct PairPos;
369
370 private:
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400371 inline bool apply (APPLY_ARG_DEF) const
372 {
373 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
374 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
375 return false;
376
377 unsigned int index = (this+coverage) (IN_CURGLYPH ());
378 if (HB_LIKELY (index == NOT_COVERED))
379 return false;
380
381 unsigned int j = buffer->in_pos + 1;
Behdad Esfahbod4189b922009-05-26 17:31:56 -0400382 while (_hb_ot_layout_skip_mark (layout, IN_INFO (j), lookup_flag, NULL))
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400383 {
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400384 if (HB_UNLIKELY (j == end))
385 return false;
386 j++;
387 }
388
389 const PairSet &pair_set = this+pairSet[index];
390
Behdad Esfahbod70632ad2009-05-19 22:30:09 -0400391 unsigned int len1 = valueFormat1.get_len ();
392 unsigned int len2 = valueFormat2.get_len ();
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400393 unsigned int record_len = 1 + len1 + len2;
394
395 unsigned int count = pair_set.len;
396 const PairValueRecord *record = pair_set.array;
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400397 for (unsigned int i = 0; i < count; i++)
398 {
399 if (IN_GLYPH (j) == record->secondGlyph)
400 {
Behdad Esfahbodb24ecba2009-05-19 22:16:04 -0400401 valueFormat1.apply_value (layout, (const char *) this, record->values, CURPOSITION ());
402 valueFormat2.apply_value (layout, (const char *) this, record->values + len1, POSITION (j));
403 if (len2)
404 j++;
405 buffer->in_pos = j;
406 return true;
407 }
408 record += record_len;
409 }
410
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400411 return false;
412 }
413
414 private:
415 USHORT format; /* Format identifier--format = 1 */
416 OffsetTo<Coverage>
417 coverage; /* Offset to Coverage table--from
418 * beginning of subtable */
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400419 ValueFormat valueFormat1; /* Defines the types of data in
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400420 * ValueRecord1--for the first glyph
421 * in the pair--may be zero (0) */
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400422 ValueFormat valueFormat2; /* Defines the types of data in
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400423 * ValueRecord2--for the second glyph
424 * in the pair--may be zero (0) */
425 OffsetArrayOf<PairSet>
426 pairSet; /* Array of PairSet tables
427 * ordered by Coverage Index */
428};
429ASSERT_SIZE (PairPosFormat1, 10);
430
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400431struct PairPosFormat2
432{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400433 friend struct PairPos;
434
435 private:
Behdad Esfahbod70632ad2009-05-19 22:30:09 -0400436 inline bool apply (APPLY_ARG_DEF) const
437 {
438 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
439 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
440 return false;
441
442 unsigned int index = (this+coverage) (IN_CURGLYPH ());
443 if (HB_LIKELY (index == NOT_COVERED))
444 return false;
445
446 unsigned int j = buffer->in_pos + 1;
Behdad Esfahbod4189b922009-05-26 17:31:56 -0400447 while (_hb_ot_layout_skip_mark (layout, IN_INFO (j), lookup_flag, NULL))
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400448 {
Behdad Esfahbod70632ad2009-05-19 22:30:09 -0400449 if (HB_UNLIKELY (j == end))
450 return false;
451 j++;
452 }
453
454 unsigned int len1 = valueFormat1.get_len ();
455 unsigned int len2 = valueFormat2.get_len ();
456 unsigned int record_len = len1 + len2;
457
458 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
459 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
460 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
461 return false;
462
463 const Value *v = values + record_len * (klass1 * class2Count + klass2);
464 valueFormat1.apply_value (layout, (const char *) this, v, CURPOSITION ());
465 valueFormat2.apply_value (layout, (const char *) this, v + len1, POSITION (j));
466
467 if (len2)
468 j++;
469 buffer->in_pos = j;
470
471 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400472 }
473
Behdad Esfahbod70632ad2009-05-19 22:30:09 -0400474
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400475 private:
476 USHORT format; /* Format identifier--format = 2 */
477 OffsetTo<Coverage>
478 coverage; /* Offset to Coverage table--from
479 * beginning of subtable */
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400480 ValueFormat valueFormat1; /* ValueRecord definition--for the
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400481 * first glyph of the pair--may be zero
482 * (0) */
Behdad Esfahbod056c7ec2009-05-18 19:47:52 -0400483 ValueFormat valueFormat2; /* ValueRecord definition--for the
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400484 * second glyph of the pair--may be
485 * zero (0) */
486 OffsetTo<ClassDef>
487 classDef1; /* Offset to ClassDef table--from
488 * beginning of PairPos subtable--for
489 * the first glyph of the pair */
490 OffsetTo<ClassDef>
491 classDef2; /* Offset to ClassDef table--from
492 * beginning of PairPos subtable--for
493 * the second glyph of the pair */
494 USHORT class1Count; /* Number of classes in ClassDef1
495 * table--includes Class0 */
496 USHORT class2Count; /* Number of classes in ClassDef2
497 * table--includes Class0 */
498 ValueRecord values; /* Matrix of value pairs:
499 * class1-major, class2-minor,
500 * Each entry has value1 and value2 */
501};
502ASSERT_SIZE (PairPosFormat2, 16);
503
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400504struct PairPos
505{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400506 friend struct PosLookupSubTable;
507
508 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400509 inline bool apply (APPLY_ARG_DEF) const
510 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400511 switch (u.format) {
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -0400512 case 1: return u.format1->apply (APPLY_ARG);
513 case 2: return u.format2->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400514 default:return false;
515 }
516 }
517
518 private:
519 union {
520 USHORT format; /* Format identifier */
521 PairPosFormat1 format1[];
522 PairPosFormat2 format2[];
523 } u;
524};
525ASSERT_SIZE (PairPos, 2);
526
527
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400528struct EntryExitRecord
529{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400530 OffsetTo<Anchor>
531 entryAnchor; /* Offset to EntryAnchor table--from
532 * beginning of CursivePos
533 * subtable--may be NULL */
534 OffsetTo<Anchor>
535 exitAnchor; /* Offset to ExitAnchor table--from
536 * beginning of CursivePos
537 * subtable--may be NULL */
538};
539ASSERT_SIZE (EntryExitRecord, 4);
540
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400541struct CursivePosFormat1
542{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400543 friend struct CursivePos;
544
545 private:
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400546 inline bool apply (APPLY_ARG_DEF) const
547 {
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400548 /* Now comes the messiest part of the whole OpenType
549 specification. At first glance, cursive connections seem easy
550 to understand, but there are pitfalls! The reason is that
551 the specs don't mention how to compute the advance values
552 resp. glyph offsets. I was told it would be an omission, to
553 be fixed in the next OpenType version... Again many thanks to
554 Andrei Burago <andreib@microsoft.com> for clarifications.
555
556 Consider the following example:
557
558 | xadv1 |
559 +---------+
560 | |
561 +-----+--+ 1 |
562 | | .| |
563 | 0+--+------+
564 | 2 |
565 | |
566 0+--------+
567 | xadv2 |
568
569 glyph1: advance width = 12
570 anchor point = (3,1)
571
572 glyph2: advance width = 11
573 anchor point = (9,4)
574
575 LSB is 1 for both glyphs (so the boxes drawn above are glyph
576 bboxes). Writing direction is R2L; `0' denotes the glyph's
577 coordinate origin.
578
579 Now the surprising part: The advance width of the *left* glyph
580 (resp. of the *bottom* glyph) will be modified, no matter
581 whether the writing direction is L2R or R2L (resp. T2B or
582 B2T)! This assymetry is caused by the fact that the glyph's
583 coordinate origin is always the lower left corner for all
584 writing directions.
585
586 Continuing the above example, we can compute the new
587 (horizontal) advance width of glyph2 as
588
589 9 - 3 = 6 ,
590
591 and the new vertical offset of glyph2 as
592
593 1 - 4 = -3 .
594
595
596 Vertical writing direction is far more complicated:
597
598 a) Assuming that we recompute the advance height of the lower glyph:
599
600 --
601 +---------+
602 -- | |
603 +-----+--+ 1 | yadv1
604 | | .| |
605 yadv2 | 0+--+------+ -- BSB1 --
606 | 2 | -- -- y_offset
607 | |
608 BSB2 -- 0+--------+ --
609 -- --
610
611 glyph1: advance height = 6
612 anchor point = (3,1)
613
614 glyph2: advance height = 7
615 anchor point = (9,4)
616
617 TSB is 1 for both glyphs; writing direction is T2B.
618
619
620 BSB1 = yadv1 - (TSB1 + ymax1)
621 BSB2 = yadv2 - (TSB2 + ymax2)
622 y_offset = y2 - y1
623
624 vertical advance width of glyph2
625 = y_offset + BSB2 - BSB1
626 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
627 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
628 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
629
630
631 b) Assuming that we recompute the advance height of the upper glyph:
632
633 -- --
634 +---------+ -- TSB1
635 -- -- | |
636 TSB2 -- +-----+--+ 1 | yadv1 ymax1
637 | | .| |
638 yadv2 | 0+--+------+ -- --
639 ymax2 | 2 | -- y_offset
640 | |
641 -- 0+--------+ --
642 --
643
644 glyph1: advance height = 6
645 anchor point = (3,1)
646
647 glyph2: advance height = 7
648 anchor point = (9,4)
649
650 TSB is 1 for both glyphs; writing direction is T2B.
651
652 y_offset = y2 - y1
653
654 vertical advance width of glyph2
655 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
656 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
657
658
659 Comparing a) with b) shows that b) is easier to compute. I'll wait
660 for a reply from Andrei to see what should really be implemented...
661
662 Since horizontal advance widths or vertical advance heights
663 can be used alone but not together, no ambiguity occurs. */
664
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400665 struct hb_ot_layout_t::gpos_info_t *gpi = &layout->gpos_info;
666 hb_codepoint_t last_pos = gpi->last;
Behdad Esfahbodc968fc22009-05-25 04:04:24 -0400667 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400668
669 /* We don't handle mark glyphs here. */
670 if (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
671 return false;
672
673 unsigned int index = (this+coverage) (IN_CURGLYPH ());
674 if (HB_LIKELY (index == NOT_COVERED))
675 return false;
676
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400677 const EntryExitRecord &record = entryExitRecord[index];
678
679 hb_position_t entry_x, entry_y, exit_x, exit_y;
680
Behdad Esfahbodc968fc22009-05-25 04:04:24 -0400681 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400682 goto end;
683
684 (this+record.entryAnchor).get_anchor (layout, IN_CURGLYPH (), &entry_x, &entry_y);
685
686 if (gpi->r2l)
687 {
688 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
689 POSITION (buffer->in_pos)->new_advance = TRUE;
690 }
691 else
692 {
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400693 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
694 POSITION (last_pos)->new_advance = TRUE;
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400695 }
696
697 if (lookup_flag & LookupFlag::RightToLeft)
698 {
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400699 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
700 POSITION (last_pos)->y_pos = entry_y - gpi->anchor_y;
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400701 }
702 else
703 {
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400704 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400705 POSITION (buffer->in_pos)->y_pos = gpi->anchor_y - entry_y;
706 }
707
708 end:
709 if (record.exitAnchor)
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400710 {
711 gpi->last = buffer->in_pos;
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400712 (this+record.exitAnchor).get_anchor (layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
Behdad Esfahbod0f7e6b22009-05-20 04:16:35 -0400713 }
Behdad Esfahbodd18fd8e2009-05-19 23:25:41 -0400714
715 buffer->in_pos++;
716 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400717 }
718
719 private:
720 USHORT format; /* Format identifier--format = 1 */
721 OffsetTo<Coverage>
722 coverage; /* Offset to Coverage table--from
723 * beginning of subtable */
724 ArrayOf<EntryExitRecord>
725 entryExitRecord; /* Array of EntryExit records--in
726 * Coverage Index order */
727};
728ASSERT_SIZE (CursivePosFormat1, 6);
729
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400730struct CursivePos
731{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400732 friend struct PosLookupSubTable;
733
734 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400735 inline bool apply (APPLY_ARG_DEF) const
736 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400737 switch (u.format) {
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -0400738 case 1: return u.format1->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400739 default:return false;
740 }
741 }
742
743 private:
744 union {
745 USHORT format; /* Format identifier */
746 CursivePosFormat1 format1[];
747 } u;
748};
749ASSERT_SIZE (CursivePos, 2);
750
751
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400752struct BaseArray
753{
Behdad Esfahbodfb3b5cc2009-05-21 04:47:05 -0400754 friend struct MarkBasePosFormat1;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400755
756 private:
Behdad Esfahbodfb3b5cc2009-05-21 04:47:05 -0400757 USHORT len; /* Number of rows */
758 OffsetTo<Anchor>
759 matrix[]; /* Matrix of offsets to Anchor tables--
760 * from beginning of BaseArray table--
761 * base-major--in order of
762 * BaseCoverage Index--, mark-minor--
763 * ordered by class--zero-based. */
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400764};
765ASSERT_SIZE (BaseArray, 2);
766
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400767struct MarkBasePosFormat1
768{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400769 friend struct MarkBasePos;
770
771 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400772 inline bool apply (APPLY_ARG_DEF) const
773 {
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400774 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
775 if (HB_LIKELY (mark_index == NOT_COVERED))
776 return false;
777
778 /* now we search backwards for a non-mark glyph */
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400779 unsigned int count = buffer->in_pos;
780 unsigned int i = 1, j = count - 1;
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -0400781 while (_hb_ot_layout_skip_mark (layout, IN_INFO (j), LookupFlag::IgnoreMarks, NULL))
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400782 {
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -0400783 if (HB_UNLIKELY (i == count))
784 return false;
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400785 i++, j--;
786 }
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400787
Behdad Esfahbodfe550f42009-05-21 08:27:07 -0400788#if 0
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -0400789 /* The following assertion is too strong. */
790 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400791 return false;
Behdad Esfahbodfe550f42009-05-21 08:27:07 -0400792#endif
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400793
794 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
Behdad Esfahbodfe550f42009-05-21 08:27:07 -0400795 if (base_index == NOT_COVERED)
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400796 return false;
797
798 const MarkArray& mark_array = this+markArray;
799 const BaseArray& base_array = this+baseArray;
800
801 unsigned int mark_class = mark_array.get_class (mark_index);
802 const Anchor& mark_anchor = mark_array.get_anchor (mark_index);
803
804 if (HB_UNLIKELY (mark_class >= classCount || base_index >= base_array.len))
805 return false;
806
807 hb_position_t mark_x, mark_y, base_x, base_y;
808
809 mark_anchor.get_anchor (layout, IN_CURGLYPH (), &mark_x, &mark_y);
810 unsigned int index = base_index * classCount + mark_class;
811 (&base_array+base_array.matrix[index]).get_anchor (layout, IN_GLYPH (j), &base_x, &base_y);
812
Behdad Esfahbodc968fc22009-05-25 04:04:24 -0400813 hb_glyph_position_t *o = POSITION (buffer->in_pos);
Behdad Esfahbod357ccde2009-05-21 06:32:01 -0400814 o->x_pos = base_x - mark_x;
815 o->y_pos = base_y - mark_y;
816 o->x_advance = 0;
817 o->y_advance = 0;
818 o->back = i;
819
820 buffer->in_pos++;
821 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400822 }
823
824 private:
825 USHORT format; /* Format identifier--format = 1 */
Behdad Esfahbodfb3b5cc2009-05-21 04:47:05 -0400826 OffsetTo<Coverage>
827 markCoverage; /* Offset to MarkCoverage table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400828 * beginning of MarkBasePos subtable */
Behdad Esfahbodfb3b5cc2009-05-21 04:47:05 -0400829 OffsetTo<Coverage>
830 baseCoverage; /* Offset to BaseCoverage table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400831 * beginning of MarkBasePos subtable */
832 USHORT classCount; /* Number of classes defined for marks */
Behdad Esfahbodfb3b5cc2009-05-21 04:47:05 -0400833 OffsetTo<MarkArray>
834 markArray; /* Offset to MarkArray table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400835 * beginning of MarkBasePos subtable */
Behdad Esfahbodfb3b5cc2009-05-21 04:47:05 -0400836 OffsetTo<BaseArray>
837 baseArray; /* Offset to BaseArray table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400838 * beginning of MarkBasePos subtable */
839};
840ASSERT_SIZE (MarkBasePosFormat1, 12);
841
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400842struct MarkBasePos
843{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400844 friend struct PosLookupSubTable;
845
846 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400847 inline bool apply (APPLY_ARG_DEF) const
848 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400849 switch (u.format) {
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -0400850 case 1: return u.format1->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400851 default:return false;
852 }
853 }
854
855 private:
856 union {
857 USHORT format; /* Format identifier */
858 MarkBasePosFormat1 format1[];
859 } u;
860};
861ASSERT_SIZE (MarkBasePos, 2);
862
863
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400864struct LigatureAttach
865{
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400866 friend struct MarkLigPosFormat1;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400867
868 private:
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400869 USHORT len; /* Number of ComponentRecords in this
870 * ligature, ie. number of rows */
871 OffsetTo<Anchor>
872 matrix[]; /* Matrix of offsets to Anchor tables--
873 * from beginning of LigatureAttach table--
874 * component-major--in order of
875 * writing direction--, mark-minor--
876 * ordered by class--zero-based. */
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400877};
878ASSERT_SIZE (LigatureAttach, 2);
879
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400880typedef OffsetArrayOf<LigatureAttach> LigatureArray;
881 /* Array of LigatureAttach
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400882 * tables ordered by
883 * LigatureCoverage Index */
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400884ASSERT_SIZE (LigatureArray, 2);
885
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400886struct MarkLigPosFormat1
887{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400888 friend struct MarkLigPos;
889
890 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400891 inline bool apply (APPLY_ARG_DEF) const
892 {
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400893 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
894 if (HB_LIKELY (mark_index == NOT_COVERED))
895 return false;
896
897 /* now we search backwards for a non-mark glyph */
898 unsigned int count = buffer->in_pos;
899 unsigned int i = 1, j = count - 1;
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -0400900 while (_hb_ot_layout_skip_mark (layout, IN_INFO (j), LookupFlag::IgnoreMarks, NULL))
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400901 {
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -0400902 if (HB_UNLIKELY (i == count))
903 return false;
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400904 i++, j--;
905 }
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400906
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400907#if 0
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -0400908 /* The following assertion is too strong. */
909 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400910 return false;
911#endif
912
913 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
914 if (lig_index == NOT_COVERED)
915 return false;
916
917 const MarkArray& mark_array = this+markArray;
918 const LigatureArray& lig_array = this+ligatureArray;
919
920 unsigned int mark_class = mark_array.get_class (mark_index);
921 const Anchor& mark_anchor = mark_array.get_anchor (mark_index);
922
923 if (HB_UNLIKELY (mark_class >= classCount || lig_index >= lig_array.len))
924 return false;
925
926 const LigatureAttach& lig_attach = &lig_array+lig_array[lig_index];
927 count = lig_attach.len;
928 if (HB_UNLIKELY (!count))
929 return false;
930
931 unsigned int comp_index;
932 /* We must now check whether the ligature ID of the current mark glyph
933 * is identical to the ligature ID of the found ligature. If yes, we
934 * can directly use the component index. If not, we attach the mark
935 * glyph to the last component of the ligature. */
936 if (IN_LIGID (j) == IN_LIGID (buffer->in_pos))
937 {
938 comp_index = IN_COMPONENT (buffer->in_pos);
939 if (comp_index >= count)
940 comp_index = count - 1;
941 }
942 else
943 comp_index = count - 1;
944
945 hb_position_t mark_x, mark_y, lig_x, lig_y;
946
947 mark_anchor.get_anchor (layout, IN_CURGLYPH (), &mark_x, &mark_y);
948 unsigned int index = comp_index * classCount + mark_class;
949 (&lig_attach+lig_attach.matrix[index]).get_anchor (layout, IN_GLYPH (j), &lig_x, &lig_y);
950
Behdad Esfahbodc968fc22009-05-25 04:04:24 -0400951 hb_glyph_position_t *o = POSITION (buffer->in_pos);
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400952 o->x_pos = lig_x - mark_x;
953 o->y_pos = lig_y - mark_y;
954 o->x_advance = 0;
955 o->y_advance = 0;
956 o->back = i;
957
958 buffer->in_pos++;
959 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400960 }
961
962 private:
963 USHORT format; /* Format identifier--format = 1 */
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400964 OffsetTo<Coverage>
965 markCoverage; /* Offset to Mark Coverage table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400966 * beginning of MarkLigPos subtable */
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400967 OffsetTo<Coverage>
968 ligatureCoverage; /* Offset to Ligature Coverage
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400969 * table--from beginning of MarkLigPos
970 * subtable */
971 USHORT classCount; /* Number of defined mark classes */
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400972 OffsetTo<MarkArray>
973 markArray; /* Offset to MarkArray table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400974 * beginning of MarkLigPos subtable */
Behdad Esfahbod9b006bc2009-05-22 18:29:45 -0400975 OffsetTo<LigatureArray>
976 ligatureArray; /* Offset to LigatureArray table--from
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400977 * beginning of MarkLigPos subtable */
978};
979ASSERT_SIZE (MarkLigPosFormat1, 12);
980
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400981struct MarkLigPos
982{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400983 friend struct PosLookupSubTable;
984
985 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -0400986 inline bool apply (APPLY_ARG_DEF) const
987 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400988 switch (u.format) {
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -0400989 case 1: return u.format1->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -0400990 default:return false;
991 }
992 }
993
994 private:
995 union {
996 USHORT format; /* Format identifier */
997 MarkLigPosFormat1 format1[];
998 } u;
999};
1000ASSERT_SIZE (MarkLigPos, 2);
1001
1002
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001003struct Mark2Array
1004{
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001005 friend struct MarkMarkPosFormat1;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001006
1007 private:
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001008 USHORT len; /* Number of rows */
1009 OffsetTo<Anchor>
1010 matrix[]; /* Matrix of offsets to Anchor tables--
1011 * from beginning of Mark2Array table--
1012 * mark2-major--in order of
1013 * Mark2Coverage Index--, mark1-minor--
1014 * ordered by class--zero-based. */
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001015};
1016ASSERT_SIZE (Mark2Array, 2);
1017
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001018struct MarkMarkPosFormat1
1019{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001020 friend struct MarkMarkPos;
1021
1022 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001023 inline bool apply (APPLY_ARG_DEF) const
1024 {
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001025 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1026 if (HB_LIKELY (mark1_index == NOT_COVERED))
1027 return false;
1028
1029 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1030 unsigned int count = buffer->in_pos;
1031 unsigned int i = 1, j = count - 1;
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -04001032 while (_hb_ot_layout_skip_mark (layout, IN_INFO (j), lookup_flag, NULL))
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001033 {
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -04001034 if (HB_UNLIKELY (i == count))
1035 return false;
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001036 i++, j--;
1037 }
Behdad Esfahbod80ea5bd2009-05-26 17:58:37 -04001038 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1039 return false;
1040
1041 /* Two marks match only if they belong to the same base, or same component
1042 * of the same ligature. */
1043 if (IN_LIGID (j) != IN_LIGID (buffer->in_pos) ||
1044 IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos))
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001045 return false;
1046
1047 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1048 if (mark2_index == NOT_COVERED)
1049 return false;
1050
1051 const MarkArray& mark1_array = this+mark1Array;
1052 const Mark2Array& mark2_array = this+mark2Array;
1053
1054 unsigned int mark1_class = mark1_array.get_class (mark1_index);
1055 const Anchor& mark1_anchor = mark1_array.get_anchor (mark1_index);
1056
1057 if (HB_UNLIKELY (mark1_class >= classCount || mark2_index >= mark2_array.len))
1058 return false;
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001059
1060 hb_position_t mark1_x, mark1_y, mark2_x, mark2_y;
1061
1062 mark1_anchor.get_anchor (layout, IN_CURGLYPH (), &mark1_x, &mark1_y);
1063 unsigned int index = mark2_index * classCount + mark1_class;
1064 (&mark2_array+mark2_array.matrix[index]).get_anchor (layout, IN_GLYPH (j), &mark2_x, &mark2_y);
1065
Behdad Esfahbodc968fc22009-05-25 04:04:24 -04001066 hb_glyph_position_t *o = POSITION (buffer->in_pos);
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001067 o->x_pos = mark2_x - mark1_x;
1068 o->y_pos = mark2_y - mark1_y;
1069 o->x_advance = 0;
1070 o->y_advance = 0;
1071 o->back = i;
1072
1073 buffer->in_pos++;
1074 return true;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001075 }
1076
1077 private:
1078 USHORT format; /* Format identifier--format = 1 */
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001079 OffsetTo<Coverage>
1080 mark1Coverage; /* Offset to Combining Mark1 Coverage
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001081 * table--from beginning of MarkMarkPos
1082 * subtable */
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001083 OffsetTo<Coverage>
1084 mark2Coverage; /* Offset to Combining Mark2 Coverage
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001085 * table--from beginning of MarkMarkPos
1086 * subtable */
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001087 USHORT classCount; /* Number of defined mark classes */
1088 OffsetTo<MarkArray>
1089 mark1Array; /* Offset to Mark1Array table--from
1090 * beginning of MarkMarkPos subtable */
1091 OffsetTo<Mark2Array>
1092 mark2Array; /* Offset to Mark2Array table--from
1093 * beginning of MarkMarkPos subtable */
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001094};
Behdad Esfahbodfe550f42009-05-21 08:27:07 -04001095ASSERT_SIZE (MarkMarkPosFormat1, 12);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001096
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001097struct MarkMarkPos
1098{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001099 friend struct PosLookupSubTable;
1100
1101 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001102 inline bool apply (APPLY_ARG_DEF) const
1103 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001104 switch (u.format) {
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -04001105 case 1: return u.format1->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001106 default:return false;
1107 }
1108 }
1109
1110 private:
1111 union {
1112 USHORT format; /* Format identifier */
1113 MarkMarkPosFormat1 format1[];
1114 } u;
1115};
1116ASSERT_SIZE (MarkMarkPos, 2);
1117
1118
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -04001119static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001120
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001121struct ContextPos : Context
1122{
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001123 friend struct PosLookupSubTable;
1124
1125 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001126 inline bool apply (APPLY_ARG_DEF) const
Behdad Esfahbod79420ad2009-05-26 12:24:16 -04001127 { return Context::apply (APPLY_ARG, position_lookup); }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001128};
1129ASSERT_SIZE (ContextPos, 2);
1130
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001131struct ChainContextPos : ChainContext
1132{
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001133 friend struct PosLookupSubTable;
1134
1135 private:
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001136 inline bool apply (APPLY_ARG_DEF) const
Behdad Esfahbod79420ad2009-05-26 12:24:16 -04001137 { return ChainContext::apply (APPLY_ARG, position_lookup); }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001138};
1139ASSERT_SIZE (ChainContextPos, 2);
1140
1141
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001142struct ExtensionPos : Extension
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001143{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001144 friend struct PosLookupSubTable;
1145
1146 private:
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001147 inline bool apply (APPLY_ARG_DEF) const;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001148};
1149ASSERT_SIZE (ExtensionPos, 2);
1150
1151
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001152
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001153/*
1154 * PosLookup
1155 */
1156
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001157
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001158struct PosLookupSubTable
1159{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001160 friend struct PosLookup;
1161
Behdad Esfahbodff05d252009-05-20 03:53:00 -04001162 enum {
1163 Single = 1,
1164 Pair = 2,
1165 Cursive = 3,
1166 MarkBase = 4,
1167 MarkLig = 5,
1168 MarkMark = 6,
1169 Context = 7,
1170 ChainContext = 8,
1171 Extension = 9,
1172 };
1173
Behdad Esfahbod923923f2009-05-22 17:58:09 -04001174 bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001175 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001176 switch (lookup_type) {
Behdad Esfahbodff05d252009-05-20 03:53:00 -04001177 case Single: return u.single->apply (APPLY_ARG);
1178 case Pair: return u.pair->apply (APPLY_ARG);
1179 case Cursive: return u.cursive->apply (APPLY_ARG);
1180 case MarkBase: return u.markBase->apply (APPLY_ARG);
1181 case MarkLig: return u.markLig->apply (APPLY_ARG);
1182 case MarkMark: return u.markMark->apply (APPLY_ARG);
1183 case Context: return u.context->apply (APPLY_ARG);
1184 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1185 case Extension: return u.extension->apply (APPLY_ARG);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001186 default:return false;
1187 }
1188 }
1189
1190 private:
1191 union {
Behdad Esfahbodff05d252009-05-20 03:53:00 -04001192 USHORT format;
1193 SinglePos single[];
1194 PairPos pair[];
1195 CursivePos cursive[];
1196 MarkBasePos markBase[];
1197 MarkLigPos markLig[];
1198 MarkMarkPos markMark[];
1199 ContextPos context[];
1200 ChainContextPos chainContext[];
1201 ExtensionPos extension[];
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001202 } u;
1203};
1204ASSERT_SIZE (PosLookupSubTable, 2);
1205
1206
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001207struct PosLookup : Lookup
1208{
1209 inline const PosLookupSubTable& get_subtable (unsigned int i) const
Behdad Esfahbod79420ad2009-05-26 12:24:16 -04001210 { return (const PosLookupSubTable&) Lookup::get_subtable (i); }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001211
1212 /* Like get_type(), but looks through extension lookups.
1213 * Never returns Extension */
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001214 inline unsigned int get_effective_type (void) const
1215 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001216 unsigned int type = get_type ();
1217
Behdad Esfahbodff05d252009-05-20 03:53:00 -04001218 if (HB_UNLIKELY (type == PosLookupSubTable::Extension))
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001219 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001220 unsigned int count = get_subtable_count ();
1221 type = get_subtable(0).u.extension->get_type ();
1222 /* The spec says all subtables should have the same type.
1223 * This is specially important if one has a reverse type! */
1224 for (unsigned int i = 1; i < count; i++)
1225 if (get_subtable(i).u.extension->get_type () != type)
1226 return 0;
1227 }
1228
1229 return type;
1230 }
1231
Behdad Esfahbod923923f2009-05-22 17:58:09 -04001232 inline bool apply_once (hb_ot_layout_t *layout,
1233 hb_buffer_t *buffer,
1234 unsigned int context_length,
1235 unsigned int nesting_level_left) const
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001236 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001237 unsigned int lookup_type = get_type ();
1238 unsigned int lookup_flag = get_flag ();
Behdad Esfahbod923923f2009-05-22 17:58:09 -04001239 unsigned int property;
1240
Behdad Esfahbodc968fc22009-05-25 04:04:24 -04001241 if (!_hb_ot_layout_check_glyph_property (layout, IN_CURINFO (), lookup_flag, &property))
Behdad Esfahbod923923f2009-05-22 17:58:09 -04001242 return false;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001243
1244 for (unsigned int i = 0; i < get_subtable_count (); i++)
Behdad Esfahbodeb0dfc82009-05-18 18:22:44 -04001245 if (get_subtable (i).apply (APPLY_ARG, lookup_type))
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001246 return true;
1247
1248 return false;
1249 }
1250
Behdad Esfahbod2a8e6ac2009-05-18 18:21:44 -04001251 bool apply_string (hb_ot_layout_t *layout,
1252 hb_buffer_t *buffer,
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001253 hb_ot_layout_feature_mask_t mask) const
1254 {
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001255 bool ret = false;
1256
1257 if (HB_UNLIKELY (!buffer->in_length))
1258 return false;
1259
Behdad Esfahbodc968fc22009-05-25 04:04:24 -04001260 layout->gpos_info.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
Behdad Esfahbod9c42f052009-05-18 17:43:49 -04001261
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001262 buffer->in_pos = 0;
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001263 while (buffer->in_pos < buffer->in_length)
1264 {
Behdad Esfahbod9c42f052009-05-18 17:43:49 -04001265 bool done;
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001266 if (~IN_PROPERTIES (buffer->in_pos) & mask)
1267 {
Behdad Esfahbod923923f2009-05-22 17:58:09 -04001268 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
Behdad Esfahbod9c42f052009-05-18 17:43:49 -04001269 ret |= done;
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001270 }
1271 else
1272 {
Behdad Esfahbod9c42f052009-05-18 17:43:49 -04001273 done = false;
1274 /* Contrary to properties defined in GDEF, user-defined properties
1275 will always stop a possible cursive positioning. */
Behdad Esfahbodc968fc22009-05-25 04:04:24 -04001276 layout->gpos_info.last = HB_OT_LAYOUT_GPOS_NO_LAST;
Behdad Esfahbod9c42f052009-05-18 17:43:49 -04001277 }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001278
Behdad Esfahbod9c42f052009-05-18 17:43:49 -04001279 if (!done)
1280 buffer->in_pos++;
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001281 }
1282
1283 return ret;
1284 }
1285};
1286ASSERT_SIZE (PosLookup, 6);
1287
1288
1289/*
1290 * GPOS
1291 */
1292
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001293struct GPOS : GSUBGPOS
1294{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001295 static const hb_tag_t Tag = HB_TAG ('G','P','O','S');
1296
Behdad Esfahbod5876bf12009-05-24 00:53:28 -04001297 static inline const GPOS& get_for_data (const char *data)
Behdad Esfahbod79420ad2009-05-26 12:24:16 -04001298 { return (const GPOS&) GSUBGPOS::get_for_data (data); }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001299
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001300 inline const PosLookup& get_lookup (unsigned int i) const
Behdad Esfahbod79420ad2009-05-26 12:24:16 -04001301 { return (const PosLookup&) GSUBGPOS::get_lookup (i); }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001302
1303 inline bool position_lookup (hb_ot_layout_t *layout,
1304 hb_buffer_t *buffer,
1305 unsigned int lookup_index,
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001306 hb_ot_layout_feature_mask_t mask) const
Behdad Esfahbod79420ad2009-05-26 12:24:16 -04001307 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001308
1309};
1310ASSERT_SIZE (GPOS, 10);
1311
1312
1313/* Out-of-class implementation for methods recursing */
1314
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001315inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001316{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001317 unsigned int lookup_type = get_type ();
1318
Behdad Esfahbodff05d252009-05-20 03:53:00 -04001319 if (HB_UNLIKELY (lookup_type == PosLookupSubTable::Extension))
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001320 return false;
1321
Behdad Esfahbodd468f9a2009-05-21 22:31:33 -04001322 return ((PosLookupSubTable&) get_subtable ()).apply (APPLY_ARG, lookup_type);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001323}
1324
Behdad Esfahbod4c44d832009-05-19 23:42:30 -04001325static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1326{
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001327 const GPOS &gpos = *(layout->gpos);
1328 const PosLookup &l = gpos.get_lookup (lookup_index);
1329
1330 if (HB_UNLIKELY (nesting_level_left == 0))
1331 return false;
1332 nesting_level_left--;
1333
1334 if (HB_UNLIKELY (context_length < 1))
1335 return false;
1336
Behdad Esfahbod923923f2009-05-22 17:58:09 -04001337 return l.apply_once (layout, buffer, context_length, nesting_level_left);
Behdad Esfahbod5e5eb052009-05-18 17:09:33 -04001338}
1339
1340
1341#endif /* HB_OT_LAYOUT_GPOS_PRIVATE_H */