using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Net;
using System.IO;
namespace FastReport.Utils
{
///
/// Advanced text renderer is used to perform the following tasks:
/// - draw justified text, text with custom line height, text containing html tags;
/// - calculate text height, get part of text that does not fit in the display rectangle;
/// - get paragraphs, lines, words and char sequence to perform accurate export to such
/// formats as PDF, TXT, RTF
///
/// Here is how one may operate the renderer items:
///
/// foreach (AdvancedTextRenderer.Paragraph paragraph in renderer.Paragraphs)
/// {
/// foreach (AdvancedTextRenderer.Line line in paragraph.Lines)
/// {
/// foreach (AdvancedTextRenderer.Word word in line.Words)
/// {
/// if (renderer.HtmlTags)
/// {
/// foreach (AdvancedTextRenderer.Run run in word.Runs)
/// {
/// using (Font f = run.GetFont())
/// using (Brush b = run.GetBrush())
/// {
/// g.DrawString(run.Text, f, b, run.Left, run.Top, renderer.Format);
/// }
/// }
/// }
/// else
/// {
/// g.DrawString(word.Text, renderer.Font, renderer.Brush, word.Left, word.Top, renderer.Format);
/// }
/// }
/// }
/// }
///
///
public class AdvancedTextRenderer
{
#region Fields
private List paragraphs;
private string text;
private IGraphics graphics;
private Font font;
private Brush brush;
private Pen outlinePen;
private RectangleF displayRect;
private StringFormat format;
private HorzAlign horzAlign;
private VertAlign vertAlign;
private float lineHeight;
private float fontLineHeight;
private int angle;
private float widthRatio;
private bool forceJustify;
private bool wysiwyg;
private bool htmlTags;
private bool pDFMode;
private float spaceWidth;
private float scale;
private InlineImageCache cache;
private float fontScale;
#endregion
#region Properties
public List Paragraphs
{
get { return paragraphs; }
}
public IGraphics Graphics
{
get { return graphics; }
}
public Font Font
{
get { return font; }
}
public Brush Brush
{
get { return brush; }
}
public Pen OutlinePen
{
get { return outlinePen; }
}
public Color BrushColor
{
get { return brush is SolidBrush ? (brush as SolidBrush).Color : Color.Black; }
}
public RectangleF DisplayRect
{
get { return displayRect; }
}
public StringFormat Format
{
get { return format; }
}
public HorzAlign HorzAlign
{
get { return horzAlign; }
}
public VertAlign VertAlign
{
get { return vertAlign; }
}
public float LineHeight
{
get { return lineHeight; }
}
public float FontLineHeight
{
get { return fontLineHeight; }
}
public int Angle
{
get { return angle; }
}
public float WidthRatio
{
get { return widthRatio; }
}
public bool ForceJustify
{
get { return forceJustify; }
}
public bool Wysiwyg
{
get { return wysiwyg; }
}
public bool HtmlTags
{
get { return htmlTags; }
}
public float TabSize
{
get
{
// re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0]
float firstTab = 0;
float[] tabSizes = Format.GetTabStops(out firstTab);
if (tabSizes.Length > 1)
return tabSizes[1];
return 0;
}
}
public float TabOffset
{
get
{
// re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0]
float firstTab = 0;
float[] tabSizes = Format.GetTabStops(out firstTab);
if (tabSizes.Length > 0)
return tabSizes[0];
return 0;
}
}
public bool WordWrap
{
get { return (Format.FormatFlags & StringFormatFlags.NoWrap) == 0; }
}
public bool RightToLeft
{
get { return (Format.FormatFlags & StringFormatFlags.DirectionRightToLeft) != 0; }
}
public bool PDFMode
{
get { return pDFMode; }
}
internal float SpaceWidth
{
get { return spaceWidth; }
}
///
/// The scale for font tag
///
public float FontScale { get { return fontScale; } set { fontScale = value; } }
public float Scale { get { return scale; } set { scale = value; } }
public InlineImageCache Cache
{
get
{
if (cache == null)
cache = new InlineImageCache();
return cache;
}
}
#endregion
#region Private Methods
const string ab = "abcdefabcdef";
const string a40b = "abcdef abcdef";
internal static float CalculateSpaceSize(IGraphics g, Font f)
{
float w_ab = g.MeasureString(ab, f).Width;
float w_a40b = g.MeasureString(a40b, f).Width;
return (w_a40b - w_ab) / 40;
}
private void SplitToParagraphs(string text)
{
StyleDescriptor style = new StyleDescriptor(Font.Style, BrushColor, BaseLine.Normal);
if (HtmlTags)
text = text.Replace("
", "\r\n").Replace("
", "\r\n").Replace("
", "\r\n");
string[] lines = text.Split('\n');
int originalCharIndex = 0;
foreach (string line in lines)
{
string s = line;
if (s.Length > 0 && s[s.Length - 1] == '\r')
s = s.Remove(s.Length - 1);
Paragraph paragraph = new Paragraph(s, this, originalCharIndex);
paragraphs.Add(paragraph);
if (HtmlTags)
style = paragraph.WrapHtmlLines(style);
else
paragraph.WrapLines();
originalCharIndex += line.Length + 1;
}
// skip empty paragraphs at the end
for (int i = paragraphs.Count - 1; i >= 0; i--)
{
if (paragraphs[i].IsEmpty && paragraphs.Count != 1)
paragraphs.RemoveAt(i);
else
break;
}
}
private void AdjustParagraphLines()
{
// calculate text height
float height = CalcHeight();
// calculate Y offset
float offsetY = DisplayRect.Top;
if (VertAlign == VertAlign.Center)
offsetY += (DisplayRect.Height - height) / 2;
else if (VertAlign == VertAlign.Bottom)
offsetY += (DisplayRect.Height - height) - 1;
for (int i = 0; i < Paragraphs.Count; i++)
{
Paragraph paragraph = Paragraphs[i];
paragraph.AlignLines(i == Paragraphs.Count - 1 && ForceJustify);
// adjust line tops
foreach (Line line in paragraph.Lines)
{
line.Top = offsetY;
line.MakeUnderlines();
offsetY += line.CalcHeight();
}
}
}
#endregion
#region Public Methods
public void Draw()
{
// set clipping
IGraphicsState state = Graphics.Save();
Graphics.SetClip(DisplayRect, CombineMode.Intersect);
// reset alignment
StringAlignment saveAlign = Format.Alignment;
StringAlignment saveLineAlign = Format.LineAlignment;
Format.Alignment = StringAlignment.Near;
Format.LineAlignment = StringAlignment.Near;
if (Angle != 0)
{
Graphics.TranslateTransform(DisplayRect.Left + DisplayRect.Width / 2,
DisplayRect.Top + DisplayRect.Height / 2);
Graphics.RotateTransform(Angle);
}
Graphics.ScaleTransform(WidthRatio, 1);
foreach (Paragraph paragraph in Paragraphs)
{
paragraph.Draw();
}
// restore alignment and clipping
Format.Alignment = saveAlign;
Format.LineAlignment = saveLineAlign;
Graphics.Restore(state);
}
public float CalcHeight()
{
int charsFit = 0;
StyleDescriptor style = null;
return CalcHeight(out charsFit, out style);
}
public float CalcHeight(out int charsFit, out StyleDescriptor style)
{
charsFit = 0;
style = null;
float height = 0;
float displayHeight = DisplayRect.Height;
if (LineHeight > displayHeight)
return 0;
foreach (Paragraph paragraph in Paragraphs)
{
foreach (Line line in paragraph.Lines)
{
height += line.CalcHeight();
if (charsFit == 0 && height > displayHeight)
{
charsFit = line.OriginalCharIndex;
if (HtmlTags)
style = line.Style;
}
}
}
if (charsFit == 0)
charsFit = text.Length;
return height;
}
public float CalcWidth()
{
float width = 0;
foreach (Paragraph paragraph in Paragraphs)
{
foreach (Line line in paragraph.Lines)
{
if (width < line.Width)
width = line.Width;
}
}
return width + spaceWidth;
}
internal float GetTabPosition(float pos)
{
float tabOffset = TabOffset;
float tabSize = TabSize;
int tabPosition = (int)((pos - tabOffset) / tabSize);
if (pos < tabOffset)
return tabOffset;
return (tabPosition + 1) * tabSize + tabOffset;
}
#endregion
public AdvancedTextRenderer(string text, IGraphics g, Font font, Brush brush, Pen outlinePen,
RectangleF rect, StringFormat format, HorzAlign horzAlign, VertAlign vertAlign,
float lineHeight, int angle, float widthRatio,
bool forceJustify, bool wysiwyg, bool htmlTags, bool pdfMode,
float scale, float fontScale, InlineImageCache cache, bool isPrinting = false)
{
this.cache = cache;
this.scale = scale;
this.fontScale = fontScale;
paragraphs = new List();
this.text = text;
graphics = g;
this.font = font;
this.brush = brush;
this.outlinePen = outlinePen;
displayRect = rect;
this.format = format;
this.horzAlign = horzAlign;
this.vertAlign = vertAlign;
this.lineHeight = lineHeight;
fontLineHeight = font.GetHeight(g.Graphics);
if (this.lineHeight == 0)
{
this.lineHeight = fontLineHeight;
if (isPrinting && Config.IsRunningOnMono && DrawUtils.GetMonoRendering(g.Graphics) == MonoRendering.Pango)
{
// we need this in order to fix inconsistent line spacing when print using Pango rendering
this.lineHeight = fontLineHeight * 1.33f;
}
}
this.angle = angle % 360;
this.widthRatio = widthRatio;
this.forceJustify = forceJustify;
this.wysiwyg = wysiwyg;
this.htmlTags = htmlTags;
pDFMode = pdfMode;
spaceWidth = CalculateSpaceSize(g, font);// g.MeasureString(" ", font).Width;
StringFormatFlags saveFlags = Format.FormatFlags;
StringTrimming saveTrimming = Format.Trimming;
// match DrawString behavior:
// if height is less than 1.25 of font height, turn off word wrap
// commented out due to bug with band.break
//if (rect.Height < FFontLineHeight * 1.25f)
//FFormat.FormatFlags |= StringFormatFlags.NoWrap;
// if word wrap is set, ignore trimming
if (WordWrap)
Format.Trimming = StringTrimming.Word;
// LineLimit flag is essential in linux
Format.FormatFlags = Format.FormatFlags | StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.LineLimit;
if (Angle != 0)
{
// shift displayrect
displayRect.X = -DisplayRect.Width / 2;
displayRect.Y = -DisplayRect.Height / 2;
// rotate displayrect if angle is 90 or 270
if ((Angle >= 90 && Angle < 180) || (Angle >= 270 && Angle < 360))
displayRect = new RectangleF(DisplayRect.Y, DisplayRect.X, DisplayRect.Height, DisplayRect.Width);
}
displayRect.X /= WidthRatio;
displayRect.Width /= WidthRatio;
SplitToParagraphs(text);
AdjustParagraphLines();
// restore original values
displayRect = rect;
Format.FormatFlags = saveFlags;
Format.Trimming = saveTrimming;
}
///
/// Paragraph represents single paragraph. It consists of one or several .
///
public class Paragraph
{
#region Fields
private List lines;
private AdvancedTextRenderer renderer;
private string text;
private int originalCharIndex;
#endregion
#region Properties
public List Lines
{
get { return lines; }
}
public AdvancedTextRenderer Renderer
{
get { return renderer; }
}
public bool Last
{
get { return renderer.Paragraphs[renderer.Paragraphs.Count - 1] == this; }
}
public bool IsEmpty
{
get { return String.IsNullOrEmpty(text); }
}
public string Text
{
get { return text; }
}
#endregion
#region Private Methods
private int MeasureString(string text)
{
if (text.Length > 0)
{
// BEGIN: The fix for linux and core app a264aae5-193b-4e5c-955c-0818de3ca01b
float left = 0;
int tabFit = 0;
while (text.Length > 0 && text[0] == '\t')
{
left = Renderer.GetTabPosition(left);
text = text.Substring(1);
if (Renderer.DisplayRect.Width < left)
return tabFit;
tabFit++;
}
if (tabFit > 0 && Renderer.DisplayRect.Width < left)
return tabFit;
int charsFit = 0;
int linesFit = 0;
// END: The fix for linux and core app a264aae5-193b-4e5c-955c-0818de3ca01b
Renderer.Graphics.MeasureString(text, Renderer.Font,
new SizeF(Renderer.DisplayRect.Width - left, Renderer.FontLineHeight * 1.25f),
Renderer.Format, out charsFit, out linesFit);
return charsFit + tabFit;
}
return 0;
}
#endregion
#region Public Methods
public void WrapLines()
{
string text = this.text;
int charsFit = 0;
if (String.IsNullOrEmpty(text))
{
lines.Add(new Line("", this, originalCharIndex));
return;
}
if (Renderer.WordWrap)
{
int originalCharIndex = this.originalCharIndex;
while (text.Length > 0)
{
charsFit = MeasureString(text);
// avoid infinite loop when width of object less than width of one character
if (charsFit == 0)
{
break;
}
string textFit = text.Substring(0, charsFit).TrimEnd(' ');
lines.Add(new Line(textFit, this, originalCharIndex));
text = text.Substring(charsFit)
// Fix for linux system
.TrimStart(' ');
originalCharIndex += charsFit;
}
}
else
{
string ellipsis = "\u2026";
StringTrimming trimming = Renderer.Format.Trimming;
if (trimming == StringTrimming.EllipsisPath)
Renderer.Format.Trimming = StringTrimming.Character;
charsFit = MeasureString(text);
switch (trimming)
{
case StringTrimming.Character:
case StringTrimming.Word:
text = text.Substring(0, charsFit);
break;
case StringTrimming.EllipsisCharacter:
case StringTrimming.EllipsisWord:
if (charsFit < text.Length)
{
text = text.Substring(0, charsFit);
if (text.EndsWith(" "))
text = text.Substring(0, text.Length - 1);
text += ellipsis;
}
break;
case StringTrimming.EllipsisPath:
if (charsFit < text.Length)
{
while (text.Length > 3)
{
int mid = text.Length / 2;
string newText = text.Substring(0, mid) + ellipsis + text.Substring(mid + 1);
if (MeasureString(newText) == newText.Length)
{
text = newText;
break;
}
else
{
text = text.Remove(mid, 1);
}
}
}
break;
}
lines.Add(new Line(text, this, originalCharIndex));
}
}
public StyleDescriptor WrapHtmlLines(StyleDescriptor style)
{
Line line = new Line("", this, this.originalCharIndex);
lines.Add(line);
Word word = new Word("", line);
line.Words.Add(word);
// for img
//RunImage img = null;
//end img
string text = this.text;
StringBuilder currentWord = new StringBuilder(100);
float width = 0;
bool skipSpace = true;
int originalCharIndex = this.originalCharIndex;
for (int i = 0; i < text.Length; i++)
{
char lastChar = text[i];
if (lastChar == '&')
{
if (Converter.FromHtmlEntities(text, ref i, currentWord))
{
if (i >= text.Length - 1)
{
word.Runs.Add(new Run(currentWord.ToString(), style, word));
// check width
width += word.Width + Renderer.SpaceWidth;
if (width > Renderer.DisplayRect.Width)
{
// line is too long, make a new line
if (line.Words.Count > 1)
{
// if line has several words, delete the last word from the current line
line.Words.RemoveAt(line.Words.Count - 1);
// make new line
line = new Line("", this, originalCharIndex);
// and add word to it
line.Words.Add(word);
word.SetLine(line);
lines.Add(line);
}
}
#if DOTNET_4
currentWord.Clear(); // .NET 2.0 doesn't have Clear()
#else
currentWord.Length = 0;
#endif
lastChar = ' ';
}
else
{
if (currentWord[currentWord.Length - 1] == '\t')
{
currentWord.Length--;
lastChar = '\t';
}
else
{
continue;
}
}
}
}
if (lastChar == '<')
{
// probably html tag
StyleDescriptor newStyle = new StyleDescriptor(style.FontStyle, style.Color, style.BaseLine);
newStyle.Font = style.Font;
newStyle.Size = style.Size;
string tag = "";
bool match = false;
// , ,
if (i + 3 <= text.Length)
{
match = true;
tag = text.Substring(i, 3).ToLower();
if (tag == "")
newStyle.FontStyle |= FontStyle.Bold;
else if (tag == "")
newStyle.FontStyle |= FontStyle.Italic;
else if (tag == "")
newStyle.FontStyle |= FontStyle.Underline;
else
match = false;
if (match)
i += 3;
}
// , ,
if (!match && i + 4 <= text.Length && text[i + 1] == '/')
{
match = true;
tag = text.Substring(i, 4).ToLower();
if (tag == "")
newStyle.FontStyle &= ~FontStyle.Bold;
else if (tag == "")
newStyle.FontStyle &= ~FontStyle.Italic;
else if (tag == "")
newStyle.FontStyle &= ~FontStyle.Underline;
else
match = false;
if (match)
i += 4;
}
// , //
")
newStyle.BaseLine = BaseLine.Subscript;
else if (tag == "")
newStyle.BaseLine = BaseLine.Superscript;
else if (tag == "
', i + 5);
if (right <= 0) match = false;
else
{
//found img and parse them
string src = null;
string alt = " ";
//currentWord = "";
int src_ind = text.IndexOf("src=\"", i + 5);
if (src_ind < right && src_ind >= 0)
{
src_ind += 5;
int src_end = text.IndexOf("\"", src_ind);
if (src_end < right && src_end >= 0)
{
src = text.Substring(src_ind, src_end - src_ind);
}
}
int alt_ind = text.IndexOf("alt=\"", i + 5);
if (alt_ind < right && alt_ind >= 0)
{
alt_ind += 5;
int alt_end = text.IndexOf("\"", alt_ind);
if (alt_end < right && alt_end >= 0)
{
alt = text.Substring(alt_ind, alt_end - alt_ind);
}
}
//begin
if (currentWord.Length != 0)
{
// finish the word
word.Runs.Add(new Run(currentWord.ToString(), style, word));
}
#if DOTNET_4
currentWord.Clear(); // .NET 2.0 doesn't have Clear()
#else
currentWord.Length = 0;
#endif
//end
word.Runs.Add(new RunImage(src, alt, style, word));
skipSpace = false;
i = right - 4;
}
}
else if (tag == "', i + 5);
if (right <= 0) match = false;
else
{
//found font and parse them
string color = null;
string face = null;
string size = null;
int color_ind = text.IndexOf("color=\"", i + 5);
if (color_ind < right && color_ind >= 0)
{
color_ind += 7;
int color_end = text.IndexOf("\"", color_ind);
if (color_end < right && color_end >= 0)
{
color = text.Substring(color_ind, color_end - color_ind);
}
}
int face_ind = text.IndexOf("face=\"", i + 5);
if (face_ind < right && face_ind >= 0)
{
face_ind += 6;
int face_end = text.IndexOf("\"", face_ind);
if (face_end < right && face_end >= 0)
{
face = text.Substring(face_ind, face_end - face_ind);
}
}
int size_ind = text.IndexOf("size=\"", i + 5);
if (size_ind < right && size_ind >= 0)
{
size_ind += 6;
int size_end = text.IndexOf("\"", size_ind);
if (size_end < right && size_end >= 0)
{
size = text.Substring(size_ind, size_end - size_ind);
}
}
if (color != null)
{
if (color.StartsWith("\"") && color.EndsWith("\""))
color = color.Substring(1, color.Length - 2);
if (color.StartsWith("#"))
{
newStyle.Color = Color.FromArgb((int)(0xFF000000 + uint.Parse(color.Substring(1), NumberStyles.HexNumber)));
}
else
{
newStyle.Color = Color.FromName(color);
}
}
newStyle.Font = face;
if (size != null)
{
try
{
size = size.Trim(' ');
switch (size[0])
{
case '-':
size = size.Substring(1);
if (style.Size == 0)
newStyle.Size = Renderer.Font.Size - (float)Converter.FromString(typeof(float), size) * Renderer.FontScale;
else
newStyle.Size = style.Size - (float)Converter.FromString(typeof(float), size) * Renderer.FontScale;
break;
case '+':
size = size.Substring(1);
if (style.Size == 0)
newStyle.Size = Renderer.Font.Size + (float)Converter.FromString(typeof(float), size) * Renderer.FontScale;
else
newStyle.Size = style.Size + (float)Converter.FromString(typeof(float), size) * Renderer.FontScale;
break;
default: newStyle.Size = (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; break;
}
if (newStyle.Size < 0) newStyle.Size = 0;
}
catch { }
}
i = right - 4;
}
}
else
match = false;
if (match)
i += 5;
}
// ,
if (!match && i + 6 <= text.Length && text[i + 1] == '/')
{
match = true;
tag = text.Substring(i, 6).ToLower();
if (tag == "")
newStyle.BaseLine = BaseLine.Normal;
else if (tag == "")
newStyle.BaseLine = BaseLine.Normal;
else
match = false;
if (match)
i += 6;
}
//
if (!match && i + 8 <= text.Length && text.Substring(i, 8).ToLower() == "")
{
newStyle.FontStyle |= FontStyle.Strikeout;
match = true;
i += 8;
}
//
if (!match && i + 9 <= text.Length && text.Substring(i, 9).ToLower() == "")
{
newStyle.FontStyle &= ~FontStyle.Strikeout;
match = true;
i += 9;
}
/*
//
if (!match && i + 7 <= text.Length && text.Substring(i, 7).ToLower() == "")
{
newStyle.Color = Renderer.BrushColor;
newStyle.Size = 0;
newStyle.Font = null;
match = true;
i += 7;
}
if (match)
{
if (currentWord.Length != 0)
{
// finish the word
word.Runs.Add(new Run(currentWord.ToString(), style, word));
}
#if DOTNET_4
currentWord.Clear(); // .NET 2.0 doesn't have Clear()
#else
currentWord.Length = 0;
#endif
style = newStyle;
i--;
if (i >= text.Length - 1)
{
// check width
width += word.Width + Renderer.SpaceWidth;
if (width > Renderer.DisplayRect.Width)
{
// line is too long, make a new line
if (line.Words.Count > 1)
{
// if line has several words, delete the last word from the current line
line.Words.RemoveAt(line.Words.Count - 1);
// make new line
line = new Line("", this, originalCharIndex);
// and add word to it
line.Words.Add(word);
word.SetLine(line);
lines.Add(line);
}
}
}
continue;
}
}
if (lastChar == ' ' || lastChar == '\t' || i == text.Length - 1)
{
// finish the last word
bool isLastWord = i == text.Length - 1;
if (isLastWord)
{
currentWord.Append(lastChar);
skipSpace = false;
}
if (lastChar == '\t')
skipSpace = false;
// space
if (skipSpace)
{
currentWord.Append(lastChar);
}
else
{
// finish the word
if (currentWord.Length != 0)
word.Runs.Add(new Run(currentWord.ToString(), style, word));
// check width
width += word.Width + word.SpaceWidth;
if (width > Renderer.DisplayRect.Width)
{
// line is too long, make a new line
width = 0;
if (line.Words.Count > 1)
{
// if line has several words, delete the last word from the current line
line.Words.RemoveAt(line.Words.Count - 1);
// make new line
line = new Line("", this, originalCharIndex);
// and add word to it
line.Words.Add(word);
word.SetLine(line);
width += word.Width + word.SpaceWidth;
}
else
{
line = new Line("", this, i + 1);
}
lines.Add(line);
}
// TAB symbol
if (lastChar == '\t')
{
if (currentWord.Length == 0 && line.Words.Count > 0 && line.Words[line.Words.Count - 1].Width == 0)
line.Words.RemoveAt(line.Words.Count - 1);
word = new Word("\t", line);
line.Words.Add(word);
// adjust width
width = Renderer.GetTabPosition(width);
}
if (!isLastWord)
{
word = new Word("", line);
line.Words.Add(word);
#if DOTNET_4
currentWord.Clear(); // .NET 2.0 doesn't have Clear()
#else
currentWord.Length = 0;
#endif
originalCharIndex = this.originalCharIndex + i + 1;
skipSpace = true;
}
}
}
else
{
// symbol
currentWord.Append(lastChar);
skipSpace = false;
}
}
return style;
}
public void AlignLines(bool forceJustify)
{
for (int i = 0; i < Lines.Count; i++)
{
HorzAlign align = Renderer.HorzAlign;
if (align == HorzAlign.Justify && i == Lines.Count - 1 && !forceJustify)
align = HorzAlign.Left;
Lines[i].AlignWords(align);
}
}
public void Draw()
{
foreach (Line line in Lines)
{
line.Draw();
}
}
#endregion
public Paragraph(string text, AdvancedTextRenderer renderer, int originalCharIndex)
{
lines = new List();
this.text = text;
this.renderer = renderer;
this.originalCharIndex = originalCharIndex;
}
}
///
/// Line represents single text line. It consists of one or several .
/// Simple line (that does not contain tabs, html tags, and is not justified) has
/// single which contains all the text.
///
public class Line
{
#region Fields
private List words;
private string text;
private bool hasTabs;
private Paragraph paragraph;
private float top;
private float width;
private int originalCharIndex;
private List underlines;
private List strikeouts;
#endregion
#region Properties
public List Words
{
get { return words; }
}
public string Text
{
get { return text; }
}
public bool HasTabs
{
get { return hasTabs; }
}
public float Left
{
get { return Words.Count > 0 ? Words[0].Left : 0; }
}
public float Top
{
get { return top; }
set { top = value; }
}
public float Width
{
get { return width; }
}
public int OriginalCharIndex
{
get { return originalCharIndex; }
}
public AdvancedTextRenderer Renderer
{
get { return paragraph.Renderer; }
}
public StyleDescriptor Style
{
get
{
if (Words.Count > 0)
if (Words[0].Runs.Count > 0)
return Words[0].Runs[0].Style;
return null;
}
}
public bool Last
{
get { return paragraph.Lines[paragraph.Lines.Count - 1] == this; }
}
public List Underlines
{
get { return underlines; }
}
public List Strikeouts
{
get { return strikeouts; }
}
#endregion
#region Private Methods
private void PrepareUnderlines(List list, FontStyle style)
{
list.Clear();
if (Words.Count == 0)
return;
if (Renderer.HtmlTags)
{
float left = 0;
float right = 0;
bool styleOn = false;
foreach (Word word in Words)
{
foreach (Run run in word.Runs)
{
using (Font fnt = run.GetFont())
{
if ((fnt.Style & style) > 0)
{
if (!styleOn)
{
styleOn = true;
left = run.Left;
}
right = run.Left + run.Width;
}
if ((fnt.Style & style) == 0 && styleOn)
{
styleOn = false;
list.Add(new RectangleF(left, Top, right - left, 1));
}
}
}
}
// close the style
if (styleOn)
list.Add(new RectangleF(left, Top, right - left, 1));
}
else if ((Renderer.Font.Style & style) > 0)
{
float lineWidth = Width;
if (Renderer.HorzAlign == HorzAlign.Justify && (!Last || (paragraph.Last && Renderer.ForceJustify)))
lineWidth = Renderer.DisplayRect.Width - Renderer.SpaceWidth;
list.Add(new RectangleF(Left, Top, lineWidth, 1));
}
}
#endregion
#region Public Methods
public void AlignWords(HorzAlign align)
{
width = 0;
// handle each word
if (align == HorzAlign.Justify || HasTabs || Renderer.Wysiwyg || Renderer.HtmlTags)
{
float left = 0;
Word word = null;
for (int i = 0; i < Words.Count; i++)
{
word = Words[i];
word.Left = left;
if (word.Text == "\t")
{
left = Renderer.GetTabPosition(left);
// remove tab
Words.RemoveAt(i);
i--;
}
else
left += word.Width + word.SpaceWidth;
}
if (word != null)
width = left - word.SpaceWidth;
else
width = left - Renderer.SpaceWidth;
}
else
{
// join all words into one
Words.Clear();
Words.Add(new Word(text, this));
width = Words[0].Width;
}
float rectWidth = Renderer.DisplayRect.Width;
if (align == HorzAlign.Justify)
{
float delta = (rectWidth - width - Renderer.SpaceWidth) / (Words.Count - 1);
float curDelta = delta;
for (int i = 1; i < Words.Count; i++)
{
words[i].Left += curDelta;
curDelta += delta;
}
}
else
{
float delta = 0;
if (align == HorzAlign.Center)
delta = (rectWidth - width) / 2;
else if (align == HorzAlign.Right)
delta = rectWidth - width - Renderer.SpaceWidth;
for (int i = 0; i < Words.Count; i++)
{
words[i].Left += delta;
}
}
// adjust X offset
foreach (Word word in Words)
{
if (Renderer.RightToLeft)
word.Left = Renderer.DisplayRect.Right - word.Left;
else
word.Left += Renderer.DisplayRect.Left;
word.AdjustRuns();
if (Renderer.RightToLeft && Renderer.PDFMode)
word.Left -= word.Width;
}
}
public void MakeUnderlines()
{
PrepareUnderlines(underlines, FontStyle.Underline);
PrepareUnderlines(strikeouts, FontStyle.Strikeout);
}
public void Draw()
{
foreach (Word word in Words)
{
word.Draw();
}
if (Underlines.Count > 0 || Strikeouts.Count > 0)
{
using (Pen pen = new Pen(Renderer.Brush, Renderer.Font.Size * 0.1f))
{
float h = Renderer.FontLineHeight;
float w = h * 0.1f; // to match .net char X offset
// invert offset in case of rtl
if (Renderer.RightToLeft)
w = -w;
// emulate underline & strikeout
foreach (RectangleF rect in Underlines)
{
Renderer.Graphics.DrawLine(pen, rect.Left + w, rect.Top + h - w, rect.Right + w, rect.Top + h - w);
}
h /= 2;
foreach (RectangleF rect in Strikeouts)
{
Renderer.Graphics.DrawLine(pen, rect.Left + w, rect.Top + h, rect.Right + w, rect.Top + h);
}
}
}
}
public float CalcHeight()
{
float height = -1;
foreach (Word word in Words)
{
height = Math.Max(height, word.CalcHeight());
}
if (height < 0)
height = Renderer.LineHeight;
return height;
}
#endregion
public Line(string text, Paragraph paragraph, int originalCharIndex)
{
this.words = new List();
this.text = text;
this.paragraph = paragraph;
this.originalCharIndex = originalCharIndex;
underlines = new List();
strikeouts = new List();
hasTabs = text.Contains("\t");
// split text by spaces
string[] words = text.Split(' ');
string textWithSpaces = "";
foreach (string word in words)
{
if (word == "")
textWithSpaces += " ";
else
{
// split text by tabs
textWithSpaces += word;
string[] tabWords = textWithSpaces.Split('\t');
foreach (string word1 in tabWords)
{
if (word1 == "")
this.words.Add(new Word("\t", this));
else
{
this.words.Add(new Word(word1, this));
this.words.Add(new Word("\t", this));
}
}
// remove last tab
this.words.RemoveAt(this.words.Count - 1);
textWithSpaces = "";
}
}
}
internal float CalcBaseLine()
{
float baseline = 0;
foreach (Word word in Words)
{
baseline = Math.Max(baseline, word.CalcBaseLine());
}
return baseline;
}
internal float CalcUnderBaseLine()
{
float underbaseline = 0;
foreach (Word word in Words)
{
underbaseline = Math.Max(underbaseline, word.CalcUnderBaseLine());
}
return underbaseline;
}
}
///
/// Word represents single word. It may consist of one or several , in case
/// when HtmlTags are enabled in the main class.
///
public class Word
{
#region Fields
private List runs;
protected string text;
private float left;
private float width;
internal Line line;
#endregion
#region Properties
public string Text
{
get { return text; }
}
public float Left
{
get { return left; }
set { left = value; }
}
public float Width
{
get
{
if (width == -1)
{
if (Renderer.HtmlTags)
{
width = 0;
foreach (Run run in Runs)
{
width += run.Width;
}
}
else
{
width = Renderer.Graphics.MeasureString(text, Renderer.Font, 10000, StringFormat.GenericTypographic).Width;
}
}
return width;
}
}
public float Top
{
get { return line.Top; }
}
public AdvancedTextRenderer Renderer
{
get { return line.Renderer; }
}
public List Runs
{
get { return runs; }
}
public float SpaceWidth
{
get
{
if (Runs == null || Runs.Count == 0)
return Renderer.SpaceWidth;
return Runs[Runs.Count - 1].SpaceWidth;
}
}
#endregion
#region Public Methods
public void AdjustRuns()
{
float left = Left;
foreach (Run run in Runs)
{
run.Left = left;
if (Renderer.RightToLeft)
{
left -= run.Width;
if (Renderer.PDFMode)
run.Left -= run.Width;
}
else
left += run.Width;
}
}
public void SetLine(Line line)
{
this.line = line;
}
public void Draw()
{
if (Renderer.HtmlTags)
{
foreach (Run run in Runs)
{
run.Draw();
}
}
else
{
// don't draw underlines & strikeouts because they are drawn in the Line.Draw method
Font font = Renderer.Font;
bool disposeFont = false;
if ((Renderer.Font.Style & FontStyle.Underline) > 0 || (Renderer.Font.Style & FontStyle.Strikeout) > 0)
{
font = new Font(Renderer.Font, Renderer.Font.Style & ~FontStyle.Underline & ~FontStyle.Strikeout);
disposeFont = true;
}
if (Renderer.OutlinePen == null)
{
Renderer.Graphics.DrawString(Text, font, Renderer.Brush, Left, Top, Renderer.Format);
}
else
{
GraphicsPath path = new GraphicsPath();
path.AddString(Text, font.FontFamily, Convert.ToInt32(font.Style), Renderer.Graphics.DpiY * font.Size / 72, new PointF(Left - 1, Top - 1), Renderer.Format);
Renderer.Graphics.FillAndDrawPath(Renderer.OutlinePen, Renderer.Brush, path);
}
if (disposeFont)
{
font.Dispose();
font = null;
}
}
}
internal float CalcHeight()
{
if (Renderer.HtmlTags)
{
float height = -1;
foreach (Run run in Runs)
{
height = Math.Max(height, run.Height);
}
if (height < 0)
height = Renderer.LineHeight;
return height;
}
else
{
#if SKIA
// we need actual height of a text because it may have font fallback with different metrics
if (!string.IsNullOrEmpty(text))
{
return DrawUtils.MeasureString(Renderer.Graphics.Graphics, text, Renderer.Font, Renderer.Format).Height;
}
#endif
return Renderer.LineHeight;
}
}
internal float CalcBaseLine()
{
float baseLine = 0;
if (Renderer.HtmlTags)
{
foreach (Run run in Runs)
{
baseLine = Math.Max(baseLine, run.CurrentBaseLine);
}
return baseLine;
}
else
{
return 0;
}
}
internal float CalcUnderBaseLine()
{
float underbaseLine = 0;
if (Renderer.HtmlTags)
{
foreach (Run run in Runs)
{
underbaseLine = Math.Max(underbaseLine, run.CurrentUnderBaseLine);
}
return underbaseLine;
}
else
{
return 0;
}
}
#endregion
public Word(string text, Line line)
{
this.text = text;
runs = new List();
this.line = line;
width = -1;
}
}
///
/// Represents character placement.
///
public enum BaseLine
{
Normal,
Subscript,
Superscript
}
///
/// Represents a style used in HtmlTags mode.
///
public class StyleDescriptor
{
#region Fields
private FontStyle fontStyle;
private Color color;
private BaseLine baseLine;
private string font;
private float size;
#endregion
#region Properties
public FontStyle FontStyle
{
get { return fontStyle; }
set { fontStyle = value; }
}
public string Font
{
get { return font; }
set { font = value; }
}
public float Size
{
get { return size; }
set { size = value; }
}
public Color Color
{
get { return color; }
set { color = value; }
}
public BaseLine BaseLine
{
get { return baseLine; }
set { baseLine = value; }
}
#endregion
#region Public Methods
public override string ToString()
{
string result = "";
if ((FontStyle & FontStyle.Bold) != 0)
result += "";
if ((FontStyle & FontStyle.Italic) != 0)
result += "";
if ((FontStyle & FontStyle.Underline) != 0)
result += "";
if ((FontStyle & FontStyle.Strikeout) != 0)
result += "";
if (BaseLine == BaseLine.Subscript)
result += "";
if (BaseLine == BaseLine.Superscript)
result += "";
result += "";
return result;
}
#endregion
public StyleDescriptor(FontStyle fontStyle, Color color, BaseLine baseLine)
{
this.fontStyle = fontStyle;
this.color = color;
this.baseLine = baseLine;
}
}
///
/// Represents sequence of characters that have the same .
///
public class Run
{
#region Fields
protected string text;
private StyleDescriptor style;
protected Word word;
private float left;
protected float width;
protected float lineHeight;
protected float fontLineHeight;
private float baseLine;
protected float underBaseLine;
protected float spaceWidth;
#endregion
#region Properties
public string Text
{
get { return text; }
}
public StyleDescriptor Style
{
get { return style; }
}
public AdvancedTextRenderer Renderer
{
get { return word.Renderer; }
}
public float Left
{
get { return left; }
set { left = value; }
}
public float LineHeight
{
get
{
if (lineHeight == 0)
{
if (style.Font == null && style.Size <= 0)
lineHeight = Renderer.LineHeight;
else
lineHeight = GetFont().GetHeight(Renderer.Graphics.Graphics);
}
return lineHeight;
}
}
virtual public float CurrentBaseLine
{
get
{
if (baseLine < 0)
{
Font ff = GetFont();
float lineSpace = ff.FontFamily.GetLineSpacing(Style.FontStyle);
float ascent = ff.FontFamily.GetCellAscent(Style.FontStyle);
baseLine = FontLineHeight * ascent / lineSpace;
underBaseLine = FontLineHeight - baseLine;
}
return baseLine;
}
}
virtual public float CurrentUnderBaseLine
{
get
{
if (underBaseLine < 0)
{
Font ff = GetFont();
float lineSpace = ff.FontFamily.GetLineSpacing(Style.FontStyle);
float ascent = ff.FontFamily.GetCellAscent(Style.FontStyle);
baseLine = FontLineHeight * ascent / lineSpace;
underBaseLine = FontLineHeight - baseLine;
}
return baseLine;
}
}
public float FontLineHeight
{
get
{
if (fontLineHeight == 0)
{
if (style.Font == null && style.Size <= 0)
fontLineHeight = Renderer.FontLineHeight;
else
fontLineHeight = GetFont().GetHeight(Renderer.Graphics.Graphics);
}
return fontLineHeight;
}
}
public virtual float Top
{
get
{
float baseLine = 0;
if (Style.BaseLine == BaseLine.Subscript)
baseLine += FontLineHeight * 0.45f;
else if (Style.BaseLine == BaseLine.Superscript)
baseLine -= FontLineHeight * 0.15f;
return word.Top + word.line.CalcBaseLine() - CurrentBaseLine + baseLine;
}
}
virtual public float Width
{
get { return width; }
}
virtual public float Height
{
get
{
return LineHeight;
}
}
public float SpaceWidth
{
get
{
if (spaceWidth < 0)
{
spaceWidth = CalculateSpaceSize(Renderer.Graphics, GetFont());// Renderer.Graphics.MeasureString(" ", GetFont()).Width;
}
return spaceWidth;
}
}
#endregion
#region Private Methods
private Font GetFont(bool disableUnderlinesStrikeouts)
{
float fontSize = Renderer.Font.Size;
if (Style.Size != 0)
fontSize = Style.Size;
if (Style.BaseLine != BaseLine.Normal)
fontSize *= 0.6f;
FontStyle fontStyle = Style.FontStyle;
if (disableUnderlinesStrikeouts)
fontStyle = fontStyle & ~FontStyle.Underline & ~FontStyle.Strikeout;
if (Style.Font != null)
return new Font(Style.Font, fontSize, fontStyle);
return new Font(Renderer.Font.FontFamily, fontSize, fontStyle);
}
#endregion
#region Public Methods
public Font GetFont()
{
return GetFont(false);
}
public Brush GetBrush()
{
return new SolidBrush(Style.Color);
}
public virtual void Draw()
{
using (Font font = GetFont(true))
using (Brush brush = GetBrush())
{
Renderer.Graphics.DrawString(text, font, brush, Left, Top, Renderer.Format);
}
}
#endregion
public Run(string text, StyleDescriptor style, Word word)
{
baseLine = float.MinValue;
underBaseLine = float.MinValue;
this.text = text;
this.style = new StyleDescriptor(style.FontStyle, style.Color, style.BaseLine);
this.style.Font = style.Font;
this.style.Size = style.Size;
this.word = word;
spaceWidth = -1;
using (Font font = GetFont())
{
width = Renderer.Graphics.MeasureString(text, font, 10000, StringFormat.GenericTypographic).Width;
}
}
}
///
/// Represents inline Image.
///
internal class RunImage : Run
{
public Image Image { get { return image; } }
override public float Width
{
get
{
if (Image == null) return base.Width;
return Image.Width;
}
}
override public float Top
{
get
{
float baseLine = 0;
if (Style.BaseLine == BaseLine.Subscript)
baseLine += FontLineHeight * 0.45f;
else if (Style.BaseLine == BaseLine.Superscript)
baseLine -= FontLineHeight * 0.15f;
return word.Top + word.line.CalcBaseLine() - CurrentBaseLine + baseLine;
}
}
override public float CurrentBaseLine
{
get
{
if (Image == null) return base.CurrentBaseLine;
return Image.Height;
}
}
override public float Height
{
get
{
if (Image == null) return base.Height;
return Image.Height + word.line.CalcUnderBaseLine();
}
}
private Image image;
override public void Draw()
{
if (Image == null)
{
base.Draw();
return;
}
Renderer.Graphics.DrawImage(Image, Left, Top);// (FText, font, brush, Left, Top, Renderer.Format);
}
public static Bitmap ResizeImage(Image image, float scale)
{
int width = (int)(image.Width * scale);
int height = (int)(image.Height * scale);
if (width == 0) width = 1;
if (height == 0) height = 1;
Rectangle destRect = new Rectangle(0, 0, width, height);
Bitmap destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (Graphics graphics = System.Drawing.Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (System.Drawing.Imaging.ImageAttributes wrapMode = new System.Drawing.Imaging.ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
public RunImage(string src, string text, StyleDescriptor style, Word word) : base(text, style, word)
{
underBaseLine = 0;
image = ResizeImage(InlineImageCache.Load(Renderer.Cache, src), Renderer.Scale);
}
}
}
///
/// Standard text renderer uses standard DrawString method to draw text. It also supports:
/// - text rotation;
/// - fonts with non-standard width ratio.
/// In case your text is justified, or contains html tags, use the
/// class instead.
///
internal class StandardTextRenderer
{
public static void Draw(string text, IGraphics g, Font font, Brush brush, Pen outlinePen,
RectangleF rect, StringFormat format, int angle, float widthRatio)
{
IGraphicsState state = g.Save();
g.SetClip(rect, CombineMode.Intersect);
g.TranslateTransform(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
g.RotateTransform(angle);
rect.X = -rect.Width / 2;
rect.Y = -rect.Height / 2;
if ((angle >= 90 && angle < 180) || (angle >= 270 && angle < 360))
rect = new RectangleF(rect.Y, rect.X, rect.Height, rect.Width);
g.ScaleTransform(widthRatio, 1);
rect.X /= widthRatio;
rect.Width /= widthRatio;
if (outlinePen == null)
{
g.DrawString(text, font, brush, rect, format);
}
else
{
GraphicsPath path = new GraphicsPath();
path.AddString(text, font.FontFamily, Convert.ToInt32(font.Style), g.DpiY * font.Size / 72, rect, format);
g.FillAndDrawPath(outlinePen, brush, path);
}
g.Restore(state);
}
}
///
/// Cache for rendering img tags in textobject.
/// You can use only HTTP[s] protocol with absolute urls.
///
public class InlineImageCache : IDisposable
{
#region Private Fields
private WebClient client;
private Dictionary items;
private bool serialized;
private object locker;
#endregion Private Fields
#region Public Properties
///
/// Is serialized
///
public bool Serialized { get { return serialized; } set { serialized = value; } }
#endregion Public Properties
#region Private Properties
///
/// Get or set WebClient for downloading imgs by url
///
private WebClient Client
{
get
{
if (client == null)
{
client = new WebClient();
}
return client;
}
set
{
client = value;
}
}
#endregion Private Properties
#region Public Events
///
/// Occurs before image load
///
public static event EventHandler AfterLoad;
///
/// Occurs after image load
///
public static event EventHandler BeforeLoad;
#endregion Public Events
#region Public Methods
///
/// Enumerates all values
///
///
public IEnumerable AllItems()
{
List list = new List();
lock (locker)
{
if (items != null)
{
foreach (KeyValuePair item in items)
{
item.Value.Src = item.Key;
list.Add(item.Value);
}
}
}
return list;
}
///
/// Return CacheItem by src
///
/// Src attribute from img tag
///
public CacheItem Get(string src)
{
CacheItem item = null;
if (!Validate(src))
item = new CacheItem();
if (String.IsNullOrEmpty(src))
return item;
lock (locker)
{
if (items == null)
{
items = new Dictionary();
if (item == null)
item = new CacheItem();
items[src] = item;
Serialized = false;
}
if (items.ContainsKey(src))
return items[src];
}
return item;
}
///
///
///
///
///
public Image Load(string src)
{
CacheItem item = null;
if (String.IsNullOrEmpty(src))
item = new CacheItem();
else
lock (locker)
{
if (items == null)
items = new Dictionary();
else
if (items.ContainsKey(src))
return items[src].Image;
item = new CacheItem();
if (Validate(src))
{
try
{
if (src.StartsWith("data:"))
{
item.Set(src.Substring(src.IndexOf("base64,") + "base64,".Length));
}
else
item.Set(Client.DownloadData(src));
}
catch
{
item.Set("");
}
}
items[src] = item;
Serialized = false;
}
item.Src = src;
return item.Image;
}
///
/// Set CacheItem by src
///
/// Src attribute from img tag
/// CacheItem
///
public CacheItem Set(string src, CacheItem item)
{
if (String.IsNullOrEmpty(src))
return new CacheItem();
lock (locker)
{
if (items == null)
items = new Dictionary();
if (!Validate(src))
item = new CacheItem();
items[src] = item;
Serialized = false;
}
item.Src = src;
return item;
}
///
/// Validate src attribute from image
///
/// Src attribute from img tag
/// return true if src is valid
public bool Validate(string src)
{
if (String.IsNullOrEmpty(src))
return false;
src = src.ToLower();
if (src.StartsWith("http://"))
return true;
if (src.StartsWith("https://"))
return true;
if (src.StartsWith("data:") && src.IndexOf("base64,") > 0)
return true;
return false;
}
#endregion Public Methods
#region Internal Methods
static internal Image Load(InlineImageCache cache, string src)
{
LoadEventArgs args = new LoadEventArgs(cache, src);
if (BeforeLoad != null) BeforeLoad(null, args);
Image result = null;
if (!args.Handled) result = cache.Load(src);
args.Handled = false;
if (AfterLoad != null) AfterLoad(null, args);
if (args.Handled)
return cache.Get(src).Image;
return result;
}
#endregion Internal Methods
#region Public Constructors
///
public InlineImageCache()
{
locker = new object();
client = null;
}
///
///
///
~InlineImageCache()
{
Dispose(false);
}
#endregion Public Constructors
#region Public Classes
///
/// Item of image cache Dictionary
///
public class CacheItem : IDisposable
{
#region Private Fields
private string base64;
private bool error;
private Image image;
//private int FId;
private string src;
private byte[] stream;
#endregion Private Fields
#region Public Properties
///
/// Get Base64 string
///
public string Base64
{
get
{
try
{//For strange img tag
if (base64 != null)
return base64;
if (stream != null)
{
base64 = Convert.ToBase64String(stream);
return base64;
}
if (image != null)
{
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Flush();
stream = ms.ToArray();
}
base64 = Convert.ToBase64String(stream);
return base64;
}
}
catch { }
GetErrorImage();
return "";
}
}
///
/// Return true if has some error with Image
///
public bool Error
{
get { return error; }
}
///
/// Get Image
///
public Image Image
{
get
{
try
{//for strange img tag
if (image != null)
return image;
if (stream != null)
{
MemoryStream ms = new MemoryStream(stream);
image = Bitmap.FromStream(ms);
return image;
}
if (base64 != null)
{
this.stream = Convert.FromBase64String(base64);
MemoryStream ms = new MemoryStream(stream);
image = Bitmap.FromStream(ms);
return image;
}
}
catch { }
return GetErrorImage();
}
}
///
/// Get byte array
///
public byte[] Stream
{
get
{
if (stream != null) return stream;
if (base64 != null)
{
stream = Convert.FromBase64String(base64);
return stream;
}
if (image != null)
{
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Flush();
stream = ms.ToArray();
}
return stream;
}
return new byte[0];
}
}
#endregion Public Properties
#region Internal Properties
internal string Src
{
get { return src; }
set { src = value; }
}
#endregion Internal Properties
#region Public Methods
///
/// Return error image and set true to error property
///
///
public Image GetErrorImage()
{
error = true;
base64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAFdJREFUOE9jbGlq+c9AIqipq2GEawEZQAo4dvgYqoXD0QAGhv9ATyKCBY1PXBjANKEbBjSWOANA9mPRDBImzgCKXECVMMCTsojzwtAzAOQvUjCJmRe/cgDt6ZAkZx23LwAAAABJRU5ErkJggg==";
src = "data:image/png;base64," + base64;
stream = Convert.FromBase64String(base64);
using (MemoryStream ms = new MemoryStream(stream))
image = Bitmap.FromStream(ms);
return image;
}
///
/// Set value for cache item
///
/// Image encoded base64 string
public void Set(string base64)
{
this.base64 = base64;
image = null;
stream = null;
}
///
/// Set value for cache item
///
/// Image
public void Set(Image img)
{
base64 = null;
image = img;
stream = null;
}
///
/// Set value for cache item
///
/// Image
public void Set(byte[] arr)
{
base64 = null;
image = null;
stream = arr;
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
///
///
///
///
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (image != null)
{
image.Dispose();
image = null;
}
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~CacheItem() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
#endregion Public Methods
}
///
/// WebClientEventArgs
///
public class LoadEventArgs : EventArgs
{
#region Private Fields
private InlineImageCache cache;
private bool handled;
private string source;
#endregion Private Fields
#region Public Properties
///
/// Gets a cache
///
public InlineImageCache Cache { get { return cache; } }
///
/// Gets or sets a value indicating whether the event was handled.
///
public bool Handled { get { return handled; } set { handled = value; } }
///
/// Gets or sets a url from src attribue of img tag
///
public string Source { get { return source; } set { source = value; } }
#endregion Public Properties
#region Internal Constructors
internal LoadEventArgs(InlineImageCache c, string src)
{
cache = c;
source = src;
handled = false;
}
#endregion Internal Constructors
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
///
///
///
///
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
if (this.items != null)
{
Dictionary items = this.items;
this.items = null;
foreach (CacheItem item in items.Values)
item.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~InlineImageCache() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
#endregion Public Classes
}
}