123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- 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
- {
- /// <summary>
- /// Specifies the export font class.
- /// </summary>
- internal partial class ExportTTFFont : IDisposable
- {
- #region Private variables
- private Bitmap tempBitmap;
- private float dpiFX;
- private Dictionary<ushort, GlyphChar> 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<GlyphChar> UsedGlyphChars
- {
- get { return usedGlyphChars.Values; }
- }
- /// <summary>
- /// Return text metric structure, need to use after FillOutlineTextMetrix()
- /// </summary>
- public TrueTypeFont.OutlineTextMetric TextMetric
- {
- get { return textMetric; }
- }
- /// <summary>
- /// Gets or sets internal font name
- /// </summary>
- public string Name
- {
- get { return name; }
- set { name = value; }
- }
- /// <summary>
- /// Return source font used in constructor
- /// </summary>
- public Font SourceFont
- {
- get { return sourceFont; }
- }
- /// <summary>
- /// Returns multiplier for stroke bold emulation
- /// </summary>
- public double SourceFontBoldMultiplier
- {
- get { return Math.E / (2 * Math.PI) * baseSize / 9; }
- }
- /// <summary>
- /// Gets or sets internal reference
- /// </summary>
- public long Reference
- {
- get { return reference; }
- set { reference = value; }
- }
- /// <summary>
- /// Gets or sets internal property - save flag
- /// </summary>
- public bool Saved
- {
- get { return saved; }
- set { saved = value; }
- }
- /// <summary>
- /// True if bold style is not supported by font
- /// </summary>
- public bool NeedSimulateBold
- {
- get { return simulateBold; }
- set { simulateBold = value; }
- }
- /// <summary>
- /// True if italic style is not supported by font
- /// </summary>
- public bool NeedSimulateItalic
- {
- get { return simulateItalic; }
- set { simulateItalic = value; }
- }
- /// <summary>
- /// Mark font as editable for InteractiveForms
- /// </summary>
- public bool Editable
- {
- get { return editable; }
- set { editable = value; }
- }
- /// <summary>
- /// Get PostScript name of font
- /// </summary>
- public string PostscriptName { get { return postscript_name; } }
- #endregion
- #region Public Methods
- /// <summary>
- /// Return font file
- /// </summary>
- /// <returns></returns>
- 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;
- }
- /// <summary>
- /// Get font data and set NeedSimulateBold and NeedSimulateItalic properties. Call this method after FillOutlineTextMetrix
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// Returns a list of font runs from the string specified. Also adds glyphs to the used glyphs table.
- /// </summary>
- /// <param name="str"></param>
- /// <param name="rtl"></param>
- /// <param name="originalFont"></param>
- /// <returns></returns>
- 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<RunInfo> runs = LayoutString(str, rtl, originalFont);
- // try to coalesce runs
- List<RunInfo> result = new List<RunInfo>();
- 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<RunInfo> 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<ushort>(ref run.Glyphs, oldLen);
- Array.Resize<string>(ref run.GlyphToUnicode, oldLen);
- Array.Resize<float>(ref run.Widths, oldLen);
- Array.Resize<float>(ref run.Kernings, oldLen);
- Array.Resize<float>(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]);
- }
- }
- }
- }
- /// <summary>
- /// Get alphabet's subset from generation of pattern.
- /// </summary>
- /// <param name="pattern">Regular expression with pattern generator</param>
- /// <param name="rtl">Use left-to-right rules</param>
- 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<GlyphTTF> result = new List<GlyphTTF>();
- 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();
- }
- /// <summary>
- /// Return english name of source font
- /// </summary>
- /// <returns></returns>
- 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
- /// <summary>
- /// Create object of ExportTTFFont.
- /// </summary>
- /// <param name="font"></param>
- 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<ushort, GlyphChar>();
- tempBitmap = new Bitmap(1, 1);
- simulateItalic = false;
- simulateBold = false;
- editable = false;
- }
- /// <summary>
- /// Destructor
- /// </summary>
- 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
- }
- }
|