ExportFont.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using FastReport.Utils;
  6. using FastReport.Fonts;
  7. using System.Text;
  8. using System.Linq;
  9. namespace FastReport.Export.TTF
  10. {
  11. /// <summary>
  12. /// Specifies the export font class.
  13. /// </summary>
  14. internal partial class ExportTTFFont : IDisposable
  15. {
  16. #region Private variables
  17. private Bitmap tempBitmap;
  18. private float dpiFX;
  19. private Dictionary<ushort, GlyphChar> usedGlyphChars;
  20. private TrueTypeFont.OutlineTextMetric textMetric;
  21. private string name;
  22. private Font sourceFont;
  23. private float baseSize;
  24. private long reference;
  25. private bool saved;
  26. private bool simulateBold;
  27. private bool simulateItalic;
  28. private bool editable;
  29. private bool repack_I2L = false;
  30. private string postscript_name;
  31. #endregion
  32. #region Public fields
  33. public IEnumerable<GlyphChar> UsedGlyphChars
  34. {
  35. get { return usedGlyphChars.Values; }
  36. }
  37. /// <summary>
  38. /// Return text metric structure, need to use after FillOutlineTextMetrix()
  39. /// </summary>
  40. public TrueTypeFont.OutlineTextMetric TextMetric
  41. {
  42. get { return textMetric; }
  43. }
  44. /// <summary>
  45. /// Gets or sets internal font name
  46. /// </summary>
  47. public string Name
  48. {
  49. get { return name; }
  50. set { name = value; }
  51. }
  52. /// <summary>
  53. /// Return source font used in constructor
  54. /// </summary>
  55. public Font SourceFont
  56. {
  57. get { return sourceFont; }
  58. }
  59. /// <summary>
  60. /// Returns multiplier for stroke bold emulation
  61. /// </summary>
  62. public double SourceFontBoldMultiplier
  63. {
  64. get { return Math.E / (2 * Math.PI) * baseSize / 9; }
  65. }
  66. /// <summary>
  67. /// Gets or sets internal reference
  68. /// </summary>
  69. public long Reference
  70. {
  71. get { return reference; }
  72. set { reference = value; }
  73. }
  74. /// <summary>
  75. /// Gets or sets internal property - save flag
  76. /// </summary>
  77. public bool Saved
  78. {
  79. get { return saved; }
  80. set { saved = value; }
  81. }
  82. /// <summary>
  83. /// True if bold style is not supported by font
  84. /// </summary>
  85. public bool NeedSimulateBold
  86. {
  87. get { return simulateBold; }
  88. set { simulateBold = value; }
  89. }
  90. /// <summary>
  91. /// True if italic style is not supported by font
  92. /// </summary>
  93. public bool NeedSimulateItalic
  94. {
  95. get { return simulateItalic; }
  96. set { simulateItalic = value; }
  97. }
  98. /// <summary>
  99. /// Mark font as editable for InteractiveForms
  100. /// </summary>
  101. public bool Editable
  102. {
  103. get { return editable; }
  104. set { editable = value; }
  105. }
  106. /// <summary>
  107. /// Get PostScript name of font
  108. /// </summary>
  109. public string PostscriptName { get { return postscript_name; } }
  110. #endregion
  111. #region Public Methods
  112. /// <summary>
  113. /// Return font file
  114. /// </summary>
  115. /// <returns></returns>
  116. public byte[] GetFontData(bool subsetting)
  117. {
  118. byte[] result = null;
  119. if (sourceFont != null)
  120. {
  121. using (var fontState = new TTFState(sourceFont))
  122. {
  123. var font = fontState.Value;
  124. if (font != null)
  125. {
  126. int LicensingRights = font.LicensingRights;
  127. if ((LicensingRights & 0x000f) == 2)
  128. throw new System.ComponentModel.LicenseException(typeof(TrueTypeFont), this,
  129. String.Format(new MyRes("Fonts").Get("FontMustNotBeModifiedEmbeddedOrExchanged"), font.Name));
  130. if ((LicensingRights & 0x0200) == 0x0200)
  131. throw new System.ComponentModel.LicenseException(typeof(TrueTypeFont), this,
  132. String.Format("Font {0}: Bitmap embedding only", font.Name));
  133. if (subsetting)
  134. {
  135. if ((LicensingRights & 0x0100) == 0x0100)
  136. {
  137. throw new System.ComponentModel.LicenseException(typeof(TrueTypeFont), this,
  138. String.Format(new MyRes("Fonts").Get("FontMayNotBeSubsettedPriorToEmbedding"), font.Name));
  139. }
  140. font.PrepareIndexes();
  141. if (font.IsClassicFormat)
  142. {
  143. TrueTypeFont.FontPackOptions options = TrueTypeFont.FontPackOptions.Default_Options;
  144. if (this.repack_I2L)
  145. options = TrueTypeFont.FontPackOptions.Pack_Indexes;
  146. result = font.PackFont(ref usedGlyphChars, options);
  147. }
  148. else if (font.Type == FontType.OpenTypeFont)
  149. {
  150. // TODO: Pack Compact Font Format
  151. result = font.GetFontData();
  152. }
  153. else
  154. {
  155. // Generate warning and load full
  156. result = font.GetFullRawFontData();
  157. }
  158. }
  159. else
  160. result = font.GetRawFontData();
  161. // Style simulation here
  162. if (sourceFont.Bold != font.Bold)
  163. {
  164. simulateBold = true;
  165. }
  166. if (sourceFont.Italic != font.Italic)
  167. {
  168. simulateItalic = true;
  169. }
  170. }
  171. }
  172. }
  173. return result;
  174. }
  175. /// <summary>
  176. /// Get font data and set NeedSimulateBold and NeedSimulateItalic properties. Call this method after FillOutlineTextMetrix
  177. /// </summary>
  178. public void FillEmulate()
  179. {
  180. using (var fontState = new TTFState(sourceFont))
  181. {
  182. var ttf = fontState.Value;
  183. postscript_name = ttf.PostscriptName;
  184. NeedSimulateBold = SourceFont.Bold && !ttf.Bold;
  185. NeedSimulateItalic = SourceFont.Italic && !ttf.Italic;
  186. }
  187. }
  188. /// <summary>
  189. /// Returns a list of font runs from the string specified. Also adds glyphs to the used glyphs table.
  190. /// </summary>
  191. /// <param name="str"></param>
  192. /// <param name="rtl"></param>
  193. /// <param name="originalFont"></param>
  194. /// <returns></returns>
  195. public RunInfo[] GetFontRuns(string str, bool rtl, Font originalFont = null)
  196. {
  197. // control chars should not be here...
  198. str = str.Replace("\r", "").Replace("\n", "").Replace((char)0xa0, ' ').Replace((char)0x2011, '-');
  199. if (string.IsNullOrEmpty(str))
  200. return new RunInfo[] { };
  201. if (originalFont == null)
  202. originalFont = sourceFont;
  203. List<RunInfo> runs = LayoutString(str, rtl, originalFont);
  204. // try to coalesce runs
  205. List<RunInfo> result = new List<RunInfo>();
  206. Font currentFont = runs[0].Font;
  207. int start = 0;
  208. for (int i = 0; i < runs.Count; i++)
  209. {
  210. RunInfo run = runs[i];
  211. if (run.Font != currentFont || (i > start && run.ZeroOffsetX != 0))
  212. {
  213. result.Add(CoalesceRuns(runs, start, i));
  214. currentFont = run.Font;
  215. start = i;
  216. }
  217. }
  218. result.Add(CoalesceRuns(runs, start, runs.Count));
  219. // now extract runs with non-zero VerticalOffset
  220. for (int i = 0; i < result.Count; i++)
  221. {
  222. RunInfo run = result[i];
  223. for (int j = 0; j < run.VerticalOffsets.Length; j++)
  224. {
  225. if (run.VerticalOffsets[j] != 0)
  226. {
  227. if (j == 0)
  228. j = 1;
  229. if (j < run.VerticalOffsets.Length)
  230. {
  231. var remainRun = SplitRun(run, j);
  232. result.Insert(i + 1, remainRun);
  233. }
  234. break;
  235. }
  236. }
  237. }
  238. return result.ToArray();
  239. }
  240. private RunInfo CoalesceRuns(List<RunInfo> runs, int start, int end)
  241. {
  242. int glyphLen = 0;
  243. for (int i = start; i < end; i++)
  244. {
  245. glyphLen += runs[i].Glyphs.Length;
  246. }
  247. RunInfo r = new RunInfo(glyphLen)
  248. {
  249. Font = runs[start].Font,
  250. OffsetX = runs[start].OffsetX,
  251. };
  252. int offset = 0;
  253. for (int i = start; i < end; i++)
  254. {
  255. var run = runs[i];
  256. int len = run.Glyphs.Length;
  257. Array.Copy(run.Glyphs, 0, r.Glyphs, offset, len);
  258. Array.Copy(run.GlyphToUnicode, 0, r.GlyphToUnicode, offset, len);
  259. Array.Copy(run.Widths, 0, r.Widths, offset, len);
  260. Array.Copy(run.Kernings, 0, r.Kernings, offset, len);
  261. Array.Copy(run.VerticalOffsets, 0, r.VerticalOffsets, offset, len);
  262. offset += len;
  263. }
  264. return r;
  265. }
  266. // leaves first splitPos elements in the run, returns the new run with remain elements
  267. private RunInfo SplitRun(RunInfo run, int splitPos)
  268. {
  269. int newLen = run.Glyphs.Length - splitPos;
  270. RunInfo r = new RunInfo(newLen)
  271. {
  272. Font = run.Font
  273. };
  274. // copy remain elements to the new run
  275. Array.Copy(run.Glyphs, splitPos, r.Glyphs, 0, newLen);
  276. Array.Copy(run.GlyphToUnicode, splitPos, r.GlyphToUnicode, 0, newLen);
  277. Array.Copy(run.Widths, splitPos, r.Widths, 0, newLen);
  278. Array.Copy(run.Kernings, splitPos, r.Kernings, 0, newLen);
  279. Array.Copy(run.VerticalOffsets, splitPos, r.VerticalOffsets, 0, newLen);
  280. int oldLen = splitPos;
  281. // resize original run's elements
  282. Array.Resize<ushort>(ref run.Glyphs, oldLen);
  283. Array.Resize<string>(ref run.GlyphToUnicode, oldLen);
  284. Array.Resize<float>(ref run.Widths, oldLen);
  285. Array.Resize<float>(ref run.Kernings, oldLen);
  286. Array.Resize<float>(ref run.VerticalOffsets, oldLen);
  287. // calc OffsetX for the new run
  288. float offsetX = run.OffsetX;
  289. for (int i = 0; i < oldLen; i++)
  290. {
  291. offsetX += (run.Widths[i] + run.Kernings[i]) * run.Font.Size / sourceFont.Size;
  292. }
  293. r.OffsetX = offsetX;
  294. return r;
  295. }
  296. internal void AddUsedGlyphs(RunInfo run)
  297. {
  298. for (int glyphIndex = 0; glyphIndex < run.Glyphs.Length; glyphIndex++)
  299. {
  300. ushort c = run.Glyphs[glyphIndex];
  301. // uniscribe-related ifdef removed: we provide constant glyph width now (see comments in ExportFont.Uniscribe.cs, GetRunInfo)
  302. lock (usedGlyphChars)
  303. {
  304. if (!usedGlyphChars.ContainsKey(c))
  305. {
  306. string codePoint = run.GlyphToUnicode[glyphIndex];
  307. if (codePoint != null && codePoint.Length == 1)
  308. usedGlyphChars[c] = new GlyphChar(c, run.Widths[glyphIndex], codePoint[0]);
  309. else if (codePoint != null && codePoint.Length > 1)
  310. usedGlyphChars[c] = new GlyphChar(c, run.Widths[glyphIndex], codePoint);
  311. else
  312. usedGlyphChars[c] = new GlyphChar(c, run.Widths[glyphIndex]);
  313. }
  314. }
  315. }
  316. }
  317. /// <summary>
  318. /// Get alphabet's subset from generation of pattern.
  319. /// </summary>
  320. /// <param name="pattern">Regular expression with pattern generator</param>
  321. /// <param name="rtl">Use left-to-right rules</param>
  322. public RunInfo[] GetPatternRuns(string pattern, bool rtl)
  323. {
  324. // Insert pattern extractor here
  325. RegexPatternExtractor regex = new RegexPatternExtractor();
  326. regex.AddExpression(pattern);
  327. return GetFontRuns(regex.Pattern, rtl);
  328. }
  329. public GlyphTTF[] GetGlyphPath(RunInfo run)
  330. {
  331. float size = run.Font.Size;
  332. List<GlyphTTF> result = new List<GlyphTTF>();
  333. using (var fontState = new TTFState(sourceFont))
  334. {
  335. var ttfont = fontState.Value;
  336. if (ttfont != null)
  337. {
  338. for (int i = 0; i < run.Glyphs.Length; i++)
  339. {
  340. FastGraphicsPath aPath = ttfont.GetGlyph(run.Glyphs[i], size / dpiFX);
  341. GraphicsPath path;
  342. if (aPath.PointCount != 0)
  343. path = new GraphicsPath(aPath.PathPoints, aPath.PathTypes, FillMode.Winding);
  344. else
  345. path = new GraphicsPath();
  346. result.Add(new GlyphTTF(path,
  347. (run.Widths[i] + run.Kernings[i]) / sourceFont.Size * size,
  348. ttfont.baseLine * size / 0.75f));
  349. }
  350. }
  351. }
  352. return result.ToArray();
  353. }
  354. /// <summary>
  355. /// Return english name of source font
  356. /// </summary>
  357. /// <returns></returns>
  358. public string GetEnglishFontName()
  359. {
  360. // get the english name of a font
  361. string fontName = sourceFont.FontFamily.GetName(1033);
  362. FastString Result = new FastString(fontName.Length * 3);
  363. foreach (char c in fontName)
  364. {
  365. switch (c)
  366. {
  367. case ' ':
  368. case '%':
  369. case '(':
  370. case ')':
  371. case '<':
  372. case '>':
  373. case '[':
  374. case ']':
  375. case '{':
  376. case '}':
  377. case '/':
  378. case '#':
  379. Result.Append("#");
  380. Result.Append(((int)c).ToString("X2"));
  381. break;
  382. default:
  383. if ((int)c > 126 || (int)c < 32)
  384. {
  385. Result.Append('#');
  386. Result.Append(((int)c).ToString("X2"));
  387. }
  388. else
  389. Result.Append(c);
  390. break;
  391. }
  392. }
  393. FastString Style = new FastString(9);
  394. if ((sourceFont.Style & FontStyle.Bold) > 0 && !this.simulateBold)
  395. Style.Append("Bold");
  396. if ((sourceFont.Style & FontStyle.Italic) > 0 && !this.simulateItalic)
  397. Style.Append("Italic");
  398. if (Style.Length > 0)
  399. Result.Append(",").Append(Style.ToString());
  400. return Result.ToString();
  401. }
  402. #endregion
  403. /// <summary>
  404. /// Create object of ExportTTFFont.
  405. /// </summary>
  406. /// <param name="font"></param>
  407. public ExportTTFFont(Font font)
  408. {
  409. InternalInit();
  410. baseSize = font.Size;
  411. bool isPrivateFont = String.IsNullOrEmpty(font.OriginalFontName);
  412. if (isPrivateFont)
  413. sourceFont = new Font(font.FontFamily, 750 * dpiFX, font.Style);
  414. else
  415. sourceFont = new Font(font.Name, 750 * dpiFX, font.Style);
  416. saved = false;
  417. textMetric = new TrueTypeFont.OutlineTextMetric();
  418. usedGlyphChars = new Dictionary<ushort, GlyphChar>();
  419. tempBitmap = new Bitmap(1, 1);
  420. simulateItalic = false;
  421. simulateBold = false;
  422. editable = false;
  423. }
  424. /// <summary>
  425. /// Destructor
  426. /// </summary>
  427. public void Dispose()
  428. {
  429. InternalDispose();
  430. tempBitmap.Dispose();
  431. sourceFont.Dispose();
  432. }
  433. #region Private classes
  434. public class GlyphTTF
  435. {
  436. public GraphicsPath Path;
  437. public float Width;
  438. public float BaseLine;
  439. public GlyphTTF(GraphicsPath path, float width)
  440. {
  441. Path = path;
  442. Width = width;
  443. }
  444. public GlyphTTF(GraphicsPath path, float width, float baseLine) : this(path, width)
  445. {
  446. BaseLine = baseLine;
  447. }
  448. }
  449. internal class RunInfo
  450. {
  451. public Font Font;
  452. public float OffsetX;
  453. public float ZeroOffsetX;
  454. public ushort[] Glyphs;
  455. public string[] GlyphToUnicode;
  456. public float[] Widths;
  457. public float[] Kernings;
  458. public float[] VerticalOffsets;
  459. public RunInfo(int len)
  460. {
  461. Glyphs = new ushort[len];
  462. GlyphToUnicode = new string[len];
  463. Widths = new float[len];
  464. Kernings = new float[len];
  465. VerticalOffsets = new float[len];
  466. }
  467. }
  468. #endregion
  469. }
  470. }