using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using FastReport.Utils; using FastReport.Fonts; using System.Text; using System.Linq; namespace FastReport.Export.TTF { /// /// Specifies the export font class. /// internal partial class ExportTTFFont : IDisposable { #region Private variables private Bitmap tempBitmap; private float dpiFX; private Dictionary usedGlyphChars; private TrueTypeFont.OutlineTextMetric textMetric; private string name; private Font sourceFont; private float baseSize; private long reference; private bool saved; private bool simulateBold; private bool simulateItalic; private bool editable; private bool repack_I2L = false; private string postscript_name; #endregion #region Public fields public IEnumerable UsedGlyphChars { get { return usedGlyphChars.Values; } } /// /// Return text metric structure, need to use after FillOutlineTextMetrix() /// public TrueTypeFont.OutlineTextMetric TextMetric { get { return textMetric; } } /// /// Gets or sets internal font name /// public string Name { get { return name; } set { name = value; } } /// /// Return source font used in constructor /// public Font SourceFont { get { return sourceFont; } } /// /// Returns multiplier for stroke bold emulation /// public double SourceFontBoldMultiplier { get { return Math.E / (2 * Math.PI) * baseSize / 9; } } /// /// Gets or sets internal reference /// public long Reference { get { return reference; } set { reference = value; } } /// /// Gets or sets internal property - save flag /// public bool Saved { get { return saved; } set { saved = value; } } /// /// True if bold style is not supported by font /// public bool NeedSimulateBold { get { return simulateBold; } set { simulateBold = value; } } /// /// True if italic style is not supported by font /// public bool NeedSimulateItalic { get { return simulateItalic; } set { simulateItalic = value; } } /// /// Mark font as editable for InteractiveForms /// public bool Editable { get { return editable; } set { editable = value; } } /// /// Get PostScript name of font /// public string PostscriptName { get { return postscript_name; } } #endregion #region Public Methods /// /// Return font file /// /// public byte[] GetFontData(bool subsetting) { byte[] result = null; if (sourceFont != null) { using (var fontState = new TTFState(sourceFont)) { var font = fontState.Value; if (font != null) { int LicensingRights = font.LicensingRights; if ((LicensingRights & 0x000f) == 2) throw new System.ComponentModel.LicenseException(typeof(TrueTypeFont), this, String.Format(new MyRes("Fonts").Get("FontMustNotBeModifiedEmbeddedOrExchanged"), font.Name)); if ((LicensingRights & 0x0200) == 0x0200) throw new System.ComponentModel.LicenseException(typeof(TrueTypeFont), this, String.Format("Font {0}: Bitmap embedding only", font.Name)); if (subsetting) { if ((LicensingRights & 0x0100) == 0x0100) { throw new System.ComponentModel.LicenseException(typeof(TrueTypeFont), this, String.Format(new MyRes("Fonts").Get("FontMayNotBeSubsettedPriorToEmbedding"), font.Name)); } font.PrepareIndexes(); if (font.IsClassicFormat) { TrueTypeFont.FontPackOptions options = TrueTypeFont.FontPackOptions.Default_Options; if (this.repack_I2L) options = TrueTypeFont.FontPackOptions.Pack_Indexes; result = font.PackFont(ref usedGlyphChars, options); } else if (font.Type == FontType.OpenTypeFont) { // TODO: Pack Compact Font Format result = font.GetFontData(); } else { // Generate warning and load full result = font.GetFullRawFontData(); } } else result = font.GetRawFontData(); // Style simulation here if (sourceFont.Bold != font.Bold) { simulateBold = true; } if (sourceFont.Italic != font.Italic) { simulateItalic = true; } } } } return result; } /// /// Get font data and set NeedSimulateBold and NeedSimulateItalic properties. Call this method after FillOutlineTextMetrix /// public void FillEmulate() { using (var fontState = new TTFState(sourceFont)) { var ttf = fontState.Value; postscript_name = ttf.PostscriptName; NeedSimulateBold = SourceFont.Bold && !ttf.Bold; NeedSimulateItalic = SourceFont.Italic && !ttf.Italic; } } /// /// Returns a list of font runs from the string specified. Also adds glyphs to the used glyphs table. /// /// /// /// /// public RunInfo[] GetFontRuns(string str, bool rtl, Font originalFont = null) { // control chars should not be here... str = str.Replace("\r", "").Replace("\n", "").Replace((char)0xa0, ' ').Replace((char)0x2011, '-'); if (string.IsNullOrEmpty(str)) return new RunInfo[] { }; if (originalFont == null) originalFont = sourceFont; List runs = LayoutString(str, rtl, originalFont); // try to coalesce runs List result = new List(); Font currentFont = runs[0].Font; int start = 0; for (int i = 0; i < runs.Count; i++) { RunInfo run = runs[i]; if (run.Font != currentFont || (i > start && run.ZeroOffsetX != 0)) { result.Add(CoalesceRuns(runs, start, i)); currentFont = run.Font; start = i; } } result.Add(CoalesceRuns(runs, start, runs.Count)); // now extract runs with non-zero VerticalOffset for (int i = 0; i < result.Count; i++) { RunInfo run = result[i]; for (int j = 0; j < run.VerticalOffsets.Length; j++) { if (run.VerticalOffsets[j] != 0) { if (j == 0) j = 1; if (j < run.VerticalOffsets.Length) { var remainRun = SplitRun(run, j); result.Insert(i + 1, remainRun); } break; } } } return result.ToArray(); } private RunInfo CoalesceRuns(List runs, int start, int end) { int glyphLen = 0; for (int i = start; i < end; i++) { glyphLen += runs[i].Glyphs.Length; } RunInfo r = new RunInfo(glyphLen) { Font = runs[start].Font, OffsetX = runs[start].OffsetX, }; int offset = 0; for (int i = start; i < end; i++) { var run = runs[i]; int len = run.Glyphs.Length; Array.Copy(run.Glyphs, 0, r.Glyphs, offset, len); Array.Copy(run.GlyphToUnicode, 0, r.GlyphToUnicode, offset, len); Array.Copy(run.Widths, 0, r.Widths, offset, len); Array.Copy(run.Kernings, 0, r.Kernings, offset, len); Array.Copy(run.VerticalOffsets, 0, r.VerticalOffsets, offset, len); offset += len; } return r; } // leaves first splitPos elements in the run, returns the new run with remain elements private RunInfo SplitRun(RunInfo run, int splitPos) { int newLen = run.Glyphs.Length - splitPos; RunInfo r = new RunInfo(newLen) { Font = run.Font }; // copy remain elements to the new run Array.Copy(run.Glyphs, splitPos, r.Glyphs, 0, newLen); Array.Copy(run.GlyphToUnicode, splitPos, r.GlyphToUnicode, 0, newLen); Array.Copy(run.Widths, splitPos, r.Widths, 0, newLen); Array.Copy(run.Kernings, splitPos, r.Kernings, 0, newLen); Array.Copy(run.VerticalOffsets, splitPos, r.VerticalOffsets, 0, newLen); int oldLen = splitPos; // resize original run's elements Array.Resize(ref run.Glyphs, oldLen); Array.Resize(ref run.GlyphToUnicode, oldLen); Array.Resize(ref run.Widths, oldLen); Array.Resize(ref run.Kernings, oldLen); Array.Resize(ref run.VerticalOffsets, oldLen); // calc OffsetX for the new run float offsetX = run.OffsetX; for (int i = 0; i < oldLen; i++) { offsetX += (run.Widths[i] + run.Kernings[i]) * run.Font.Size / sourceFont.Size; } r.OffsetX = offsetX; return r; } internal void AddUsedGlyphs(RunInfo run) { for (int glyphIndex = 0; glyphIndex < run.Glyphs.Length; glyphIndex++) { ushort c = run.Glyphs[glyphIndex]; // uniscribe-related ifdef removed: we provide constant glyph width now (see comments in ExportFont.Uniscribe.cs, GetRunInfo) lock (usedGlyphChars) { if (!usedGlyphChars.ContainsKey(c)) { string codePoint = run.GlyphToUnicode[glyphIndex]; if (codePoint != null && codePoint.Length == 1) usedGlyphChars[c] = new GlyphChar(c, run.Widths[glyphIndex], codePoint[0]); else if (codePoint != null && codePoint.Length > 1) usedGlyphChars[c] = new GlyphChar(c, run.Widths[glyphIndex], codePoint); else usedGlyphChars[c] = new GlyphChar(c, run.Widths[glyphIndex]); } } } } /// /// Get alphabet's subset from generation of pattern. /// /// Regular expression with pattern generator /// Use left-to-right rules public RunInfo[] GetPatternRuns(string pattern, bool rtl) { // Insert pattern extractor here RegexPatternExtractor regex = new RegexPatternExtractor(); regex.AddExpression(pattern); return GetFontRuns(regex.Pattern, rtl); } public GlyphTTF[] GetGlyphPath(RunInfo run) { float size = run.Font.Size; List result = new List(); using (var fontState = new TTFState(sourceFont)) { var ttfont = fontState.Value; if (ttfont != null) { for (int i = 0; i < run.Glyphs.Length; i++) { FastGraphicsPath aPath = ttfont.GetGlyph(run.Glyphs[i], size / dpiFX); GraphicsPath path; if (aPath.PointCount != 0) path = new GraphicsPath(aPath.PathPoints, aPath.PathTypes, FillMode.Winding); else path = new GraphicsPath(); result.Add(new GlyphTTF(path, (run.Widths[i] + run.Kernings[i]) / sourceFont.Size * size, ttfont.baseLine * size / 0.75f)); } } } return result.ToArray(); } /// /// Return english name of source font /// /// public string GetEnglishFontName() { // get the english name of a font string fontName = sourceFont.FontFamily.GetName(1033); FastString Result = new FastString(fontName.Length * 3); foreach (char c in fontName) { switch (c) { case ' ': case '%': case '(': case ')': case '<': case '>': case '[': case ']': case '{': case '}': case '/': case '#': Result.Append("#"); Result.Append(((int)c).ToString("X2")); break; default: if ((int)c > 126 || (int)c < 32) { Result.Append('#'); Result.Append(((int)c).ToString("X2")); } else Result.Append(c); break; } } FastString Style = new FastString(9); if ((sourceFont.Style & FontStyle.Bold) > 0 && !this.simulateBold) Style.Append("Bold"); if ((sourceFont.Style & FontStyle.Italic) > 0 && !this.simulateItalic) Style.Append("Italic"); if (Style.Length > 0) Result.Append(",").Append(Style.ToString()); return Result.ToString(); } #endregion /// /// Create object of ExportTTFFont. /// /// public ExportTTFFont(Font font) { InternalInit(); baseSize = font.Size; bool isPrivateFont = String.IsNullOrEmpty(font.OriginalFontName); if (isPrivateFont) sourceFont = new Font(font.FontFamily, 750 * dpiFX, font.Style); else sourceFont = new Font(font.Name, 750 * dpiFX, font.Style); saved = false; textMetric = new TrueTypeFont.OutlineTextMetric(); usedGlyphChars = new Dictionary(); tempBitmap = new Bitmap(1, 1); simulateItalic = false; simulateBold = false; editable = false; } /// /// Destructor /// public void Dispose() { InternalDispose(); tempBitmap.Dispose(); sourceFont.Dispose(); } #region Private classes public class GlyphTTF { public GraphicsPath Path; public float Width; public float BaseLine; public GlyphTTF(GraphicsPath path, float width) { Path = path; Width = width; } public GlyphTTF(GraphicsPath path, float width, float baseLine) : this(path, width) { BaseLine = baseLine; } } internal class RunInfo { public Font Font; public float OffsetX; public float ZeroOffsetX; public ushort[] Glyphs; public string[] GlyphToUnicode; public float[] Widths; public float[] Kernings; public float[] VerticalOffsets; public RunInfo(int len) { Glyphs = new ushort[len]; GlyphToUnicode = new string[len]; Widths = new float[len]; Kernings = new float[len]; VerticalOffsets = new float[len]; } } #endregion } }