using System;
using System.Text;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using FastReport.Utils;
using FastReport.Format;
using FastReport.Code;
using System.Windows.Forms;
using System.Drawing.Design;
namespace FastReport
{
///
/// Specifies the horizontal alignment of a text in the TextObject object.
///
public enum HorzAlign
{
///
/// Specifies that text is aligned in the left of the layout rectangle.
///
Left,
///
/// Specifies that text is aligned in the center of the layout rectangle.
///
Center,
///
/// Specifies that text is aligned in the right of the layout rectangle.
///
Right,
///
/// Specifies that text is aligned in the left and right sides of the layout rectangle.
///
Justify
}
///
/// Specifies the vertical alignment of a text in the TextObject object.
///
public enum VertAlign
{
///
/// Specifies that text is aligned in the top of the layout rectangle.
///
Top,
///
/// Specifies that text is aligned in the center of the layout rectangle.
///
Center,
///
/// Specifies that text is aligned in the bottom of the layout rectangle.
///
Bottom
}
///
/// The type of text renderer
///
public enum TextRenderType
{
///
/// The default render
///
Default,
///
/// Render with some html tags and stable logic
///
HtmlTags,
///
/// Render with img tags, span etc. Experimental and unstable logic
///
HtmlParagraph,
///
/// Renders a text in a simplest way. For internal use only.
///
Inline
}
///
/// The format of paragraph
///
[TypeConverter(typeof(TypeConverters.FRExpandableObjectConverter))]
public class ParagraphFormat
{
private float firstLineIndent;
private float lineSpacing;
private LineSpacingType lineSpacingType;
private bool skipFirstLineIndent;
///
/// The first line on each paragraph, not effect if value less then 0
///
[Browsable(true)]
[DefaultValue(0f)]
[TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
public float FirstLineIndent
{
get { return firstLineIndent; }
set { if (value >= 0) firstLineIndent = value; }
}
///
/// The distance between lines, not effect if value less then 0
///
[Browsable(true)]
[DefaultValue(0f)]
[TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
public float LineSpacing
{
get { return lineSpacing; }
set { if (value >= 0) lineSpacing = value; }
}
///
/// The spacing type for distance between line calculation
///
[Browsable(true)]
[DefaultValue(LineSpacingType.Single)]
public LineSpacingType LineSpacingType
{
get { return lineSpacingType; }
set { lineSpacingType = value; }
}
///
/// The value for a multiplication line height for adding spacing
///
[Browsable(true)]
[DefaultValue(0f)]
public float LineSpacingMultiple
{
get { return lineSpacing / 100f; }
set { if (value >= 0) lineSpacing = value * 100f; }
}
///
/// Skip the line indent in the first paragraph, for broken paragraphs
///
[Browsable(false)]
[DefaultValue(false)]
public bool SkipFirstLineIndent
{
get { return skipFirstLineIndent; }
set { skipFirstLineIndent = value; }
}
///
/// clone with new scale;
///
///
///
internal ParagraphFormat MultipleScale(float scale)
{
ParagraphFormat clone = new ParagraphFormat();
clone.lineSpacingType = lineSpacingType;
if (LineSpacingType == LineSpacingType.Multiple)
clone.lineSpacing = lineSpacing;
else
clone.lineSpacing = lineSpacing * scale;
clone.firstLineIndent = firstLineIndent * scale;
clone.skipFirstLineIndent = skipFirstLineIndent;
return clone;
}
internal void Assign(ParagraphFormat p)
{
lineSpacingType = p.lineSpacingType;
lineSpacing = p.lineSpacing;
firstLineIndent = p.firstLineIndent;
skipFirstLineIndent = p.skipFirstLineIndent;
}
public override bool Equals(object obj)
{
ParagraphFormat format = obj as ParagraphFormat;
return format != null &&
firstLineIndent == format.firstLineIndent &&
lineSpacing == format.lineSpacing &&
lineSpacingType == format.lineSpacingType &&
skipFirstLineIndent == format.skipFirstLineIndent;
}
public override int GetHashCode()
{
unchecked
{
int hashCode = -1051315095;
hashCode = hashCode * -1521134295 + firstLineIndent.GetHashCode();
hashCode = hashCode * -1521134295 + lineSpacing.GetHashCode();
hashCode = hashCode * -1521134295 + lineSpacingType.GetHashCode();
hashCode = hashCode * -1521134295 + skipFirstLineIndent.GetHashCode();
return hashCode;
}
}
}
///
/// The spacing type between lines
///
public enum LineSpacingType
{
///
/// Single spacing, not effect from LineSpacing
///
Single,
///
/// Minimal spacing in exactly size
///
AtLeast,
///
/// The specific distance between the lines, for some exports, does not work if the distance value is too small.
///
Exactly,
///
/// The calculated distance between lines, for some exports, does not work if the distance value is too small.
///
Multiple
}
///
/// Specifies the behavior of the AutoShrink feature of TextObject.
///
public enum AutoShrinkMode
{
///
/// AutoShrink is disabled.
///
None,
///
/// AutoShrink decreases the Font.Size property of the TextObject.
///
FontSize,
///
/// AutoShrink decreases the FontWidthRatio property of the TextObject.
///
FontWidth
}
///
/// Represents the Text object that may display one or several text lines.
///
///
/// Specify the object's text in the Text property.
/// Text may contain expressions and data items, for example: "Today is [Date]". When report
/// is running, all expressions are calculated and replaced with actual values, so the text
/// would be "Today is 01.01.2008".
/// The symbols used to find expressions in a text are set in the
/// Brackets property. You also may disable expressions
/// using the AllowExpressions property.
/// To format an expression value, use the property.
///
public partial class TextObject : TextObjectBase
{
#region Fields
private bool autoWidth;
private HorzAlign horzAlign;
private VertAlign vertAlign;
private int angle;
private bool rightToLeft;
private bool wordWrap;
private bool underlines;
private Font font;
private FillBase textFill;
private TextOutline textOutline;
private StringTrimming trimming;
private float fontWidthRatio;
private float firstTabOffset;
private float tabWidth;
private FloatCollection tabPositions;
private bool clip;
private ConditionCollection highlight;
private bool wysiwyg;
private float lineHeight;
private bool forceJustify;
private TextRenderType textRenderType;
private AutoShrinkMode autoShrink;
private float autoShrinkMinSize;
private float paragraphOffset;
private FillBase savedTextFill;
private Font savedFont;
private string savedText;
private FormatBase savedFormat;
private InlineImageCache inlineImageCache;
private ParagraphFormat paragraphFormat;
private bool preserveLastLineSpace;
#endregion
#region Properties
///
/// Gets or sets a paragraph format for a new html rendering type, not for others rendering
///
[Category("Appearance")]
public ParagraphFormat ParagraphFormat
{
get { return paragraphFormat; }
set { paragraphFormat = value; }
}
///
/// Gets or sets a value that determines if the text object should handle its width automatically.
///
[DefaultValue(false)]
[Category("Behavior")]
public bool AutoWidth
{
get { return autoWidth; }
set { autoWidth = value; }
}
///
/// Gets or sets a value that indicates whether the font size should shrink to
/// display the longest text line without word wrap.
///
///
/// To limit the minimum size, use the property.
///
[DefaultValue(AutoShrinkMode.None)]
[Category("Behavior")]
public AutoShrinkMode AutoShrink
{
get { return autoShrink; }
set { autoShrink = value; }
}
///
/// Gets or sets the minimum size of font (or minimum width ratio) if the
/// mode is on.
///
///
/// This property determines the minimum font size (in case the property is set to
/// FontSize), or the minimum font width ratio (if AutoShrink is set to FontWidth).
/// The default value is 0, that means no limits.
///
[DefaultValue(0f)]
[Category("Behavior")]
public float AutoShrinkMinSize
{
get { return autoShrinkMinSize; }
set { autoShrinkMinSize = value; }
}
///
/// Gets or sets the horizontal alignment of a text in the TextObject object.
///
[DefaultValue(HorzAlign.Left)]
[Category("Appearance")]
public HorzAlign HorzAlign
{
get { return horzAlign; }
set { horzAlign = value; }
}
///
/// Gets or sets the vertical alignment of a text in the TextObject object.
///
[DefaultValue(VertAlign.Top)]
[Category("Appearance")]
public VertAlign VertAlign
{
get { return vertAlign; }
set { vertAlign = value; }
}
///
/// Gets or sets the text angle, in degrees.
///
[DefaultValue(0)]
[Category("Appearance")]
[Editor("FastReport.TypeEditors.AngleEditor, FastReport", typeof(UITypeEditor))]
public int Angle
{
get { return angle; }
set { angle = value; }
}
///
/// Gets or sets a value that indicates whether the component should draw right-to-left for RTL languages.
///
[DefaultValue(false)]
[Category("Behavior")]
public bool RightToLeft
{
get { return rightToLeft; }
set { rightToLeft = value; }
}
///
/// Gets or sets a value that indicates if lines are automatically word-wrapped.
///
[DefaultValue(true)]
[Category("Behavior")]
public bool WordWrap
{
get { return wordWrap; }
set { wordWrap = value; }
}
///
/// Gets or sets a value that determines if the text object will underline each text line.
///
[DefaultValue(false)]
[Category("Appearance")]
public bool Underlines
{
get { return underlines; }
set { underlines = value; }
}
///
/// Gets or sets the font settings for this object.
///
[Category("Appearance")]
public Font Font
{
get { return font; }
set
{
font = value;
if (!String.IsNullOrEmpty(Style))
Style = "";
}
}
///
/// Gets or sets a collection of TAB symbol positions, in pixels.
///
/// Use collection methods to add or remove TAB positions.
public FloatCollection TabPositions
{
get { return tabPositions; }
set
{
if (value == null)
tabPositions.Clear();
else
tabPositions = value;
}
}
///
/// Gets or sets the fill color used to draw a text.
///
///
/// Default fill is . You may specify other fill types, for example:
///
/// text1.TextFill = new HatchFill(Color.Black, Color.White, HatchStyle.Cross);
///
/// Use the property to set the solid text color.
///
[Category("Appearance")]
[Editor("FastReport.TypeEditors.FillEditor, FastReport", typeof(UITypeEditor))]
public FillBase TextFill
{
get { return textFill; }
set
{
if (value == null)
throw new ArgumentNullException("TextFill");
textFill = value;
if (!String.IsNullOrEmpty(Style))
Style = "";
}
}
///
/// Gets or sets the text outline.
///
[Category("Appearance")]
[Editor("FastReport.TypeEditors.OutlineEditor, FastReport", typeof(UITypeEditor))]
public TextOutline TextOutline
{
get { return textOutline; }
set
{
if (value == null)
throw new ArgumentNullException("TextOutline");
textOutline = value;
if (!String.IsNullOrEmpty(Style))
Style = "";
}
}
///
/// Gets or sets the text color in a simple manner.
///
///
/// This property can be used in a report script to change the text color of the object. It is
/// equivalent to: textObject1.TextFill = new SolidFill(color);
///
[Browsable(false)]
public Color TextColor
{
get { return TextFill is SolidFill ? (TextFill as SolidFill).Color : Color.Black; }
set { TextFill = new SolidFill(value); }
}
///
/// Gets or sets the string trimming options.
///
[DefaultValue(StringTrimming.None)]
[Category("Behavior")]
public StringTrimming Trimming
{
get { return trimming; }
set { trimming = value; }
}
///
/// Gets or sets the width ratio of the font.
///
///
/// Default value is 1. To make a font wider, set a value grether than 1; to make a font narrower,
/// set a value less than 1.
///
[DefaultValue(1f)]
[Category("Appearance")]
public float FontWidthRatio
{
get { return fontWidthRatio; }
set
{
if (value > 0)
fontWidthRatio = value;
else
fontWidthRatio = 1;
}
}
///
/// Gets or sets the height of single text line, in pixels.
///
[DefaultValue(0f)]
[Category("Appearance")]
public float LineHeight
{
get { return lineHeight; }
set { lineHeight = value; }
}
///
/// Gets or sets the offset of the first TAB symbol.
///
[DefaultValue(0f)]
[Category("Appearance")]
//[TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
public float FirstTabOffset
{
get { return firstTabOffset; }
set { firstTabOffset = value; }
}
///
/// Gets or sets the width of TAB symbol, in pixels.
///
[DefaultValue(58f)]
[Category("Appearance")]
public float TabWidth
{
get { return tabWidth; }
set { tabWidth = value; }
}
///
/// Gets or sets a value that indicates if text should be clipped inside the object's bounds.
///
[DefaultValue(true)]
[Category("Behavior")]
public bool Clip
{
get { return clip; }
set { clip = value; }
}
///
/// Gets the collection of conditional highlight attributes.
///
///
/// Conditional highlight is used to change the visual appearance of the Text object
/// depending on some condition(s). For example, you may highlight negative values displayed by
/// the Text object with red color. To do this, add the highlight condition:
///
/// TextObject text1;
/// HighlightCondition highlight = new HighlightCondition();
/// highlight.Expression = "Value < 0";
/// highlight.Fill = new SolidFill(Color.Red);
/// highlight.ApplyFill = true;
/// text1.Highlight.Add(highlight);
///
///
[Category("Data")]
[Editor("FastReport.TypeEditors.HighlightEditor, FastReport", typeof(UITypeEditor))]
public ConditionCollection Highlight
{
get { return highlight; }
}
///
/// Gets or sets a value that indicates if the text object should display its contents similar to the printout.
///
[DefaultValue(false)]
[Category("Behavior")]
public bool Wysiwyg
{
get { return wysiwyg; }
set { wysiwyg = value; }
}
///
/// Forces justify for the last text line.
///
[Browsable(false)]
public bool ForceJustify
{
get { return forceJustify; }
set { forceJustify = value; }
}
///
/// Allows handling html tags in the text.
///
///
/// The following html tags can be used in the object's text: <b>, <i>, <u>,
/// <strike>, <sub>, <sup>, </b>, </i>, </u>,
/// </strike>, </sub>, </sup>,
/// <font color=&...&>, </font>. Font size cannot
/// be changed due to limitations in the rendering engine.
///
[DefaultValue(false)]
[Category("Behavior")]
[Browsable(false)]
[Obsolete("This method is deprecated please use TextRenderer")]
public bool HtmlTags
{
get
{
return HasHtmlTags;
}
set { textRenderType = value ? TextRenderType.HtmlTags : TextRenderType.Default; }
}
///
/// Indicates handling html tags in the text.
///
/// To set the value use the TextRenderer property.
[DefaultValue(false)]
[Category("Behavior")]
[Browsable(false)]
public bool HasHtmlTags
{
get
{
switch (textRenderType)
{
case TextRenderType.HtmlTags:
case TextRenderType.HtmlParagraph:
return true;
default:
return false;
}
}
}
///
/// The type of text render
///
/// ///
/// The following html tags can be used in the object's text: <b>, <i>, <u>,
/// <strike>, <sub>, <sup>, </b>, </i>, </u>,
/// </strike>, </sub>, </sup>,
/// <font color=&...&>, </font>. Font size cannot
/// be changed due to limitations in the rendering engine.
///
[DefaultValue(TextRenderType.Default)]
[Category("Behavior")]
public TextRenderType TextRenderType
{
get { return textRenderType; }
set { textRenderType = value; }
}
///
/// Gets or sets the paragraph offset, in pixels. For HtmlParagraph use ParagraphFormat.FirstLineIndent.
///
[DefaultValue(0f)]
[Category("Appearance")]
[TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
public float ParagraphOffset
{
get { return paragraphOffset; }
set { paragraphOffset = value; }
}
internal bool IsAdvancedRendererNeeded
{
get { return HorzAlign == HorzAlign.Justify || Wysiwyg || LineHeight != 0 || HasHtmlTags; }
}
internal bool PreserveLastLineSpace { get { return preserveLastLineSpace; } set { preserveLastLineSpace = value; } }
///
/// Cache for inline images
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public InlineImageCache InlineImageCache
{
get
{
if (inlineImageCache == null)
inlineImageCache = new InlineImageCache();
return inlineImageCache;
}
}
#endregion
#region Private Methods
private void DrawUnderlines(FRPaintEventArgs e)
{
if (!Underlines || Angle != 0)
return;
IGraphics g = e.Graphics;
float lineHeight = LineHeight == 0 ? Font.GetHeight() * DrawUtils.ScreenDpiFX : LineHeight;
lineHeight *= e.ScaleY;
float curY = AbsTop * e.ScaleY + lineHeight + 1;
Pen pen = e.Cache.GetPen(Border.Color, Border.Width * e.ScaleY, DashStyle.Solid);
while (curY < AbsBottom * e.ScaleY)
{
g.DrawLine(pen, AbsLeft * e.ScaleX, curY, AbsRight * e.ScaleY, curY);
curY += lineHeight;
}
}
private SizeF CalcSize()
{
Report report = Report;
if (String.IsNullOrEmpty(Text) || report == null)
return new SizeF(0, 0);
Font font = report.GraphicCache.GetFont(Font.FontFamily, Font.Size * 96f / DrawUtils.ScreenDpi, Font.Style);
float width = 0;
if (WordWrap)
{
if (Angle == 90 || Angle == 270)
width = Height - Padding.Vertical;
else
width = Width - Padding.Horizontal;
}
IGraphics g = report.MeasureGraphics;
IGraphicsState state = g.Save();
try
{
if (report.TextQuality != TextQuality.Default)
g.TextRenderingHint = report.GetTextQuality();
if (TextRenderType == TextRenderType.HtmlParagraph)
{
if (width == 0)
width = 100000;
using (HtmlTextRenderer htmlRenderer = GetHtmlTextRenderer(g, new RectangleF(0, 0, width, 100000), 1, 1))
{
float height = htmlRenderer.CalcHeight();
width = htmlRenderer.CalcWidth();
width += Padding.Horizontal + 1;
if (LineHeight == 0)
height += Padding.Vertical + 1;
return new SizeF(width, height);
}
}
#if SKIA || !CROSSPLATFORM
if (IsAdvancedRendererNeeded)
#endif
{
if (width == 0)
width = 100000;
AdvancedTextRenderer renderer = new AdvancedTextRenderer(Text, g, font, Brushes.Black, Pens.Black,
new RectangleF(0, 0, width, 100000), GetStringFormat(report.GraphicCache, 0),
HorzAlign, VertAlign, LineHeight, Angle, FontWidthRatio, false, Wysiwyg, HasHtmlTags, false, 96f / DrawUtils.ScreenDpi,
96f / DrawUtils.ScreenDpi, InlineImageCache);
float height = renderer.CalcHeight();
width = renderer.CalcWidth();
width += Padding.Horizontal + 1;
if (LineHeight == 0)
height += Padding.Vertical + 1;
return new SizeF(width, height);
}
#if SKIA || !CROSSPLATFORM
else
{
if (FontWidthRatio != 1)
width /= FontWidthRatio;
SizeF size = g.MeasureString(Text, font, new SizeF(width, 100000));
size.Width += Padding.Horizontal + 1;
size.Height += Padding.Vertical + 1;
return size;
}
#endif
}
finally
{
g.Restore(state);
}
}
private float InternalCalcWidth()
{
bool saveWordWrap = WordWrap;
WordWrap = false;
float result = 0;
try
{
SizeF size = CalcSize();
result = size.Width;
}
finally
{
WordWrap = saveWordWrap;
}
return result;
}
private float InternalCalcHeight()
{
return CalcSize().Height;
}
private string BreakTextHtml(out bool endOnEnter)
{
endOnEnter = false;
ForceJustify = false;
if (String.IsNullOrEmpty(Text))
return "";
string result = null;
Report report = Report;
if (report == null)
return "";
StringFormat format = GetStringFormat(report.GraphicCache, StringFormatFlags.LineLimit);
RectangleF textRect = new RectangleF(0, 0, Width - Padding.Horizontal, Height - Padding.Vertical);
int charactersFitted;
IGraphics g = report.MeasureGraphics;
IGraphicsState state = g.Save();
if (report.TextQuality != TextQuality.Default)
g.TextRenderingHint = report.GetTextQuality();
try
{
using (HtmlTextRenderer htmlRenderer = GetHtmlTextRenderer(g, 1, 1, textRect, format))
{
htmlRenderer.CalcHeight(out charactersFitted);
if (charactersFitted == 0)
return null;
Text = HtmlTextRenderer.BreakHtml(Text, charactersFitted, out result, out endOnEnter);
if (HorzAlign == HorzAlign.Justify && !endOnEnter && result != "")
{
if (Text.EndsWith(" "))
Text = Text.TrimEnd(' ');
ForceJustify = true;
}
}
}
finally
{
g.Restore(state);
}
return result;
}
private string BreakText()
{
ForceJustify = false;
if (String.IsNullOrEmpty(Text))
return "";
string result = null;
Report report = Report;
if (report == null)
return "";
Font font = report.GraphicCache.GetFont(Font.FontFamily, Font.Size * 96f / DrawUtils.ScreenDpi, Font.Style);
StringFormat format = GetStringFormat(report.GraphicCache, StringFormatFlags.LineLimit);
RectangleF textRect = new RectangleF(0, 0, Width - Padding.Horizontal, Height - Padding.Vertical);
if (textRect.Height < 0)
return null;
int charactersFitted;
int linesFilled;
IGraphics g = report.MeasureGraphics;
IGraphicsState state = g.Save();
try
{
if (report.TextQuality != TextQuality.Default)
g.TextRenderingHint = report.GetTextQuality();
AdvancedTextRenderer.StyleDescriptor htmlStyle = null;
if (IsAdvancedRendererNeeded)
{
AdvancedTextRenderer renderer = new AdvancedTextRenderer(Text, g, font, Brushes.Black, Pens.Black,
textRect, format, HorzAlign, VertAlign, LineHeight, Angle, FontWidthRatio, false, Wysiwyg, HasHtmlTags, false, 96f / DrawUtils.ScreenDpi,
96f / DrawUtils.ScreenDpi, InlineImageCache);
renderer.CalcHeight(out charactersFitted, out htmlStyle);
if (charactersFitted == 0)
linesFilled = 0;
else
linesFilled = 2;
}
else
{
g.MeasureString(Text, font, textRect.Size, format, out charactersFitted, out linesFilled);
}
if (linesFilled == 0)
return null;
if (linesFilled == 1)
{
// check if there is enough space for one line
float lineHeight = g.MeasureString("Wg", font).Height;
if (textRect.Height < lineHeight)
return null;
}
if (charactersFitted < Text.Length)
result = Text.Substring(charactersFitted);
else
result = "";
Text = Text.Substring(0, charactersFitted);
if (HorzAlign == HorzAlign.Justify && !Text.EndsWith("\n") && result != "")
{
if (Text.EndsWith(" "))
Text = Text.Substring(0, Text.Length - 1);
ForceJustify = true;
}
if (HasHtmlTags && htmlStyle != null && result != "")
result = htmlStyle.ToString() + result;
}
finally
{
g.Restore(state);
}
return result;
}
private void ProcessAutoShrink()
{
if (TextRenderType == TextRenderType.HtmlParagraph)
return;
if (String.IsNullOrEmpty(Text))
return;
if (AutoShrink == AutoShrinkMode.FontSize)
{
while (CalcWidth() > Width - 1 && Font.Size > AutoShrinkMinSize && Font.Size>1)
{
Font = new Font(Font.FontFamily, Font.Size - 1, Font.Style);
}
}
else if (AutoShrink == AutoShrinkMode.FontWidth)
{
FontWidthRatio = 1;
float ratio = Converter.DecreasePrecision((Width - 1) / CalcWidth(), 2) - 0.01f;
if (ratio < 1)
FontWidthRatio = Math.Max(ratio, AutoShrinkMinSize);
}
}
private string MakeParagraphOffset(string text)
{
// fixed issue 2823
FirstTabOffset = ParagraphOffset;
string[] lines = text.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
if (!lines[i].StartsWith("\t"))
lines[i] = "\t" + lines[i];
}
return String.Join("\n", lines);
}
#endregion
#region Protected Methods
///
protected override void DeserializeSubItems(FRReader reader)
{
if (String.Compare(reader.ItemName, "Highlight", true) == 0)
reader.Read(Highlight);
else
base.DeserializeSubItems(reader);
}
#endregion
#region Public Methods
///
/// Returns StringFormat object.
///
/// Report graphic cache.
/// StringFormat flags.
/// StringFormat object.
public StringFormat GetStringFormat(GraphicCache cache, StringFormatFlags flags)
{
return GetStringFormat(cache, flags, 1);
}
internal StringFormat GetStringFormat(GraphicCache cache, StringFormatFlags flags, float scale)
{
StringAlignment align = StringAlignment.Near;
if (HorzAlign == HorzAlign.Center)
align = StringAlignment.Center;
else if (HorzAlign == HorzAlign.Right)
align = StringAlignment.Far;
StringAlignment lineAlign = StringAlignment.Near;
if (VertAlign == VertAlign.Center)
lineAlign = StringAlignment.Center;
else if (VertAlign == VertAlign.Bottom)
lineAlign = StringAlignment.Far;
if (RightToLeft)
flags |= StringFormatFlags.DirectionRightToLeft;
if (!WordWrap)
flags |= StringFormatFlags.NoWrap;
if (!Clip)
flags |= StringFormatFlags.NoClip;
if (TabPositions.Count > 0)
{
FloatCollection tabPositions = new FloatCollection();
foreach (var i in TabPositions)
tabPositions.Add((float)i * scale);
return cache.GetStringFormat(align, lineAlign, Trimming, flags, firstTabOffset * scale, tabPositions,
48 * scale);
}
return cache.GetStringFormat(align, lineAlign, Trimming, flags, firstTabOffset * scale, tabWidth * scale);
}
///
public override void Assign(Base source)
{
base.Assign(source);
TextObject src = source as TextObject;
AutoWidth = src.AutoWidth;
HorzAlign = src.HorzAlign;
VertAlign = src.VertAlign;
Angle = src.Angle;
RightToLeft = src.RightToLeft;
WordWrap = src.WordWrap;
Underlines = src.Underlines;
Font = src.Font;
TextFill = src.TextFill.Clone();
TextOutline.Assign(src.TextOutline);
Trimming = src.Trimming;
FontWidthRatio = src.FontWidthRatio;
FirstTabOffset = src.FirstTabOffset;
TabWidth = src.TabWidth;
TabPositions.Assign(src.TabPositions);
Clip = src.Clip;
Highlight.Assign(src.Highlight);
Wysiwyg = src.Wysiwyg;
LineHeight = src.LineHeight;
TextRenderType = src.TextRenderType;
AutoShrink = src.AutoShrink;
AutoShrinkMinSize = src.AutoShrinkMinSize;
ParagraphOffset = src.ParagraphOffset;
inlineImageCache = src.inlineImageCache;
paragraphFormat.Assign(src.paragraphFormat);
}
///
/// Returns an instance of html text renderer.
///
/// Scale ratio.
/// Font scale ratio.
/// The html text renderer.
public HtmlTextRenderer GetHtmlTextRenderer(float scale, float fontScale)
{
return GetHtmlTextRenderer(Report.MeasureGraphics, scale, fontScale);
}
internal HtmlTextRenderer GetHtmlTextRenderer(IGraphics g, float scale, float fontScale)
{
StringFormat format = GetStringFormat(Report.GraphicCache, 0, scale);
RectangleF textRect = new RectangleF(
(AbsLeft + Padding.Left) * scale,
(AbsTop + Padding.Top) * scale,
(Width - Padding.Horizontal) * scale,
(Height - Padding.Vertical) * scale);
return GetHtmlTextRenderer(g, scale, fontScale, textRect, format);
}
internal HtmlTextRenderer GetHtmlTextRenderer(IGraphics g, RectangleF textRect, float scale, float fontScale)
{
StringFormat format = GetStringFormat(Report.GraphicCache, 0, fontScale);
return GetHtmlTextRenderer(g, scale, fontScale, textRect, format);
}
internal HtmlTextRenderer GetHtmlTextRenderer(IGraphics g, float scale, float fontScale, RectangleF textRect, StringFormat format)
{
return GetHtmlTextRenderer(Text, g, fontScale, scale, fontScale, textRect, format, false);
}
internal HtmlTextRenderer GetHtmlTextRenderer(string text, IGraphics g, float formatScale, float scale, float fontScale,
RectangleF textRect, StringFormat format, bool isPrinting)
{
#if true
HtmlTextRenderer.RendererContext context;
context.text = text;
context.g = g;
context.font = font.FontFamily;
context.size = font.Size;
context.style = font.Style; // no keep
context.color = TextColor; // no keep
context.underlineColor = textOutline.Color;
context.rect = textRect;
context.underlines = Underlines;
context.format = format; // no keep
context.horzAlign = horzAlign;
context.vertAlign = vertAlign;
context.paragraphFormat = ParagraphFormat.MultipleScale(formatScale);
context.forceJustify = ForceJustify;
context.scale = scale* 96f / DrawUtils.ScreenDpi;
context.fontScale = fontScale * 96f / DrawUtils.ScreenDpi;
context.cache = InlineImageCache;
context.isPrinting = isPrinting;
context.isDifferentTabPositions = TabPositions.Count > 0;
context.keepLastLineSpace = this.PreserveLastLineSpace;
return new HtmlTextRenderer(context);
#else
bool isDifferentTabPositions = TabPositions.Count > 0;
return new HtmlTextRenderer(text, g, font.FontFamily, font.Size, font.Style, TextColor,
textOutline.Color, textRect, Underlines,
format, horzAlign, vertAlign, ParagraphFormat.MultipleScale(formatScale), ForceJustify,
scale * 96f / DrawUtils.ScreenDpi, fontScale * 96f / DrawUtils.ScreenDpi, InlineImageCache,
isPrinting, isDifferentTabPositions);
#endif
}
///
/// Draws a text.
///
/// Paint event data.
public void DrawText(FRPaintEventArgs e)
{
string text = GetDisplayText();
if (!String.IsNullOrEmpty(text))
{
IGraphics g = e.Graphics;
RectangleF textRect = new RectangleF(
(AbsLeft + Padding.Left) * e.ScaleX,
(AbsTop + Padding.Top) * e.ScaleY,
(Width - Padding.Horizontal) * e.ScaleX,
(Height - Padding.Vertical) * e.ScaleY);
if (ParagraphOffset != 0 && IsDesigning)
text = MakeParagraphOffset(text);
StringFormat format = GetStringFormat(e.Cache, 0, e.ScaleX);
Font font = e.Cache.GetFont(Font.FontFamily,
IsPrinting ? Font.Size : Font.Size * e.ScaleX * 96f / DrawUtils.ScreenDpi,
Font.Style);
Brush textBrush = null;
if (TextFill is SolidFill)
textBrush = e.Cache.GetBrush((TextFill as SolidFill).Color);
else
textBrush = TextFill.CreateBrush(textRect, e.ScaleX, e.ScaleY);
Pen outlinePen = null;
if (textOutline.Enabled)
outlinePen = e.Cache.GetPen(textOutline.Color, textOutline.Width * e.ScaleX, textOutline.Style);
Report report = Report;
if (report != null && report.TextQuality != TextQuality.Default)
g.TextRenderingHint = report.GetTextQuality();
if (textRect.Width > 0 && textRect.Height > 0)
{
switch (TextRenderType)
{
case TextRenderType.Inline:
g.DrawString(text, font, textBrush, textRect.X, textRect.Y, StringFormat.GenericTypographic);
break;
case TextRenderType.HtmlParagraph:
try
{
using (HtmlTextRenderer htmlRenderer = GetHtmlTextRenderer(text, e.Graphics, e.ScaleX,
IsPrinting ? 1 : e.ScaleX, IsPrinting ? 1 / (96f / DrawUtils.ScreenDpi) : e.ScaleX, textRect, format, IsPrinting))
{
htmlRenderer.Draw();
}
}
catch
{
textBrush.Dispose();
textBrush = null;
}
break;
default:
if (IsAdvancedRendererNeeded)
{
// use advanced rendering
AdvancedTextRenderer advancedRenderer = new AdvancedTextRenderer(text, g, font, textBrush,
outlinePen, textRect, format, HorzAlign, VertAlign, LineHeight * e.ScaleY, Angle,
FontWidthRatio, ForceJustify, Wysiwyg, HasHtmlTags, false,
e.ScaleX * 96f / DrawUtils.ScreenDpi,
IsPrinting ? 1 : e.ScaleX * 96f / DrawUtils.ScreenDpi, InlineImageCache, IsPrinting);
advancedRenderer.Draw();
}
else
{
// use simple rendering
if (Angle == 0 && FontWidthRatio == 1)
{
if (outlinePen == null)
{
if (WordWrap)
format.Trimming = StringTrimming.Word;
g.DrawString(text, font, textBrush, textRect, format);
}
else
{
GraphicsPath path = new GraphicsPath();
path.AddString(text, font.FontFamily, Convert.ToInt32(font.Style),
g.DpiY * font.Size / 72, textRect, format);
IGraphicsState state = g.Save();
g.SetClip(textRect);
//g.FillPath(textBrush, path);
//regime for text output with drawbehind
if (TextOutline.DrawBehind)
{
g.DrawPath(outlinePen, path);
g.FillPath(textBrush, path);
}
else //without. default
{
g.FillAndDrawPath(outlinePen, textBrush, path);
}
g.Restore(state);
}
}
else
StandardTextRenderer.Draw(text, g, font, textBrush, outlinePen, textRect, format, Angle,
FontWidthRatio);
}
DrawUnderlines(e);
break;
}
}
if (!(TextFill is SolidFill))
{
textBrush.Dispose();
}
}
}
///
public override void Draw(FRPaintEventArgs e)
{
base.Draw(e);
DrawText(e);
DrawMarkers(e);
Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height));
DrawDesign(e);
}
///
public override void ApplyStyle(Style style)
{
if (style.ApplyTextFill)
TextFill = style.TextFill.Clone();
if (style.ApplyFont)
Font = style.Font;
base.ApplyStyle(style);
}
///
public override void SaveStyle()
{
base.SaveStyle();
savedTextFill = TextFill;
savedFont = Font;
}
///
public override void RestoreStyle()
{
base.RestoreStyle();
TextFill = savedTextFill;
Font = savedFont;
}
///
public override void Serialize(FRWriter writer)
{
if (writer.SerializeTo == SerializeTo.Preview && AutoWidth)
{
WordWrap = false;
float width = CalcSize().Width;
if ((Anchor & AnchorStyles.Right) != 0)
Left = Right - width;
Width = width;
}
TextObject c = writer.DiffObject as TextObject;
base.Serialize(writer);
if (c == null)
return; // RichObject here
if (AutoWidth != c.AutoWidth)
writer.WriteBool("AutoWidth", AutoWidth);
if (AutoShrink != c.AutoShrink)
writer.WriteValue("AutoShrink", AutoShrink);
if (FloatDiff(AutoShrinkMinSize, c.AutoShrinkMinSize))
writer.WriteFloat("AutoShrinkMinSize", AutoShrinkMinSize);
if (HorzAlign != c.HorzAlign)
writer.WriteValue("HorzAlign", HorzAlign);
if (VertAlign != c.VertAlign)
writer.WriteValue("VertAlign", VertAlign);
if (Angle != c.Angle)
writer.WriteInt("Angle", Angle);
if (RightToLeft != c.RightToLeft)
writer.WriteBool("RightToLeft", RightToLeft);
if (WordWrap != c.WordWrap)
writer.WriteBool("WordWrap", WordWrap);
if (Underlines != c.Underlines)
writer.WriteBool("Underlines", Underlines);
if ((writer.SerializeTo != SerializeTo.Preview || !Font.Equals(c.Font)) && writer.ItemName != "inherited")
writer.WriteValue("Font", Font);
TextFill.Serialize(writer, "TextFill", c.TextFill);
if (TextOutline != null)
TextOutline.Serialize(writer, "TextOutline", c.TextOutline);
if (Trimming != c.Trimming)
writer.WriteValue("Trimming", Trimming);
if (FontWidthRatio != c.FontWidthRatio)
writer.WriteFloat("FontWidthRatio", FontWidthRatio);
if (FirstTabOffset != c.FirstTabOffset)
writer.WriteFloat("FirstTabOffset", FirstTabOffset);
if (TabWidth != c.TabWidth)
writer.WriteFloat("TabWidth", TabWidth);
if (TabPositions.Count > 0)
writer.WriteValue("TabPositions", TabPositions);
if (Clip != c.Clip)
writer.WriteBool("Clip", Clip);
if (Wysiwyg != c.Wysiwyg)
writer.WriteBool("Wysiwyg", Wysiwyg);
if (LineHeight != c.LineHeight)
writer.WriteFloat("LineHeight", LineHeight);
if (TextRenderType != c.TextRenderType)
writer.WriteValue("TextRenderType", TextRenderType);
if (ParagraphOffset != c.ParagraphOffset)
writer.WriteFloat("ParagraphOffset", ParagraphOffset);
if (ForceJustify != c.ForceJustify)
writer.WriteBool("ForceJustify", ForceJustify);
if (writer.SerializeTo != SerializeTo.Preview)
{
if (Style != c.Style)
writer.WriteStr("Style", Style);
if (Highlight.Count > 0)
writer.Write(Highlight);
}
if (ParagraphFormat.FirstLineIndent > 0)
writer.WriteFloat("ParagraphFormat.FirstLineIndent", ParagraphFormat.FirstLineIndent);
if (ParagraphFormat.LineSpacing > 0)
writer.WriteFloat("ParagraphFormat.LineSpacing", ParagraphFormat.LineSpacing);
if (ParagraphFormat.LineSpacingType != LineSpacingType.Single)
writer.WriteValue("ParagraphFormat.LineSpacingType", ParagraphFormat.LineSpacingType);
if (ParagraphFormat.SkipFirstLineIndent)
writer.WriteBool("ParagraphFormat.SkipFirstLineIndent", ParagraphFormat.SkipFirstLineIndent);
StringBuilder sb = null;
if (InlineImageCache != null && writer.BlobStore != null && HasHtmlTags == true)
foreach (InlineImageCache.CacheItem item in InlineImageCache.AllItems())
{
if (item.Src.StartsWith("data:")) continue;
if (sb == null)
sb = new StringBuilder();
sb.Append(writer.BlobStore.AddOrUpdate(item.Stream, item.Src))
.Append(',');
}
if (sb != null)
{
sb.Length--;
writer.WriteStr("InlineImageCacheIndexes", sb.ToString());
}
}
///
public override void Deserialize(FRReader reader)
{
base.Deserialize(reader);
TextFill.Deserialize(reader, "TextFill");
if (reader.BlobStore != null)
{
string indexes = reader.ReadStr("InlineImageCacheIndexes");
if (indexes != "null" && !String.IsNullOrEmpty(indexes))
{
string[] arr = indexes.Split(',');
foreach (string index in arr)
{
int val = 0;
if (Int32.TryParse(index, out val))
{
if (val >= 0 && val < reader.BlobStore.Count)
{
InlineImageCache.CacheItem it = new InlineImageCache.CacheItem();
it.Set(reader.BlobStore.Get(val));
InlineImageCache.Set(reader.BlobStore.GetSource(val), it);
}
}
}
}
}
switch (reader.DeserializeFrom)
{
case SerializeTo.Undo:
case SerializeTo.Preview:
case SerializeTo.Clipboard:
// skip
break;
default:
if (!reader.HasProperty("Font") && reader.ItemName != "inherited")
{
string creatorVersion = reader.Root.GetProp("ReportInfo.CreatorVersion");
if (!String.IsNullOrEmpty(creatorVersion))
{
try
{
string[] versions = creatorVersion.Split('.');
int major = 0;
if (Int32.TryParse(versions[0], out major))
{
if (major < 2016)
{
Font = new Font("Arial", 10);
}
}
}
catch
{
}
}
}
break;
}
}
public override void InitializeComponent()
{
base.InitializeComponent();
TextFill.InitializeComponent();
}
public override void FinalizeComponent()
{
base.FinalizeComponent();
TextFill.FinalizeComponent();
}
internal void ApplyCondition(HighlightCondition c)
{
if (c.ApplyBorder)
Border = c.Border.Clone();
if (c.ApplyFill)
Fill = c.Fill.Clone();
if (c.ApplyTextFill)
TextFill = c.TextFill.Clone();
if (c.ApplyFont)
Font = c.Font;
Visible = c.Visible;
}
#endregion
#region Report Engine
///
public override string[] GetExpressions()
{
List expressions = new List();
expressions.AddRange(base.GetExpressions());
if (AllowExpressions && !String.IsNullOrEmpty(Brackets))
{
string[] brackets = Brackets.Split(',');
// collect expressions found in the text
expressions.AddRange(CodeUtils.GetExpressions(Text, brackets[0], brackets[1]));
}
// add highlight conditions
foreach (HighlightCondition condition in Highlight)
{
expressions.Add(condition.Expression);
}
return expressions.ToArray();
}
///
public override void SaveState()
{
base.SaveState();
savedText = Text;
savedTextFill = TextFill;
savedFont = Font;
savedFormat = Format;
}
///
public override void RestoreState()
{
base.RestoreState();
Text = savedText;
TextFill = savedTextFill;
Font = savedFont;
Format = savedFormat;
}
///
/// Calculates the object's width.
///
/// The width, in pixels.
public float CalcWidth()
{
if (Angle == 90 || Angle == 270)
return InternalCalcHeight();
return InternalCalcWidth();
}
///
public override float CalcHeight()
{
if (Angle == 90 || Angle == 270)
return InternalCalcWidth();
return InternalCalcHeight();
}
///
public override void GetData()
{
base.GetData();
// process expressions
if (AllowExpressions)
{
if (!String.IsNullOrEmpty(Brackets))
{
string[] brackets = Brackets.Split(',');
FindTextArgs args = new FindTextArgs();
args.Text = new FastString(Text);
args.OpenBracket = brackets[0];
args.CloseBracket = brackets[1];
int expressionIndex = 0;
while (args.StartIndex < args.Text.Length)
{
string expression = CodeUtils.GetExpression(args, false);
if (expression == null)
break;
string formattedValue = CalcAndFormatExpression(expression, expressionIndex);
args.Text.Remove(args.StartIndex, args.EndIndex - args.StartIndex);
args.Text.Insert(args.StartIndex, formattedValue);
args.StartIndex += formattedValue.Length;
expressionIndex++;
}
Text = args.Text.ToString();
}
}
// process highlight
Variant varValue = new Variant(Value);
foreach (HighlightCondition condition in Highlight)
{
try
{
object val = Report.Calc(condition.Expression, varValue);
if (val != null && (bool)val == true)
{
ApplyCondition(condition);
break;
}
}
catch (Exception e)
{
throw new Exception(Name + ": " + Res.Get("Messages,ErrorInHighlightCondition") + ": " + condition.Expression, e.InnerException);
}
}
// make paragraph offset
if (ParagraphOffset != 0)
Text = MakeParagraphOffset(Text);
// process AutoShrink
ProcessAutoShrink();
}
///
public override bool Break(BreakableComponent breakTo)
{
switch (TextRenderType)
{
case TextRenderType.HtmlParagraph:
bool endOnEnter;
string breakTextHtml = BreakTextHtml(out endOnEnter);
if (breakTextHtml != null && breakTo != null)
{
(breakTo as TextObject).Text = breakTextHtml;
if (!endOnEnter)
(breakTo as TextObject).ParagraphFormat.SkipFirstLineIndent = true;
}
return breakTextHtml != null;
default:
string breakText = BreakText();
if (breakText != null && breakTo != null)
(breakTo as TextObject).Text = breakText;
return breakText != null;
}
}
internal IEnumerable GetPictureFromHtmlText(AdvancedTextRenderer renderer)
{
if (renderer == null)
{
using (Bitmap b = new Bitmap(1, 1))
using (IGraphics g = new GdiGraphics(b))
{
RectangleF textRect = new RectangleF(
(AbsLeft + Padding.Left),
(AbsTop + Padding.Top),
(Width - Padding.Horizontal),
(Height - Padding.Vertical));
StringFormat format = GetStringFormat(Report.GraphicCache, StringFormatFlags.LineLimit);
renderer = new AdvancedTextRenderer(Text, g, Font, Brushes.Black, Pens.Black,
textRect, format, HorzAlign, VertAlign, LineHeight, Angle, FontWidthRatio,
ForceJustify, Wysiwyg, HasHtmlTags, false, 1, 1,
InlineImageCache);
foreach (PictureObject obj in GetPictureFromHtmlText(renderer))
yield return obj;
}
}
else
{
RectangleF textRect = renderer.DisplayRect;
foreach (AdvancedTextRenderer.Paragraph paragraph in renderer.Paragraphs)
foreach (AdvancedTextRenderer.Line line in paragraph.Lines)
foreach (AdvancedTextRenderer.Word word in line.Words)
foreach (AdvancedTextRenderer.Run run in word.Runs)
if (run is AdvancedTextRenderer.RunImage)
{
AdvancedTextRenderer.RunImage runImage = run as AdvancedTextRenderer.RunImage;
PictureObject obj = new PictureObject();
float left = runImage.Left - textRect.Left;
float top = runImage.Top - textRect.Top;
float width =
runImage.Left + runImage.Width > textRect.Right ?
textRect.Right - (left < 0 ? textRect.Left : runImage.Left) :
(
runImage.Left < textRect.Left ?
runImage.Left + runImage.Width - textRect.Left :
runImage.Width
);
float height =
runImage.Top + runImage.Height > textRect.Bottom ?
textRect.Bottom - (top < 0 ? textRect.Top : runImage.Top) :
(
runImage.Top < textRect.Top ?
runImage.Top + runImage.Height - textRect.Top :
runImage.Height
);
if (left < 0 || top < 0 || width < runImage.Width || height < runImage.Height)
{
Bitmap bmp = new Bitmap((int)width, (int)height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(runImage.Image, new PointF(
left < 0 ? left : 0,
top < 0 ? top : 0
));
}
obj.Image = bmp;
obj.Left = (left < 0 ? textRect.Left : runImage.Left) / renderer.Scale;
obj.Top = (top < 0 ? textRect.Top : runImage.Top) / renderer.Scale;
obj.Width = width / renderer.Scale;
obj.Height = height / renderer.Scale;
obj.SizeMode = PictureBoxSizeMode.StretchImage;
}
else
{
obj.Image = runImage.Image;
obj.Left = runImage.Left / renderer.Scale;
obj.Top = runImage.Top / renderer.Scale;
obj.Width = runImage.Width / renderer.Scale;
obj.Height = runImage.Height / renderer.Scale;
obj.SizeMode = PictureBoxSizeMode.StretchImage;
}
yield return obj;
}
}
}
#endregion
///
/// Initializes a new instance of the class with default settings.
///
public TextObject()
{
paragraphFormat = new ParagraphFormat();
wordWrap = true;
font = DrawUtils.DefaultReportFont;
textFill = new SolidFill(Color.Black);
textOutline = new TextOutline();
trimming = StringTrimming.None;
fontWidthRatio = 1;
tabWidth = 58;
tabPositions = new FloatCollection();
clip = true;
highlight = new ConditionCollection();
FlagSerializeStyle = false;
SetFlags(Flags.HasSmartTag, true);
preserveLastLineSpace = false;
}
}
}