ExportFont.cs 18 KB

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