OLD | NEW |
| (Empty) |
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.htm
l | |
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, P
B, 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, D
B, 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, I
B, 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, I
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, I
B, 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, P
B, 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, D
B, 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, D
B, 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, I
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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, D
B, 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_CharAttribute
s *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_LineBrea
k_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_ForcedBr
eak; | |
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 { | |
187 int tcls = ncls; | |
188 // for south east asian chars that require a complex (dictionary ana
lysis), the unicode | |
189 // standard recommends to treat them as AL. thai_attributes and othe
r 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; | |
195 | |
196 int brk = breakTable[cls][tcls]; | |
197 switch (brk) { | |
198 case DirectBreak: | |
199 lineBreakType = HB_Break; | |
200 if (uc[i-1] == 0xad) // soft hyphen | |
201 lineBreakType = HB_SoftHyphen; | |
202 break; | |
203 case IndirectBreak: | |
204 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBrea
k; | |
205 break; | |
206 case CombiningIndirectBreak: | |
207 lineBreakType = HB_NoBreak; | |
208 if (lcls == HB_LineBreak_SP){ | |
209 if (i > 1) | |
210 charAttributes[i-2].lineBreakType = HB_Break; | |
211 } else { | |
212 goto next_no_cls_update; | |
213 } | |
214 break; | |
215 case CombiningProhibitedBreak: | |
216 lineBreakType = HB_NoBreak; | |
217 if (lcls != HB_LineBreak_SP) | |
218 goto next_no_cls_update; | |
219 case ProhibitedBreak: | |
220 default: | |
221 break; | |
222 } | |
223 } | |
224 next: | |
225 cls = ncls; | |
226 next_no_cls_update: | |
227 lcls = ncls; | |
228 grapheme = ngrapheme; | |
229 charAttributes[i-1].lineBreakType = lineBreakType; | |
230 } | |
231 charAttributes[len-1].lineBreakType = HB_ForcedBreak; | |
232 } | |
233 | |
234 // -----------------------------------------------------------------------------
--------------------------------------------------------------- | |
235 // | |
236 // Basic processing | |
237 // | |
238 // -----------------------------------------------------------------------------
--------------------------------------------------------------- | |
239 | |
240 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast) | |
241 { | |
242 int nmarks = glast - gfrom; | |
243 assert(nmarks > 0); | |
244 | |
245 HB_Glyph *glyphs = item->glyphs; | |
246 HB_GlyphAttributes *attributes = item->attributes; | |
247 | |
248 HB_GlyphMetrics baseMetrics; | |
249 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics); | |
250 | |
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; | |
255 | |
256 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast); | |
257 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseI
nfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff); | |
258 | |
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); | |
263 else | |
264 offsetBase += size; | |
265 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1; | |
266 // qDebug("offset = %f", offsetBase); | |
267 | |
268 bool rightToLeft = item->item.bidiLevel % 2; | |
269 | |
270 int i; | |
271 unsigned char lastCmb = 0; | |
272 HB_GlyphMetrics attachmentRect; | |
273 memset(&attachmentRect, 0, sizeof(attachmentRect)); | |
274 | |
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); | |
279 HB_FixedPoint p; | |
280 p.x = p.y = 0; | |
281 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y,
markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff); | |
282 | |
283 HB_Fixed offset = offsetBase; | |
284 unsigned char cmb = attributes[gfrom+i].combiningClass; | |
285 | |
286 // ### maybe the whole position determination should move down to heuris
ticSetGlyphAttributes. Would save some | |
287 // bits in the glyphAttributes structure. | |
288 if (cmb < 200) { | |
289 // fixed position classes. We approximate by mapping to one of the o
thers. | |
290 // currently I added only the ones for arabic, hebrew, lao and thai. | |
291 | |
292 // for Lao and Thai marks with class 0, see below (heuristicSetGlyph
Attributes) | |
293 | |
294 // add a bit more offset to arabic, a bit hacky | |
295 if (cmb >= 27 && cmb <= 36 && offset < 3) | |
296 offset +=1; | |
297 // below | |
298 if ((cmb >= 10 && cmb <= 18) || | |
299 cmb == 20 || cmb == 22 || | |
300 cmb == 29 || cmb == 32) | |
301 cmb = HB_Combining_Below; | |
302 // above | |
303 else if (cmb == 23 || cmb == 27 || cmb == 28 || | |
304 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36)) | |
305 cmb = HB_Combining_Above; | |
306 //below-right | |
307 else if (cmb == 9 || cmb == 103 || cmb == 118) | |
308 cmb = HB_Combining_BelowRight; | |
309 // above-right | |
310 else if (cmb == 24 || cmb == 107 || cmb == 122) | |
311 cmb = HB_Combining_AboveRight; | |
312 else if (cmb == 25) | |
313 cmb = HB_Combining_AboveLeft; | |
314 // fixed: | |
315 // 19 21 | |
316 | |
317 } | |
318 | |
319 // combining marks of different class don't interact. Reset the rectangl
e. | |
320 if (cmb != lastCmb) { | |
321 //qDebug("resetting rect"); | |
322 attachmentRect = baseMetrics; | |
323 } | |
324 | |
325 switch(cmb) { | |
326 case HB_Combining_DoubleBelow: | |
327 // ### wrong in rtl context! | |
328 case HB_Combining_BelowLeft: | |
329 p.y += offset; | |
330 case HB_Combining_BelowLeftAttached: | |
331 p.x += attachmentRect.x - markMetrics.x; | |
332 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y; | |
333 break; | |
334 case HB_Combining_Below: | |
335 p.y += offset; | |
336 case HB_Combining_BelowAttached: | |
337 p.x += attachmentRect.x - markMetrics.x; | |
338 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y; | |
339 | |
340 p.x += (attachmentRect.width - markMetrics.width) / 2; | |
341 break; | |
342 case HB_Combining_BelowRight: | |
343 p.y += offset; | |
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; | |
347 break; | |
348 case HB_Combining_Left: | |
349 p.x -= offset; | |
350 case HB_Combining_LeftAttached: | |
351 break; | |
352 case HB_Combining_Right: | |
353 p.x += offset; | |
354 case HB_Combining_RightAttached: | |
355 break; | |
356 case HB_Combining_DoubleAbove: | |
357 // ### wrong in RTL context! | |
358 case HB_Combining_AboveLeft: | |
359 p.y -= offset; | |
360 case HB_Combining_AboveLeftAttached: | |
361 p.x += attachmentRect.x - markMetrics.x; | |
362 p.y += attachmentRect.y - markMetrics.y - markMetrics.height; | |
363 break; | |
364 case HB_Combining_Above: | |
365 p.y -= offset; | |
366 case HB_Combining_AboveAttached: | |
367 p.x += attachmentRect.x - markMetrics.x; | |
368 p.y += attachmentRect.y - markMetrics.y - markMetrics.height; | |
369 | |
370 p.x += (attachmentRect.width - markMetrics.width) / 2; | |
371 break; | |
372 case HB_Combining_AboveRight: | |
373 p.y -= offset; | |
374 case HB_Combining_AboveRightAttached: | |
375 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - mar
kMetrics.width; | |
376 p.y += attachmentRect.y - markMetrics.y - markMetrics.height; | |
377 break; | |
378 | |
379 case HB_Combining_IotaSubscript: | |
380 default: | |
381 break; | |
382 } | |
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; | |
386 | |
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.wi
dth, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x; | |
391 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.h
eight, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y; | |
392 attachmentRect = unitedAttachmentRect; | |
393 | |
394 lastCmb = cmb; | |
395 if (rightToLeft) { | |
396 item->offsets[gfrom+i].x = p.x; | |
397 item->offsets[gfrom+i].y = p.y; | |
398 } else { | |
399 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset; | |
400 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset; | |
401 } | |
402 item->advances[gfrom+i] = 0; | |
403 } | |
404 } | |
405 | |
406 void HB_HeuristicPosition(HB_ShaperItem *item) | |
407 { | |
408 HB_GetGlyphAdvances(item); | |
409 HB_GlyphAttributes *attributes = item->attributes; | |
410 | |
411 int cEnd = -1; | |
412 int i = item->num_glyphs; | |
413 while (i--) { | |
414 if (cEnd == -1 && attributes[i].mark) { | |
415 cEnd = i; | |
416 } else if (cEnd != -1 && !attributes[i].mark) { | |
417 positionCluster(item, i, cEnd); | |
418 cEnd = -1; | |
419 } | |
420 } | |
421 } | |
422 | |
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) | |
427 { | |
428 const HB_UChar16 *uc = item->string + item->item.pos; | |
429 hb_uint32 length = item->item.length; | |
430 | |
431 // ### zeroWidth and justification are missing here!!!!! | |
432 | |
433 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item-
>num_glyphs); | |
434 HB_GlyphAttributes *attributes = item->attributes; | |
435 unsigned short *logClusters = item->log_clusters; | |
436 | |
437 hb_uint32 glyph_pos = 0; | |
438 hb_uint32 i; | |
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; | |
444 } else { | |
445 logClusters[i] = glyph_pos; | |
446 } | |
447 ++glyph_pos; | |
448 } | |
449 | |
450 // first char in a run is never (treated as) a mark | |
451 int cStart = 0; | |
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_IsControlCh
ar(uc[0]); | |
456 | |
457 int pos = 0; | |
458 HB_CharCategory lastCat; | |
459 int dummy; | |
460 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy); | |
461 for (i = 1; i < length; ++i) { | |
462 if (logClusters[i] == pos) | |
463 // same glyph | |
464 continue; | |
465 ++pos; | |
466 while (pos < logClusters[i]) { | |
467 attributes[pos] = attributes[pos-1]; | |
468 ++pos; | |
469 } | |
470 // hide soft-hyphens by default | |
471 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i])) | |
472 attributes[pos].dontPrint = true; | |
473 HB_CharCategory cat; | |
474 int cmb; | |
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]; | |
481 } else { | |
482 if (cmb == 0) { | |
483 // Fix 0 combining classes | |
484 if ((uc[pos] & 0xff00) == 0x0e00) { | |
485 // thai or lao | |
486 if (uc[pos] == 0xe31 || | |
487 uc[pos] == 0xe34 || | |
488 uc[pos] == 0xe35 || | |
489 uc[pos] == 0xe36 || | |
490 uc[pos] == 0xe37 || | |
491 uc[pos] == 0xe47 || | |
492 uc[pos] == 0xe4c || | |
493 uc[pos] == 0xe4d || | |
494 uc[pos] == 0xe4e) { | |
495 cmb = HB_Combining_AboveRight; | |
496 } else if (uc[pos] == 0xeb1 || | |
497 uc[pos] == 0xeb4 || | |
498 uc[pos] == 0xeb5 || | |
499 uc[pos] == 0xeb6 || | |
500 uc[pos] == 0xeb7 || | |
501 uc[pos] == 0xebb || | |
502 uc[pos] == 0xecc || | |
503 uc[pos] == 0xecd) { | |
504 cmb = HB_Combining_Above; | |
505 } else if (uc[pos] == 0xebc) { | |
506 cmb = HB_Combining_Below; | |
507 } | |
508 } | |
509 } | |
510 | |
511 attributes[pos].mark = true; | |
512 attributes[pos].clusterStart = false; | |
513 attributes[pos].combiningClass = cmb; | |
514 logClusters[i] = cStart; | |
515 } | |
516 // one gets an inter character justification point if the current char i
s 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; | |
523 else | |
524 attributes[pos-1].justification = HB_NoJustification; | |
525 | |
526 lastCat = cat; | |
527 } | |
528 pos = logClusters[length-1]; | |
529 if (lastCat == HB_Separator_Space) | |
530 attributes[pos].justification = HB_Space; | |
531 else | |
532 attributes[pos].justification = HB_Character; | |
533 } | |
534 | |
535 #ifndef NO_OPENTYPE | |
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 }, | |
540 {0, 0} | |
541 }; | |
542 #endif | |
543 | |
544 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item) | |
545 { | |
546 if (shaper_item->glyphIndicesPresent) { | |
547 shaper_item->num_glyphs = shaper_item->initialGlyphCount; | |
548 shaper_item->glyphIndicesPresent = false; | |
549 return true; | |
550 } | |
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); | |
556 } | |
557 | |
558 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item) | |
559 { | |
560 #ifndef NO_OPENTYPE | |
561 const int availableGlyphs = shaper_item->num_glyphs; | |
562 #endif | |
563 | |
564 if (!HB_ConvertStringToGlyphIndices(shaper_item)) | |
565 return false; | |
566 | |
567 HB_HeuristicSetGlyphAttributes(shaper_item); | |
568 | |
569 #ifndef NO_OPENTYPE | |
570 if (HB_SelectScript(shaper_item, basic_features)) { | |
571 HB_OpenTypeShape(shaper_item, /*properties*/0); | |
572 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters
*/true); | |
573 } | |
574 #endif | |
575 | |
576 HB_HeuristicPosition(shaper_item); | |
577 return true; | |
578 } | |
579 | |
580 const HB_ScriptEngine HB_ScriptEngines[] = { | |
581 // Common | |
582 { HB_BasicShape, 0}, | |
583 // Greek | |
584 { HB_GreekShape, 0}, | |
585 // Cyrillic | |
586 { HB_BasicShape, 0}, | |
587 // Armenian | |
588 { HB_BasicShape, 0}, | |
589 // Hebrew | |
590 { HB_HebrewShape, 0 }, | |
591 // Arabic | |
592 { HB_ArabicShape, 0}, | |
593 // Syriac | |
594 { HB_ArabicShape, 0}, | |
595 // Thaana | |
596 { HB_BasicShape, 0 }, | |
597 // Devanagari | |
598 { HB_IndicShape, HB_IndicAttributes }, | |
599 // Bengali | |
600 { HB_IndicShape, HB_IndicAttributes }, | |
601 // Gurmukhi | |
602 { HB_IndicShape, HB_IndicAttributes }, | |
603 // Gujarati | |
604 { HB_IndicShape, HB_IndicAttributes }, | |
605 // Oriya | |
606 { HB_IndicShape, HB_IndicAttributes }, | |
607 // Tamil | |
608 { HB_IndicShape, HB_IndicAttributes }, | |
609 // Telugu | |
610 { HB_IndicShape, HB_IndicAttributes }, | |
611 // Kannada | |
612 { HB_IndicShape, HB_IndicAttributes }, | |
613 // Malayalam | |
614 { HB_IndicShape, HB_IndicAttributes }, | |
615 // Sinhala | |
616 { HB_IndicShape, HB_IndicAttributes }, | |
617 // Thai | |
618 { HB_BasicShape, HB_ThaiAttributes }, | |
619 // Lao | |
620 { HB_BasicShape, 0 }, | |
621 // Tibetan | |
622 { HB_TibetanShape, HB_TibetanAttributes }, | |
623 // Myanmar | |
624 { HB_MyanmarShape, HB_MyanmarAttributes }, | |
625 // Georgian | |
626 { HB_BasicShape, 0 }, | |
627 // Hangul | |
628 { HB_HangulShape, 0 }, | |
629 // Ogham | |
630 { HB_BasicShape, 0 }, | |
631 // Runic | |
632 { HB_BasicShape, 0 }, | |
633 // Khmer | |
634 { HB_KhmerShape, HB_KhmerAttributes }, | |
635 // N'Ko | |
636 { HB_ArabicShape, 0} | |
637 }; | |
638 | |
639 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength, | |
640 const HB_ScriptItem *items, hb_uint32 numItems, | |
641 HB_CharAttributes *attributes) | |
642 { | |
643 calcLineBreaks(string, stringLength, attributes); | |
644 | |
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].charAt
tributes; | |
650 if (!attributeFunction) | |
651 continue; | |
652 attributeFunction(script, string, items[i].pos, items[i].length, attribu
tes); | |
653 } | |
654 } | |
655 | |
656 | |
657 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 }; | |
658 | |
659 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNum
Let + 1] = { | |
660 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendN
umLet | |
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 | |
669 }; | |
670 | |
671 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength, | |
672 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*
/, | |
673 HB_CharAttributes *attributes) | |
674 { | |
675 if (stringLength == 0) | |
676 return; | |
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; | |
682 continue; | |
683 } | |
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); | |
687 continue; | |
688 } | |
689 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk]; | |
690 if (rule == Middle) { | |
691 rule = Break; | |
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[look
ahead]) != HB_Sentence_Sep) { | |
696 ++lookahead; | |
697 continue; | |
698 } | |
699 if (testbrk == brk) { | |
700 rule = NoBreak; | |
701 while (i < lookahead) | |
702 attributes[i++].wordBoundary = false; | |
703 nbrk = testbrk; | |
704 } | |
705 break; | |
706 } | |
707 } | |
708 attributes[i].wordBoundary = (rule == Break); | |
709 brk = nbrk; | |
710 } | |
711 } | |
712 | |
713 | |
714 enum SentenceBreakStates { | |
715 SB_Initial, | |
716 SB_Upper, | |
717 SB_UpATerm, | |
718 SB_ATerm, | |
719 SB_ATermC, | |
720 SB_ACS, | |
721 SB_STerm, | |
722 SB_STermC, | |
723 SB_SCS, | |
724 SB_BAfter, | |
725 SB_Break, | |
726 SB_Look | |
727 }; | |
728 | |
729 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Clos
e + 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 | |
733 | |
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, | |
738 | |
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, | |
743 }; | |
744 | |
745 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength, | |
746 const HB_ScriptItem * /*items*/, hb_uint32 /*numIt
ems*/, | |
747 HB_CharAttributes *attributes) | |
748 { | |
749 if (stringLength == 0) | |
750 return; | |
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; | |
756 continue; | |
757 } | |
758 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])]; | |
759 if (brk == SB_Look) { | |
760 brk = SB_Break; | |
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) { | |
765 break; | |
766 } else if (sbrk == HB_Sentence_Lower) { | |
767 brk = SB_Initial; | |
768 break; | |
769 } | |
770 ++lookahead; | |
771 } | |
772 if (brk == SB_Initial) { | |
773 while (i < lookahead) | |
774 attributes[i++].sentenceBoundary = false; | |
775 } | |
776 } | |
777 if (brk == SB_Break) { | |
778 attributes[i].sentenceBoundary = true; | |
779 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])]
; | |
780 } else { | |
781 attributes[i].sentenceBoundary = false; | |
782 } | |
783 } | |
784 } | |
785 | |
786 | |
787 static inline char *tag_to_string(HB_UInt tag) | |
788 { | |
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; | |
794 string[4] = 0; | |
795 return string; | |
796 } | |
797 | |
798 #ifdef OT_DEBUG | |
799 static void dump_string(HB_Buffer buffer) | |
800 { | |
801 for (uint i = 0; i < buffer->in_length; ++i) { | |
802 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_str
ing[i].cluster); | |
803 } | |
804 } | |
805 #define DEBUG printf | |
806 #else | |
807 #define DEBUG if (1) ; else printf | |
808 #endif | |
809 | |
810 #define DefaultLangSys 0xffff | |
811 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T') | |
812 | |
813 enum { | |
814 RequiresGsub = 1, | |
815 RequiresGpos = 2 | |
816 }; | |
817 | |
818 struct OTScripts { | |
819 unsigned int tag; | |
820 int flags; | |
821 }; | |
822 static const OTScripts ot_scripts [] = { | |
823 // Common | |
824 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 }, | |
825 // Greek | |
826 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 }, | |
827 // Cyrillic | |
828 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 }, | |
829 // Armenian | |
830 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 }, | |
831 // Hebrew | |
832 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 }, | |
833 // Arabic | |
834 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 }, | |
835 // Syriac | |
836 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 }, | |
837 // Thaana | |
838 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 }, | |
839 // Devanagari | |
840 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 }, | |
841 // Bengali | |
842 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 }, | |
843 // Gurmukhi | |
844 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 }, | |
845 // Gujarati | |
846 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 }, | |
847 // Oriya | |
848 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 }, | |
849 // Tamil | |
850 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 }, | |
851 // Telugu | |
852 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 }, | |
853 // Kannada | |
854 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 }, | |
855 // Malayalam | |
856 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 }, | |
857 // Sinhala | |
858 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 }, | |
859 // Thai | |
860 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 }, | |
861 // Lao | |
862 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 }, | |
863 // Tibetan | |
864 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 }, | |
865 // Myanmar | |
866 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 }, | |
867 // Georgian | |
868 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 }, | |
869 // Hangul | |
870 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 }, | |
871 // Ogham | |
872 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 }, | |
873 // Runic | |
874 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 }, | |
875 // Khmer | |
876 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 }, | |
877 // N'Ko | |
878 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 } | |
879 }; | |
880 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) }; | |
881 | |
882 static HB_Bool checkScript(HB_Face face, int script) | |
883 { | |
884 assert(script < HB_ScriptCount); | |
885 | |
886 if (!face->gsub && !face->gpos) | |
887 return false; | |
888 | |
889 unsigned int tag = ot_scripts[script].tag; | |
890 int requirements = ot_scripts[script].flags; | |
891 | |
892 if (requirements & RequiresGsub) { | |
893 if (!face->gsub) | |
894 return false; | |
895 | |
896 HB_UShort script_index; | |
897 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index); | |
898 if (error) { | |
899 DEBUG("could not select script %d in GSub table: %d", (int)script, e
rror); | |
900 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L',
'T'), &script_index); | |
901 if (error) | |
902 return false; | |
903 } | |
904 } | |
905 | |
906 if (requirements & RequiresGpos) { | |
907 if (!face->gpos) | |
908 return false; | |
909 | |
910 HB_UShort script_index; | |
911 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index
); | |
912 if (error) { | |
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); | |
915 if (error) | |
916 return false; | |
917 } | |
918 | |
919 } | |
920 return true; | |
921 } | |
922 | |
923 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Ta
g tag) | |
924 { | |
925 HB_Error error; | |
926 HB_UInt length = 0; | |
927 HB_Stream stream = 0; | |
928 | |
929 if (!font) | |
930 return 0; | |
931 | |
932 error = tableFunc(font, tag, 0, &length); | |
933 if (error) | |
934 return 0; | |
935 stream = (HB_Stream)malloc(sizeof(HB_StreamRec)); | |
936 if (!stream) | |
937 return 0; | |
938 stream->base = (HB_Byte*)malloc(length); | |
939 if (!stream->base) { | |
940 free(stream); | |
941 return 0; | |
942 } | |
943 error = tableFunc(font, tag, stream->base, &length); | |
944 if (error) { | |
945 _hb_close_stream(stream); | |
946 return 0; | |
947 } | |
948 stream->size = length; | |
949 stream->pos = 0; | |
950 stream->cursor = NULL; | |
951 return stream; | |
952 } | |
953 | |
954 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc) | |
955 { | |
956 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec)); | |
957 if (!face) | |
958 return 0; | |
959 | |
960 face->isSymbolFont = false; | |
961 face->gdef = 0; | |
962 face->gpos = 0; | |
963 face->gsub = 0; | |
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; | |
970 face->buffer = 0; | |
971 | |
972 HB_Error error = HB_Err_Ok; | |
973 HB_Stream stream; | |
974 HB_Stream gdefStream; | |
975 | |
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); | |
980 face->gdef = 0; | |
981 } | |
982 | |
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))) { | |
987 face->gsub = 0; | |
988 if (error != HB_Err_Not_Covered) { | |
989 //DEBUG("error loading gsub table: %d", error); | |
990 } else { | |
991 //DEBUG("face doesn't have a gsub table"); | |
992 } | |
993 } | |
994 _hb_close_stream(stream); | |
995 | |
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))) { | |
999 face->gpos = 0; | |
1000 DEBUG("error loading gpos table: %d", error); | |
1001 } | |
1002 _hb_close_stream(stream); | |
1003 | |
1004 _hb_close_stream(gdefStream); | |
1005 | |
1006 for (unsigned int i = 0; i < HB_ScriptCount; ++i) | |
1007 face->supported_scripts[i] = checkScript(face, i); | |
1008 | |
1009 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) { | |
1010 HB_FreeFace(face); | |
1011 return 0; | |
1012 } | |
1013 | |
1014 return face; | |
1015 } | |
1016 | |
1017 void HB_FreeFace(HB_Face face) | |
1018 { | |
1019 if (!face) | |
1020 return; | |
1021 if (face->gpos) | |
1022 HB_Done_GPOS_Table(face->gpos); | |
1023 if (face->gsub) | |
1024 HB_Done_GSUB_Table(face->gsub); | |
1025 if (face->gdef) | |
1026 HB_Done_GDEF_Table(face->gdef); | |
1027 if (face->buffer) | |
1028 hb_buffer_free(face->buffer); | |
1029 if (face->tmpAttributes) | |
1030 free(face->tmpAttributes); | |
1031 if (face->tmpLogClusters) | |
1032 free(face->tmpLogClusters); | |
1033 free(face); | |
1034 } | |
1035 | |
1036 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *fe
atures) | |
1037 { | |
1038 HB_Script script = shaper_item->item.script; | |
1039 | |
1040 if (!shaper_item->face->supported_scripts[script]) | |
1041 return false; | |
1042 | |
1043 HB_Face face = shaper_item->face; | |
1044 if (face->current_script == script && face->current_flags == shaper_item->sh
aperFlags) | |
1045 return true; | |
1046 | |
1047 face->current_script = script; | |
1048 face->current_flags = shaper_item->shaperFlags; | |
1049 | |
1050 assert(script < HB_ScriptCount); | |
1051 // find script in our list of supported scripts. | |
1052 unsigned int tag = ot_scripts[script].tag; | |
1053 | |
1054 if (face->gsub && features) { | |
1055 #ifdef OT_DEBUG | |
1056 { | |
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)); | |
1063 } | |
1064 } | |
1065 #endif | |
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); | |
1069 if (!error) { | |
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); | |
1074 if (!error) { | |
1075 DEBUG(" adding feature %s", tag_to_string(features->tag)); | |
1076 HB_GSUB_Add_Feature(face->gsub, feature_index, features->pro
perty); | |
1077 } | |
1078 ++features; | |
1079 } | |
1080 } | |
1081 } | |
1082 | |
1083 // reset | |
1084 face->has_opentype_kerning = false; | |
1085 | |
1086 if (face->gpos) { | |
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); | |
1090 if (!error) { | |
1091 #ifdef OT_DEBUG | |
1092 { | |
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_ind
ex, 0xffff, &feature_index); | |
1100 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag)); | |
1101 } | |
1102 } | |
1103 #endif | |
1104 HB_UInt *feature_tag_list_buffer; | |
1105 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &fe
ature_tag_list_buffer); | |
1106 if (!error) { | |
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) { | |
1112 ++feature_tag_list; | |
1113 continue; | |
1114 } | |
1115 face->has_opentype_kerning = true; | |
1116 } | |
1117 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list
, script_index, 0xffff, &feature_index); | |
1118 if (!error) | |
1119 HB_GPOS_Add_Feature(face->gpos, feature_index, Positioni
ngProperties); | |
1120 ++feature_tag_list; | |
1121 } | |
1122 FREE(feature_tag_list_buffer); | |
1123 } | |
1124 } | |
1125 } | |
1126 | |
1127 return true; | |
1128 } | |
1129 | |
1130 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties) | |
1131 { | |
1132 HB_GlyphAttributes *tmpAttributes; | |
1133 unsigned int *tmpLogClusters; | |
1134 | |
1135 HB_Face face = item->face; | |
1136 | |
1137 face->length = item->num_glyphs; | |
1138 | |
1139 hb_buffer_clear(face->buffer); | |
1140 | |
1141 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->le
ngth*sizeof(HB_GlyphAttributes)); | |
1142 if (!tmpAttributes) | |
1143 return false; | |
1144 face->tmpAttributes = tmpAttributes; | |
1145 | |
1146 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length
*sizeof(unsigned int)); | |
1147 if (!tmpLogClusters) | |
1148 return false; | |
1149 face->tmpLogClusters = tmpLogClusters; | |
1150 | |
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 ? properti
es[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]; | |
1157 } | |
1158 | |
1159 #ifdef OT_DEBUG | |
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); | |
1168 #endif | |
1169 | |
1170 face->glyphs_substituted = false; | |
1171 if (face->gsub) { | |
1172 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer); | |
1173 if (error && error != HB_Err_Not_Covered) | |
1174 return false; | |
1175 face->glyphs_substituted = (error != HB_Err_Not_Covered); | |
1176 } | |
1177 | |
1178 #ifdef OT_DEBUG | |
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); | |
1187 #endif | |
1188 | |
1189 return true; | |
1190 } | |
1191 | |
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) | |
1195 { | |
1196 HB_UShort property; | |
1197 | |
1198 if (!item->face->gdef) | |
1199 return; | |
1200 | |
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], &prop
erty) == HB_Err_Ok && | |
1204 property == HB_GDEF_MARK) { | |
1205 item->advances[i] = 0; | |
1206 } | |
1207 } | |
1208 } | |
1209 | |
1210 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool do
LogClusters) | |
1211 { | |
1212 HB_Face face = item->face; | |
1213 | |
1214 bool glyphs_positioned = false; | |
1215 if (face->gpos) { | |
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->c
urrent_flags, face->buffer, false, false) != HB_Err_Not_Covered; | |
1220 } | |
1221 | |
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 | |
1227 } | |
1228 | |
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; | |
1232 return false; | |
1233 } | |
1234 | |
1235 HB_Glyph *glyphs = item->glyphs; | |
1236 HB_GlyphAttributes *attributes = item->attributes; | |
1237 | |
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; | |
1243 } | |
1244 item->num_glyphs = face->buffer->in_length; | |
1245 | |
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; | |
1250 int oldCi = 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; | |
1260 clusterStart = i; | |
1261 oldCi = ci; | |
1262 } | |
1263 } | |
1264 for (int j = oldCi; j < face->length; j++) | |
1265 logClusters[j] = clusterStart; | |
1266 } | |
1267 | |
1268 // calulate the advances for the shaped glyphs | |
1269 // DEBUG("unpositioned: "); | |
1270 | |
1271 // positioning code: | |
1272 if (glyphs_positioned) { | |
1273 HB_GetGlyphAdvances(item); | |
1274 HB_Position positions = face->buffer->positions; | |
1275 HB_Fixed *advances = item->advances; | |
1276 | |
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_a
dvance >> 6), | |
1282 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >
> 6), | |
1283 // positions[i].back, positions[i].new_advance); | |
1284 | |
1285 HB_Fixed adjustment = positions[i].x_advance; | |
1286 | |
1287 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics)) | |
1288 adjustment = HB_FIXED_ROUND(adjustment); | |
1289 | |
1290 if (positions[i].new_advance == 0) | |
1291 advances[i] += adjustment; | |
1292 | |
1293 int back = 0; | |
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; | |
1301 } | |
1302 offsets[i].y = -offsets[i].y; | |
1303 | |
1304 if (item->item.bidiLevel % 2) { | |
1305 // ### may need to go back multiple glyphs like in ltr | |
1306 back = positions[i].back; | |
1307 while (back--) | |
1308 offsets[i].x -= advances[i-back]; | |
1309 } else { | |
1310 back = 0; | |
1311 while (positions[i - back].back) { | |
1312 back += positions[i - back].back; | |
1313 offsets[i].x -= advances[i-back]; | |
1314 } | |
1315 } | |
1316 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)", | |
1317 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), g
lyphs[i].offset.y.toInt()); | |
1318 } | |
1319 item->kerning_applied = face->has_opentype_kerning; | |
1320 } else { | |
1321 HB_HeuristicPosition(item); | |
1322 } | |
1323 | |
1324 #ifdef OT_DEBUG | |
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]); | |
1329 } | |
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 adv
ance=%d/%d offset=%d/%d", | |
1333 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attri
butes.mark, | |
1334 glyphs[i].attributes.combiningClass, glyphs[i].attributes.cluster
Start, | |
1335 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(), | |
1336 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt()); | |
1337 DEBUG("-----------------------------------------"); | |
1338 #endif | |
1339 return true; | |
1340 } | |
1341 | |
1342 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item) | |
1343 { | |
1344 HB_Bool result = false; | |
1345 if (shaper_item->num_glyphs < shaper_item->item.length) { | |
1346 shaper_item->num_glyphs = shaper_item->item.length; | |
1347 return false; | |
1348 } | |
1349 assert(shaper_item->item.script < HB_ScriptCount); | |
1350 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item); | |
1351 shaper_item->glyphIndicesPresent = false; | |
1352 return result; | |
1353 } | |
OLD | NEW |