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
}
}