2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * This is part of HarfBuzz, an OpenType Layout engine library.
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.
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
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.
25 #include "harfbuzz-shaper.h"
26 #include "harfbuzz-shaper-private.h"
28 #include "harfbuzz-stream-private.h"
32 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
33 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
35 // -----------------------------------------------------------------------------------------------------
37 // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
39 // -----------------------------------------------------------------------------------------------------
41 /* The Unicode algorithm does in our opinion allow line breaks at some
42 places they shouldn't be allowed. The following changes were thus
43 made in comparison to the Unicode reference:
63 // The following line break classes are not treated by the table:
64 // AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
67 // the first 4 values have to agree with the enum in QCharAttributes
68 ProhibitedBreak
, // PB in table
69 DirectBreak
, // DB in table
70 IndirectBreak
, // IB in table
71 CombiningIndirectBreak
, // CI in table
72 CombiningProhibitedBreak
// CP in table
74 #define DB DirectBreak
75 #define IB IndirectBreak
76 #define CI CombiningIndirectBreak
77 #define CP CombiningProhibitedBreak
78 #define PB ProhibitedBreak
80 static const hb_uint8 breakTable
[HB_LineBreak_JT
+1][HB_LineBreak_JT
+1] =
82 /* OP CL QU GL NS EX SY IS PR PO NU AL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT */
83 /* OP */ { PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, PB
, CP
, PB
, PB
, PB
, PB
, PB
, PB
},
84 /* CL */ { DB
, PB
, IB
, IB
, PB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
85 /* QU */ { PB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, PB
, CI
, PB
, IB
, IB
, IB
, IB
, IB
},
86 /* GL */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, PB
, CI
, PB
, IB
, IB
, IB
, IB
, IB
},
87 /* NS */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, DB
, DB
, DB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
88 /* EX */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, IB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
89 /* SY */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
90 /* IS */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, DB
, IB
, IB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
91 /* PR */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, IB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, IB
, IB
, IB
, IB
, IB
},
92 /* PO */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
93 /* NU */ { IB
, PB
, IB
, IB
, IB
, IB
, PB
, PB
, IB
, IB
, IB
, IB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
94 /* AL */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
95 /* ID */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
96 /* IN */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, DB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
97 /* HY */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, DB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
98 /* BA */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, DB
, DB
, DB
, DB
, DB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
99 /* BB */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, PB
, CI
, PB
, IB
, IB
, IB
, IB
, IB
},
100 /* B2 */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, DB
, DB
, DB
, DB
, DB
, IB
, IB
, DB
, PB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
101 /* ZW */ { DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
, PB
, DB
, DB
, DB
, DB
, DB
, DB
, DB
},
102 /* CM */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, DB
, IB
, IB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, DB
},
103 /* WJ */ { IB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, IB
, PB
, CI
, PB
, IB
, IB
, IB
, IB
, IB
},
104 /* H2 */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, IB
, IB
},
105 /* H3 */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, IB
},
106 /* JL */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, IB
, IB
, IB
, IB
, DB
},
107 /* JV */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, IB
, IB
},
108 /* JT */ { DB
, PB
, IB
, IB
, IB
, PB
, PB
, PB
, DB
, IB
, DB
, DB
, DB
, IB
, IB
, IB
, DB
, DB
, PB
, CI
, PB
, DB
, DB
, DB
, DB
, IB
}
116 static const hb_uint8 graphemeTable
[HB_Grapheme_LVT
+ 1][HB_Grapheme_LVT
+ 1] =
118 // Other, CR, LF, Control,Extend,L, V, T, LV, LVT
119 { true , true , true , true , true , true , true , true , true , true }, // Other,
120 { true , true , true , true , true , true , true , true , true , true }, // CR,
121 { true , false, true , true , true , true , true , true , true , true }, // LF,
122 { true , true , true , true , true , true , true , true , true , true }, // Control,
123 { false, true , true , true , false, false, false, false, false, false }, // Extend,
124 { true , true , true , true , true , false, true , true , true , true }, // L,
125 { true , true , true , true , true , false, false, true , false, true }, // V,
126 { true , true , true , true , true , true , false, false, false, false }, // T,
127 { true , true , true , true , true , false, true , true , true , true }, // LV,
128 { true , true , true , true , true , false, true , true , true , true }, // LVT
131 static void calcLineBreaks(const HB_UChar16
*uc
, hb_uint32 len
, HB_CharAttributes
*charAttributes
)
136 // ##### can this fail if the first char is a surrogate?
137 HB_LineBreakClass cls
;
138 HB_GraphemeClass grapheme
;
139 HB_GetGraphemeAndLineBreakClass(*uc
, &grapheme
, &cls
);
140 // handle case where input starts with an LF
141 if (cls
== HB_LineBreak_LF
)
142 cls
= HB_LineBreak_BK
;
144 charAttributes
[0].whiteSpace
= (cls
== HB_LineBreak_SP
|| cls
== HB_LineBreak_BK
);
145 charAttributes
[0].charStop
= true;
148 for (hb_uint32 i
= 1; i
< len
; ++i
) {
149 charAttributes
[i
].whiteSpace
= false;
150 charAttributes
[i
].charStop
= true;
152 HB_UChar32 code
= uc
[i
];
153 HB_GraphemeClass ngrapheme
;
154 HB_LineBreakClass ncls
;
155 HB_GetGraphemeAndLineBreakClass(code
, &ngrapheme
, &ncls
);
156 charAttributes
[i
].charStop
= graphemeTable
[ngrapheme
][grapheme
];
158 if (ncls
== HB_LineBreak_SG
) {
159 if (HB_IsHighSurrogate(uc
[i
]) && i
< len
- 1 && HB_IsLowSurrogate(uc
[i
+1])) {
161 } else if (HB_IsLowSurrogate(uc
[i
]) && HB_IsHighSurrogate(uc
[i
-1])) {
162 code
= HB_SurrogateToUcs4(uc
[i
-1], uc
[i
]);
163 HB_GetGraphemeAndLineBreakClass(code
, &ngrapheme
, &ncls
);
164 charAttributes
[i
].charStop
= false;
166 ncls
= HB_LineBreak_AL
;
170 // set white space and char stop flag
171 if (ncls
>= HB_LineBreak_SP
)
172 charAttributes
[i
].whiteSpace
= true;
174 HB_LineBreakType lineBreakType
= HB_NoBreak
;
175 if (cls
>= HB_LineBreak_LF
) {
176 lineBreakType
= HB_ForcedBreak
;
177 } else if(cls
== HB_LineBreak_CR
) {
178 lineBreakType
= (ncls
== HB_LineBreak_LF
) ? HB_NoBreak
: HB_ForcedBreak
;
181 if (ncls
== HB_LineBreak_SP
)
182 goto next_no_cls_update
;
183 if (ncls
>= HB_LineBreak_CR
)
188 // for south east asian chars that require a complex (dictionary analysis), the unicode
189 // standard recommends to treat them as AL. thai_attributes and other attribute methods that
190 // do dictionary analysis can override
191 if (tcls
>= HB_LineBreak_SA
)
192 tcls
= HB_LineBreak_AL
;
193 if (cls
>= HB_LineBreak_SA
)
194 cls
= HB_LineBreak_AL
;
196 int brk
= breakTable
[cls
][tcls
];
199 lineBreakType
= HB_Break
;
200 if (uc
[i
-1] == 0xad) // soft hyphen
201 lineBreakType
= HB_SoftHyphen
;
204 lineBreakType
= (lcls
== HB_LineBreak_SP
) ? HB_Break
: HB_NoBreak
;
206 case CombiningIndirectBreak
:
207 lineBreakType
= HB_NoBreak
;
208 if (lcls
== HB_LineBreak_SP
){
210 charAttributes
[i
-2].lineBreakType
= HB_Break
;
212 goto next_no_cls_update
;
215 case CombiningProhibitedBreak
:
216 lineBreakType
= HB_NoBreak
;
217 if (lcls
!= HB_LineBreak_SP
)
218 goto next_no_cls_update
;
219 case ProhibitedBreak
:
228 grapheme
= ngrapheme
;
229 charAttributes
[i
-1].lineBreakType
= lineBreakType
;
231 charAttributes
[len
-1].lineBreakType
= HB_ForcedBreak
;
234 // --------------------------------------------------------------------------------------------------------------------------------------------
238 // --------------------------------------------------------------------------------------------------------------------------------------------
240 static inline void positionCluster(HB_ShaperItem
*item
, int gfrom
, int glast
)
242 int nmarks
= glast
- gfrom
;
245 HB_Glyph
*glyphs
= item
->glyphs
;
246 HB_GlyphAttributes
*attributes
= item
->attributes
;
248 HB_GlyphMetrics baseMetrics
;
249 item
->font
->klass
->getGlyphMetrics(item
->font
, glyphs
[gfrom
], &baseMetrics
);
251 if (item
->item
.script
== HB_Script_Hebrew
252 && (-baseMetrics
.y
) > baseMetrics
.height
)
253 // we need to attach below the baseline, because of the hebrew iud.
254 baseMetrics
.height
= -baseMetrics
.y
;
256 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
257 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
259 HB_Fixed size
= item
->font
->klass
->getFontMetric(item
->font
, HB_FontAscent
) / 10;
260 HB_Fixed offsetBase
= HB_FIXED_CONSTANT(1) + (size
- HB_FIXED_CONSTANT(4)) / 4;
261 if (size
> HB_FIXED_CONSTANT(4))
262 offsetBase
+= HB_FIXED_CONSTANT(4);
265 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
266 // qDebug("offset = %f", offsetBase);
268 bool rightToLeft
= item
->item
.bidiLevel
% 2;
271 unsigned char lastCmb
= 0;
272 HB_GlyphMetrics attachmentRect
;
273 memset(&attachmentRect
, 0, sizeof(attachmentRect
));
275 for(i
= 1; i
<= nmarks
; i
++) {
276 HB_Glyph mark
= glyphs
[gfrom
+i
];
277 HB_GlyphMetrics markMetrics
;
278 item
->font
->klass
->getGlyphMetrics(item
->font
, mark
, &markMetrics
);
281 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
283 HB_Fixed offset
= offsetBase
;
284 unsigned char cmb
= attributes
[gfrom
+i
].combiningClass
;
286 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
287 // bits in the glyphAttributes structure.
289 // fixed position classes. We approximate by mapping to one of the others.
290 // currently I added only the ones for arabic, hebrew, lao and thai.
292 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
294 // add a bit more offset to arabic, a bit hacky
295 if (cmb
>= 27 && cmb
<= 36 && offset
< 3)
298 if ((cmb
>= 10 && cmb
<= 18) ||
299 cmb
== 20 || cmb
== 22 ||
300 cmb
== 29 || cmb
== 32)
301 cmb
= HB_Combining_Below
;
303 else if (cmb
== 23 || cmb
== 27 || cmb
== 28 ||
304 cmb
== 30 || cmb
== 31 || (cmb
>= 33 && cmb
<= 36))
305 cmb
= HB_Combining_Above
;
307 else if (cmb
== 9 || cmb
== 103 || cmb
== 118)
308 cmb
= HB_Combining_BelowRight
;
310 else if (cmb
== 24 || cmb
== 107 || cmb
== 122)
311 cmb
= HB_Combining_AboveRight
;
313 cmb
= HB_Combining_AboveLeft
;
319 // combining marks of different class don't interact. Reset the rectangle.
320 if (cmb
!= lastCmb
) {
321 //qDebug("resetting rect");
322 attachmentRect
= baseMetrics
;
326 case HB_Combining_DoubleBelow
:
327 // ### wrong in rtl context!
328 case HB_Combining_BelowLeft
:
330 case HB_Combining_BelowLeftAttached
:
331 p
.x
+= attachmentRect
.x
- markMetrics
.x
;
332 p
.y
+= (attachmentRect
.y
+ attachmentRect
.height
) - markMetrics
.y
;
334 case HB_Combining_Below
:
336 case HB_Combining_BelowAttached
:
337 p
.x
+= attachmentRect
.x
- markMetrics
.x
;
338 p
.y
+= (attachmentRect
.y
+ attachmentRect
.height
) - markMetrics
.y
;
340 p
.x
+= (attachmentRect
.width
- markMetrics
.width
) / 2;
342 case HB_Combining_BelowRight
:
344 case HB_Combining_BelowRightAttached
:
345 p
.x
+= attachmentRect
.x
+ attachmentRect
.width
- markMetrics
.width
- markMetrics
.x
;
346 p
.y
+= attachmentRect
.y
+ attachmentRect
.height
- markMetrics
.y
;
348 case HB_Combining_Left
:
350 case HB_Combining_LeftAttached
:
352 case HB_Combining_Right
:
354 case HB_Combining_RightAttached
:
356 case HB_Combining_DoubleAbove
:
357 // ### wrong in RTL context!
358 case HB_Combining_AboveLeft
:
360 case HB_Combining_AboveLeftAttached
:
361 p
.x
+= attachmentRect
.x
- markMetrics
.x
;
362 p
.y
+= attachmentRect
.y
- markMetrics
.y
- markMetrics
.height
;
364 case HB_Combining_Above
:
366 case HB_Combining_AboveAttached
:
367 p
.x
+= attachmentRect
.x
- markMetrics
.x
;
368 p
.y
+= attachmentRect
.y
- markMetrics
.y
- markMetrics
.height
;
370 p
.x
+= (attachmentRect
.width
- markMetrics
.width
) / 2;
372 case HB_Combining_AboveRight
:
374 case HB_Combining_AboveRightAttached
:
375 p
.x
+= attachmentRect
.x
+ attachmentRect
.width
- markMetrics
.x
- markMetrics
.width
;
376 p
.y
+= attachmentRect
.y
- markMetrics
.y
- markMetrics
.height
;
379 case HB_Combining_IotaSubscript
:
383 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
384 markMetrics
.x
+= p
.x
;
385 markMetrics
.y
+= p
.y
;
387 HB_GlyphMetrics unitedAttachmentRect
= attachmentRect
;
388 unitedAttachmentRect
.x
= HB_MIN(attachmentRect
.x
, markMetrics
.x
);
389 unitedAttachmentRect
.y
= HB_MIN(attachmentRect
.y
, markMetrics
.y
);
390 unitedAttachmentRect
.width
= HB_MAX(attachmentRect
.x
+ attachmentRect
.width
, markMetrics
.x
+ markMetrics
.width
) - unitedAttachmentRect
.x
;
391 unitedAttachmentRect
.height
= HB_MAX(attachmentRect
.y
+ attachmentRect
.height
, markMetrics
.y
+ markMetrics
.height
) - unitedAttachmentRect
.y
;
392 attachmentRect
= unitedAttachmentRect
;
396 item
->offsets
[gfrom
+i
].x
= p
.x
;
397 item
->offsets
[gfrom
+i
].y
= p
.y
;
399 item
->offsets
[gfrom
+i
].x
= p
.x
- baseMetrics
.xOffset
;
400 item
->offsets
[gfrom
+i
].y
= p
.y
- baseMetrics
.yOffset
;
402 item
->advances
[gfrom
+i
] = 0;
406 void HB_HeuristicPosition(HB_ShaperItem
*item
)
408 HB_GetGlyphAdvances(item
);
409 HB_GlyphAttributes
*attributes
= item
->attributes
;
412 int i
= item
->num_glyphs
;
414 if (cEnd
== -1 && attributes
[i
].mark
) {
416 } else if (cEnd
!= -1 && !attributes
[i
].mark
) {
417 positionCluster(item
, i
, cEnd
);
423 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
424 // and no reordering.
425 // also computes logClusters heuristically
426 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem
*item
)
428 const HB_UChar16
*uc
= item
->string
+ item
->item
.pos
;
429 hb_uint32 length
= item
->item
.length
;
431 // ### zeroWidth and justification are missing here!!!!!
433 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
434 HB_GlyphAttributes
*attributes
= item
->attributes
;
435 unsigned short *logClusters
= item
->log_clusters
;
437 hb_uint32 glyph_pos
= 0;
439 for (i
= 0; i
< length
; i
++) {
440 if (HB_IsHighSurrogate(uc
[i
]) && i
< length
- 1
441 && HB_IsLowSurrogate(uc
[i
+ 1])) {
442 logClusters
[i
] = glyph_pos
;
443 logClusters
[++i
] = glyph_pos
;
445 logClusters
[i
] = glyph_pos
;
450 // first char in a run is never (treated as) a mark
452 const bool symbolFont
= item
->face
->isSymbolFont
;
453 attributes
[0].mark
= false;
454 attributes
[0].clusterStart
= true;
455 attributes
[0].dontPrint
= (!symbolFont
&& uc
[0] == 0x00ad) || HB_IsControlChar(uc
[0]);
458 HB_CharCategory lastCat
;
460 HB_GetUnicodeCharProperties(uc
[0], &lastCat
, &dummy
);
461 for (i
= 1; i
< length
; ++i
) {
462 if (logClusters
[i
] == pos
)
466 while (pos
< logClusters
[i
]) {
467 attributes
[pos
] = attributes
[pos
-1];
470 // hide soft-hyphens by default
471 if ((!symbolFont
&& uc
[i
] == 0x00ad) || HB_IsControlChar(uc
[i
]))
472 attributes
[pos
].dontPrint
= true;
475 HB_GetUnicodeCharProperties(uc
[i
], &cat
, &cmb
);
476 if (cat
!= HB_Mark_NonSpacing
) {
477 attributes
[pos
].mark
= false;
478 attributes
[pos
].clusterStart
= true;
479 attributes
[pos
].combiningClass
= 0;
480 cStart
= logClusters
[i
];
483 // Fix 0 combining classes
484 if ((uc
[pos
] & 0xff00) == 0x0e00) {
486 if (uc
[pos
] == 0xe31 ||
495 cmb
= HB_Combining_AboveRight
;
496 } else if (uc
[pos
] == 0xeb1 ||
504 cmb
= HB_Combining_Above
;
505 } else if (uc
[pos
] == 0xebc) {
506 cmb
= HB_Combining_Below
;
511 attributes
[pos
].mark
= true;
512 attributes
[pos
].clusterStart
= false;
513 attributes
[pos
].combiningClass
= cmb
;
514 logClusters
[i
] = cStart
;
516 // one gets an inter character justification point if the current char is not a non spacing mark.
517 // as then the current char belongs to the last one and one gets a space justification point
518 // after the space char.
519 if (lastCat
== HB_Separator_Space
)
520 attributes
[pos
-1].justification
= HB_Space
;
521 else if (cat
!= HB_Mark_NonSpacing
)
522 attributes
[pos
-1].justification
= HB_Character
;
524 attributes
[pos
-1].justification
= HB_NoJustification
;
528 pos
= logClusters
[length
-1];
529 if (lastCat
== HB_Separator_Space
)
530 attributes
[pos
].justification
= HB_Space
;
532 attributes
[pos
].justification
= HB_Character
;
536 static const HB_OpenTypeFeature basic_features
[] = {
537 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty
},
538 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty
},
539 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty
},
544 HB_Bool
HB_ConvertStringToGlyphIndices(HB_ShaperItem
*shaper_item
)
546 if (shaper_item
->glyphIndicesPresent
) {
547 shaper_item
->num_glyphs
= shaper_item
->initialGlyphCount
;
548 shaper_item
->glyphIndicesPresent
= false;
551 return shaper_item
->font
->klass
552 ->convertStringToGlyphIndices(shaper_item
->font
,
553 shaper_item
->string
+ shaper_item
->item
.pos
, shaper_item
->item
.length
,
554 shaper_item
->glyphs
, &shaper_item
->num_glyphs
,
555 shaper_item
->item
.bidiLevel
% 2);
558 HB_Bool
HB_BasicShape(HB_ShaperItem
*shaper_item
)
561 const int availableGlyphs
= shaper_item
->num_glyphs
;
564 if (!HB_ConvertStringToGlyphIndices(shaper_item
))
567 HB_HeuristicSetGlyphAttributes(shaper_item
);
570 if (HB_SelectScript(shaper_item
, basic_features
)) {
571 HB_OpenTypeShape(shaper_item
, /*properties*/0);
572 return HB_OpenTypePosition(shaper_item
, availableGlyphs
, /*doLogClusters*/true);
576 HB_HeuristicPosition(shaper_item
);
580 const HB_ScriptEngine HB_ScriptEngines
[] = {
590 { HB_HebrewShape
, 0 },
592 { HB_ArabicShape
, 0},
594 { HB_ArabicShape
, 0},
596 { HB_BasicShape
, 0 },
598 { HB_IndicShape
, HB_IndicAttributes
},
600 { HB_IndicShape
, HB_IndicAttributes
},
602 { HB_IndicShape
, HB_IndicAttributes
},
604 { HB_IndicShape
, HB_IndicAttributes
},
606 { HB_IndicShape
, HB_IndicAttributes
},
608 { HB_IndicShape
, HB_IndicAttributes
},
610 { HB_IndicShape
, HB_IndicAttributes
},
612 { HB_IndicShape
, HB_IndicAttributes
},
614 { HB_IndicShape
, HB_IndicAttributes
},
616 { HB_IndicShape
, HB_IndicAttributes
},
618 { HB_BasicShape
, HB_ThaiAttributes
},
620 { HB_BasicShape
, 0 },
622 { HB_TibetanShape
, HB_TibetanAttributes
},
624 { HB_MyanmarShape
, HB_MyanmarAttributes
},
626 { HB_BasicShape
, 0 },
628 { HB_HangulShape
, 0 },
630 { HB_BasicShape
, 0 },
632 { HB_BasicShape
, 0 },
634 { HB_KhmerShape
, HB_KhmerAttributes
},
639 void HB_GetCharAttributes(const HB_UChar16
*string
, hb_uint32 stringLength
,
640 const HB_ScriptItem
*items
, hb_uint32 numItems
,
641 HB_CharAttributes
*attributes
)
643 calcLineBreaks(string
, stringLength
, attributes
);
645 for (hb_uint32 i
= 0; i
< numItems
; ++i
) {
646 HB_Script script
= items
[i
].script
;
647 if (script
== HB_Script_Inherited
)
648 script
= HB_Script_Common
;
649 HB_AttributeFunction attributeFunction
= HB_ScriptEngines
[script
].charAttributes
;
650 if (!attributeFunction
)
652 attributeFunction(script
, string
, items
[i
].pos
, items
[i
].length
, attributes
);
657 enum BreakRule
{ NoBreak
= 0, Break
= 1, Middle
= 2 };
659 static const hb_uint8 wordbreakTable
[HB_Word_ExtendNumLet
+ 1][HB_Word_ExtendNumLet
+ 1] = {
660 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
661 { Break
, Break
, Break
, Break
, Break
, Break
, Break
, Break
}, // Other
662 { Break
, Break
, Break
, Break
, Break
, Break
, Break
, Break
}, // Format
663 { Break
, Break
, NoBreak
, Break
, Break
, Break
, Break
, NoBreak
}, // Katakana
664 { Break
, Break
, Break
, NoBreak
, Middle
, Break
, NoBreak
, NoBreak
}, // ALetter
665 { Break
, Break
, Break
, Break
, Break
, Break
, Break
, Break
}, // MidLetter
666 { Break
, Break
, Break
, Break
, Break
, Break
, Break
, Break
}, // MidNum
667 { Break
, Break
, Break
, NoBreak
, Break
, Middle
, NoBreak
, NoBreak
}, // Numeric
668 { Break
, Break
, NoBreak
, NoBreak
, Break
, Break
, NoBreak
, NoBreak
}, // ExtendNumLet
671 void HB_GetWordBoundaries(const HB_UChar16
*string
, hb_uint32 stringLength
,
672 const HB_ScriptItem
* /*items*/, hb_uint32
/*numItems*/,
673 HB_CharAttributes
*attributes
)
675 if (stringLength
== 0)
677 unsigned int brk
= HB_GetWordClass(string
[0]);
678 attributes
[0].wordBoundary
= true;
679 for (hb_uint32 i
= 1; i
< stringLength
; ++i
) {
680 if (!attributes
[i
].charStop
) {
681 attributes
[i
].wordBoundary
= false;
684 hb_uint32 nbrk
= HB_GetWordClass(string
[i
]);
685 if (nbrk
== HB_Word_Format
) {
686 attributes
[i
].wordBoundary
= (HB_GetSentenceClass(string
[i
-1]) == HB_Sentence_Sep
);
689 BreakRule rule
= (BreakRule
)wordbreakTable
[brk
][nbrk
];
690 if (rule
== Middle
) {
692 hb_uint32 lookahead
= i
+ 1;
693 while (lookahead
< stringLength
) {
694 hb_uint32 testbrk
= HB_GetWordClass(string
[lookahead
]);
695 if (testbrk
== HB_Word_Format
&& HB_GetSentenceClass(string
[lookahead
]) != HB_Sentence_Sep
) {
699 if (testbrk
== brk
) {
701 while (i
< lookahead
)
702 attributes
[i
++].wordBoundary
= false;
708 attributes
[i
].wordBoundary
= (rule
== Break
);
714 enum SentenceBreakStates
{
729 static const hb_uint8 sentenceBreakTable
[HB_Sentence_Close
+ 1][HB_Sentence_Close
+ 1] = {
730 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
731 { SB_Initial
, SB_BAfter
, SB_Initial
, SB_Initial
, SB_Initial
, SB_Upper
, SB_Initial
, SB_Initial
, SB_ATerm
, SB_STerm
, SB_Initial
}, // SB_Initial,
732 { SB_Initial
, SB_BAfter
, SB_Upper
, SB_Initial
, SB_Initial
, SB_Upper
, SB_Initial
, SB_Initial
, SB_UpATerm
, SB_STerm
, SB_Initial
}, // SB_Upper
734 { SB_Look
, SB_BAfter
, SB_UpATerm
, SB_ACS
, SB_Initial
, SB_Upper
, SB_Break
, SB_Initial
, SB_ATerm
, SB_STerm
, SB_ATermC
}, // SB_UpATerm
735 { SB_Look
, SB_BAfter
, SB_ATerm
, SB_ACS
, SB_Initial
, SB_Break
, SB_Break
, SB_Initial
, SB_ATerm
, SB_STerm
, SB_ATermC
}, // SB_ATerm
736 { SB_Look
, SB_BAfter
, SB_ATermC
, SB_ACS
, SB_Initial
, SB_Break
, SB_Break
, SB_Look
, SB_ATerm
, SB_STerm
, SB_ATermC
}, // SB_ATermC,
737 { SB_Look
, SB_BAfter
, SB_ACS
, SB_ACS
, SB_Initial
, SB_Break
, SB_Break
, SB_Look
, SB_ATerm
, SB_STerm
, SB_Look
}, // SB_ACS,
739 { SB_Break
, SB_BAfter
, SB_STerm
, SB_SCS
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_ATerm
, SB_STerm
, SB_STermC
}, // SB_STerm,
740 { SB_Break
, SB_BAfter
, SB_STermC
, SB_SCS
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_ATerm
, SB_STerm
, SB_STermC
}, // SB_STermC,
741 { SB_Break
, SB_BAfter
, SB_SCS
, SB_SCS
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_ATerm
, SB_STerm
, SB_Break
}, // SB_SCS,
742 { SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
, SB_Break
}, // SB_BAfter,
745 void HB_GetSentenceBoundaries(const HB_UChar16
*string
, hb_uint32 stringLength
,
746 const HB_ScriptItem
* /*items*/, hb_uint32
/*numItems*/,
747 HB_CharAttributes
*attributes
)
749 if (stringLength
== 0)
751 hb_uint32 brk
= sentenceBreakTable
[SB_Initial
][HB_GetSentenceClass(string
[0])];
752 attributes
[0].sentenceBoundary
= true;
753 for (hb_uint32 i
= 1; i
< stringLength
; ++i
) {
754 if (!attributes
[i
].charStop
) {
755 attributes
[i
].sentenceBoundary
= false;
758 brk
= sentenceBreakTable
[brk
][HB_GetSentenceClass(string
[i
])];
759 if (brk
== SB_Look
) {
761 hb_uint32 lookahead
= i
+ 1;
762 while (lookahead
< stringLength
) {
763 hb_uint32 sbrk
= HB_GetSentenceClass(string
[lookahead
]);
764 if (sbrk
!= HB_Sentence_Other
&& sbrk
!= HB_Sentence_Numeric
&& sbrk
!= HB_Sentence_Close
) {
766 } else if (sbrk
== HB_Sentence_Lower
) {
772 if (brk
== SB_Initial
) {
773 while (i
< lookahead
)
774 attributes
[i
++].sentenceBoundary
= false;
777 if (brk
== SB_Break
) {
778 attributes
[i
].sentenceBoundary
= true;
779 brk
= sentenceBreakTable
[SB_Initial
][HB_GetSentenceClass(string
[i
])];
781 attributes
[i
].sentenceBoundary
= false;
787 static inline char *tag_to_string(HB_UInt tag
)
789 static char string
[5];
790 string
[0] = (tag
>> 24)&0xff;
791 string
[1] = (tag
>> 16)&0xff;
792 string
[2] = (tag
>> 8)&0xff;
793 string
[3] = tag
&0xff;
799 static void dump_string(HB_Buffer buffer
)
801 for (uint i
= 0; i
< buffer
->in_length
; ++i
) {
802 qDebug(" %x: cluster=%d", buffer
->in_string
[i
].gindex
, buffer
->in_string
[i
].cluster
);
807 #define DEBUG if (1) ; else printf
810 #define DefaultLangSys 0xffff
811 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
822 static const OTScripts ot_scripts
[] = {
824 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
826 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
828 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
830 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
832 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
834 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
836 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
838 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
840 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
842 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
844 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
846 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
848 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
850 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
852 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
854 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
856 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
858 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
860 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
862 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
864 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
866 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
868 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
870 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
872 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
874 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
876 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
878 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
880 enum { NumOTScripts
= sizeof(ot_scripts
)/sizeof(OTScripts
) };
882 static HB_Bool
checkScript(HB_Face face
, int script
)
884 assert(script
< HB_ScriptCount
);
886 if (!face
->gsub
&& !face
->gpos
)
889 unsigned int tag
= ot_scripts
[script
].tag
;
890 int requirements
= ot_scripts
[script
].flags
;
892 if (requirements
& RequiresGsub
) {
896 HB_UShort script_index
;
897 HB_Error error
= HB_GSUB_Select_Script(face
->gsub
, tag
, &script_index
);
899 DEBUG("could not select script %d in GSub table: %d", (int)script
, error
);
900 error
= HB_GSUB_Select_Script(face
->gsub
, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index
);
906 if (requirements
& RequiresGpos
) {
910 HB_UShort script_index
;
911 HB_Error error
= HB_GPOS_Select_Script(face
->gpos
, script
, &script_index
);
913 DEBUG("could not select script in gpos table: %d", error
);
914 error
= HB_GPOS_Select_Script(face
->gpos
, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index
);
923 static HB_Stream
getTableStream(void *font
, HB_GetFontTableFunc tableFunc
, HB_Tag tag
)
927 HB_Stream stream
= 0;
932 error
= tableFunc(font
, tag
, 0, &length
);
935 stream
= (HB_Stream
)malloc(sizeof(HB_StreamRec
));
938 stream
->base
= (HB_Byte
*)malloc(length
);
943 error
= tableFunc(font
, tag
, stream
->base
, &length
);
945 _hb_close_stream(stream
);
948 stream
->size
= length
;
950 stream
->cursor
= NULL
;
954 HB_Face
HB_NewFace(void *font
, HB_GetFontTableFunc tableFunc
)
956 HB_Face face
= (HB_Face
)malloc(sizeof(HB_FaceRec
));
960 face
->isSymbolFont
= false;
964 face
->current_script
= HB_ScriptCount
;
965 face
->current_flags
= HB_ShaperFlag_Default
;
966 face
->has_opentype_kerning
= false;
967 face
->tmpAttributes
= 0;
968 face
->tmpLogClusters
= 0;
969 face
->glyphs_substituted
= false;
972 HB_Error error
= HB_Err_Ok
;
974 HB_Stream gdefStream
;
976 gdefStream
= getTableStream(font
, tableFunc
, TTAG_GDEF
);
977 error
= HB_Err_Not_Covered
;
978 if (!gdefStream
|| (error
= HB_Load_GDEF_Table(gdefStream
, &face
->gdef
))) {
979 //DEBUG("error loading gdef table: %d", error);
983 //DEBUG() << "trying to load gsub table";
984 stream
= getTableStream(font
, tableFunc
, TTAG_GSUB
);
985 error
= HB_Err_Not_Covered
;
986 if (!stream
|| (error
= HB_Load_GSUB_Table(stream
, &face
->gsub
, face
->gdef
, gdefStream
))) {
988 if (error
!= HB_Err_Not_Covered
) {
989 //DEBUG("error loading gsub table: %d", error);
991 //DEBUG("face doesn't have a gsub table");
994 _hb_close_stream(stream
);
996 stream
= getTableStream(font
, tableFunc
, TTAG_GPOS
);
997 error
= HB_Err_Not_Covered
;
998 if (!stream
|| (error
= HB_Load_GPOS_Table(stream
, &face
->gpos
, face
->gdef
, gdefStream
))) {
1000 DEBUG("error loading gpos table: %d", error
);
1002 _hb_close_stream(stream
);
1004 _hb_close_stream(gdefStream
);
1006 for (unsigned int i
= 0; i
< HB_ScriptCount
; ++i
)
1007 face
->supported_scripts
[i
] = checkScript(face
, i
);
1009 if (hb_buffer_new(&face
->buffer
) != HB_Err_Ok
) {
1017 void HB_FreeFace(HB_Face face
)
1022 HB_Done_GPOS_Table(face
->gpos
);
1024 HB_Done_GSUB_Table(face
->gsub
);
1026 HB_Done_GDEF_Table(face
->gdef
);
1028 hb_buffer_free(face
->buffer
);
1029 if (face
->tmpAttributes
)
1030 free(face
->tmpAttributes
);
1031 if (face
->tmpLogClusters
)
1032 free(face
->tmpLogClusters
);
1036 HB_Bool
HB_SelectScript(HB_ShaperItem
*shaper_item
, const HB_OpenTypeFeature
*features
)
1038 HB_Script script
= shaper_item
->item
.script
;
1040 if (!shaper_item
->face
->supported_scripts
[script
])
1043 HB_Face face
= shaper_item
->face
;
1044 if (face
->current_script
== script
&& face
->current_flags
== shaper_item
->shaperFlags
)
1047 face
->current_script
= script
;
1048 face
->current_flags
= shaper_item
->shaperFlags
;
1050 assert(script
< HB_ScriptCount
);
1051 // find script in our list of supported scripts.
1052 unsigned int tag
= ot_scripts
[script
].tag
;
1054 if (face
->gsub
&& features
) {
1057 HB_FeatureList featurelist
= face
->gsub
->FeatureList
;
1058 int numfeatures
= featurelist
.FeatureCount
;
1059 DEBUG("gsub table has %d features", numfeatures
);
1060 for (int i
= 0; i
< numfeatures
; i
++) {
1061 HB_FeatureRecord
*r
= featurelist
.FeatureRecord
+ i
;
1062 DEBUG(" feature '%s'", tag_to_string(r
->FeatureTag
));
1066 HB_GSUB_Clear_Features(face
->gsub
);
1067 HB_UShort script_index
;
1068 HB_Error error
= HB_GSUB_Select_Script(face
->gsub
, tag
, &script_index
);
1070 DEBUG("script %s has script index %d", tag_to_string(script
), script_index
);
1071 while (features
->tag
) {
1072 HB_UShort feature_index
;
1073 error
= HB_GSUB_Select_Feature(face
->gsub
, features
->tag
, script_index
, 0xffff, &feature_index
);
1075 DEBUG(" adding feature %s", tag_to_string(features
->tag
));
1076 HB_GSUB_Add_Feature(face
->gsub
, feature_index
, features
->property
);
1084 face
->has_opentype_kerning
= false;
1087 HB_GPOS_Clear_Features(face
->gpos
);
1088 HB_UShort script_index
;
1089 HB_Error error
= HB_GPOS_Select_Script(face
->gpos
, tag
, &script_index
);
1093 HB_FeatureList featurelist
= face
->gpos
->FeatureList
;
1094 int numfeatures
= featurelist
.FeatureCount
;
1095 DEBUG("gpos table has %d features", numfeatures
);
1096 for(int i
= 0; i
< numfeatures
; i
++) {
1097 HB_FeatureRecord
*r
= featurelist
.FeatureRecord
+ i
;
1098 HB_UShort feature_index
;
1099 HB_GPOS_Select_Feature(face
->gpos
, r
->FeatureTag
, script_index
, 0xffff, &feature_index
);
1100 DEBUG(" feature '%s'", tag_to_string(r
->FeatureTag
));
1104 HB_UInt
*feature_tag_list_buffer
;
1105 error
= HB_GPOS_Query_Features(face
->gpos
, script_index
, 0xffff, &feature_tag_list_buffer
);
1107 HB_UInt
*feature_tag_list
= feature_tag_list_buffer
;
1108 while (*feature_tag_list
) {
1109 HB_UShort feature_index
;
1110 if (*feature_tag_list
== HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1111 if (face
->current_flags
& HB_ShaperFlag_NoKerning
) {
1115 face
->has_opentype_kerning
= true;
1117 error
= HB_GPOS_Select_Feature(face
->gpos
, *feature_tag_list
, script_index
, 0xffff, &feature_index
);
1119 HB_GPOS_Add_Feature(face
->gpos
, feature_index
, PositioningProperties
);
1122 FREE(feature_tag_list_buffer
);
1130 HB_Bool
HB_OpenTypeShape(HB_ShaperItem
*item
, const hb_uint32
*properties
)
1132 HB_GlyphAttributes
*tmpAttributes
;
1133 unsigned int *tmpLogClusters
;
1135 HB_Face face
= item
->face
;
1137 face
->length
= item
->num_glyphs
;
1139 hb_buffer_clear(face
->buffer
);
1141 tmpAttributes
= (HB_GlyphAttributes
*) realloc(face
->tmpAttributes
, face
->length
*sizeof(HB_GlyphAttributes
));
1144 face
->tmpAttributes
= tmpAttributes
;
1146 tmpLogClusters
= (unsigned int *) realloc(face
->tmpLogClusters
, face
->length
*sizeof(unsigned int));
1147 if (!tmpLogClusters
)
1149 face
->tmpLogClusters
= tmpLogClusters
;
1151 const int itemLength
= item
->item
.length
;
1152 assert(itemLength
> 0);
1153 for (int i
= 0; i
< face
->length
; ++i
) {
1154 hb_buffer_add_glyph(face
->buffer
, item
->glyphs
[i
], properties
? properties
[i
] : 0, i
);
1155 face
->tmpAttributes
[i
] = item
->attributes
[i
];
1156 face
->tmpLogClusters
[i
] = i
< itemLength
? item
->log_clusters
[i
] : item
->log_clusters
[itemLength
- 1];
1160 DEBUG("-----------------------------------------");
1161 // DEBUG("log clusters before shaping:");
1162 // for (int j = 0; j < length; j++)
1163 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1164 DEBUG("original glyphs: %p", item
->glyphs
);
1165 for (int i
= 0; i
< length
; ++i
)
1166 DEBUG(" glyph=%4x", hb_buffer
->in_string
[i
].gindex
);
1167 // dump_string(hb_buffer);
1170 face
->glyphs_substituted
= false;
1172 unsigned int error
= HB_GSUB_Apply_String(face
->gsub
, face
->buffer
);
1173 if (error
&& error
!= HB_Err_Not_Covered
)
1175 face
->glyphs_substituted
= (error
!= HB_Err_Not_Covered
);
1179 // DEBUG("log clusters before shaping:");
1180 // for (int j = 0; j < length; j++)
1181 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1182 DEBUG("shaped glyphs:");
1183 for (int i
= 0; i
< length
; ++i
)
1184 DEBUG(" glyph=%4x", hb_buffer
->in_string
[i
].gindex
);
1185 DEBUG("-----------------------------------------");
1186 // dump_string(hb_buffer);
1192 /* See comments near the definition of HB_ShaperFlag_ForceMarksToZeroWidth for a description
1193 of why this function exists. */
1194 void HB_FixupZeroWidth(HB_ShaperItem
*item
)
1198 if (!item
->face
->gdef
)
1201 for (unsigned int i
= 0; i
< item
->num_glyphs
; ++i
) {
1202 /* If the glyph is a mark, force its advance to zero. */
1203 if (HB_GDEF_Get_Glyph_Property (item
->face
->gdef
, item
->glyphs
[i
], &property
) == HB_Err_Ok
&&
1204 property
== HB_GDEF_MARK
) {
1205 item
->advances
[i
] = 0;
1210 HB_Bool
HB_OpenTypePosition(HB_ShaperItem
*item
, int availableGlyphs
, HB_Bool doLogClusters
)
1212 HB_Face face
= item
->face
;
1214 bool glyphs_positioned
= false;
1216 if (face
->buffer
->positions
)
1217 memset(face
->buffer
->positions
, 0, face
->buffer
->in_length
*sizeof(HB_PositionRec
));
1218 // #### check that passing "false,false" is correct
1219 glyphs_positioned
= HB_GPOS_Apply_String(item
->font
, face
->gpos
, face
->current_flags
, face
->buffer
, false, false) != HB_Err_Not_Covered
;
1222 if (!face
->glyphs_substituted
&& !glyphs_positioned
) {
1223 HB_GetGlyphAdvances(item
);
1224 if (item
->face
->current_flags
& HB_ShaperFlag_ForceMarksToZeroWidth
)
1225 HB_FixupZeroWidth(item
);
1226 return true; // nothing to do for us
1229 // make sure we have enough space to write everything back
1230 if (availableGlyphs
< (int)face
->buffer
->in_length
) {
1231 item
->num_glyphs
= face
->buffer
->in_length
;
1235 HB_Glyph
*glyphs
= item
->glyphs
;
1236 HB_GlyphAttributes
*attributes
= item
->attributes
;
1238 for (unsigned int i
= 0; i
< face
->buffer
->in_length
; ++i
) {
1239 glyphs
[i
] = face
->buffer
->in_string
[i
].gindex
;
1240 attributes
[i
] = face
->tmpAttributes
[face
->buffer
->in_string
[i
].cluster
];
1241 if (i
&& face
->buffer
->in_string
[i
].cluster
== face
->buffer
->in_string
[i
-1].cluster
)
1242 attributes
[i
].clusterStart
= false;
1244 item
->num_glyphs
= face
->buffer
->in_length
;
1246 if (doLogClusters
&& face
->glyphs_substituted
) {
1247 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1248 unsigned short *logClusters
= item
->log_clusters
;
1249 int clusterStart
= 0;
1251 // #### the reconstruction of the logclusters currently does not work if the original string
1252 // contains surrogate pairs
1253 for (unsigned int i
= 0; i
< face
->buffer
->in_length
; ++i
) {
1254 int ci
= face
->buffer
->in_string
[i
].cluster
;
1255 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1256 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1257 if (!attributes
[i
].mark
&& attributes
[i
].clusterStart
&& ci
!= oldCi
) {
1258 for (int j
= oldCi
; j
< ci
; j
++)
1259 logClusters
[j
] = clusterStart
;
1264 for (int j
= oldCi
; j
< face
->length
; j
++)
1265 logClusters
[j
] = clusterStart
;
1268 // calulate the advances for the shaped glyphs
1269 // DEBUG("unpositioned: ");
1271 // positioning code:
1272 if (glyphs_positioned
) {
1273 HB_GetGlyphAdvances(item
);
1274 HB_Position positions
= face
->buffer
->positions
;
1275 HB_Fixed
*advances
= item
->advances
;
1277 // DEBUG("positioned glyphs:");
1278 for (unsigned int i
= 0; i
< face
->buffer
->in_length
; i
++) {
1279 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1280 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1281 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1282 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1283 // positions[i].back, positions[i].new_advance);
1285 HB_Fixed adjustment
= positions
[i
].x_advance
;
1287 if (!(face
->current_flags
& HB_ShaperFlag_UseDesignMetrics
))
1288 adjustment
= HB_FIXED_ROUND(adjustment
);
1290 if (positions
[i
].new_advance
== 0)
1291 advances
[i
] += adjustment
;
1294 HB_FixedPoint
*offsets
= item
->offsets
;
1295 offsets
[i
].x
= positions
[i
].x_pos
;
1296 offsets
[i
].y
= positions
[i
].y_pos
;
1297 while (positions
[i
- back
].back
) {
1298 back
+= positions
[i
- back
].back
;
1299 offsets
[i
].x
+= positions
[i
- back
].x_pos
;
1300 offsets
[i
].y
+= positions
[i
- back
].y_pos
;
1302 offsets
[i
].y
= -offsets
[i
].y
;
1304 if (item
->item
.bidiLevel
% 2) {
1305 // ### may need to go back multiple glyphs like in ltr
1306 back
= positions
[i
].back
;
1308 offsets
[i
].x
-= advances
[i
-back
];
1311 while (positions
[i
- back
].back
) {
1312 back
+= positions
[i
- back
].back
;
1313 offsets
[i
].x
-= advances
[i
-back
];
1316 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1317 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1319 item
->kerning_applied
= face
->has_opentype_kerning
;
1321 HB_HeuristicPosition(item
);
1325 if (doLogClusters
) {
1326 DEBUG("log clusters after shaping:");
1327 for (int j
= 0; j
< length
; j
++)
1328 DEBUG(" log[%d] = %d", j
, item
->log_clusters
[j
]);
1330 DEBUG("final glyphs:");
1331 for (int i
= 0; i
< (int)hb_buffer
->in_length
; ++i
)
1332 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
1333 glyphs
[i
].glyph
, hb_buffer
->in_string
[i
].cluster
, glyphs
[i
].attributes
.mark
,
1334 glyphs
[i
].attributes
.combiningClass
, glyphs
[i
].attributes
.clusterStart
,
1335 glyphs
[i
].advance
.x
.toInt(), glyphs
[i
].advance
.y
.toInt(),
1336 glyphs
[i
].offset
.x
.toInt(), glyphs
[i
].offset
.y
.toInt());
1337 DEBUG("-----------------------------------------");
1342 HB_Bool
HB_ShapeItem(HB_ShaperItem
*shaper_item
)
1344 HB_Bool result
= false;
1345 if (shaper_item
->num_glyphs
< shaper_item
->item
.length
) {
1346 shaper_item
->num_glyphs
= shaper_item
->item
.length
;
1349 assert(shaper_item
->item
.script
< HB_ScriptCount
);
1350 result
= HB_ScriptEngines
[shaper_item
->item
.script
].shape(shaper_item
);
1351 shaper_item
->glyphIndicesPresent
= false;