| 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 |