diff --git a/FastReport.Base/Base.cs b/FastReport.Base/Base.cs
index 98b67f7a..f362fd84 100644
--- a/FastReport.Base/Base.cs
+++ b/FastReport.Base/Base.cs
@@ -18,6 +18,7 @@ namespace FastReport
/// Specifies a set of actions that cannot be performed on the object in the design mode.
///
[Flags]
+ [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))]
public enum Restrictions
{
///
@@ -60,6 +61,7 @@ public enum Restrictions
/// Specifies a set of actions that can be performed on the object in the design mode.
///
[Flags]
+ [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))]
public enum Flags
{
///
diff --git a/FastReport.Base/Gauge/Radial/RadialGauge.cs b/FastReport.Base/Gauge/Radial/RadialGauge.cs
index aa6cb76d..2478727c 100644
--- a/FastReport.Base/Gauge/Radial/RadialGauge.cs
+++ b/FastReport.Base/Gauge/Radial/RadialGauge.cs
@@ -35,6 +35,7 @@ public enum RadialGaugeType
/// Radial Gauge position types
///
[Flags]
+ [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))]
public enum RadialGaugePosition
{
///
diff --git a/FastReport.Base/PictureObject.cs b/FastReport.Base/PictureObject.cs
index 141698cf..cd78f293 100644
--- a/FastReport.Base/PictureObject.cs
+++ b/FastReport.Base/PictureObject.cs
@@ -142,7 +142,6 @@ public int GrayscaleHash
set { grayscaleHash = value; }
}
-
///
/// Gets or sets the color of the image that will be treated as transparent.
///
@@ -180,8 +179,6 @@ public float Transparency
}
}
-
-
///
/// Gets or sets a value indicating that the image should be tiled.
///
@@ -193,8 +190,6 @@ public bool Tile
set { tile = value; }
}
-
-
///
/// Gets or sets a value indicating that the image stored in the
/// property should be disposed when this object is disposed.
@@ -211,15 +206,6 @@ public bool ShouldDisposeImage
set { shouldDisposeImage = value; }
}
-
-
-
-
-
-
-
-
-
///
/// Gets or sets a bitmap transparent image
///
@@ -314,6 +300,38 @@ private void UpdateTransparentImage()
}
}
}
+
+#if MONO
+ private GraphicsPath GetRoundRectPath(RectangleF rectangleF, float radius)
+ {
+ GraphicsPath gp = new GraphicsPath();
+ if (radius < 1)
+ radius = 1;
+ gp.AddLine(rectangleF.X + radius, rectangleF.Y, rectangleF.Width + rectangleF.X - radius, rectangleF.Y);
+ gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Y, radius + 1, radius + 1, 270, 90);
+ gp.AddLine(rectangleF.Width + rectangleF.X, rectangleF.Y + radius, rectangleF.Width + rectangleF.X, rectangleF.Height + rectangleF.Y - radius);
+ gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 0, 90);
+ gp.AddLine(rectangleF.Width + rectangleF.X - radius, rectangleF.Height + rectangleF.Y, rectangleF.X + radius, rectangleF.Height + rectangleF.Y);
+ gp.AddArc(rectangleF.X, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 90, 90);
+ gp.AddLine(rectangleF.X, rectangleF.Height + rectangleF.Y - radius, rectangleF.X, rectangleF.Y + radius);
+ gp.AddArc(rectangleF.X, rectangleF.Y, radius, radius, 180, 90);
+ gp.CloseFigure();
+ return gp;
+ }
+#else
+ private GraphicsPath GetRoundRectPath(RectangleF rectangleF, float radius)
+ {
+ GraphicsPath gp = new GraphicsPath();
+ if (radius < 1)
+ radius = 1;
+ gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Y, radius + 1, radius + 1, 270, 90);
+ gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 0, 90);
+ gp.AddArc(rectangleF.X, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 90, 90);
+ gp.AddArc(rectangleF.X, rectangleF.Y, radius, radius, 180, 90);
+ gp.CloseFigure();
+ return gp;
+ }
+#endif
#endregion
#region Protected Methods
@@ -373,12 +391,16 @@ public override void DrawImage(FRPaintEventArgs e)
drawWidth,
drawHeight);
+ GraphicsPath path = new GraphicsPath();
IGraphicsState state = g.Save();
try
{
//if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
- g.ResetClip();
- g.SetClip(drawRect);
+ g.ResetClip();
+
+ EstablishImageForm(path, drawLeft, drawTop, drawWidth, drawHeight);
+
+ g.SetClip(path, CombineMode.Replace);
Report report = Report;
if (report != null && report.SmoothGraphics)
{
@@ -411,6 +433,8 @@ public override void DrawImage(FRPaintEventArgs e)
finally
{
g.Restore(state);
+ g.ResetClip();
+ path.Dispose();
}
if (IsPrinting)
@@ -552,17 +576,6 @@ public override void Deserialize(FRReader reader)
}
}
-
-
-
-
-
- //static int number = 0;
-
-
-
-
-
///
/// Loads image
///
@@ -602,9 +615,56 @@ protected override void ResetImageIndex()
{
imageIndex = -1;
}
+
+ ///
+ /// The shape of the image is set using GraphicsPath
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void EstablishImageForm(GraphicsPath path, float drawLeft, float drawTop, float drawWidth, float drawHeight)
+ {
+ RectangleF drawRect = new RectangleF(
+ drawLeft,
+ drawTop,
+ drawWidth,
+ drawHeight);
+
+ switch (Shape)
+ {
+ case ShapeKind.Rectangle:
+ path.AddRectangle(drawRect);
+ break;
+ case ShapeKind.RoundRectangle:
+ float min = Math.Min(drawWidth, drawHeight) / 4;
+ path.AddPath(GetRoundRectPath(drawRect, min), false);
+ break;
+ case ShapeKind.Ellipse:
+ path.AddEllipse(drawLeft, drawTop, drawWidth, drawHeight);
+ break;
+ case ShapeKind.Triangle:
+ PointF[] triPoints =
+ {
+ new PointF(drawLeft + drawWidth, drawTop + drawHeight), new PointF(drawLeft, drawTop + drawHeight),
+ new PointF(drawLeft + drawWidth / 2, drawTop), new PointF(drawLeft + drawWidth, drawTop + drawHeight)
+ };
+ path.AddPolygon(triPoints);
+ break;
+ case ShapeKind.Diamond:
+ PointF[] diaPoints =
+ {
+ new PointF(drawLeft + drawWidth / 2, drawTop), new PointF(drawLeft + drawWidth, drawTop + drawHeight / 2),
+ new PointF(drawLeft + drawWidth / 2, drawTop + drawHeight), new PointF(drawLeft, drawTop + drawHeight / 2)
+ };
+ path.AddPolygon(diaPoints);
+ break;
+ }
+ }
#endregion
-#region Report Engine
+ #region Report Engine
///
diff --git a/FastReport.Base/PictureObjectBase.cs b/FastReport.Base/PictureObjectBase.cs
index 65406064..81a76e20 100644
--- a/FastReport.Base/PictureObjectBase.cs
+++ b/FastReport.Base/PictureObjectBase.cs
@@ -92,6 +92,7 @@ public abstract partial class PictureObjectBase : ReportComponentBase
private bool showErrorImage;
private PictureBoxSizeMode sizeModeInternal;
private ImageAlign imageAlign;
+ private ShapeKind shape;
#endregion Private Fields
@@ -333,6 +334,17 @@ public ImageAlign ImageAlign
set { imageAlign = value; }
}
+ ///
+ /// Gets or sets a shape kind.
+ ///
+ [DefaultValue(ShapeKind.Rectangle)]
+ [Category("Appearance")]
+ public ShapeKind Shape
+ {
+ get { return shape; }
+ set { shape = value; }
+ }
+
#endregion Public Properties
@@ -358,6 +370,7 @@ public ImageAlign ImageAlign
public PictureObjectBase()
{
sizeModeInternal = PictureBoxSizeMode.Zoom;
+ shape = ShapeKind.Rectangle;
padding = new Padding();
imageLocation = "";
dataColumn = "";
@@ -387,6 +400,7 @@ public override void Assign(Base source)
Grayscale = src.Grayscale;
ShowErrorImage = src.ShowErrorImage;
ImageAlign = src.ImageAlign;
+ Shape = src.Shape;
}
}
@@ -668,8 +682,6 @@ private void UpdateAlign(RectangleF drawRect, ref PointF upperLeft, ref PointF u
lowerLeft.Y += offsetY;
}
-
-
///
/// Loads image
///
@@ -776,6 +788,8 @@ public override void Serialize(FRWriter writer)
writer.WriteBool("ShowErrorImage", ShowErrorImage);
if (ImageAlign != ImageAlign.None)
writer.WriteValue("ImageAlign", ImageAlign);
+ if (Shape != c.Shape)
+ writer.WriteValue("Shape", Shape);
}
#endregion Public Methods
diff --git a/FastReport.Base/Preview/PreparedPagePostprocessor.cs b/FastReport.Base/Preview/PreparedPagePostprocessor.cs
index ce9b1505..5b0511e8 100644
--- a/FastReport.Base/Preview/PreparedPagePostprocessor.cs
+++ b/FastReport.Base/Preview/PreparedPagePostprocessor.cs
@@ -1,3 +1,4 @@
+using FastReport.Utils;
using System;
using System.Collections.Generic;
@@ -6,6 +7,7 @@ namespace FastReport.Preview
internal class PreparedPagePostprocessor
{
private Dictionary> duplicates;
+ private Dictionary> mergedTextObjects;
private Dictionary bands;
int iBand;
@@ -112,6 +114,111 @@ private void CloseDuplicatesMerge(List list)
lastObj.Height += delta;
}
+ private void CollectMergedTextObjects(TextObject obj)
+ {
+ if (mergedTextObjects.ContainsKey(obj.Band.Name))
+ {
+ List list = mergedTextObjects[obj.Band.Name];
+ list.Add(obj);
+ }
+ else
+ {
+ List list = new List() { obj };
+ mergedTextObjects.Add(obj.Band.Name, list);
+ }
+ }
+
+ private void MergeTextObjects
+ ()
+ {
+ foreach (var band in mergedTextObjects)
+ {
+ band.Value.Sort(delegate (TextObject txt, TextObject txt2)
+ {
+ if (txt.AbsLeft.CompareTo(txt2.AbsLeft) == 0)
+ return txt.AbsTop.CompareTo(txt2.AbsTop);
+
+ return txt.AbsLeft.CompareTo(txt2.AbsLeft);
+ });
+
+ //Vertical merge
+ MergeTextObjectsInBand(band.Value);
+
+ //May be horizontal merge
+ MergeTextObjectsInBand(band.Value);
+ }
+ }
+
+ private void MergeTextObjectsInBand(List band)
+ {
+ for (int i = 0; i < band.Count; i++)
+ {
+ for (int j = i + 1; j < band.Count; j++)
+ {
+ if (Merge(band[j], band[i]))
+ {
+ TextObject removeObj = band[j];
+ band.Remove(removeObj);
+ removeObj.Dispose();
+ if(j > 0)
+ j--;
+ }
+ }
+ }
+ }
+
+ private bool Merge(TextObject obj, TextObject obj2)
+ {
+ if (obj2.Text != obj.Text)
+ return false;
+
+ var bounds = obj.AbsBounds;
+ if (bounds.Width < 0 || bounds.Height < 0)
+ Validator.NormalizeBounds(ref bounds);
+
+ var bounds2 = obj2.AbsBounds;
+ if (bounds2.Width < 0 || bounds2.Height < 0)
+ Validator.NormalizeBounds(ref bounds2);
+
+ if (obj.MergeMode.HasFlag(MergeMode.Vertical) && obj2.MergeMode.HasFlag(MergeMode.Vertical)
+ && IsEqualWithInaccuracy(bounds2.Width, bounds.Width) && IsEqualWithInaccuracy(bounds2.Left, bounds.Left))
+ {
+ if (IsEqualWithInaccuracy(bounds2.Bottom, bounds.Top))
+ {
+ obj2.Height += bounds.Height;
+ return true;
+ }
+ else if (IsEqualWithInaccuracy(bounds2.Top, bounds.Bottom))
+ {
+ obj2.Height += bounds.Height;
+ obj2.Top -= bounds.Height;
+ return true;
+ }
+ }
+ else if (obj.MergeMode.HasFlag(MergeMode.Horizontal) && obj2.MergeMode.HasFlag(MergeMode.Horizontal)
+ && IsEqualWithInaccuracy(bounds2.Height, bounds.Height) && IsEqualWithInaccuracy(bounds2.Top, bounds.Top))
+ {
+ if (IsEqualWithInaccuracy(bounds2.Right, bounds.Left))
+ {
+ obj2.Width += bounds.Width;
+ return true;
+ }
+ else if (IsEqualWithInaccuracy(bounds2.Left, bounds.Right))
+ {
+ obj2.Width += bounds.Width;
+ obj2.Left -= bounds.Width;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsEqualWithInaccuracy(float value1, float value2)
+ {
+ return Math.Abs(value1 - value2) < 0.01;
+ }
+
public void Postprocess(ReportPage page)
{
page.ExtractMacros();
@@ -128,14 +235,19 @@ public void Postprocess(ReportPage page)
if (c is TextObjectBase txt && txt.Duplicates != Duplicates.Show)
ProcessDuplicates(txt);
+
+ if (c is TextObject text && text.MergeMode != MergeMode.None)
+ CollectMergedTextObjects(text);
}
+ MergeTextObjects();
CloseDuplicates();
}
public PreparedPagePostprocessor()
{
duplicates = new Dictionary>();
+ mergedTextObjects = new Dictionary>();
bands = new Dictionary();
iBand = 0;
}
@@ -153,6 +265,11 @@ public void PostprocessUnlimited(PreparedPage preparedPage, ReportPage page)
ProcessDuplicates(txt);
flag = true; //flag for keep in dictionary
}
+ if (c is TextObject text && text.MergeMode != MergeMode.None)
+ {
+ CollectMergedTextObjects(text);
+ flag = true;
+ }
}
i++;
if (flag)
@@ -165,6 +282,8 @@ public void PostprocessUnlimited(PreparedPage preparedPage, ReportPage page)
b.Dispose();
}
}
+
+ MergeTextObjects();
CloseDuplicates();
}
diff --git a/FastReport.Base/ReportComponentBase.cs b/FastReport.Base/ReportComponentBase.cs
index 5801fd29..ee96cd4f 100644
--- a/FastReport.Base/ReportComponentBase.cs
+++ b/FastReport.Base/ReportComponentBase.cs
@@ -34,6 +34,7 @@ public enum ShiftMode
/// Specifies where to print an object.
///
[Flags]
+ [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))]
public enum PrintOn
{
///
diff --git a/FastReport.Base/TextObject.cs b/FastReport.Base/TextObject.cs
index 3788fafb..4eacddd3 100644
--- a/FastReport.Base/TextObject.cs
+++ b/FastReport.Base/TextObject.cs
@@ -245,6 +245,29 @@ public enum AutoShrinkMode
FontWidth
}
+ ///
+ /// Specifies the behavior of the MergeMode feature of TextObject.
+ ///
+ [TypeConverter(typeof(FastReport.TypeConverters.FlagConverter))]
+ [Flags]
+ public enum MergeMode
+ {
+ ///
+ /// Merge is disabled.
+ ///
+ None = 0,
+
+ ///
+ /// Allows horizontal merging.
+ ///
+ Horizontal = 1,
+
+ ///
+ /// Allows vertical merging.
+ ///
+ Vertical = 2,
+ }
+
///
/// Represents the Text object that may display one or several text lines.
///
@@ -261,6 +284,7 @@ public enum AutoShrinkMode
public partial class TextObject : TextObjectBase
{
#region Fields
+ private MergeMode mergeMode;
private bool autoWidth;
private HorzAlign horzAlign;
private VertAlign vertAlign;
@@ -688,8 +712,6 @@ public TextRenderType TextRenderType
set { textRenderType = value; }
}
-
-
///
/// Gets or sets the paragraph offset, in pixels. For HtmlParagraph use ParagraphFormat.FirstLineIndent.
///
@@ -722,6 +744,18 @@ public InlineImageCache InlineImageCache
return inlineImageCache;
}
}
+
+ ///
+ /// Gets or sets a value indicating whether the text should be merged with other nearby text objects.
+ ///
+ [DefaultValue(MergeMode.None)]
+ [Category("Behavior")]
+ [Editor("FastReport.TypeEditors.FlagsEditor, FastReport", typeof(UITypeEditor))]
+ public MergeMode MergeMode
+ {
+ get { return mergeMode; }
+ set { mergeMode = value; }
+ }
#endregion
#region Private Methods
@@ -1097,6 +1131,7 @@ public override void Assign(Base source)
inlineImageCache = src.inlineImageCache;
PreserveLastLineSpace = src.PreserveLastLineSpace;
paragraphFormat.Assign(src.paragraphFormat);
+ MergeMode = src.MergeMode;
}
///
@@ -1394,6 +1429,8 @@ public override void Serialize(FRWriter writer)
writer.WriteFloat("ParagraphOffset", ParagraphOffset);
if (ForceJustify != c.ForceJustify)
writer.WriteBool("ForceJustify", ForceJustify);
+ if (MergeMode != c.MergeMode)
+ writer.WriteValue("MergeMode", MergeMode);
if (writer.SerializeTo != SerializeTo.Preview)
{
if (Style != c.Style)
diff --git a/FastReport.Base/TypeConverters/FlagConverter.cs b/FastReport.Base/TypeConverters/FlagConverter.cs
new file mode 100644
index 00000000..d4277055
--- /dev/null
+++ b/FastReport.Base/TypeConverters/FlagConverter.cs
@@ -0,0 +1,20 @@
+using System;
+using System.ComponentModel;
+
+namespace FastReport.TypeConverters
+{
+ ///
+ /// Blocks keyboard editing, you need to select a value from the drop-down list for editing
+ ///
+ internal class FlagConverter : EnumConverter
+ {
+ public FlagConverter(Type type) : base(type)
+ {
+ }
+
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
+ {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FastReport.Base/Utils/GraphicCache.cs b/FastReport.Base/Utils/GraphicCache.cs
index 2165195d..d8d6c1cb 100644
--- a/FastReport.Base/Utils/GraphicCache.cs
+++ b/FastReport.Base/Utils/GraphicCache.cs
@@ -163,6 +163,7 @@ public StringFormat GetStringFormat(StringAlignment align, StringAlignment lineA
result.Trimming = trimming;
result.FormatFlags = flags;
float[] tabStops = new float[64];
+ float sumWidth = firstTab;
// fixed issue 2823
tabStops[0] = firstTab < 0 ? 0 : firstTab;
for (int i = 1; i < 64; i++)
@@ -170,9 +171,14 @@ public StringFormat GetStringFormat(StringAlignment align, StringAlignment lineA
if (i > tabWidth.Count)
{
tabStops[i] = defaultTab < 0 ? 0 : defaultTab;
+ //tab stops have static positions we need to go back to them
+ if (sumWidth % defaultTab != 0)
+ tabStops[i] = defaultTab - sumWidth % defaultTab;
+ sumWidth += tabStops[i];
continue;
}
tabStops[i] = tabWidth[i - 1] < 0 ? 0 : tabWidth[i - 1];
+ sumWidth += tabStops[i];
}
result.SetTabStops(0, tabStops);
stringFormats[hash] = result;
diff --git a/FastReport.Base/Utils/HtmlTextRenderer.cs b/FastReport.Base/Utils/HtmlTextRenderer.cs
index df71c61e..62b6c86c 100644
--- a/FastReport.Base/Utils/HtmlTextRenderer.cs
+++ b/FastReport.Base/Utils/HtmlTextRenderer.cs
@@ -439,7 +439,7 @@ internal void Draw()
IGraphicsState state = graphics.Save();
//RectangleF rect = new RectangleF(FDisplayRect.Location, SizeF.Add(FDisplayRect.Size, new SizeF(width_dotnet, 0)));
//FGraphics.SetClip(rect, CombineMode.Intersect);
-
+ graphics.SetClip(displayRect, CombineMode.Intersect);
// reset alignment
//StringAlignment saveAlign = FFormat.Alignment;
//StringAlignment saveLineAlign = FFormat.LineAlignment;
@@ -695,7 +695,7 @@ private void SplitToParagraphs(string text)
Stack elements = new Stack();
SimpleFastReportHtmlReader reader = new SimpleFastReportHtmlReader(this.text);
List currentWord = new List();
- float width = paragraphFormat.SkipFirstLineIndent ? 0 : paragraphFormat.FirstLineIndent;
+ float width = paragraphFormat.SkipFirstLineIndent ? 0 : GetStartPosition(true);
Paragraph paragraph = new Paragraph(this);
int charIndex = 0;
int tabIndex = 0;
@@ -763,7 +763,6 @@ private void SplitToParagraphs(string text)
word = new Word(this, line, WordType.Tab);
-
Run tabRun = new RunText(this, word, style, new List(new CharWithIndex[] { reader.Character }), width, charIndex);
word.Runs.Add(tabRun);
float width2 = GetTabPosition(width);
@@ -785,6 +784,13 @@ private void SplitToParagraphs(string text)
width2 = GetTabPosition(width, tabIndex);
}
}
+ // decrease by (DrawUtils.ScreenDpi / 96f) repeats the work of the Word, if the next tab position is a pixel further than the left indent,
+ // then the tab stop occurs in the tab position, otherwise the stop will be in the place of the left indentation
+ if (width < -paragraphFormat.FirstLineIndent && width2 - (DrawUtils.ScreenDpi / 96f) > -paragraphFormat.FirstLineIndent)
+ {
+ width2 = -paragraphFormat.FirstLineIndent;
+ }
+
tabIndex++;
line.Words.Add(word);
tabRun.Width = width2 - width;
@@ -819,7 +825,7 @@ private void SplitToParagraphs(string text)
//word.Runs.Add(runText);
line = new Line(this, paragraph, charIndex);
word = null;
- width = 0;
+ width = GetStartPosition();
currentWord.Clear();
paragraph.Lines.Add(line);
break;
@@ -850,7 +856,7 @@ private void SplitToParagraphs(string text)
paragraphs.Add(paragraph);
line = new Line(this, paragraph, charIndex);
word = null;
- width = paragraphFormat.FirstLineIndent;
+ width = GetStartPosition(true);
paragraph.Lines.Add(line);
break;
@@ -1119,7 +1125,7 @@ private Line WrapLine(Paragraph paragraph, Line line, int wordCharIndex, float a
paragraph.Lines.Add(line);
newWord = new Word(newWord.Renderer, line, newWord.Type);
line.Words.Add(newWord);
- secondPart.Left = 0;
+ secondPart.Left = GetStartPosition();
width = secondPart.Width;
currentWord = newWord;
if (width < availableWidth)
@@ -1152,11 +1158,12 @@ private Line WrapLine(Paragraph paragraph, Line line, int wordCharIndex, float a
line.Words.RemoveAt(line.Words.Count - 1);
Line result = new Line(this, paragraph, wordCharIndex);
paragraph.Lines.Add(result);
- newWidth = 0;
- if (line.Words.Count > 2 && line.Words[line.Words.Count - 2].Type == WordType.Tab)
+ newWidth = GetStartPosition();
+
+ if (line.Words.Count > 1 && line.Words[line.Words.Count - 1].Type == WordType.Tab)
{
- Word tabWord = line.Words[line.Words.Count - 2];
- line.Words.RemoveAt(line.Words.Count - 2);
+ Word tabWord = line.Words[line.Words.Count - 1];
+ line.Words.RemoveAt(line.Words.Count - 1);
float width2 = GetTabPosition(newWidth);
if (isDifferentTabPositions)
{
@@ -1167,7 +1174,7 @@ private Line WrapLine(Paragraph paragraph, Line line, int wordCharIndex, float a
tabIndex = 1;
result.Words.Add(tabWord);
tabWord.Line = result;
- tabWord.Runs[0].Left = 0;
+ tabWord.Runs[0].Left = GetStartPosition();
tabWord.Runs[0].Width = width2 - newWidth;
newWidth = width2;
}
@@ -1185,6 +1192,24 @@ private Line WrapLine(Paragraph paragraph, Line line, int wordCharIndex, float a
}
}
+ ///
+ /// Get start position of line.
+ ///
+ ///
+ /// if this parameter is true, the starting position of the line in the new paragraph will be returned
+ ///
+ ///
+ private float GetStartPosition(bool isNewParagraph = false)
+ {
+ // if we have a back indent, we take it as zero and shift all the initial positions of the text in the lines by the size of this indent.
+ if (isNewParagraph && paragraphFormat.FirstLineIndent > 0)
+ return paragraphFormat.FirstLineIndent;
+
+ if (paragraphFormat.FirstLineIndent < 0 && !isNewParagraph)
+ return -paragraphFormat.FirstLineIndent;
+ return 0;
+ }
+
#endregion Private Methods
#region Public Enums
@@ -2236,9 +2261,9 @@ public override void Draw()
//#if DEBUG
//SizeF size = renderer.graphics.MeasureString(text, font, int.MaxValue, renderer.format);
//if (renderer.RightToLeft)
- // renderer.graphics.DrawRectangle(Pens.Red, Left - size.Width, Top, size.Width, size.Height);
+ // renderer.graphics.DrawRectangle(Pens.Red, Left - size.Width, Top, size.Width, size.Height);
//else
- // renderer.graphics.DrawRectangle(Pens.Red, Left, Top, size.Width, size.Height);
+ // renderer.graphics.DrawRectangle(Pens.Red, Left, Top, Width, Height);
//#endif
renderer.graphics.DrawString(text, font, brush, Left, Top, renderer.format);
}
diff --git a/FastReport.Base/Utils/Validator.cs b/FastReport.Base/Utils/Validator.cs
index e6ea0a01..e8063369 100644
--- a/FastReport.Base/Utils/Validator.cs
+++ b/FastReport.Base/Utils/Validator.cs
@@ -35,7 +35,7 @@ public ValidationError(string name, ErrorLevel level, string message, ReportComp
///
public static class Validator
{
- private static void NormalizeBounds(ref RectangleF bounds)
+ internal static void NormalizeBounds(ref RectangleF bounds)
{
if (bounds.Width < 0)
{