PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp

https://bitbucket.org/gcubar/qt
C++ | 1423 lines | 1063 code | 145 blank | 215 comment | 328 complexity | ed4e37d6ca779a5ca1b0dca121ec3756 MD5 | raw file
Possible License(s): CC0-1.0, CC-BY-SA-4.0, LGPL-2.1, GPL-3.0, Apache-2.0, LGPL-2.0, LGPL-3.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file