Typeface.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. //Apache2, 2017-present, WinterDev
  2. //Apache2, 2014-2016, Samuel Carlsson, WinterDev
  3. using System;
  4. using System.Collections.Generic;
  5. using Typography.OpenFont.Tables;
  6. namespace Typography.OpenFont
  7. {
  8. public partial class Typeface
  9. {
  10. //TODO: implement vertical metrics
  11. HorizontalMetrics _hMetrics;
  12. NameEntry _nameEntry;
  13. Glyph[] _glyphs;
  14. CFF.Cff1FontSet _cff1FontSet;
  15. TableHeader[] _tblHeaders;
  16. bool _hasTtfOutline;
  17. bool _hasCffData;
  18. internal bool _useTypographicMertic;
  19. #if DEBUG
  20. static int s_dbugTotalId;
  21. public readonly int dbugId = ++s_dbugTotalId;
  22. #endif
  23. internal Typeface()
  24. {
  25. //blank typefaces
  26. #if DEBUG
  27. if (dbugId == 5)
  28. {
  29. }
  30. #endif
  31. }
  32. internal void SetTableEntryCollection(TableHeader[] headers) => _tblHeaders = headers;
  33. internal void SetBasicTypefaceTables(OS2Table os2Table,
  34. NameEntry nameEntry,
  35. Head head,
  36. HorizontalMetrics horizontalMetrics)
  37. {
  38. OS2Table = os2Table;
  39. _nameEntry = nameEntry;
  40. Head = head;
  41. Bounds = head.Bounds;
  42. UnitsPerEm = head.UnitsPerEm;
  43. _hMetrics = horizontalMetrics;
  44. }
  45. internal Head Head { get; set; }
  46. internal void SetTtfGlyphs(Glyph[] glyphs)
  47. {
  48. _glyphs = glyphs;
  49. _hasTtfOutline = true;
  50. }
  51. internal void SetBitmapGlyphs(Glyph[] glyphs, BitmapFontGlyphSource bitmapFontGlyphSource)
  52. {
  53. _glyphs = glyphs;
  54. _bitmapFontGlyphSource = bitmapFontGlyphSource;
  55. }
  56. internal void SetCffFontSet(CFF.Cff1FontSet cff1FontSet)
  57. {
  58. _cff1FontSet = cff1FontSet;
  59. _hasCffData = true;
  60. Glyph[] exisitingGlyphs = _glyphs;
  61. _glyphs = cff1FontSet._fonts[0]._glyphs; //TODO: review _fonts[0]
  62. if (exisitingGlyphs != null)
  63. {
  64. //
  65. #if DEBUG
  66. if (_glyphs.Length != exisitingGlyphs.Length)
  67. {
  68. throw new OpenFontNotSupportedException();
  69. }
  70. #endif
  71. for (int i = 0; i < exisitingGlyphs.Length; ++i)
  72. {
  73. Glyph.CopyExistingGlyphInfo(exisitingGlyphs[i], _glyphs[i]);
  74. }
  75. }
  76. }
  77. public Languages Languages { get; } = new Languages();
  78. /// <summary>
  79. /// control values in Font unit
  80. /// </summary>
  81. internal int[] ControlValues { get; set; }
  82. internal byte[] PrepProgramBuffer { get; set; }
  83. internal byte[] FpgmProgramBuffer { get; set; }
  84. internal MaxProfile MaxProfile { get; set; }
  85. internal Cmap CmapTable { get; set; }
  86. internal Kern KernTable { get; set; }
  87. internal Gasp GaspTable { get; set; }
  88. internal HorizontalHeader HheaTable { get; set; }
  89. public OS2Table OS2Table { get; set; }
  90. //
  91. public bool HasPrepProgramBuffer => PrepProgramBuffer != null;
  92. /// <summary>
  93. /// actual font filename (optional)
  94. /// </summary>
  95. public string Filename { get; set; }
  96. /// <summary>
  97. /// OS2 sTypoAscender/HheaTable.Ascent, in font designed unit
  98. /// </summary>
  99. public short Ascender => _useTypographicMertic ? OS2Table.sTypoAscender : HheaTable.Ascent;
  100. /// <summary>
  101. /// OS2 sTypoDescender, in font designed unit
  102. /// </summary>
  103. public short Descender => _useTypographicMertic ? OS2Table.sTypoDescender : HheaTable.Descent;
  104. /// <summary>
  105. /// OS2 usWinAscender
  106. /// </summary>
  107. public ushort ClipedAscender => OS2Table.usWinAscent;
  108. /// <summary>
  109. /// OS2 usWinDescender
  110. /// </summary>
  111. public ushort ClipedDescender => OS2Table.usWinDescent;
  112. /// <summary>
  113. /// OS2 Linegap
  114. /// </summary>
  115. public short LineGap => _useTypographicMertic ? OS2Table.sTypoLineGap : HheaTable.LineGap;
  116. //The typographic line gap for this font.
  117. //Remember that this is not the same as the LineGap value in the 'hhea' table,
  118. //which Apple defines in a far different manner.
  119. //The suggested usage for sTypoLineGap is
  120. //that it be used in conjunction with unitsPerEm
  121. //to compute a typographically correct default line spacing.
  122. //
  123. //Typical values average 7 - 10 % of units per em.
  124. //The goal is to free applications from Macintosh or Windows - specific metrics
  125. //which are constrained by backward compatability requirements
  126. //(see chapter, “Recommendations for OpenType Fonts”).
  127. //These new metrics, when combined with the character design widths,
  128. //will allow applications to lay out documents in a typographically correct and portable fashion.
  129. //These metrics will be exposed through Windows APIs.
  130. //Macintosh applications will need to access the 'sfnt' resource and
  131. //parse it to extract this data from the “OS / 2” table
  132. //(unless Apple exposes the 'OS/2' table through a new API)
  133. //---------------
  134. public string Name => _nameEntry.FontName;
  135. public string FamilyName => string.IsNullOrEmpty(_nameEntry.TypographicFamilyName)?_nameEntry.FontName:_nameEntry.TypographicFamilyName;
  136. public string FontSubFamily => string.IsNullOrEmpty(_nameEntry.TypographyicSubfamilyName)?_nameEntry.FontSubFamily:_nameEntry.TypographyicSubfamilyName;
  137. public string PostScriptName => _nameEntry.PostScriptName;
  138. public string VersionString => _nameEntry.VersionString;
  139. public string UniqueFontIden => _nameEntry.UniqueFontIden;
  140. internal NameEntry NameEntry => _nameEntry;
  141. public int GlyphCount => _glyphs.Length;
  142. /// <summary>
  143. /// find glyph index by codepoint
  144. /// </summary>
  145. /// <param name="codepoint"></param>
  146. /// <param name="nextCodepoint"></param>
  147. /// <returns></returns>
  148. public ushort GetGlyphIndex(int codepoint, int nextCodepoint, out bool skipNextCodepoint)
  149. {
  150. return CmapTable.GetGlyphIndex(codepoint, nextCodepoint, out skipNextCodepoint);
  151. }
  152. public ushort GetGlyphIndex(int codepoint)
  153. {
  154. return CmapTable.GetGlyphIndex(codepoint, 0, out bool skipNextCodepoint);
  155. }
  156. public void CollectUnicode(List<uint> unicodes)
  157. {
  158. CmapTable.CollectUnicode(unicodes);
  159. }
  160. public void CollectUnicode(int platform, List<uint> unicodes, List<ushort> glyphIndexList)
  161. {
  162. CmapTable.CollectUnicode(platform, unicodes, glyphIndexList);
  163. }
  164. public Glyph GetGlyphByName(string glyphName) => GetGlyph(GetGlyphIndexByName(glyphName));
  165. Dictionary<string, ushort> _cachedGlyphDicByName;
  166. void UpdateCff1FontSetNamesCache()
  167. {
  168. if (_cff1FontSet != null && _cachedGlyphDicByName == null)
  169. {
  170. //create cache data
  171. _cachedGlyphDicByName = new Dictionary<string, ushort>();
  172. for (int i = 1; i < _glyphs.Length; ++i)
  173. {
  174. Glyph glyph = _glyphs[i];
  175. if (glyph._cff1GlyphData != null && glyph._cff1GlyphData.Name != null)
  176. {
  177. _cachedGlyphDicByName.Add(glyph._cff1GlyphData.Name, (ushort)i);
  178. }
  179. else
  180. {
  181. #if DEBUG
  182. System.Diagnostics.Debug.WriteLine("Cff unknown glyphname");
  183. #endif
  184. }
  185. }
  186. }
  187. }
  188. public ushort GetGlyphIndexByName(string glyphName)
  189. {
  190. if (glyphName == null) return 0;
  191. if (_cff1FontSet != null && _cachedGlyphDicByName == null)
  192. {
  193. //we create a dictionary
  194. //create cache data
  195. _cachedGlyphDicByName = new Dictionary<string, ushort>();
  196. for (int i = 1; i < _glyphs.Length; ++i)
  197. {
  198. Glyph glyph = _glyphs[i];
  199. if (glyph._cff1GlyphData.Name != null)
  200. {
  201. _cachedGlyphDicByName.Add(glyph._cff1GlyphData.Name, (ushort)i);
  202. }
  203. else
  204. {
  205. #if DEBUG
  206. System.Diagnostics.Debug.WriteLine("Cff unknown glyphname");
  207. #endif
  208. }
  209. }
  210. return _cachedGlyphDicByName.TryGetValue(glyphName, out ushort glyphIndex) ? glyphIndex : (ushort)0;
  211. }
  212. else if (PostTable != null)
  213. {
  214. if (PostTable.Version == 2)
  215. {
  216. return PostTable.GetGlyphIndex(glyphName);
  217. }
  218. else
  219. {
  220. //check data from adobe glyph list
  221. //from the unicode value
  222. //select glyph index
  223. //we use AdobeGlyphList
  224. //from https://github.com/adobe-type-tools/agl-aglfn/blob/master/glyphlist.txt
  225. //but user can provide their own map here...
  226. return GetGlyphIndex(AdobeGlyphList.GetUnicodeValueByGlyphName(glyphName));
  227. }
  228. }
  229. return 0;
  230. }
  231. public IEnumerable<GlyphNameMap> GetGlyphNameIter()
  232. {
  233. if (_cachedGlyphDicByName == null && _cff1FontSet != null)
  234. {
  235. UpdateCff1FontSetNamesCache();
  236. }
  237. if (_cachedGlyphDicByName != null)
  238. {
  239. //iter from here
  240. foreach (var kv in _cachedGlyphDicByName)
  241. {
  242. yield return new GlyphNameMap(kv.Value, kv.Key);
  243. }
  244. }
  245. else if (PostTable.Version == 2)
  246. {
  247. foreach (var kp in PostTable.GlyphNames)
  248. {
  249. yield return new GlyphNameMap(kp.Key, kp.Value);
  250. }
  251. }
  252. }
  253. public Glyph GetGlyph(ushort glyphIndex)
  254. {
  255. if (glyphIndex < _glyphs.Length)
  256. {
  257. return _glyphs[glyphIndex];
  258. }
  259. else
  260. {
  261. #if DEBUG
  262. System.Diagnostics.Debug.WriteLine("found unknown glyph:" + glyphIndex);
  263. #endif
  264. return _glyphs[0]; //return empty glyph?;
  265. }
  266. }
  267. public ushort GetAdvanceWidthFromGlyphIndex(ushort glyphIndex) => _hMetrics.GetAdvanceWidth(glyphIndex);
  268. public short GetLeftSideBearing(ushort glyphIndex) => _hMetrics.GetLeftSideBearing(glyphIndex);
  269. public short GetKernDistance(ushort leftGlyphIndex, ushort rightGlyphIndex)
  270. {
  271. //DEPRECATED -> use OpenFont layout instead
  272. return this.KernTable.GetKerningDistance(leftGlyphIndex, rightGlyphIndex);
  273. }
  274. //
  275. public Bounds Bounds { get; private set; }
  276. public ushort UnitsPerEm { get; private set; }
  277. public short UnderlinePosition => PostTable.UnderlinePosition; //TODO: review here
  278. //
  279. const int s_pointsPerInch = 72;//point per inch, fix?
  280. /// <summary>
  281. /// default dpi
  282. /// </summary>
  283. public static uint DefaultDpi { get; set; } = 96;
  284. /// <summary>
  285. /// convert from point-unit value to pixel value
  286. /// </summary>
  287. /// <param name="targetPointSize"></param>
  288. /// <param name="resolution">dpi</param>
  289. /// <returns></returns>
  290. public static float ConvPointsToPixels(float targetPointSize, int resolution = -1)
  291. {
  292. //http://stackoverflow.com/questions/139655/convert-pixels-to-points
  293. //points = pixels * 72 / 96
  294. //------------------------------------------------
  295. //pixels = targetPointSize * 96 /72
  296. //pixels = targetPointSize * resolution / pointPerInch
  297. if (resolution < 0)
  298. {
  299. //use current DefaultDPI
  300. resolution = (int)DefaultDpi;
  301. }
  302. return targetPointSize * resolution / s_pointsPerInch;
  303. }
  304. /// <summary>
  305. /// calculate scale to target pixel size based on current typeface's UnitsPerEm
  306. /// </summary>
  307. /// <param name="targetPixelSize">target font size in point unit</param>
  308. /// <returns></returns>
  309. public float CalculateScaleToPixel(float targetPixelSize)
  310. {
  311. //1. return targetPixelSize / UnitsPerEm
  312. return targetPixelSize / this.UnitsPerEm;
  313. }
  314. /// <summary>
  315. /// calculate scale to target pixel size based on current typeface's UnitsPerEm
  316. /// </summary>
  317. /// <param name="targetPointSize">target font size in point unit</param>
  318. /// <param name="resolution">dpi</param>
  319. /// <returns></returns>
  320. public float CalculateScaleToPixelFromPointSize(float targetPointSize, int resolution = -1)
  321. {
  322. //1. var sizeInPixels = ConvPointsToPixels(sizeInPointUnit);
  323. //2. return sizeInPixels / UnitsPerEm
  324. if (resolution < 0)
  325. {
  326. //use current DefaultDPI
  327. resolution = (int)DefaultDpi;
  328. }
  329. return (targetPointSize * resolution / s_pointsPerInch) / this.UnitsPerEm;
  330. }
  331. internal BASE BaseTable { get; set; }
  332. internal GDEF GDEFTable { get; set; }
  333. public COLR COLRTable { get; private set; }
  334. public CPAL CPALTable { get; private set; }
  335. internal bool HasColorAndPal { get; private set; }
  336. internal void SetColorAndPalTable(COLR colr, CPAL cpal)
  337. {
  338. COLRTable = colr;
  339. CPALTable = cpal;
  340. HasColorAndPal = colr != null;
  341. }
  342. public GPOS GPOSTable { get; internal set; }
  343. public GSUB GSUBTable { get; internal set; }
  344. internal void LoadOpenFontLayoutInfo(GDEF gdefTable, GSUB gsubTable, GPOS gposTable, BASE baseTable, COLR colrTable, CPAL cpalTable)
  345. {
  346. //***
  347. this.GDEFTable = gdefTable;
  348. this.GSUBTable = gsubTable;
  349. this.GPOSTable = gposTable;
  350. this.BaseTable = baseTable;
  351. this.COLRTable = colrTable;
  352. this.CPALTable = cpalTable;
  353. //---------------------------
  354. //fill glyph definition
  355. if (gdefTable != null)
  356. {
  357. gdefTable.FillGlyphData(_glyphs);
  358. }
  359. }
  360. internal PostTable PostTable { get; set; }
  361. internal bool _evalCffGlyphBounds;
  362. public bool IsCffFont => _hasCffData;
  363. //Math Table
  364. MathGlyphs.MathGlyphInfo[] _mathGlyphInfos;
  365. internal MathTable _mathTable;
  366. //
  367. public MathGlyphs.MathConstants MathConsts => _mathTable?._mathConstTable;
  368. internal void LoadMathGlyphInfos(MathGlyphs.MathGlyphInfo[] mathGlyphInfos)
  369. {
  370. _mathGlyphInfos = mathGlyphInfos;
  371. if (mathGlyphInfos != null)
  372. {
  373. //fill to original glyph?
  374. for (int glyphIndex = 0; glyphIndex < _glyphs.Length; ++glyphIndex)
  375. {
  376. _glyphs[glyphIndex].MathGlyphInfo = mathGlyphInfos[glyphIndex];
  377. }
  378. }
  379. }
  380. public MathGlyphs.MathGlyphInfo GetMathGlyphInfo(ushort glyphIndex) => _mathGlyphInfos[glyphIndex];
  381. //-------------------------
  382. //svg and bitmap font
  383. SvgTable _svgTable;
  384. internal bool HasSvgTable { get; private set; }
  385. internal void SetSvgTable(SvgTable svgTable)
  386. {
  387. HasSvgTable = (_svgTable = svgTable) != null;
  388. }
  389. public void ReadSvgContent(ushort glyphIndex, System.Text.StringBuilder output) => _svgTable?.ReadSvgContent(glyphIndex, output);
  390. internal BitmapFontGlyphSource _bitmapFontGlyphSource;
  391. public bool IsBitmapFont => _bitmapFontGlyphSource != null;
  392. public void ReadBitmapContent(Glyph glyph, System.IO.Stream output)
  393. {
  394. _bitmapFontGlyphSource.CopyBitmapContent(glyph, output);
  395. }
  396. /// <summary>
  397. /// undate lang info
  398. /// </summary>
  399. /// <param name="metaTable"></param>
  400. internal void UpdateLangs(Meta metaTable) => Languages.Update(OS2Table, metaTable, CmapTable, this.GSUBTable, this.GPOSTable);
  401. internal ushort _whitespaceWidth; //common used value
  402. internal void UpdateFrequentlyUsedValues()
  403. {
  404. //whitespace
  405. ushort whitespace_glyphIndex = this.GetGlyphIndex(' ');
  406. if (whitespace_glyphIndex > 0)
  407. {
  408. _whitespaceWidth = this.GetAdvanceWidthFromGlyphIndex(whitespace_glyphIndex);
  409. }
  410. }
  411. #if DEBUG
  412. public override string ToString() => Name;
  413. #endif
  414. }
  415. public interface IGlyphPositions
  416. {
  417. int Count { get; }
  418. GlyphClassKind GetGlyphClassKind(int index);
  419. void AppendGlyphOffset(int index, short appendOffsetX, short appendOffsetY);
  420. void AppendGlyphAdvance(int index, short appendAdvX, short appendAdvY);
  421. ushort GetGlyph(int index, out short advW);
  422. ushort GetGlyph(int index, out ushort inputOffset, out short offsetX, out short offsetY, out short advW);
  423. //
  424. void GetOffset(int index, out short offsetX, out short offsetY);
  425. }
  426. public static class StringUtils
  427. {
  428. public static void FillWithCodepoints(List<int> codepoints, char[] str, int startAt = 0, int len = -1)
  429. {
  430. if (len == -1) len = str.Length;
  431. // this is important!
  432. // -----------------------
  433. // from @samhocevar's PR: (https://github.com/LayoutFarm/Typography/pull/56/commits/b71c7cf863531ebf5caa478354d3249bde40b96e)
  434. // In many places, "char" is not a valid type to handle characters, because it
  435. // only supports 16 bits.In order to handle the full range of Unicode characters,
  436. // we need to use "int".
  437. // This allows characters such as 🙌 or 𐐷 or to be treated as single codepoints even
  438. // though they are encoded as two "char"s in a C# string.
  439. for (int i = 0; i < len; ++i)
  440. {
  441. char ch = str[startAt + i];
  442. int codepoint = ch;
  443. if (char.IsHighSurrogate(ch) && i + 1 < len)
  444. {
  445. char nextCh = str[startAt + i + 1];
  446. if (char.IsLowSurrogate(nextCh))
  447. {
  448. ++i;
  449. codepoint = char.ConvertToUtf32(ch, nextCh);
  450. }
  451. }
  452. codepoints.Add(codepoint);
  453. }
  454. }
  455. public static IEnumerable<int> GetCodepoints(char[] str, int startAt = 0, int len = -1)
  456. {
  457. if (len == -1) len = str.Length;
  458. // this is important!
  459. // -----------------------
  460. // from @samhocevar's PR: (https://github.com/LayoutFarm/Typography/pull/56/commits/b71c7cf863531ebf5caa478354d3249bde40b96e)
  461. // In many places, "char" is not a valid type to handle characters, because it
  462. // only supports 16 bits.In order to handle the full range of Unicode characters,
  463. // we need to use "int".
  464. // This allows characters such as 🙌 or 𐐷 or to be treated as single codepoints even
  465. // though they are encoded as two "char"s in a C# string.
  466. for (int i = 0; i < len; ++i)
  467. {
  468. char ch = str[startAt + i];
  469. int codepoint = ch;
  470. if (char.IsHighSurrogate(ch) && i + 1 < len)
  471. {
  472. char nextCh = str[startAt + i + 1];
  473. if (char.IsLowSurrogate(nextCh))
  474. {
  475. ++i;
  476. codepoint = char.ConvertToUtf32(ch, nextCh);
  477. }
  478. }
  479. yield return codepoint;
  480. }
  481. }
  482. }
  483. public readonly struct GlyphNameMap
  484. {
  485. public readonly ushort glyphIndex;
  486. public readonly string glyphName;
  487. public GlyphNameMap(ushort glyphIndex, string glyphName)
  488. {
  489. this.glyphIndex = glyphIndex;
  490. this.glyphName = glyphName;
  491. }
  492. }
  493. }