GlyphLayout.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. //MIT, 2016-present, WinterDev
  2. using System;
  3. using System.Collections.Generic;
  4. using Typography.OpenFont;
  5. namespace Typography.TextLayout
  6. {
  7. /// <summary>
  8. /// unscaled glyph-plan
  9. /// </summary>
  10. public readonly struct UnscaledGlyphPlan
  11. {
  12. public readonly ushort input_cp_offset;
  13. public readonly ushort glyphIndex;
  14. public UnscaledGlyphPlan(ushort input_cp_offset, ushort glyphIndex, short advanceW, short offsetX, short offsetY)
  15. {
  16. this.input_cp_offset = input_cp_offset;
  17. this.glyphIndex = glyphIndex;
  18. this.OffsetX = offsetX;
  19. this.OffsetY = offsetY;
  20. this.AdvanceX = advanceW;
  21. }
  22. public readonly short AdvanceX;
  23. /// <summary>
  24. /// x offset from current position
  25. /// </summary>
  26. public readonly short OffsetX;
  27. /// <summary>
  28. /// y offset from current position
  29. /// </summary>
  30. public readonly short OffsetY;
  31. public bool AdvanceMoveForward => this.AdvanceX > 0;
  32. #if DEBUG
  33. public override string ToString()
  34. {
  35. return " adv:" + AdvanceX;
  36. }
  37. #endif
  38. }
  39. public interface IUnscaledGlyphPlanList
  40. {
  41. void Append(UnscaledGlyphPlan glyphPlan);
  42. int Count { get; }
  43. UnscaledGlyphPlan this[int index] { get; }
  44. }
  45. public class UnscaledGlyphPlanList : IUnscaledGlyphPlanList
  46. {
  47. readonly List<UnscaledGlyphPlan> _list = new List<UnscaledGlyphPlan>();
  48. public int Count => _list.Count;
  49. public UnscaledGlyphPlan this[int index] => _list[index];
  50. public void Clear()
  51. {
  52. _list.Clear();
  53. }
  54. public void Append(UnscaledGlyphPlan glyphPlan)
  55. {
  56. _list.Add(glyphPlan);
  57. }
  58. }
  59. /// <summary>
  60. /// unscaled glyph-plan sequence
  61. /// </summary>
  62. public struct GlyphPlanSequence
  63. {
  64. //
  65. public static GlyphPlanSequence Empty = new GlyphPlanSequence();
  66. //
  67. readonly IUnscaledGlyphPlanList _glyphPlanList;
  68. public readonly int startAt;
  69. public readonly ushort len;
  70. bool _isRTL;
  71. public GlyphPlanSequence(IUnscaledGlyphPlanList glyphPlanList)
  72. {
  73. _glyphPlanList = glyphPlanList;
  74. this.startAt = 0;
  75. this.len = (ushort)glyphPlanList.Count;
  76. _isRTL = false;
  77. }
  78. public GlyphPlanSequence(IUnscaledGlyphPlanList glyphPlanList, int startAt, int len)
  79. {
  80. _glyphPlanList = glyphPlanList;
  81. this.startAt = startAt;
  82. this.len = (ushort)len;
  83. _isRTL = false;
  84. }
  85. public bool IsRightToLeft
  86. {
  87. get => _isRTL;
  88. set => _isRTL = value;
  89. }
  90. public UnscaledGlyphPlan this[int index]
  91. {
  92. get
  93. {
  94. if (index >= 0 && index < (startAt + len))
  95. {
  96. return _glyphPlanList[startAt + index];
  97. }
  98. else
  99. {
  100. throw new IndexOutOfRangeException();
  101. }
  102. }
  103. }
  104. //
  105. public int Count => (_glyphPlanList != null) ? len : 0;
  106. //
  107. public float CalculateWidth()
  108. {
  109. if (_glyphPlanList == null) return 0;
  110. //
  111. IUnscaledGlyphPlanList plans = _glyphPlanList;
  112. int end = startAt + len;
  113. float width = 0;
  114. for (int i = startAt; i < end; ++i)
  115. {
  116. width += plans[i].AdvanceX;
  117. }
  118. return width;
  119. }
  120. public bool IsEmpty() => _glyphPlanList == null;
  121. }
  122. public enum PositionTechnique
  123. {
  124. None,
  125. /// <summary>
  126. /// use kerning table (old)
  127. /// </summary>
  128. Kerning, //old technique-- TODO: review and remove this
  129. /// <summary>
  130. /// use openfont gpos table
  131. /// </summary>
  132. OpenFont,
  133. }
  134. class GlyphLayoutPlanCollection
  135. {
  136. readonly Dictionary<int, GlyphLayoutPlanContext> _collection = new Dictionary<int, GlyphLayoutPlanContext>();
  137. /// <summary>
  138. /// get glyph layout plan or create if not exists
  139. /// </summary>
  140. /// <param name="typeface"></param>
  141. /// <param name="scriptLang"></param>
  142. /// <returns></returns>
  143. public GlyphLayoutPlanContext GetPlanOrCreate(Typeface typeface, ScriptLang scriptLang)
  144. {
  145. int hash_code = CalculateHash(typeface, scriptLang);
  146. if (!_collection.TryGetValue(hash_code, out GlyphLayoutPlanContext context))
  147. {
  148. var g_sub = (typeface.GSUBTable != null) ? new GlyphSubstitution(typeface, scriptLang.scriptTag, scriptLang.sysLangTag) : null;
  149. var g_pos = (typeface.GPOSTable != null) ? new GlyphSetPosition(typeface, scriptLang.scriptTag, scriptLang.sysLangTag) : null;
  150. #if DEBUG
  151. if (g_sub != null)
  152. {
  153. g_sub.dbugScriptLang = scriptLang.ToString();
  154. }
  155. if (g_pos != null)
  156. {
  157. g_pos.dbugScriptLang = scriptLang.ToString();
  158. }
  159. #endif
  160. _collection.Add(hash_code, context = new GlyphLayoutPlanContext(g_sub, g_pos));
  161. }
  162. return context;
  163. }
  164. static int CalculateHash(Typeface t, ScriptLang scriptLang)
  165. {
  166. int hash = 17;
  167. hash = hash * 31 + t.GetHashCode();
  168. hash = hash * 31 + scriptLang.scriptTag.GetHashCode();
  169. hash = hash * 31 + scriptLang.sysLangTag.GetHashCode();
  170. return hash;
  171. }
  172. }
  173. readonly struct GlyphLayoutPlanContext
  174. {
  175. public readonly GlyphSubstitution _glyphSub;
  176. public readonly GlyphSetPosition _glyphPos;
  177. public GlyphLayoutPlanContext(GlyphSubstitution glyphSub, GlyphSetPosition glyphPos)
  178. {
  179. _glyphSub = glyphSub;
  180. _glyphPos = glyphPos;
  181. }
  182. }
  183. #if DEBUG
  184. readonly struct dbugCodePointFromUserChar
  185. {
  186. /// <summary>
  187. /// input codepoint
  188. /// </summary>
  189. public readonly int codePoint;
  190. /// <summary>
  191. /// offset from the start of input codepoint buffer
  192. /// </summary>
  193. public readonly ushort user_char_offset;
  194. public dbugCodePointFromUserChar(ushort user_char_offset, int codePoint)
  195. {
  196. this.user_char_offset = user_char_offset;
  197. this.codePoint = codePoint;
  198. }
  199. }
  200. #endif
  201. //TODO: rename this to ShapingEngine ?
  202. /// <summary>
  203. /// text span's glyph layout engine,
  204. /// </summary>
  205. public class GlyphLayout
  206. {
  207. readonly GlyphLayoutPlanCollection _layoutPlanCollection = new GlyphLayoutPlanCollection();
  208. Typeface _typeface;
  209. ScriptLang _scriptLang;
  210. GlyphSubstitution _gsub;
  211. GlyphSetPosition _gpos;
  212. bool _needPlanUpdate;
  213. readonly GlyphIndexList _inputGlyphs = new GlyphIndexList();//reusable input glyph
  214. readonly GlyphPosStream _glyphPositions = new GlyphPosStream();
  215. readonly static ScriptLang s_latin = new ScriptLang("latn");
  216. readonly static ScriptLang s_math = new ScriptLang("math");
  217. public GlyphLayout()
  218. {
  219. PositionTechnique = PositionTechnique.OpenFont;
  220. EnableLigature = true;
  221. EnableComposition = true;
  222. EnableGsub = EnableGpos = true;
  223. ScriptLang = s_latin;
  224. }
  225. //unscaled version
  226. internal IGlyphPositions ResultUnscaledGlyphPositions => _glyphPositions;
  227. //
  228. public PositionTechnique PositionTechnique { get; set; }
  229. public ScriptLang ScriptLang
  230. {
  231. get => _scriptLang;
  232. set
  233. {
  234. if (_scriptLang.scriptTag != value.scriptTag || _scriptLang.sysLangTag != value.sysLangTag)
  235. {
  236. _needPlanUpdate = true;
  237. }
  238. _scriptLang = value;
  239. }
  240. }
  241. public bool EnableLigature { get; set; }
  242. public bool EnableComposition { get; set; }
  243. //for some built-in mathlayout
  244. public bool EnableBuiltinMathItalicCorrection { get; set; } = true;
  245. public Typeface Typeface
  246. {
  247. get => _typeface;
  248. set
  249. {
  250. if (_typeface != value)
  251. {
  252. _typeface = value;
  253. _needPlanUpdate = true;
  254. }
  255. }
  256. }
  257. public delegate ushort GlyphNotFoundHandler(GlyphLayout glyphLayout, int codepoint, int nextcodepoint);
  258. GlyphNotFoundHandler _glyphNotFoundHandler;
  259. //not thread-safe***
  260. List<int> _reusableUserCodePoints = new List<int>();
  261. #if DEBUG
  262. List<dbugCodePointFromUserChar> _dbugReusableCodePointFromUserCharList = new List<dbugCodePointFromUserChar>();
  263. #endif
  264. public bool EnableGsub { get; set; }
  265. public bool EnableGpos { get; set; }
  266. /// <summary>
  267. /// do glyph shaping and glyph out, output is unscaled glyph-plan
  268. /// </summary>
  269. /// <param name="str"></param>
  270. /// <param name="startAt"></param>
  271. /// <param name="len"></param>
  272. public void Layout(
  273. char[] str,
  274. int startAt,
  275. int len)
  276. {
  277. //[A]
  278. //convert from char[] to codepoint-list
  279. // this is important!
  280. // -----------------------
  281. // from @samhocevar's PR: (https://github.com/LayoutFarm/Typography/pull/56/commits/b71c7cf863531ebf5caa478354d3249bde40b96e)
  282. // In many places, "char" is not a valid type to handle characters, because it
  283. // only supports 16 bits.In order to handle the full range of Unicode characters,
  284. // we need to use "int".
  285. // This allows characters such as 🙌 or 𐐷 or to be treated as single codepoints even
  286. // though they are encoded as two "char"s in a C# string.
  287. _reusableUserCodePoints.Clear();
  288. #if DEBUG
  289. _dbugReusableCodePointFromUserCharList.Clear();
  290. if (str.Length > 2)
  291. {
  292. }
  293. #endif
  294. for (int i = 0; i < len; ++i)
  295. {
  296. char ch = str[startAt + i];
  297. int codepoint = ch;
  298. if (ch >= 0xd800 && ch <= 0xdbff && i + 1 < len)//high surrogate
  299. {
  300. char nextCh = str[startAt + i + 1];
  301. if (nextCh >= 0xdc00 && nextCh <= 0xdfff) //low-surrogate
  302. {
  303. //please note:
  304. //num of codepoint may be less than original user input char
  305. ++i;
  306. codepoint = char.ConvertToUtf32(ch, nextCh);
  307. }
  308. }
  309. _reusableUserCodePoints.Add(codepoint);
  310. #if DEBUG
  311. _dbugReusableCodePointFromUserCharList.Add(new dbugCodePointFromUserChar((ushort)i, codepoint));
  312. #endif
  313. }
  314. Layout(_reusableUserCodePoints);
  315. }
  316. public void SetGlyphIndexNotFoundHandler(GlyphNotFoundHandler glyphNotFoundHandler)
  317. {
  318. _glyphNotFoundHandler = glyphNotFoundHandler;
  319. }
  320. public void Layout(IList<int> inputCodePoints)
  321. {
  322. Layout(inputCodePoints, 0, inputCodePoints.Count);
  323. }
  324. public void Layout(IList<int> inputCodePoints, int startAt, int len)
  325. {
  326. //
  327. //[B]
  328. // convert codepoint-list to input glyph-list
  329. // clear before use
  330. _inputGlyphs.Clear();
  331. int end = startAt + len;
  332. int cur_codepoint, next_codepoint;
  333. for (int i = 0; i < end; ++i)
  334. {
  335. //find glyph index by specific codepoint
  336. if (i + 1 < end)
  337. {
  338. cur_codepoint = inputCodePoints[i];
  339. next_codepoint = inputCodePoints[i + 1];
  340. }
  341. else
  342. {
  343. cur_codepoint = inputCodePoints[i];
  344. next_codepoint = 0;
  345. }
  346. ushort glyphIndex = _typeface.GetGlyphIndex(cur_codepoint, next_codepoint, out bool skipNextCodepoint);
  347. if (glyphIndex == 0 && _glyphNotFoundHandler != null)
  348. {
  349. //handle glyph not found
  350. glyphIndex = _glyphNotFoundHandler(this, cur_codepoint, next_codepoint);
  351. }
  352. _inputGlyphs.AddGlyph(i, glyphIndex);
  353. if (skipNextCodepoint)
  354. {
  355. // Maybe this is a UVS sequence; in that case,
  356. //***SKIP*** the second codepoint
  357. ++i;
  358. }
  359. }
  360. //continue below...
  361. Layout(_inputGlyphs);
  362. }
  363. void Layout(GlyphIndexList glyphs)
  364. {
  365. if (_needPlanUpdate)
  366. {
  367. UpdateLayoutPlan();
  368. }
  369. //[C]
  370. //----------------------------------------------
  371. //glyph substitution
  372. if (EnableGsub && _gsub != null && glyphs.Count > 0)
  373. {
  374. //TODO: review perf here
  375. _gsub.EnableLigation = this.EnableLigature;
  376. _gsub.EnableComposition = this.EnableComposition;
  377. _gsub.DoSubstitution(glyphs);
  378. }
  379. //----------------------------------------------
  380. //after glyph substitution,
  381. //number of input glyph MAY changed (increase or decrease).***
  382. //so count again.
  383. int finalGlyphCount = glyphs.Count;
  384. //----------------------------------------------
  385. //[D]
  386. //glyph position
  387. _glyphPositions.Clear();
  388. _glyphPositions.Typeface = _typeface;
  389. for (int i = 0; i < finalGlyphCount; ++i)
  390. {
  391. //at this stage _inputGlyphs and _glyphPositions
  392. //has member 1:1
  393. glyphs.GetGlyphIndexAndMap(i,
  394. out ushort glyphIndex,
  395. out ushort input_codepointOffset,
  396. out ushort input_mapLen);
  397. //
  398. Glyph orgGlyph = _typeface.GetGlyph(glyphIndex);
  399. //this is original value WITHOUT fit-to-grid adjust
  400. _glyphPositions.AddGlyph(input_codepointOffset, glyphIndex, orgGlyph);
  401. }
  402. PositionTechnique posTech = this.PositionTechnique;
  403. if (EnableGpos && _gpos != null && glyphs.Count > 0 && posTech == PositionTechnique.OpenFont)
  404. {
  405. _gpos.DoGlyphPosition(_glyphPositions);
  406. }
  407. //----------------------------------------------
  408. //[E]
  409. //some math correction
  410. if (_scriptLang.scriptTag == s_math.scriptTag) //***
  411. {
  412. if (EnableBuiltinMathItalicCorrection)
  413. {
  414. int pos_count = _glyphPositions.Count;
  415. //from https://docs.microsoft.com/en-us/typography/opentype/spec/math
  416. //Italics correction can be used in the following situations:
  417. //...
  418. //...
  419. //When a run of slanted characters is followed by a straight character (such as an operator or a delimiter), the italics correction of the last glyph is added to its advance width.
  420. //...
  421. //...
  422. //@prepare: note, by observation.
  423. //in math font (eg. like latin modern) glyph some glyph look upright (regular glyph)
  424. //but it has italic correction value
  425. //but value is very small when compare to actual italic glyph
  426. //so in this case we assume it is not italic
  427. //so which is that cut-point value
  428. //I use assumption that if the correction value is too small
  429. //after scale less than 1 px it should not be significant,
  430. //but inside GlyphLayout, we use unscale version,
  431. //so assume if font is 8pts, if correction give value less than 0.33px (subpixel width)=> NOT sig.
  432. //none_sig_correction > scale_to_px * original_correction
  433. //0.33f > scale_to_px(8pt) * original_correction
  434. //(0.33f/ scale_to_px(8pt)) > original_correction
  435. float none_sig_correction = 0.33f / _typeface.CalculateScaleToPixelFromPointSize(8);//assume at 8 pt size font
  436. short prevGlyph_italic_correction = 0;
  437. for (int i = 0; i < pos_count; ++i)
  438. {
  439. Glyph glyph = _typeface.GetGlyph(_glyphPositions[i].glyphIndex);
  440. if (glyph?.MathGlyphInfo?.ItalicCorrection is OpenFont.MathGlyphs.MathValueRecord value &&
  441. value.Value > none_sig_correction)
  442. {
  443. //sig correction
  444. prevGlyph_italic_correction = value.Value;
  445. }
  446. else
  447. {
  448. //no correct (or from nonsignificant correct above)
  449. if (prevGlyph_italic_correction != 0)
  450. {
  451. _glyphPositions.AppendGlyphAdvance(i - 1, prevGlyph_italic_correction, 0);
  452. }
  453. prevGlyph_italic_correction = 0;
  454. }
  455. }
  456. }
  457. //other correction...
  458. }
  459. //----------------------------------------------
  460. //at this point, all positions are layouted at its original scale ***
  461. //then we will scale it to target scale later
  462. //----------------------------------------------
  463. }
  464. /// <summary>
  465. /// generate map from user codepoint buffer to output glyph index, from latest layout result
  466. /// </summary>
  467. /// <param name="outputUserCharToGlyphIndexMapList"></param>
  468. public void CreateMapFromUserCharToGlyphIndices(List<UserCodePointToGlyphIndex> outputUserCharToGlyphIndexMapList)
  469. {
  470. //1. get map from user-input-codepoint to glyph-index
  471. _inputGlyphs.CreateMapFromUserCodePointToGlyphIndices(outputUserCharToGlyphIndexMapList);
  472. ////TODO:
  473. ////2.
  474. ////since some user-input-codepoints may be skiped in codepoint-to-glyph index lookup (see this.Layout(), [A])
  475. //int j = outputUserCharToGlyphIndexMapList.Count;
  476. //for (int i = 0; i < j; ++i)
  477. //{
  478. // UserCodePointToGlyphIndex userCodePointToGlyphIndex = outputUserCharToGlyphIndexMapList[i];
  479. // CodePointFromUserChar codePointFromUserChar = _reusableCodePointFromUserCharList[userCodePointToGlyphIndex.userCodePointIndex];
  480. //}
  481. }
  482. void UpdateLayoutPlan()
  483. {
  484. GlyphLayoutPlanContext context = _layoutPlanCollection.GetPlanOrCreate(_typeface, _scriptLang);
  485. _gpos = context._glyphPos;
  486. _gsub = context._glyphSub;
  487. _needPlanUpdate = false;
  488. }
  489. /// <summary>
  490. /// fetch layout result, unscaled version, put to IUnscaledGlyphPlanList
  491. /// </summary>
  492. /// <param name="glyphPositions"></param>
  493. /// <param name="pxscale"></param>
  494. /// <param name="outputGlyphPlanList"></param>
  495. public void GenerateUnscaledGlyphPlans(IUnscaledGlyphPlanList outputGlyphPlanList)
  496. {
  497. IGlyphPositions glyphPositions = _glyphPositions;
  498. int finalGlyphCount = glyphPositions.Count;
  499. for (int i = 0; i < finalGlyphCount; ++i)
  500. {
  501. ushort glyphIndex = glyphPositions.GetGlyph(i,
  502. out ushort input_offset,
  503. out short offsetX,
  504. out short offsetY,
  505. out short advW);
  506. //
  507. outputGlyphPlanList.Append(new UnscaledGlyphPlan(
  508. input_offset,
  509. glyphIndex,
  510. advW,
  511. offsetX,
  512. offsetY
  513. ));
  514. }
  515. }
  516. public IEnumerable<UnscaledGlyphPlan> GetUnscaledGlyphPlanIter()
  517. {
  518. //this for iterator version
  519. IGlyphPositions glyphPositions = _glyphPositions;
  520. int finalGlyphCount = glyphPositions.Count;
  521. for (int i = 0; i < finalGlyphCount; ++i)
  522. {
  523. ushort glyphIndex = glyphPositions.GetGlyph(i,
  524. out ushort input_offset,
  525. out short offsetX,
  526. out short offsetY,
  527. out short advW);
  528. yield return new UnscaledGlyphPlan(
  529. input_offset,
  530. glyphIndex,
  531. advW,
  532. offsetX,
  533. offsetY
  534. );
  535. }
  536. }
  537. }
  538. /// <summary>
  539. /// glyph position stream
  540. /// </summary>
  541. class GlyphPosStream : IGlyphPositions
  542. {
  543. List<GlyphPos> _glyphPosList = new List<GlyphPos>();
  544. Typeface _typeface;
  545. public GlyphPosStream() { }
  546. public int Count => _glyphPosList.Count;
  547. //
  548. public void Clear()
  549. {
  550. _typeface = null;
  551. _glyphPosList.Clear();
  552. }
  553. public Typeface Typeface
  554. {
  555. get => _typeface;
  556. set => _typeface = value;
  557. }
  558. public void AddGlyph(ushort o_offset, ushort glyphIndex, Glyph glyph)
  559. {
  560. if (!Glyph.HasOriginalAdvancedWidth(glyph))
  561. {
  562. //TODO: review here,
  563. //WHY? some glyph dose not have original advanced width
  564. Glyph.SetOriginalAdvancedWidth(glyph, _typeface.GetAdvanceWidthFromGlyphIndex(glyphIndex));
  565. }
  566. _glyphPosList.Add(new GlyphPos(o_offset, glyphIndex, glyph.GlyphClass, glyph.OriginalAdvanceWidth));
  567. }
  568. //
  569. public GlyphPos this[int index] => _glyphPosList[index];
  570. //
  571. public GlyphClassKind GetGlyphClassKind(int index)
  572. {
  573. return _glyphPosList[index].classKind;
  574. }
  575. /// <summary>
  576. /// get glyph-index (+ other info) at specific indexed-position,
  577. /// </summary>
  578. /// <param name="index">glyph index</param>
  579. /// <param name="advW">advanced width</param>
  580. /// <returns></returns>
  581. public ushort GetGlyph(int index, out short advW)
  582. {
  583. GlyphPos pos = _glyphPosList[index];
  584. advW = pos.advanceW;
  585. return pos.glyphIndex;
  586. }
  587. /// <summary>
  588. /// get glyph-index (+ other info) at specific indexed-position,
  589. /// </summary>
  590. /// <param name="index"></param>
  591. /// <param name="inputOffset"></param>
  592. /// <param name="offsetX"></param>
  593. /// <param name="offsetY"></param>
  594. /// <param name="advW"></param>
  595. /// <returns></returns>
  596. public ushort GetGlyph(int index, out ushort inputOffset, out short offsetX, out short offsetY, out short advW)
  597. {
  598. GlyphPos pos = _glyphPosList[index];
  599. offsetX = pos.xoffset;
  600. offsetY = pos.yoffset;
  601. advW = pos.advanceW;
  602. inputOffset = pos.o_offset;
  603. return pos.glyphIndex;
  604. }
  605. /// <summary>
  606. /// get glyph offset at specific indexed-position,
  607. /// </summary>
  608. /// <param name="index"></param>
  609. /// <param name="offsetX"></param>
  610. /// <param name="offsetY"></param>
  611. public void GetOffset(int index, out short offsetX, out short offsetY)
  612. {
  613. GlyphPos pos = _glyphPosList[index];
  614. offsetX = pos.xoffset;
  615. offsetY = pos.yoffset;
  616. }
  617. //
  618. public void AppendGlyphOffset(int index, short appendOffsetX, short appendOffsetY)
  619. {
  620. GlyphPos existing = _glyphPosList[index];
  621. existing.xoffset += appendOffsetX;
  622. existing.yoffset += appendOffsetY;
  623. _glyphPosList[index] = existing;
  624. }
  625. public void AppendGlyphAdvance(int index, short appendAdvX, short appendAdvY)
  626. {
  627. GlyphPos pos = _glyphPosList[index];
  628. pos.advanceW += appendAdvX;//TODO: review for appendY
  629. _glyphPosList[index] = pos;
  630. }
  631. }
  632. struct GlyphPos
  633. {
  634. public readonly ushort o_offset; //original user offset
  635. public readonly ushort glyphIndex;
  636. public short xoffset;
  637. public short yoffset;
  638. public short advanceW; // actually this value is ushort, TODO: review here
  639. public readonly GlyphClassKind glyphClass;
  640. public GlyphPos(ushort o_offset,
  641. ushort glyphIndex,
  642. GlyphClassKind glyphClass,
  643. ushort orgAdvanced
  644. )
  645. {
  646. this.o_offset = o_offset;
  647. this.glyphClass = glyphClass;
  648. this.glyphIndex = glyphIndex;
  649. this.advanceW = (short)orgAdvanced;
  650. xoffset = yoffset = 0;
  651. }
  652. public GlyphClassKind classKind => glyphClass;
  653. public short OffsetX => xoffset;
  654. public short OffsetY => yoffset;
  655. #if DEBUG
  656. public override string ToString()
  657. {
  658. return glyphIndex.ToString() + "(" + xoffset + "," + yoffset + ")";
  659. }
  660. #endif
  661. }
  662. }