1 | /*
|
---|
2 | * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
|
---|
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 |
|
---|
25 | #include "harfbuzz-shaper.h"
|
---|
26 | #include "harfbuzz-shaper-private.h"
|
---|
27 |
|
---|
28 | #include "harfbuzz-stream-private.h"
|
---|
29 | #include <assert.h>
|
---|
30 | #include <stdio.h>
|
---|
31 |
|
---|
32 | #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
|
---|
33 | #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
|
---|
34 |
|
---|
35 | // -----------------------------------------------------------------------------------------------------
|
---|
36 | //
|
---|
37 | // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
|
---|
38 | //
|
---|
39 | // -----------------------------------------------------------------------------------------------------
|
---|
40 |
|
---|
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:
|
---|
44 |
|
---|
45 | EX->AL from DB to IB
|
---|
46 | SY->AL from DB to IB
|
---|
47 | SY->PO from DB to IB
|
---|
48 | SY->PR from DB to IB
|
---|
49 | SY->OP from DB to IB
|
---|
50 | AL->PR from DB to IB
|
---|
51 | AL->PO from DB to IB
|
---|
52 | PR->PR from DB to IB
|
---|
53 | PO->PO from DB to IB
|
---|
54 | PR->PO from DB to IB
|
---|
55 | PO->PR from DB to IB
|
---|
56 | HY->PO from DB to IB
|
---|
57 | HY->PR from DB to IB
|
---|
58 | HY->OP from DB to IB
|
---|
59 | NU->EX from PB to IB
|
---|
60 | EX->PO from DB to IB
|
---|
61 | */
|
---|
62 |
|
---|
63 | // The following line break classes are not treated by the table:
|
---|
64 | // AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
|
---|
65 |
|
---|
66 | enum break_class {
|
---|
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
|
---|
73 | };
|
---|
74 | #define DB DirectBreak
|
---|
75 | #define IB IndirectBreak
|
---|
76 | #define CI CombiningIndirectBreak
|
---|
77 | #define CP CombiningProhibitedBreak
|
---|
78 | #define PB ProhibitedBreak
|
---|
79 |
|
---|
80 | static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] =
|
---|
81 | {
|
---|
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 }
|
---|
109 | };
|
---|
110 | #undef DB
|
---|
111 | #undef IB
|
---|
112 | #undef CI
|
---|
113 | #undef CP
|
---|
114 | #undef PB
|
---|
115 |
|
---|
116 | static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] =
|
---|
117 | {
|
---|
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
|
---|
129 | };
|
---|
130 |
|
---|
131 | static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes)
|
---|
132 | {
|
---|
133 | if (!len)
|
---|
134 | return;
|
---|
135 |
|
---|
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;
|
---|
143 |
|
---|
144 | charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
|
---|
145 | charAttributes[0].charStop = true;
|
---|
146 |
|
---|
147 | int lcls = cls;
|
---|
148 | for (hb_uint32 i = 1; i < len; ++i) {
|
---|
149 | charAttributes[i].whiteSpace = false;
|
---|
150 | charAttributes[i].charStop = true;
|
---|
151 |
|
---|
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];
|
---|
157 | // handle surrogates
|
---|
158 | if (ncls == HB_LineBreak_SG) {
|
---|
159 | if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
|
---|
160 | continue;
|
---|
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;
|
---|
165 | } else {
|
---|
166 | ncls = HB_LineBreak_AL;
|
---|
167 | }
|
---|
168 | }
|
---|
169 |
|
---|
170 | // set white space and char stop flag
|
---|
171 | if (ncls >= HB_LineBreak_SP)
|
---|
172 | charAttributes[i].whiteSpace = true;
|
---|
173 |
|
---|
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;
|
---|
179 | }
|
---|
180 |
|
---|
181 | if (ncls == HB_LineBreak_SP)
|
---|
182 | goto next_no_cls_update;
|
---|
183 | if (ncls >= HB_LineBreak_CR)
|
---|
184 | goto next;
|
---|
185 |
|
---|
186 | // two complex chars (thai or lao), thai_attributes might override, but here we do a best guess
|
---|
187 | if (cls == HB_LineBreak_SA && ncls == HB_LineBreak_SA) {
|
---|
188 | lineBreakType = HB_Break;
|
---|
189 | goto next;
|
---|
190 | }
|
---|
191 |
|
---|
192 | {
|
---|
193 | int tcls = ncls;
|
---|
194 | if (tcls >= HB_LineBreak_SA)
|
---|
195 | tcls = HB_LineBreak_ID;
|
---|
196 | if (cls >= HB_LineBreak_SA)
|
---|
197 | cls = HB_LineBreak_ID;
|
---|
198 |
|
---|
199 | int brk = breakTable[cls][tcls];
|
---|
200 | switch (brk) {
|
---|
201 | case DirectBreak:
|
---|
202 | lineBreakType = HB_Break;
|
---|
203 | if (uc[i-1] == 0xad) // soft hyphen
|
---|
204 | lineBreakType = HB_SoftHyphen;
|
---|
205 | break;
|
---|
206 | case IndirectBreak:
|
---|
207 | lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
|
---|
208 | break;
|
---|
209 | case CombiningIndirectBreak:
|
---|
210 | lineBreakType = HB_NoBreak;
|
---|
211 | if (lcls == HB_LineBreak_SP){
|
---|
212 | if (i > 1)
|
---|
213 | charAttributes[i-2].lineBreakType = HB_Break;
|
---|
214 | } else {
|
---|
215 | goto next_no_cls_update;
|
---|
216 | }
|
---|
217 | break;
|
---|
218 | case CombiningProhibitedBreak:
|
---|
219 | lineBreakType = HB_NoBreak;
|
---|
220 | if (lcls != HB_LineBreak_SP)
|
---|
221 | goto next_no_cls_update;
|
---|
222 | case ProhibitedBreak:
|
---|
223 | default:
|
---|
224 | break;
|
---|
225 | }
|
---|
226 | }
|
---|
227 | next:
|
---|
228 | cls = ncls;
|
---|
229 | next_no_cls_update:
|
---|
230 | lcls = ncls;
|
---|
231 | grapheme = ngrapheme;
|
---|
232 | charAttributes[i-1].lineBreakType = lineBreakType;
|
---|
233 | }
|
---|
234 | charAttributes[len-1].lineBreakType = HB_ForcedBreak;
|
---|
235 | }
|
---|
236 |
|
---|
237 | // --------------------------------------------------------------------------------------------------------------------------------------------
|
---|
238 | //
|
---|
239 | // Basic processing
|
---|
240 | //
|
---|
241 | // --------------------------------------------------------------------------------------------------------------------------------------------
|
---|
242 |
|
---|
243 | static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
|
---|
244 | {
|
---|
245 | int nmarks = glast - gfrom;
|
---|
246 | assert(nmarks > 0);
|
---|
247 |
|
---|
248 | HB_Glyph *glyphs = item->glyphs;
|
---|
249 | HB_GlyphAttributes *attributes = item->attributes;
|
---|
250 |
|
---|
251 | HB_GlyphMetrics baseMetrics;
|
---|
252 | item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
|
---|
253 |
|
---|
254 | if (item->item.script == HB_Script_Hebrew
|
---|
255 | && (-baseMetrics.y) > baseMetrics.height)
|
---|
256 | // we need to attach below the baseline, because of the hebrew iud.
|
---|
257 | baseMetrics.height = -baseMetrics.y;
|
---|
258 |
|
---|
259 | // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
|
---|
260 | // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
|
---|
261 |
|
---|
262 | HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
|
---|
263 | HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
|
---|
264 | if (size > HB_FIXED_CONSTANT(4))
|
---|
265 | offsetBase += HB_FIXED_CONSTANT(4);
|
---|
266 | else
|
---|
267 | offsetBase += size;
|
---|
268 | //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
|
---|
269 | // qDebug("offset = %f", offsetBase);
|
---|
270 |
|
---|
271 | bool rightToLeft = item->item.bidiLevel % 2;
|
---|
272 |
|
---|
273 | int i;
|
---|
274 | unsigned char lastCmb = 0;
|
---|
275 | HB_GlyphMetrics attachmentRect;
|
---|
276 | memset(&attachmentRect, 0, sizeof(attachmentRect));
|
---|
277 |
|
---|
278 | for(i = 1; i <= nmarks; i++) {
|
---|
279 | HB_Glyph mark = glyphs[gfrom+i];
|
---|
280 | HB_GlyphMetrics markMetrics;
|
---|
281 | item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
|
---|
282 | HB_FixedPoint p;
|
---|
283 | p.x = p.y = 0;
|
---|
284 | // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
|
---|
285 |
|
---|
286 | HB_Fixed offset = offsetBase;
|
---|
287 | unsigned char cmb = attributes[gfrom+i].combiningClass;
|
---|
288 |
|
---|
289 | // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
|
---|
290 | // bits in the glyphAttributes structure.
|
---|
291 | if (cmb < 200) {
|
---|
292 | // fixed position classes. We approximate by mapping to one of the others.
|
---|
293 | // currently I added only the ones for arabic, hebrew, lao and thai.
|
---|
294 |
|
---|
295 | // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
|
---|
296 |
|
---|
297 | // add a bit more offset to arabic, a bit hacky
|
---|
298 | if (cmb >= 27 && cmb <= 36 && offset < 3)
|
---|
299 | offset +=1;
|
---|
300 | // below
|
---|
301 | if ((cmb >= 10 && cmb <= 18) ||
|
---|
302 | cmb == 20 || cmb == 22 ||
|
---|
303 | cmb == 29 || cmb == 32)
|
---|
304 | cmb = HB_Combining_Below;
|
---|
305 | // above
|
---|
306 | else if (cmb == 23 || cmb == 27 || cmb == 28 ||
|
---|
307 | cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
|
---|
308 | cmb = HB_Combining_Above;
|
---|
309 | //below-right
|
---|
310 | else if (cmb == 9 || cmb == 103 || cmb == 118)
|
---|
311 | cmb = HB_Combining_BelowRight;
|
---|
312 | // above-right
|
---|
313 | else if (cmb == 24 || cmb == 107 || cmb == 122)
|
---|
314 | cmb = HB_Combining_AboveRight;
|
---|
315 | else if (cmb == 25)
|
---|
316 | cmb = HB_Combining_AboveLeft;
|
---|
317 | // fixed:
|
---|
318 | // 19 21
|
---|
319 |
|
---|
320 | }
|
---|
321 |
|
---|
322 | // combining marks of different class don't interact. Reset the rectangle.
|
---|
323 | if (cmb != lastCmb) {
|
---|
324 | //qDebug("resetting rect");
|
---|
325 | attachmentRect = baseMetrics;
|
---|
326 | }
|
---|
327 |
|
---|
328 | switch(cmb) {
|
---|
329 | case HB_Combining_DoubleBelow:
|
---|
330 | // ### wrong in rtl context!
|
---|
331 | case HB_Combining_BelowLeft:
|
---|
332 | p.y += offset;
|
---|
333 | case HB_Combining_BelowLeftAttached:
|
---|
334 | p.x += attachmentRect.x - markMetrics.x;
|
---|
335 | p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
|
---|
336 | break;
|
---|
337 | case HB_Combining_Below:
|
---|
338 | p.y += offset;
|
---|
339 | case HB_Combining_BelowAttached:
|
---|
340 | p.x += attachmentRect.x - markMetrics.x;
|
---|
341 | p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
|
---|
342 |
|
---|
343 | p.x += (attachmentRect.width - markMetrics.width) / 2;
|
---|
344 | break;
|
---|
345 | case HB_Combining_BelowRight:
|
---|
346 | p.y += offset;
|
---|
347 | case HB_Combining_BelowRightAttached:
|
---|
348 | p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
|
---|
349 | p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
|
---|
350 | break;
|
---|
351 | case HB_Combining_Left:
|
---|
352 | p.x -= offset;
|
---|
353 | case HB_Combining_LeftAttached:
|
---|
354 | break;
|
---|
355 | case HB_Combining_Right:
|
---|
356 | p.x += offset;
|
---|
357 | case HB_Combining_RightAttached:
|
---|
358 | break;
|
---|
359 | case HB_Combining_DoubleAbove:
|
---|
360 | // ### wrong in RTL context!
|
---|
361 | case HB_Combining_AboveLeft:
|
---|
362 | p.y -= offset;
|
---|
363 | case HB_Combining_AboveLeftAttached:
|
---|
364 | p.x += attachmentRect.x - markMetrics.x;
|
---|
365 | p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
|
---|
366 | break;
|
---|
367 | case HB_Combining_Above:
|
---|
368 | p.y -= offset;
|
---|
369 | case HB_Combining_AboveAttached:
|
---|
370 | p.x += attachmentRect.x - markMetrics.x;
|
---|
371 | p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
|
---|
372 |
|
---|
373 | p.x += (attachmentRect.width - markMetrics.width) / 2;
|
---|
374 | break;
|
---|
375 | case HB_Combining_AboveRight:
|
---|
376 | p.y -= offset;
|
---|
377 | case HB_Combining_AboveRightAttached:
|
---|
378 | p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
|
---|
379 | p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
|
---|
380 | break;
|
---|
381 |
|
---|
382 | case HB_Combining_IotaSubscript:
|
---|
383 | default:
|
---|
384 | break;
|
---|
385 | }
|
---|
386 | // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
|
---|
387 | markMetrics.x += p.x;
|
---|
388 | markMetrics.y += p.y;
|
---|
389 |
|
---|
390 | HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
|
---|
391 | unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
|
---|
392 | unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
|
---|
393 | unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
|
---|
394 | unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
|
---|
395 | attachmentRect = unitedAttachmentRect;
|
---|
396 |
|
---|
397 | lastCmb = cmb;
|
---|
398 | if (rightToLeft) {
|
---|
399 | item->offsets[gfrom+i].x = p.x;
|
---|
400 | item->offsets[gfrom+i].y = p.y;
|
---|
401 | } else {
|
---|
402 | item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
|
---|
403 | item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
|
---|
404 | }
|
---|
405 | item->advances[gfrom+i] = 0;
|
---|
406 | }
|
---|
407 | }
|
---|
408 |
|
---|
409 | void HB_HeuristicPosition(HB_ShaperItem *item)
|
---|
410 | {
|
---|
411 | HB_GetGlyphAdvances(item);
|
---|
412 | HB_GlyphAttributes *attributes = item->attributes;
|
---|
413 |
|
---|
414 | int cEnd = -1;
|
---|
415 | int i = item->num_glyphs;
|
---|
416 | while (i--) {
|
---|
417 | if (cEnd == -1 && attributes[i].mark) {
|
---|
418 | cEnd = i;
|
---|
419 | } else if (cEnd != -1 && !attributes[i].mark) {
|
---|
420 | positionCluster(item, i, cEnd);
|
---|
421 | cEnd = -1;
|
---|
422 | }
|
---|
423 | }
|
---|
424 | }
|
---|
425 |
|
---|
426 | // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
|
---|
427 | // and no reordering.
|
---|
428 | // also computes logClusters heuristically
|
---|
429 | void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
|
---|
430 | {
|
---|
431 | const HB_UChar16 *uc = item->string + item->item.pos;
|
---|
432 | hb_uint32 length = item->item.length;
|
---|
433 |
|
---|
434 | // ### zeroWidth and justification are missing here!!!!!
|
---|
435 |
|
---|
436 | assert(item->num_glyphs <= length);
|
---|
437 |
|
---|
438 | // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
|
---|
439 | HB_GlyphAttributes *attributes = item->attributes;
|
---|
440 | unsigned short *logClusters = item->log_clusters;
|
---|
441 |
|
---|
442 | hb_uint32 glyph_pos = 0;
|
---|
443 | hb_uint32 i;
|
---|
444 | for (i = 0; i < length; i++) {
|
---|
445 | if (HB_IsHighSurrogate(uc[i]) && i < length - 1
|
---|
446 | && HB_IsLowSurrogate(uc[i + 1])) {
|
---|
447 | logClusters[i] = glyph_pos;
|
---|
448 | logClusters[++i] = glyph_pos;
|
---|
449 | } else {
|
---|
450 | logClusters[i] = glyph_pos;
|
---|
451 | }
|
---|
452 | ++glyph_pos;
|
---|
453 | }
|
---|
454 | assert(glyph_pos == item->num_glyphs);
|
---|
455 |
|
---|
456 | // first char in a run is never (treated as) a mark
|
---|
457 | int cStart = 0;
|
---|
458 | const bool symbolFont = item->face->isSymbolFont;
|
---|
459 | attributes[0].mark = false;
|
---|
460 | attributes[0].clusterStart = true;
|
---|
461 | attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
|
---|
462 |
|
---|
463 | int pos = 0;
|
---|
464 | HB_CharCategory lastCat;
|
---|
465 | int dummy;
|
---|
466 | HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
|
---|
467 | for (i = 1; i < length; ++i) {
|
---|
468 | if (logClusters[i] == pos)
|
---|
469 | // same glyph
|
---|
470 | continue;
|
---|
471 | ++pos;
|
---|
472 | while (pos < logClusters[i]) {
|
---|
473 | attributes[pos] = attributes[pos-1];
|
---|
474 | ++pos;
|
---|
475 | }
|
---|
476 | // hide soft-hyphens by default
|
---|
477 | if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
|
---|
478 | attributes[pos].dontPrint = true;
|
---|
479 | HB_CharCategory cat;
|
---|
480 | int cmb;
|
---|
481 | HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
|
---|
482 | if (cat != HB_Mark_NonSpacing) {
|
---|
483 | attributes[pos].mark = false;
|
---|
484 | attributes[pos].clusterStart = true;
|
---|
485 | attributes[pos].combiningClass = 0;
|
---|
486 | cStart = logClusters[i];
|
---|
487 | } else {
|
---|
488 | if (cmb == 0) {
|
---|
489 | // Fix 0 combining classes
|
---|
490 | if ((uc[pos] & 0xff00) == 0x0e00) {
|
---|
491 | // thai or lao
|
---|
492 | if (uc[pos] == 0xe31 ||
|
---|
493 | uc[pos] == 0xe34 ||
|
---|
494 | uc[pos] == 0xe35 ||
|
---|
495 | uc[pos] == 0xe36 ||
|
---|
496 | uc[pos] == 0xe37 ||
|
---|
497 | uc[pos] == 0xe47 ||
|
---|
498 | uc[pos] == 0xe4c ||
|
---|
499 | uc[pos] == 0xe4d ||
|
---|
500 | uc[pos] == 0xe4e) {
|
---|
501 | cmb = HB_Combining_AboveRight;
|
---|
502 | } else if (uc[pos] == 0xeb1 ||
|
---|
503 | uc[pos] == 0xeb4 ||
|
---|
504 | uc[pos] == 0xeb5 ||
|
---|
505 | uc[pos] == 0xeb6 ||
|
---|
506 | uc[pos] == 0xeb7 ||
|
---|
507 | uc[pos] == 0xebb ||
|
---|
508 | uc[pos] == 0xecc ||
|
---|
509 | uc[pos] == 0xecd) {
|
---|
510 | cmb = HB_Combining_Above;
|
---|
511 | } else if (uc[pos] == 0xebc) {
|
---|
512 | cmb = HB_Combining_Below;
|
---|
513 | }
|
---|
514 | }
|
---|
515 | }
|
---|
516 |
|
---|
517 | attributes[pos].mark = true;
|
---|
518 | attributes[pos].clusterStart = false;
|
---|
519 | attributes[pos].combiningClass = cmb;
|
---|
520 | logClusters[i] = cStart;
|
---|
521 | }
|
---|
522 | // one gets an inter character justification point if the current char is not a non spacing mark.
|
---|
523 | // as then the current char belongs to the last one and one gets a space justification point
|
---|
524 | // after the space char.
|
---|
525 | if (lastCat == HB_Separator_Space)
|
---|
526 | attributes[pos-1].justification = HB_Space;
|
---|
527 | else if (cat != HB_Mark_NonSpacing)
|
---|
528 | attributes[pos-1].justification = HB_Character;
|
---|
529 | else
|
---|
530 | attributes[pos-1].justification = HB_NoJustification;
|
---|
531 |
|
---|
532 | lastCat = cat;
|
---|
533 | }
|
---|
534 | pos = logClusters[length-1];
|
---|
535 | if (lastCat == HB_Separator_Space)
|
---|
536 | attributes[pos].justification = HB_Space;
|
---|
537 | else
|
---|
538 | attributes[pos].justification = HB_Character;
|
---|
539 | }
|
---|
540 |
|
---|
541 | #ifndef NO_OPENTYPE
|
---|
542 | static const HB_OpenTypeFeature basic_features[] = {
|
---|
543 | { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
|
---|
544 | { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
|
---|
545 | { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
|
---|
546 | {0, 0}
|
---|
547 | };
|
---|
548 | #endif
|
---|
549 |
|
---|
550 | HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
|
---|
551 | {
|
---|
552 | if (shaper_item->glyphIndicesPresent) {
|
---|
553 | shaper_item->num_glyphs = shaper_item->initialGlyphCount;
|
---|
554 | shaper_item->glyphIndicesPresent = false;
|
---|
555 | return true;
|
---|
556 | }
|
---|
557 | return shaper_item->font->klass
|
---|
558 | ->convertStringToGlyphIndices(shaper_item->font,
|
---|
559 | shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
|
---|
560 | shaper_item->glyphs, &shaper_item->num_glyphs,
|
---|
561 | shaper_item->item.bidiLevel % 2);
|
---|
562 | }
|
---|
563 |
|
---|
564 | HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
|
---|
565 | {
|
---|
566 | #ifndef NO_OPENTYPE
|
---|
567 | const int availableGlyphs = shaper_item->num_glyphs;
|
---|
568 | #endif
|
---|
569 |
|
---|
570 | if (!HB_ConvertStringToGlyphIndices(shaper_item))
|
---|
571 | return false;
|
---|
572 |
|
---|
573 | HB_HeuristicSetGlyphAttributes(shaper_item);
|
---|
574 |
|
---|
575 | #ifndef NO_OPENTYPE
|
---|
576 | if (HB_SelectScript(shaper_item, basic_features)) {
|
---|
577 | HB_OpenTypeShape(shaper_item, /*properties*/0);
|
---|
578 | return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
|
---|
579 | }
|
---|
580 | #endif
|
---|
581 |
|
---|
582 | HB_HeuristicPosition(shaper_item);
|
---|
583 | return true;
|
---|
584 | }
|
---|
585 |
|
---|
586 | const HB_ScriptEngine HB_ScriptEngines[] = {
|
---|
587 | // Common
|
---|
588 | { HB_BasicShape, 0},
|
---|
589 | // Greek
|
---|
590 | { HB_BasicShape, 0},
|
---|
591 | // Cyrillic
|
---|
592 | { HB_BasicShape, 0},
|
---|
593 | // Armenian
|
---|
594 | { HB_BasicShape, 0},
|
---|
595 | // Hebrew
|
---|
596 | { HB_HebrewShape, 0 },
|
---|
597 | // Arabic
|
---|
598 | { HB_ArabicShape, 0},
|
---|
599 | // Syriac
|
---|
600 | { HB_ArabicShape, 0},
|
---|
601 | // Thaana
|
---|
602 | { HB_BasicShape, 0 },
|
---|
603 | // Devanagari
|
---|
604 | { HB_IndicShape, HB_IndicAttributes },
|
---|
605 | // Bengali
|
---|
606 | { HB_IndicShape, HB_IndicAttributes },
|
---|
607 | // Gurmukhi
|
---|
608 | { HB_IndicShape, HB_IndicAttributes },
|
---|
609 | // Gujarati
|
---|
610 | { HB_IndicShape, HB_IndicAttributes },
|
---|
611 | // Oriya
|
---|
612 | { HB_IndicShape, HB_IndicAttributes },
|
---|
613 | // Tamil
|
---|
614 | { HB_IndicShape, HB_IndicAttributes },
|
---|
615 | // Telugu
|
---|
616 | { HB_IndicShape, HB_IndicAttributes },
|
---|
617 | // Kannada
|
---|
618 | { HB_IndicShape, HB_IndicAttributes },
|
---|
619 | // Malayalam
|
---|
620 | { HB_IndicShape, HB_IndicAttributes },
|
---|
621 | // Sinhala
|
---|
622 | { HB_IndicShape, HB_IndicAttributes },
|
---|
623 | // Thai
|
---|
624 | { HB_BasicShape, HB_ThaiAttributes },
|
---|
625 | // Lao
|
---|
626 | { HB_BasicShape, 0 },
|
---|
627 | // Tibetan
|
---|
628 | { HB_TibetanShape, HB_TibetanAttributes },
|
---|
629 | // Myanmar
|
---|
630 | { HB_MyanmarShape, HB_MyanmarAttributes },
|
---|
631 | // Georgian
|
---|
632 | { HB_BasicShape, 0 },
|
---|
633 | // Hangul
|
---|
634 | { HB_HangulShape, 0 },
|
---|
635 | // Ogham
|
---|
636 | { HB_BasicShape, 0 },
|
---|
637 | // Runic
|
---|
638 | { HB_BasicShape, 0 },
|
---|
639 | // Khmer
|
---|
640 | { HB_KhmerShape, HB_KhmerAttributes },
|
---|
641 | // N'Ko
|
---|
642 | { HB_ArabicShape, 0}
|
---|
643 | };
|
---|
644 |
|
---|
645 | void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
|
---|
646 | const HB_ScriptItem *items, hb_uint32 numItems,
|
---|
647 | HB_CharAttributes *attributes)
|
---|
648 | {
|
---|
649 | calcLineBreaks(string, stringLength, attributes);
|
---|
650 |
|
---|
651 | for (hb_uint32 i = 0; i < numItems; ++i) {
|
---|
652 | HB_Script script = items[i].script;
|
---|
653 | if (script == HB_Script_Inherited)
|
---|
654 | script = HB_Script_Common;
|
---|
655 | HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
|
---|
656 | if (!attributeFunction)
|
---|
657 | continue;
|
---|
658 | attributeFunction(script, string, items[i].pos, items[i].length, attributes);
|
---|
659 | }
|
---|
660 | }
|
---|
661 |
|
---|
662 |
|
---|
663 | enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
|
---|
664 |
|
---|
665 | static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
|
---|
666 | // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
|
---|
667 | { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
|
---|
668 | { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
|
---|
669 | { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
|
---|
670 | { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
|
---|
671 | { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
|
---|
672 | { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
|
---|
673 | { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
|
---|
674 | { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
|
---|
675 | };
|
---|
676 |
|
---|
677 | void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
|
---|
678 | const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
|
---|
679 | HB_CharAttributes *attributes)
|
---|
680 | {
|
---|
681 | if (stringLength == 0)
|
---|
682 | return;
|
---|
683 | unsigned int brk = HB_GetWordClass(string[0]);
|
---|
684 | attributes[0].wordBoundary = true;
|
---|
685 | for (hb_uint32 i = 1; i < stringLength; ++i) {
|
---|
686 | if (!attributes[i].charStop) {
|
---|
687 | attributes[i].wordBoundary = false;
|
---|
688 | continue;
|
---|
689 | }
|
---|
690 | hb_uint32 nbrk = HB_GetWordClass(string[i]);
|
---|
691 | if (nbrk == HB_Word_Format) {
|
---|
692 | attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
|
---|
693 | continue;
|
---|
694 | }
|
---|
695 | BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
|
---|
696 | if (rule == Middle) {
|
---|
697 | rule = Break;
|
---|
698 | hb_uint32 lookahead = i + 1;
|
---|
699 | while (lookahead < stringLength) {
|
---|
700 | hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
|
---|
701 | if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
|
---|
702 | ++lookahead;
|
---|
703 | continue;
|
---|
704 | }
|
---|
705 | if (testbrk == brk) {
|
---|
706 | rule = NoBreak;
|
---|
707 | while (i < lookahead)
|
---|
708 | attributes[i++].wordBoundary = false;
|
---|
709 | nbrk = testbrk;
|
---|
710 | }
|
---|
711 | break;
|
---|
712 | }
|
---|
713 | }
|
---|
714 | attributes[i].wordBoundary = (rule == Break);
|
---|
715 | brk = nbrk;
|
---|
716 | }
|
---|
717 | }
|
---|
718 |
|
---|
719 |
|
---|
720 | enum SentenceBreakStates {
|
---|
721 | SB_Initial,
|
---|
722 | SB_Upper,
|
---|
723 | SB_UpATerm,
|
---|
724 | SB_ATerm,
|
---|
725 | SB_ATermC,
|
---|
726 | SB_ACS,
|
---|
727 | SB_STerm,
|
---|
728 | SB_STermC,
|
---|
729 | SB_SCS,
|
---|
730 | SB_BAfter,
|
---|
731 | SB_Break,
|
---|
732 | SB_Look
|
---|
733 | };
|
---|
734 |
|
---|
735 | static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
|
---|
736 | // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
|
---|
737 | { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
|
---|
738 | { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
|
---|
739 |
|
---|
740 | { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
|
---|
741 | { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
|
---|
742 | { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
|
---|
743 | { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
|
---|
744 |
|
---|
745 | { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
|
---|
746 | { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
|
---|
747 | { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
|
---|
748 | { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
|
---|
749 | };
|
---|
750 |
|
---|
751 | void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
|
---|
752 | const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
|
---|
753 | HB_CharAttributes *attributes)
|
---|
754 | {
|
---|
755 | if (stringLength == 0)
|
---|
756 | return;
|
---|
757 | hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
|
---|
758 | attributes[0].sentenceBoundary = true;
|
---|
759 | for (hb_uint32 i = 1; i < stringLength; ++i) {
|
---|
760 | if (!attributes[i].charStop) {
|
---|
761 | attributes[i].sentenceBoundary = false;
|
---|
762 | continue;
|
---|
763 | }
|
---|
764 | brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
|
---|
765 | if (brk == SB_Look) {
|
---|
766 | brk = SB_Break;
|
---|
767 | hb_uint32 lookahead = i + 1;
|
---|
768 | while (lookahead < stringLength) {
|
---|
769 | hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
|
---|
770 | if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
|
---|
771 | break;
|
---|
772 | } else if (sbrk == HB_Sentence_Lower) {
|
---|
773 | brk = SB_Initial;
|
---|
774 | break;
|
---|
775 | }
|
---|
776 | ++lookahead;
|
---|
777 | }
|
---|
778 | if (brk == SB_Initial) {
|
---|
779 | while (i < lookahead)
|
---|
780 | attributes[i++].sentenceBoundary = false;
|
---|
781 | }
|
---|
782 | }
|
---|
783 | if (brk == SB_Break) {
|
---|
784 | attributes[i].sentenceBoundary = true;
|
---|
785 | brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
|
---|
786 | } else {
|
---|
787 | attributes[i].sentenceBoundary = false;
|
---|
788 | }
|
---|
789 | }
|
---|
790 | }
|
---|
791 |
|
---|
792 |
|
---|
793 | static inline char *tag_to_string(HB_UInt tag)
|
---|
794 | {
|
---|
795 | static char string[5];
|
---|
796 | string[0] = (tag >> 24)&0xff;
|
---|
797 | string[1] = (tag >> 16)&0xff;
|
---|
798 | string[2] = (tag >> 8)&0xff;
|
---|
799 | string[3] = tag&0xff;
|
---|
800 | string[4] = 0;
|
---|
801 | return string;
|
---|
802 | }
|
---|
803 |
|
---|
804 | #ifdef OT_DEBUG
|
---|
805 | static void dump_string(HB_Buffer buffer)
|
---|
806 | {
|
---|
807 | for (uint i = 0; i < buffer->in_length; ++i) {
|
---|
808 | qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
|
---|
809 | }
|
---|
810 | }
|
---|
811 | #define DEBUG printf
|
---|
812 | #else
|
---|
813 | #define DEBUG if (1) ; else printf
|
---|
814 | #endif
|
---|
815 |
|
---|
816 | #define DefaultLangSys 0xffff
|
---|
817 | #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
|
---|
818 |
|
---|
819 | enum {
|
---|
820 | RequiresGsub = 1,
|
---|
821 | RequiresGpos = 2
|
---|
822 | };
|
---|
823 |
|
---|
824 | struct OTScripts {
|
---|
825 | unsigned int tag;
|
---|
826 | int flags;
|
---|
827 | };
|
---|
828 | static const OTScripts ot_scripts [] = {
|
---|
829 | // Common
|
---|
830 | { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
|
---|
831 | // Greek
|
---|
832 | { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
|
---|
833 | // Cyrillic
|
---|
834 | { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
|
---|
835 | // Armenian
|
---|
836 | { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
|
---|
837 | // Hebrew
|
---|
838 | { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
|
---|
839 | // Arabic
|
---|
840 | { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
|
---|
841 | // Syriac
|
---|
842 | { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
|
---|
843 | // Thaana
|
---|
844 | { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
|
---|
845 | // Devanagari
|
---|
846 | { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
|
---|
847 | // Bengali
|
---|
848 | { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
|
---|
849 | // Gurmukhi
|
---|
850 | { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
|
---|
851 | // Gujarati
|
---|
852 | { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
|
---|
853 | // Oriya
|
---|
854 | { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
|
---|
855 | // Tamil
|
---|
856 | { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
|
---|
857 | // Telugu
|
---|
858 | { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
|
---|
859 | // Kannada
|
---|
860 | { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
|
---|
861 | // Malayalam
|
---|
862 | { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
|
---|
863 | // Sinhala
|
---|
864 | { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
|
---|
865 | // Thai
|
---|
866 | { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
|
---|
867 | // Lao
|
---|
868 | { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
|
---|
869 | // Tibetan
|
---|
870 | { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
|
---|
871 | // Myanmar
|
---|
872 | { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
|
---|
873 | // Georgian
|
---|
874 | { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
|
---|
875 | // Hangul
|
---|
876 | { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
|
---|
877 | // Ogham
|
---|
878 | { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
|
---|
879 | // Runic
|
---|
880 | { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
|
---|
881 | // Khmer
|
---|
882 | { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
|
---|
883 | // N'Ko
|
---|
884 | { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
|
---|
885 | };
|
---|
886 | enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
|
---|
887 |
|
---|
888 | static HB_Bool checkScript(HB_Face face, int script)
|
---|
889 | {
|
---|
890 | assert(script < HB_ScriptCount);
|
---|
891 |
|
---|
892 | if (!face->gsub && !face->gpos)
|
---|
893 | return false;
|
---|
894 |
|
---|
895 | unsigned int tag = ot_scripts[script].tag;
|
---|
896 | int requirements = ot_scripts[script].flags;
|
---|
897 |
|
---|
898 | if (requirements & RequiresGsub) {
|
---|
899 | if (!face->gsub)
|
---|
900 | return false;
|
---|
901 |
|
---|
902 | HB_UShort script_index;
|
---|
903 | HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
|
---|
904 | if (error) {
|
---|
905 | DEBUG("could not select script %d in GSub table: %d", (int)script, error);
|
---|
906 | error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
|
---|
907 | if (error)
|
---|
908 | return false;
|
---|
909 | }
|
---|
910 | }
|
---|
911 |
|
---|
912 | if (requirements & RequiresGpos) {
|
---|
913 | if (!face->gpos)
|
---|
914 | return false;
|
---|
915 |
|
---|
916 | HB_UShort script_index;
|
---|
917 | HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
|
---|
918 | if (error) {
|
---|
919 | DEBUG("could not select script in gpos table: %d", error);
|
---|
920 | error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
|
---|
921 | if (error)
|
---|
922 | return false;
|
---|
923 | }
|
---|
924 |
|
---|
925 | }
|
---|
926 | return true;
|
---|
927 | }
|
---|
928 |
|
---|
929 | static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
|
---|
930 | {
|
---|
931 | HB_Error error;
|
---|
932 | HB_UInt length = 0;
|
---|
933 | HB_Stream stream = 0;
|
---|
934 |
|
---|
935 | if (!font)
|
---|
936 | return 0;
|
---|
937 |
|
---|
938 | error = tableFunc(font, tag, 0, &length);
|
---|
939 | if (error)
|
---|
940 | return 0;
|
---|
941 | stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
|
---|
942 | if (!stream)
|
---|
943 | return 0;
|
---|
944 | stream->base = (HB_Byte*)malloc(length);
|
---|
945 | if (!stream->base) {
|
---|
946 | free(stream);
|
---|
947 | return 0;
|
---|
948 | }
|
---|
949 | error = tableFunc(font, tag, stream->base, &length);
|
---|
950 | if (error) {
|
---|
951 | _hb_close_stream(stream);
|
---|
952 | return 0;
|
---|
953 | }
|
---|
954 | stream->size = length;
|
---|
955 | stream->pos = 0;
|
---|
956 | stream->cursor = NULL;
|
---|
957 | return stream;
|
---|
958 | }
|
---|
959 |
|
---|
960 | HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
|
---|
961 | {
|
---|
962 | HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
|
---|
963 | if (!face)
|
---|
964 | return 0;
|
---|
965 |
|
---|
966 | face->isSymbolFont = false;
|
---|
967 | face->gdef = 0;
|
---|
968 | face->gpos = 0;
|
---|
969 | face->gsub = 0;
|
---|
970 | face->current_script = HB_ScriptCount;
|
---|
971 | face->current_flags = HB_ShaperFlag_Default;
|
---|
972 | face->has_opentype_kerning = false;
|
---|
973 | face->tmpAttributes = 0;
|
---|
974 | face->tmpLogClusters = 0;
|
---|
975 | face->glyphs_substituted = false;
|
---|
976 | face->buffer = 0;
|
---|
977 |
|
---|
978 | HB_Error error = HB_Err_Ok;
|
---|
979 | HB_Stream stream;
|
---|
980 | HB_Stream gdefStream;
|
---|
981 |
|
---|
982 | gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
|
---|
983 | error = HB_Err_Not_Covered;
|
---|
984 | if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
|
---|
985 | //DEBUG("error loading gdef table: %d", error);
|
---|
986 | face->gdef = 0;
|
---|
987 | }
|
---|
988 |
|
---|
989 | //DEBUG() << "trying to load gsub table";
|
---|
990 | stream = getTableStream(font, tableFunc, TTAG_GSUB);
|
---|
991 | error = HB_Err_Not_Covered;
|
---|
992 | if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
|
---|
993 | face->gsub = 0;
|
---|
994 | if (error != HB_Err_Not_Covered) {
|
---|
995 | //DEBUG("error loading gsub table: %d", error);
|
---|
996 | } else {
|
---|
997 | //DEBUG("face doesn't have a gsub table");
|
---|
998 | }
|
---|
999 | }
|
---|
1000 | _hb_close_stream(stream);
|
---|
1001 |
|
---|
1002 | stream = getTableStream(font, tableFunc, TTAG_GPOS);
|
---|
1003 | error = HB_Err_Not_Covered;
|
---|
1004 | if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
|
---|
1005 | face->gpos = 0;
|
---|
1006 | DEBUG("error loading gpos table: %d", error);
|
---|
1007 | }
|
---|
1008 | _hb_close_stream(stream);
|
---|
1009 |
|
---|
1010 | _hb_close_stream(gdefStream);
|
---|
1011 |
|
---|
1012 | for (unsigned int i = 0; i < HB_ScriptCount; ++i)
|
---|
1013 | face->supported_scripts[i] = checkScript(face, i);
|
---|
1014 |
|
---|
1015 | if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
|
---|
1016 | HB_FreeFace(face);
|
---|
1017 | return 0;
|
---|
1018 | }
|
---|
1019 |
|
---|
1020 | return face;
|
---|
1021 | }
|
---|
1022 |
|
---|
1023 | void HB_FreeFace(HB_Face face)
|
---|
1024 | {
|
---|
1025 | if (!face)
|
---|
1026 | return;
|
---|
1027 | if (face->gpos)
|
---|
1028 | HB_Done_GPOS_Table(face->gpos);
|
---|
1029 | if (face->gsub)
|
---|
1030 | HB_Done_GSUB_Table(face->gsub);
|
---|
1031 | if (face->gdef)
|
---|
1032 | HB_Done_GDEF_Table(face->gdef);
|
---|
1033 | if (face->buffer)
|
---|
1034 | hb_buffer_free(face->buffer);
|
---|
1035 | if (face->tmpAttributes)
|
---|
1036 | free(face->tmpAttributes);
|
---|
1037 | if (face->tmpLogClusters)
|
---|
1038 | free(face->tmpLogClusters);
|
---|
1039 | free(face);
|
---|
1040 | }
|
---|
1041 |
|
---|
1042 | HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
|
---|
1043 | {
|
---|
1044 | HB_Script script = shaper_item->item.script;
|
---|
1045 |
|
---|
1046 | if (!shaper_item->face->supported_scripts[script])
|
---|
1047 | return false;
|
---|
1048 |
|
---|
1049 | HB_Face face = shaper_item->face;
|
---|
1050 | if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
|
---|
1051 | return true;
|
---|
1052 |
|
---|
1053 | face->current_script = script;
|
---|
1054 | face->current_flags = shaper_item->shaperFlags;
|
---|
1055 |
|
---|
1056 | assert(script < HB_ScriptCount);
|
---|
1057 | // find script in our list of supported scripts.
|
---|
1058 | unsigned int tag = ot_scripts[script].tag;
|
---|
1059 |
|
---|
1060 | if (face->gsub && features) {
|
---|
1061 | #ifdef OT_DEBUG
|
---|
1062 | {
|
---|
1063 | HB_FeatureList featurelist = face->gsub->FeatureList;
|
---|
1064 | int numfeatures = featurelist.FeatureCount;
|
---|
1065 | DEBUG("gsub table has %d features", numfeatures);
|
---|
1066 | for (int i = 0; i < numfeatures; i++) {
|
---|
1067 | HB_FeatureRecord *r = featurelist.FeatureRecord + i;
|
---|
1068 | DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
|
---|
1069 | }
|
---|
1070 | }
|
---|
1071 | #endif
|
---|
1072 | HB_GSUB_Clear_Features(face->gsub);
|
---|
1073 | HB_UShort script_index;
|
---|
1074 | HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
|
---|
1075 | if (!error) {
|
---|
1076 | DEBUG("script %s has script index %d", tag_to_string(script), script_index);
|
---|
1077 | while (features->tag) {
|
---|
1078 | HB_UShort feature_index;
|
---|
1079 | error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
|
---|
1080 | if (!error) {
|
---|
1081 | DEBUG(" adding feature %s", tag_to_string(features->tag));
|
---|
1082 | HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
|
---|
1083 | }
|
---|
1084 | ++features;
|
---|
1085 | }
|
---|
1086 | }
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | // reset
|
---|
1090 | face->has_opentype_kerning = false;
|
---|
1091 |
|
---|
1092 | if (face->gpos) {
|
---|
1093 | HB_GPOS_Clear_Features(face->gpos);
|
---|
1094 | HB_UShort script_index;
|
---|
1095 | HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
|
---|
1096 | if (!error) {
|
---|
1097 | #ifdef OT_DEBUG
|
---|
1098 | {
|
---|
1099 | HB_FeatureList featurelist = face->gpos->FeatureList;
|
---|
1100 | int numfeatures = featurelist.FeatureCount;
|
---|
1101 | DEBUG("gpos table has %d features", numfeatures);
|
---|
1102 | for(int i = 0; i < numfeatures; i++) {
|
---|
1103 | HB_FeatureRecord *r = featurelist.FeatureRecord + i;
|
---|
1104 | HB_UShort feature_index;
|
---|
1105 | HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
|
---|
1106 | DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
|
---|
1107 | }
|
---|
1108 | }
|
---|
1109 | #endif
|
---|
1110 | HB_UInt *feature_tag_list_buffer;
|
---|
1111 | error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
|
---|
1112 | if (!error) {
|
---|
1113 | HB_UInt *feature_tag_list = feature_tag_list_buffer;
|
---|
1114 | while (*feature_tag_list) {
|
---|
1115 | HB_UShort feature_index;
|
---|
1116 | if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
|
---|
1117 | if (face->current_flags & HB_ShaperFlag_NoKerning) {
|
---|
1118 | ++feature_tag_list;
|
---|
1119 | continue;
|
---|
1120 | }
|
---|
1121 | face->has_opentype_kerning = true;
|
---|
1122 | }
|
---|
1123 | error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
|
---|
1124 | if (!error)
|
---|
1125 | HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
|
---|
1126 | ++feature_tag_list;
|
---|
1127 | }
|
---|
1128 | FREE(feature_tag_list_buffer);
|
---|
1129 | }
|
---|
1130 | }
|
---|
1131 | }
|
---|
1132 |
|
---|
1133 | return true;
|
---|
1134 | }
|
---|
1135 |
|
---|
1136 | HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
|
---|
1137 | {
|
---|
1138 | HB_GlyphAttributes *tmpAttributes;
|
---|
1139 | unsigned int *tmpLogClusters;
|
---|
1140 |
|
---|
1141 | HB_Face face = item->face;
|
---|
1142 |
|
---|
1143 | face->length = item->num_glyphs;
|
---|
1144 |
|
---|
1145 | hb_buffer_clear(face->buffer);
|
---|
1146 |
|
---|
1147 | tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
|
---|
1148 | if (!tmpAttributes)
|
---|
1149 | return false;
|
---|
1150 | face->tmpAttributes = tmpAttributes;
|
---|
1151 |
|
---|
1152 | tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
|
---|
1153 | if (!tmpLogClusters)
|
---|
1154 | return false;
|
---|
1155 | face->tmpLogClusters = tmpLogClusters;
|
---|
1156 |
|
---|
1157 | for (int i = 0; i < face->length; ++i) {
|
---|
1158 | hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
|
---|
1159 | face->tmpAttributes[i] = item->attributes[i];
|
---|
1160 | face->tmpLogClusters[i] = item->log_clusters[i];
|
---|
1161 | }
|
---|
1162 |
|
---|
1163 | #ifdef OT_DEBUG
|
---|
1164 | DEBUG("-----------------------------------------");
|
---|
1165 | // DEBUG("log clusters before shaping:");
|
---|
1166 | // for (int j = 0; j < length; j++)
|
---|
1167 | // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
|
---|
1168 | DEBUG("original glyphs: %p", item->glyphs);
|
---|
1169 | for (int i = 0; i < length; ++i)
|
---|
1170 | DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
|
---|
1171 | // dump_string(hb_buffer);
|
---|
1172 | #endif
|
---|
1173 |
|
---|
1174 | face->glyphs_substituted = false;
|
---|
1175 | if (face->gsub) {
|
---|
1176 | unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
|
---|
1177 | if (error && error != HB_Err_Not_Covered)
|
---|
1178 | return false;
|
---|
1179 | face->glyphs_substituted = (error != HB_Err_Not_Covered);
|
---|
1180 | }
|
---|
1181 |
|
---|
1182 | #ifdef OT_DEBUG
|
---|
1183 | // DEBUG("log clusters before shaping:");
|
---|
1184 | // for (int j = 0; j < length; j++)
|
---|
1185 | // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
|
---|
1186 | DEBUG("shaped glyphs:");
|
---|
1187 | for (int i = 0; i < length; ++i)
|
---|
1188 | DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
|
---|
1189 | DEBUG("-----------------------------------------");
|
---|
1190 | // dump_string(hb_buffer);
|
---|
1191 | #endif
|
---|
1192 |
|
---|
1193 | return true;
|
---|
1194 | }
|
---|
1195 |
|
---|
1196 | HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
|
---|
1197 | {
|
---|
1198 | HB_Face face = item->face;
|
---|
1199 |
|
---|
1200 | bool glyphs_positioned = false;
|
---|
1201 | if (face->gpos) {
|
---|
1202 | if (face->buffer->positions)
|
---|
1203 | memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
|
---|
1204 | // #### check that passing "false,false" is correct
|
---|
1205 | glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
|
---|
1206 | }
|
---|
1207 |
|
---|
1208 | if (!face->glyphs_substituted && !glyphs_positioned) {
|
---|
1209 | HB_GetGlyphAdvances(item);
|
---|
1210 | return true; // nothing to do for us
|
---|
1211 | }
|
---|
1212 |
|
---|
1213 | // make sure we have enough space to write everything back
|
---|
1214 | if (availableGlyphs < (int)face->buffer->in_length) {
|
---|
1215 | item->num_glyphs = face->buffer->in_length;
|
---|
1216 | return false;
|
---|
1217 | }
|
---|
1218 |
|
---|
1219 | HB_Glyph *glyphs = item->glyphs;
|
---|
1220 | HB_GlyphAttributes *attributes = item->attributes;
|
---|
1221 |
|
---|
1222 | for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
|
---|
1223 | glyphs[i] = face->buffer->in_string[i].gindex;
|
---|
1224 | attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
|
---|
1225 | if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
|
---|
1226 | attributes[i].clusterStart = false;
|
---|
1227 | }
|
---|
1228 | item->num_glyphs = face->buffer->in_length;
|
---|
1229 |
|
---|
1230 | if (doLogClusters && face->glyphs_substituted) {
|
---|
1231 | // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
|
---|
1232 | unsigned short *logClusters = item->log_clusters;
|
---|
1233 | int clusterStart = 0;
|
---|
1234 | int oldCi = 0;
|
---|
1235 | // #### the reconstruction of the logclusters currently does not work if the original string
|
---|
1236 | // contains surrogate pairs
|
---|
1237 | for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
|
---|
1238 | int ci = face->buffer->in_string[i].cluster;
|
---|
1239 | // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
|
---|
1240 | // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
|
---|
1241 | if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
|
---|
1242 | for (int j = oldCi; j < ci; j++)
|
---|
1243 | logClusters[j] = clusterStart;
|
---|
1244 | clusterStart = i;
|
---|
1245 | oldCi = ci;
|
---|
1246 | }
|
---|
1247 | }
|
---|
1248 | for (int j = oldCi; j < face->length; j++)
|
---|
1249 | logClusters[j] = clusterStart;
|
---|
1250 | }
|
---|
1251 |
|
---|
1252 | // calulate the advances for the shaped glyphs
|
---|
1253 | // DEBUG("unpositioned: ");
|
---|
1254 |
|
---|
1255 | // positioning code:
|
---|
1256 | if (glyphs_positioned) {
|
---|
1257 | HB_GetGlyphAdvances(item);
|
---|
1258 | HB_Position positions = face->buffer->positions;
|
---|
1259 | HB_Fixed *advances = item->advances;
|
---|
1260 |
|
---|
1261 | // DEBUG("positioned glyphs:");
|
---|
1262 | for (unsigned int i = 0; i < face->buffer->in_length; i++) {
|
---|
1263 | // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
|
---|
1264 | // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
|
---|
1265 | // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
|
---|
1266 | // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
|
---|
1267 | // positions[i].back, positions[i].new_advance);
|
---|
1268 |
|
---|
1269 | HB_Fixed adjustment = (item->item.bidiLevel % 2) ? -positions[i].x_advance : positions[i].x_advance;
|
---|
1270 |
|
---|
1271 | if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
|
---|
1272 | adjustment = HB_FIXED_ROUND(adjustment);
|
---|
1273 |
|
---|
1274 | if (positions[i].new_advance) {
|
---|
1275 | advances[i] = adjustment;
|
---|
1276 | } else {
|
---|
1277 | advances[i] += adjustment;
|
---|
1278 | }
|
---|
1279 |
|
---|
1280 | int back = 0;
|
---|
1281 | HB_FixedPoint *offsets = item->offsets;
|
---|
1282 | offsets[i].x = positions[i].x_pos;
|
---|
1283 | offsets[i].y = positions[i].y_pos;
|
---|
1284 | while (positions[i - back].back) {
|
---|
1285 | back += positions[i - back].back;
|
---|
1286 | offsets[i].x += positions[i - back].x_pos;
|
---|
1287 | offsets[i].y += positions[i - back].y_pos;
|
---|
1288 | }
|
---|
1289 | offsets[i].y = -offsets[i].y;
|
---|
1290 |
|
---|
1291 | if (item->item.bidiLevel % 2) {
|
---|
1292 | // ### may need to go back multiple glyphs like in ltr
|
---|
1293 | back = positions[i].back;
|
---|
1294 | while (back--)
|
---|
1295 | offsets[i].x -= advances[i-back];
|
---|
1296 | } else {
|
---|
1297 | back = 0;
|
---|
1298 | while (positions[i - back].back) {
|
---|
1299 | back += positions[i - back].back;
|
---|
1300 | offsets[i].x -= advances[i-back];
|
---|
1301 | }
|
---|
1302 | }
|
---|
1303 | // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
|
---|
1304 | // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
|
---|
1305 | }
|
---|
1306 | item->kerning_applied = face->has_opentype_kerning;
|
---|
1307 | } else {
|
---|
1308 | HB_HeuristicPosition(item);
|
---|
1309 | }
|
---|
1310 |
|
---|
1311 | #ifdef OT_DEBUG
|
---|
1312 | if (doLogClusters) {
|
---|
1313 | DEBUG("log clusters after shaping:");
|
---|
1314 | for (int j = 0; j < length; j++)
|
---|
1315 | DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
|
---|
1316 | }
|
---|
1317 | DEBUG("final glyphs:");
|
---|
1318 | for (int i = 0; i < (int)hb_buffer->in_length; ++i)
|
---|
1319 | DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
|
---|
1320 | glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
|
---|
1321 | glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
|
---|
1322 | glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
|
---|
1323 | glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
|
---|
1324 | DEBUG("-----------------------------------------");
|
---|
1325 | #endif
|
---|
1326 | return true;
|
---|
1327 | }
|
---|
1328 |
|
---|
1329 | HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
|
---|
1330 | {
|
---|
1331 | HB_Bool result = false;
|
---|
1332 | if (shaper_item->num_glyphs < shaper_item->item.length) {
|
---|
1333 | shaper_item->num_glyphs = shaper_item->item.length;
|
---|
1334 | return false;
|
---|
1335 | }
|
---|
1336 | assert(shaper_item->item.script < HB_ScriptCount);
|
---|
1337 | result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
|
---|
1338 | shaper_item->glyphIndicesPresent = false;
|
---|
1339 | return result;
|
---|
1340 | }
|
---|
1341 |
|
---|