Compare commits

..

5 Commits

Author SHA1 Message Date
MarcinZiabek 5cc0397acb Added more examples for the Shrink element 2022-10-19 23:20:19 +02:00
MarcinZiabek 73448f483c Extended and renamed the MinimalBox element 2022-10-19 21:37:10 +02:00
MarcinZiabek 7b7fac07b3 Relative padding implementation 2022-10-05 00:58:00 +02:00
MarcinZiabek 759655059d Renaming 2022-10-04 22:47:18 +02:00
MarcinZiabek fa3d912e41 RelativeSize, RelativePosition 2022-10-04 22:26:25 +02:00
38 changed files with 586 additions and 416 deletions
+1 -55
View File
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using NUnit.Framework;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Examples.Engine;
@@ -35,21 +34,6 @@ namespace QuestPDF.Examples
});
}
[Test]
public void DynamicImage()
{
RenderingTest
.Create()
.PageSize(450, 350)
.ProducePdf()
.ShowResults()
.Render(page =>
{
page.Padding(25)
.Image(Placeholders.Image);
});
}
[Test]
public void Exception()
{
@@ -63,43 +47,5 @@ namespace QuestPDF.Examples
.Render(page => page.Image("non_existent.png"));
});
}
[Test]
public void ReusingTheSameImageFileShouldBePossible()
{
var fileName = Path.GetTempFileName() + ".jpg";
try
{
var image = Placeholders.Image(300, 100);
using var file = File.Create(fileName);
file.Write(image);
file.Dispose();
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Column(column =>
{
column.Spacing(20);
column.Item().Image(fileName);
column.Item().Image(fileName);
column.Item().Image(fileName);
});
});
}
finally
{
File.Delete(fileName);
}
}
}
}
@@ -0,0 +1,36 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class RelativePaddingExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(250, 250)
.ShowResults()
.Render(container =>
{
container
.Width(250)
.Height(250)
.Padding(50)
.Background(Colors.Grey.Lighten2)
.RelativePaddingLeft(0.1f)
.RelativePaddingTop(0.2f)
.RelativePaddingRight(0.3f)
.RelativePaddingBottom(0.4f)
.Background(Colors.Grey.Darken2);
});
}
}
}
@@ -0,0 +1,32 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class RelativePositionExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(500, 500)
.ShowResults()
.Render(container =>
{
container
.Padding(100)
.Background(Colors.Grey.Lighten2)
.RelativePositionVertical(0.5f, -0.5f)
.RelativePositionHorizontal(1f, -0.5f)
.RelativeWidth(0.4f)
.RelativeHeight(0.6f)
.Background(Colors.Grey.Darken2);
});
}
}
}
+38
View File
@@ -0,0 +1,38 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class RelativeSizeExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(600, 600)
.ShowResults()
.Render(container =>
{
container
.AlignMiddle()
.AlignCenter()
.Width(400)
.Height(400)
.Background(Colors.Grey.Lighten2)
.AlignMiddle()
.AlignCenter()
.Container()
.AlignMiddle()
.AlignCenter()
.RelativeWidth(0.25f)
.RelativeHeight(0.5f)
.Background(Colors.Grey.Darken2);
});
}
}
}
@@ -1,38 +0,0 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class RepeatContentExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.Decoration(decoration =>
{
decoration.Before().Text("Test").FontSize(22);
decoration.Content().Column(column =>
{
column.Spacing(20);
foreach (var _ in Enumerable.Range(0, 10))
column.Item().Background(Colors.Grey.Medium).ExtendHorizontal().Height(80);
});
});
});
}
}
}
-36
View File
@@ -105,41 +105,5 @@ namespace QuestPDF.Examples
.Row(row => { });
});
}
[Test]
public void RowElementForRelativeHeightDivision()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.MaxPages(100)
.PageSize(250, 400)
.Render(container =>
{
container
.Padding(25)
.AlignLeft()
.RotateRight()
.Row(row =>
{
row.Spacing(20);
row.RelativeItem(1).Element(Content);
row.RelativeItem(2).Element(Content);
row.RelativeItem(3).Element(Content);
void Content(IContainer container)
{
container
.RotateLeft()
.Border(1)
.Background(Placeholders.BackgroundColor())
.Padding(5)
.Text(Placeholders.Label());
}
});
});
}
}
}
+93
View File
@@ -0,0 +1,93 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class ShrinkExamples
{
[Test]
public void Shrink_Without()
{
RenderingTest
.Create()
.PageSize(300, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Border(2)
.Background(Colors.Grey.Lighten2)
.Padding(20)
.Text("This is test.")
.FontSize(20);
});
}
[Test]
public void Shrink_Horizontal()
{
RenderingTest
.Create()
.PageSize(300, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Border(2)
.ShrinkHorizontal()
.Background(Colors.Grey.Lighten2)
.Padding(20)
.Text("This is test.")
.FontSize(20);
});
}
[Test]
public void Shrink_Vertical()
{
RenderingTest
.Create()
.PageSize(300, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Border(2)
.ShrinkVertical()
.Background(Colors.Grey.Lighten2)
.Padding(20)
.Text("This is test.")
.FontSize(20);
});
}
[Test]
public void Shrink_Both()
{
RenderingTest
.Create()
.PageSize(300, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Border(2)
.Shrink()
.Background(Colors.Grey.Lighten2)
.Padding(20)
.Text("This is test.")
.FontSize(20);
});
}
}
}
-32
View File
@@ -657,37 +657,5 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void WordWrappingStability()
{
// instruction: check if any characters repeat when performing the word-wrapping algorithm
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(container =>
{
var text = "Lorem ipsum dolor sit amet consectetuer";
container
.Padding(20)
.Column(column =>
{
column.Spacing(10);
foreach (var width in Enumerable.Range(25, 200))
{
column
.Item()
.MaxWidth(width)
.Background(Colors.Grey.Lighten3)
.Text(text);
}
});
});
}
}
}
@@ -7,18 +7,20 @@ using QuestPDF.UnitTests.TestEngine;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class BoxTests
public class ShrinkTests
{
[Test]
public void Measure() => SimpleContainerTests.Measure<MinimalBox>();
public void Measure() => SimpleContainerTests.Measure<Shrink>();
[Test]
public void Draw_Wrap()
{
TestPlan
.For(x => new MinimalBox
.For(x => new Shrink
{
Child = x.CreateChild()
Child = x.CreateChild(),
ShrinkVertical = true,
ShrinkHorizontal = true
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.Wrap())
@@ -29,9 +31,11 @@ namespace QuestPDF.UnitTests
public void Measure_PartialRender()
{
TestPlan
.For(x => new MinimalBox
.For(x => new Shrink
{
Child = x.CreateChild()
Child = x.CreateChild(),
ShrinkVertical = true,
ShrinkHorizontal = true
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
@@ -43,9 +47,11 @@ namespace QuestPDF.UnitTests
public void Measure_FullRender()
{
TestPlan
.For(x => new MinimalBox
.For(x => new Shrink
{
Child = x.CreateChild()
Child = x.CreateChild(),
ShrinkVertical = true,
ShrinkHorizontal = true
})
.MeasureElement(new Size(500, 400))
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
+2 -49
View File
@@ -65,14 +65,13 @@ namespace QuestPDF.Drawing
document.Compose(container);
var content = container.Compose();
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
ApplyContentRepeatState(content, false);
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
if (Settings.EnableCaching)
ApplyCaching(content);
var pageContext = new PageContext();
var pageContext = new PageContext();
RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
RenderPass(pageContext, canvas, content, debuggingState);
}
@@ -82,8 +81,6 @@ namespace QuestPDF.Drawing
{
content.VisitChildren(x => x?.Initialize(pageContext, canvas));
content.VisitChildren(x => (x as IStateResettable)?.ResetState());
ResetIsRenderedState(content);
canvas.BeginDocument();
@@ -163,50 +160,6 @@ namespace QuestPDF.Drawing
return debuggingState;
}
private static void ApplyContentRepeatState(Element? content, bool repeatContent)
{
if (content == null)
return;
if (content is IVisual visual)
visual.RepeatContent = repeatContent;
if (content is TextBlock textBlock)
{
foreach (var textBlockItem in textBlock.Items)
{
if (textBlockItem is TextBlockElement textElement)
{
ApplyContentRepeatState(textElement.Element, true);
}
}
return;
}
// TODO: apply RepeatState in dynamic content
//if (content is DynamicHost dynamicHost)
// dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
if (content is RepeatContentSetter repeatContentSetter)
repeatContent = repeatContentSetter.RepeatContent;
foreach (var child in content.GetChildren())
ApplyContentRepeatState(child, repeatContent);
}
private static void ResetIsRenderedState(Element? content)
{
if (content == null)
return;
if (content is IVisual visual)
visual.IsRendered = false;
foreach (var child in content.GetChildren())
ResetIsRenderedState(child);
}
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
{
+4 -13
View File
@@ -7,28 +7,19 @@ namespace QuestPDF.Elements
{
public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace);
internal class Canvas : Element, IVisual, ICacheable
internal class Canvas : Element, ICacheable
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public DrawOnCanvas Handler { get; set; }
internal override SpacePlan Measure(Size availableSpace)
{
if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
return availableSpace.IsNegative()
? SpacePlan.Wrap()
: SpacePlan.FullRender(availableSpace);
}
internal override void Draw(Size availableSpace)
{
IsRendered = true;
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
if (Handler == null || skiaCanvas == null)
+11 -18
View File
@@ -6,38 +6,31 @@ using SkiaSharp;
namespace QuestPDF.Elements
{
internal class DynamicImage : Element, IVisual
internal class DynamicImage : Element
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public Func<Size, byte[]>? Source { get; set; }
internal override SpacePlan Measure(Size availableSpace)
{
if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
return availableSpace.IsNegative()
? SpacePlan.Wrap()
: SpacePlan.FullRender(availableSpace);
}
internal override void Draw(Size availableSpace)
{
IsRendered = true;
if (availableSpace.Width < Size.Epsilon || availableSpace.Height < Size.Epsilon)
return;
var imageData = Source?.Invoke(availableSpace);
if (imageData == null)
return;
using var image = SKImage.FromEncodedData(imageData);
Canvas.DrawImage(image, Position.Zero, availableSpace);
var imageElement = new Image
{
InternalImage = SKImage.FromEncodedData(imageData)
};
imageElement.Initialize(PageContext, Canvas);
imageElement.Draw(availableSpace);
}
}
}
+4 -12
View File
@@ -5,11 +5,8 @@ using SkiaSharp;
namespace QuestPDF.Elements
{
internal class Image : Element, IVisual, ICacheable
internal class Image : Element, ICacheable
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public SKImage? InternalImage { get; set; }
~Image()
@@ -19,13 +16,9 @@ namespace QuestPDF.Elements
internal override SpacePlan Measure(Size availableSpace)
{
if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
return availableSpace.IsNegative()
? SpacePlan.Wrap()
: SpacePlan.FullRender(availableSpace);
}
internal override void Draw(Size availableSpace)
@@ -33,7 +26,6 @@ namespace QuestPDF.Elements
if (InternalImage == null)
return;
IsRendered = true;
Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
}
}
+1 -1
View File
@@ -212,7 +212,7 @@ namespace QuestPDF.Elements
break;
var element = queue.Peek();
var size = element.Measure(new Size(availableSize.Width, Size.Max.Height));
var size = element.Measure(Size.Max);
if (size.Type == SpacePlanType.Wrap)
break;
+6 -14
View File
@@ -15,42 +15,34 @@ namespace QuestPDF.Elements
Horizontal
}
internal class Line : Element, ILine, IVisual, ICacheable
internal class Line : Element, ILine, ICacheable
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public LineType Type { get; set; } = LineType.Vertical;
public string Color { get; set; } = Colors.Black;
public float Thickness { get; set; } = 1;
public float Size { get; set; } = 1;
internal override SpacePlan Measure(Size availableSpace)
{
if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return Type switch
{
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(Thickness, 0),
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(0, Thickness),
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(Size, 0),
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(0, Size),
_ => SpacePlan.Wrap()
};
}
internal override void Draw(Size availableSpace)
{
IsRendered = true;
if (Type == LineType.Vertical)
{
Canvas.DrawRectangle(new Position(-Thickness/2, 0), new Size(Thickness, availableSpace.Height), Color);
Canvas.DrawRectangle(new Position(-Size/2, 0), new Size(Size, availableSpace.Height), Color);
}
else if (Type == LineType.Horizontal)
{
Canvas.DrawRectangle(new Position(0, -Thickness/2), new Size(availableSpace.Width, Thickness), Color);
Canvas.DrawRectangle(new Position(0, -Size/2), new Size(availableSpace.Width, Size), Color);
}
}
}
-18
View File
@@ -1,18 +0,0 @@
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class MinimalBox : ContainerElement
{
internal override void Draw(Size availableSpace)
{
var targetSize = base.Measure(availableSpace);
if (targetSize.Type == SpacePlanType.Wrap)
return;
base.Draw(targetSize);
}
}
}
+65
View File
@@ -0,0 +1,65 @@
using System;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class RelativePadding : ContainerElement
{
public float Top { get; set; }
public float Right { get; set; }
public float Bottom { get; set; }
public float Left { get; set; }
internal override SpacePlan Measure(Size availableSpace)
{
if (Child == null)
return SpacePlan.FullRender(0, 0);
var internalSpace = InternalSpace(availableSpace);
if (internalSpace.Width < 0 || internalSpace.Height < 0)
return SpacePlan.Wrap();
var measure = base.Measure(internalSpace);
if (measure.Type == SpacePlanType.Wrap)
return SpacePlan.Wrap();
if (measure.Type == SpacePlanType.PartialRender)
return SpacePlan.PartialRender(availableSpace);
if (measure.Type == SpacePlanType.FullRender)
return SpacePlan.FullRender(availableSpace);
throw new NotSupportedException();
}
internal override void Draw(Size availableSpace)
{
if (Child == null)
return;
var internalOffset = InternalOffset(availableSpace);
var internalSpace = InternalSpace(availableSpace);
Canvas.Translate(internalOffset);
base.Draw(internalSpace);
Canvas.Translate(internalOffset.Reverse());
}
private Position InternalOffset(Size availableSpace)
{
return new Position(
availableSpace.Width * Left,
availableSpace.Height * Top);
}
private Size InternalSpace(Size availableSpace)
{
return new Size(
availableSpace.Width * (1f - Left - Right),
availableSpace.Height * (1f - Top - Bottom));
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using System;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class RelativePosition : ContainerElement
{
public float VerticalParent { get; set; }
public float VerticalChild { get; set; }
public float HorizontalParent { get; set; }
public float HorizontalChild { get; set; }
internal override void Draw(Size availableSpace)
{
if (Child == null)
return;
var childSize = base.Measure(availableSpace);
if (childSize.Type == SpacePlanType.Wrap)
return;
var left = availableSpace.Width * HorizontalParent + childSize.Width * HorizontalChild;
var top = availableSpace.Height * VerticalParent + childSize.Height * VerticalChild;
Canvas.Translate(new Position(left, top));
base.Draw(childSize);
Canvas.Translate(new Position(-left, -top));
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class RelativeSize : ContainerElement
{
public float? WidthFactor { get; set; } = 1f;
public float? HeightFactor { get; set; } = 1f;
internal override SpacePlan Measure(Size availableSpace)
{
var internalSpace = new Size(
availableSpace.Width * (WidthFactor ?? 1),
availableSpace.Height * (HeightFactor ?? 1));
var childSpace = Child?.Measure(internalSpace) ?? SpacePlan.FullRender(0, 0);
if (childSpace.Type == SpacePlanType.Wrap)
return SpacePlan.Wrap();
var targetSpace = new Size(
WidthFactor.HasValue ? internalSpace.Width : childSpace.Width,
HeightFactor.HasValue ? internalSpace.Height : childSpace.Height);
if (childSpace.Type == SpacePlanType.PartialRender)
return SpacePlan.PartialRender(targetSpace);
if (childSpace.Type == SpacePlanType.FullRender)
return SpacePlan.FullRender(targetSpace);
throw new ArgumentException();
}
}
}
-9
View File
@@ -1,9 +0,0 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class RepeatContentSetter : ContainerElement
{
public bool RepeatContent { get; set; }
}
}
+27
View File
@@ -0,0 +1,27 @@
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class Shrink : ContainerElement
{
public bool ShrinkVertical { get; set; }
public bool ShrinkHorizontal { get; set; }
internal override void Draw(Size availableSpace)
{
var childSize = base.Measure(availableSpace);
if (childSize.Type == SpacePlanType.Wrap)
return;
var targetSize = new Size(
ShrinkVertical ? childSize.Width : availableSpace.Width,
ShrinkHorizontal ? childSize.Height : availableSpace.Height);
// TODO: adjust offset for RTL mode
base.Draw(targetSize);
}
}
}
+4 -7
View File
@@ -24,7 +24,6 @@ namespace QuestPDF.Elements.Table
// inner table: list of all cells that ends at the corresponding row
private TableCell[][] CellsCache { get; set; }
private int MaxRow { get; set; }
private int MaxRowSpan { get; set; }
internal override void Initialize(IPageContext pageContext, ICanvas canvas)
{
@@ -57,7 +56,6 @@ namespace QuestPDF.Elements.Table
if (Cells.Count == 0)
{
MaxRow = 0;
MaxRowSpan = 1;
CellsCache = Array.Empty<TableCell[]>();
return;
@@ -68,7 +66,6 @@ namespace QuestPDF.Elements.Table
.ToDictionary(x => x.Key, x => x.OrderBy(x => x.Column).ToArray());
MaxRow = groups.Max(x => x.Key);
MaxRowSpan = Cells.Max(x => x.RowSpan);
CellsCache = Enumerable
.Range(0, MaxRow + 1)
@@ -202,9 +199,9 @@ namespace QuestPDF.Elements.Table
currentRow = cell.Row;
}
// cell visibility optimizations
if (cell.Row > maxRenderingRow + MaxRowSpan)
if (cell.Row > maxRenderingRow)
break;
// calculate cell position / size
@@ -221,14 +218,14 @@ namespace QuestPDF.Elements.Table
{
maxRenderingRow = Math.Min(maxRenderingRow, cell.Row + cell.RowSpan - 1);
}
// corner case: if cell within the row want to wrap to the next page, do not attempt to render this row
if (cellSize.Type == SpacePlanType.Wrap)
{
maxRenderingRow = Math.Min(maxRenderingRow, cell.Row - 1);
continue;
}
// update position of the last row that cell occupies
var bottomRow = cell.Row + cell.RowSpan - 1;
rowBottomOffsets[bottomRow] = Math.Max(rowBottomOffsets[bottomRow], topOffset + cellSize.Height);
+6 -16
View File
@@ -121,11 +121,7 @@ namespace QuestPDF.Elements.Text
{
foreach (var textBlockItem in textBlockItems)
{
if (textBlockItem is TextBlockPageNumber or TextBlockElement)
{
yield return textBlockItem;
}
else if (textBlockItem is TextBlockSpan textBlockSpan)
if (textBlockItem is TextBlockSpan textBlockSpan and not TextBlockPageNumber)
{
if (!Settings.CheckIfAllTextGlyphsAreAvailable && textBlockSpan.Style.Fallback == null)
{
@@ -134,25 +130,19 @@ namespace QuestPDF.Elements.Text
}
var textRuns = textBlockSpan.Text.SplitWithFontFallback(textBlockSpan.Style);
foreach (var textRun in textRuns)
{
var newElement = textBlockSpan switch
yield return new TextBlockSpan
{
TextBlockHyperlink hyperlink => new TextBlockHyperlink { Url = hyperlink.Url },
TextBlockSectionLink sectionLink => new TextBlockSectionLink { SectionName = sectionLink.SectionName },
TextBlockSpan => new TextBlockSpan()
Text = textRun.Content,
Style = textRun.Style
};
newElement.Text = textRun.Content;
newElement.Style = textRun.Style;
yield return newElement;
}
}
else
{
throw new NotSupportedException();
yield return textBlockItem;
}
}
}
@@ -15,7 +15,7 @@ namespace QuestPDF.Elements.Text.Items
{
public string Text { get; set; }
public TextStyle Style { get; set; } = TextStyle.Default;
private TextShapingResult? TextShapingResult { get; set; }
public TextShapingResult? TextShapingResult { get; set; }
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new ();
protected virtual bool EnableTextCache => true;
+4 -20
View File
@@ -4,16 +4,12 @@ using System.Linq;
using QuestPDF.Drawing;
using QuestPDF.Elements.Text.Calculation;
using QuestPDF.Elements.Text.Items;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements.Text
{
internal class TextBlock : Element, IVisual, IStateResettable
internal class TextBlock : Element, IStateResettable
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
@@ -23,7 +19,7 @@ namespace QuestPDF.Elements.Text
private int CurrentElementIndex { get; set; }
private bool FontFallbackApplied { get; set; } = false;
public void ResetState()
{
ApplyFontFallback();
@@ -57,12 +53,6 @@ namespace QuestPDF.Elements.Text
internal override SpacePlan Measure(Size availableSpace)
{
if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
if (!RenderingQueue.Any())
return SpacePlan.FullRender(Size.Zero);
@@ -90,9 +80,6 @@ namespace QuestPDF.Elements.Text
internal override void Draw(Size availableSpace)
{
if (IsRendered && !RepeatContent)
return;
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList();
if (!lines.Any())
@@ -149,13 +136,10 @@ namespace QuestPDF.Elements.Text
var lastElementMeasurement = lines.Last().Elements.Last().Measurement;
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex;
if (!RenderingQueue.Any())
{
ResetState();
IsRendered = true;
}
float GetAlignmentOffset(float lineWidth)
{
if (Alignment == HorizontalAlignment.Left)
+8 -4
View File
@@ -12,7 +12,7 @@ namespace QuestPDF.Fluent
{
var container = new Container();
Decoration.Before = container;
return container.RepeatContentWhenPaging();
return container;
}
public void Before(Action<IContainer> handler)
@@ -36,7 +36,7 @@ namespace QuestPDF.Fluent
{
var container = new Container();
Decoration.After = container;
return container.RepeatContentWhenPaging();
return container;
}
public void After(Action<IContainer> handler)
@@ -49,7 +49,9 @@ namespace QuestPDF.Fluent
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
public IContainer Header()
{
return Before();
var container = new Container();
Decoration.Before = container;
return container;
}
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
@@ -61,7 +63,9 @@ namespace QuestPDF.Fluent
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
public IContainer Footer()
{
return After();
var container = new Container();
Decoration.After = container;
return container;
}
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
-11
View File
@@ -154,17 +154,6 @@ namespace QuestPDF.Fluent
});
}
[Obsolete("This element has been renamed since version 2022.1. Please use the MinimalBox method.")]
public static IContainer Box(this IContainer element)
{
return element.Element(new MinimalBox());
}
public static IContainer MinimalBox(this IContainer element)
{
return element.Element(new MinimalBox());
}
public static IContainer Unconstrained(this IContainer element)
{
return element.Element(new Unconstrained());
+1 -1
View File
@@ -24,7 +24,7 @@ namespace QuestPDF.Fluent
return container;
}
public IContainer Layer() => Layer(false).RepeatContentWhenPaging();
public IContainer Layer() => Layer(false);
public IContainer PrimaryLayer() => Layer(true);
internal void Validate()
+6 -6
View File
@@ -6,11 +6,11 @@ namespace QuestPDF.Fluent
{
public static class LineExtensions
{
private static ILine Line(this IContainer element, LineType type, float thickness)
private static ILine Line(this IContainer element, LineType type, float size)
{
var line = new Line
{
Thickness = thickness,
Size = size,
Type = type
};
@@ -18,14 +18,14 @@ namespace QuestPDF.Fluent
return line;
}
public static ILine LineVertical(this IContainer element, float thickness, Unit unit = Unit.Point)
public static ILine LineVertical(this IContainer element, float size, Unit unit = Unit.Point)
{
return element.Line(LineType.Vertical, thickness.ToPoints(unit));
return element.Line(LineType.Vertical, size.ToPoints(unit));
}
public static ILine LineHorizontal(this IContainer element, float thickness, Unit unit = Unit.Point)
public static ILine LineHorizontal(this IContainer element, float size, Unit unit = Unit.Point)
{
return element.Line(LineType.Horizontal, thickness.ToPoints(unit));
return element.Line(LineType.Horizontal, size.ToPoints(unit));
}
public static void LineColor(this ILine descriptor, string value)
@@ -0,0 +1,37 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class RelativePaddingExtensions
{
private static IContainer RelativePadding(this IContainer element, Action<RelativePadding> handler)
{
var relativePadding = element as RelativePadding ?? new RelativePadding();
handler(relativePadding);
return element.Element(relativePadding);
}
public static IContainer RelativePaddingTop(this IContainer element, float value)
{
return element.RelativePadding(x => x.Top += value);
}
public static IContainer RelativePaddingBottom(this IContainer element, float value)
{
return element.RelativePadding(x => x.Bottom += value);
}
public static IContainer RelativePaddingLeft(this IContainer element, float value)
{
return element.RelativePadding(x => x.Left += value);
}
public static IContainer RelativePaddingRight(this IContainer element, float value)
{
return element.RelativePadding(x => x.Right += value);
}
}
}
@@ -0,0 +1,35 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class RelativePositionExtensions
{
private static IContainer RelativePosition(this IContainer element, Action<RelativePosition> handler)
{
var relativePosition = element as RelativePosition ?? new RelativePosition();
handler(relativePosition);
return element.Element(relativePosition);
}
public static IContainer RelativePositionVertical(this IContainer element, float parentOffset, float childOffset)
{
return element.RelativePosition(x =>
{
x.VerticalParent = parentOffset;
x.VerticalChild = childOffset;
});
}
public static IContainer RelativePositionHorizontal(this IContainer element, float parentOffset, float childOffset)
{
return element.RelativePosition(x =>
{
x.HorizontalParent = parentOffset;
x.HorizontalChild = childOffset;
});
}
}
}
+27
View File
@@ -0,0 +1,27 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class RelativeSizeExtensions
{
private static IContainer RelativeSize(this IContainer element, Action<RelativeSize> handler)
{
var relativeSize = element as RelativeSize ?? new RelativeSize();
handler(relativeSize);
return element.Element(relativeSize);
}
public static IContainer RelativeWidth(this IContainer element, float value)
{
return element.RelativeSize(x => x.WidthFactor = value);
}
public static IContainer RelativeHeight(this IContainer element, float value)
{
return element.RelativeSize(x => x.HeightFactor = value);
}
}
}
@@ -1,27 +0,0 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class RepeatContentExtensions
{
private static IContainer RepeatContent(this IContainer element, Action<RepeatContentSetter> handler)
{
var repeatContentSetter = element as RepeatContentSetter ?? new RepeatContentSetter();
handler(repeatContentSetter);
return element.Element(repeatContentSetter);
}
public static IContainer RepeatContentWhenPaging(this IContainer element)
{
return element.RepeatContent(x => x.RepeatContent = true);
}
public static IContainer DoNotRepeatContentWhenPaging(this IContainer element)
{
return element.RepeatContent(x => x.RepeatContent = false);
}
}
}
+48
View File
@@ -0,0 +1,48 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class ShrinkExtensions
{
private static IContainer Shrink(this IContainer element, Action<Shrink> handler)
{
var shrink = element as Shrink ?? new Shrink();
handler(shrink);
return element.Element(shrink);
}
public static IContainer Shrink(this IContainer element)
{
return element.ShrinkVertical().ShrinkHorizontal();
}
public static IContainer ShrinkVertical(this IContainer element)
{
return element.Shrink(x => x.ShrinkVertical = true);
}
public static IContainer ShrinkHorizontal(this IContainer element)
{
return element.Shrink(x => x.ShrinkHorizontal = true);
}
#region Obsolete
[Obsolete("This element has been renamed since version 2022.1. Please use the Shrink method.")]
public static IContainer Box(this IContainer element)
{
return element.Element(new Shrink());
}
[Obsolete("This element has been renamed since version 2022.11. Please use the Shrink method.")]
public static IContainer MinimalBox(this IContainer element)
{
return element.Element(new Shrink());
}
#endregion
}
}
-8
View File
@@ -1,8 +0,0 @@
namespace QuestPDF.Infrastructure
{
internal interface IVisual
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2022.9.1</Version>
<Version>2022.9.0</Version>
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
<LangVersion>9</LangVersion>
-5
View File
@@ -4,8 +4,3 @@
- Significantly reduced memory allocation cost for TextStyle objects,
- Implemented optional checking if all font glyphs are available,
- Minor text-rendering optimizations.
2022.9.1
- Fixed: text hyperlinks do not work when the CheckIfAllTextGlyphsAreAvailable option or text fallback are used,
- Fixed: cells with RowSpan (greater than 1) are not always displayed properly,
- Improved predictability of the Inlined element when measuring its children.
+5 -6
View File
@@ -17,7 +17,7 @@ It offers a layouting engine designed with a full paging support in mind. The do
Unlike other libraries, it does not rely on the HTML-to-PDF conversion which in many cases is not reliable. Instead, it implements its own layouting engine that is optimized to cover all paging-related requirements.
## Please help by giving a star
## Please show the value
Choosing a project dependency could be difficult. We need to ensure stability and maintainability of our projects. Surveys show that GitHub stars count play an important factor when assessing library quality.
@@ -47,7 +47,6 @@ Special thanks to all companies that decided to sponsor QuestPDF development. Th
| Company | Description |
|--------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------|
| <img src="Resources/jetbrains-logo.svg" width="100px"> | [JetBrains](https://www.jetbrains.com/) supports this project as part of the OSS Power-Ups program. Thank you!<br/>100$ / month |
| <img src="https://avatars.githubusercontent.com/u/2712328?v=4" width="100px"> | [Mark Gould](https://github.com/markgould) supports this project. Thank you!<br/>100$ / month |
[![Sponsor project](https://img.shields.io/badge/%E2%9D%A4%EF%B8%8F%20sponsor-QuestPDF-red)](https://github.com/sponsors/QuestPDF)
@@ -63,14 +62,14 @@ Install-Package QuestPDF
dotnet add package QuestPDF
// Package reference in .csproj file
<PackageReference Include="QuestPDF" Version="2022.9.0" />
<PackageReference Include="QuestPDF" Version="2022.6.0" />
```
[![Nuget version](https://img.shields.io/badge/package%20details-QuestPDF-blue?logo=nuget)](https://www.nuget.org/packages/QuestPDF/)
## Documentation
[![Getting started tutorial]( https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue)](https://www.questpdf.com/getting-started)
[![Getting started tutorial]( https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue)](https://www.questpdf.com/getting-started.html)
A short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
@@ -78,14 +77,14 @@ A short and easy to follow tutorial showing how to design an invoice document un
A detailed description of behavior of all available components and how to use them with C# Fluent API.
[![Patterns and Practices](https://img.shields.io/badge/%E2%9C%A8%20read-patterns%20and%20practices-blue)](https://www.questpdf.com/design-patterns)
[![Patterns and Practices](https://img.shields.io/badge/%E2%9C%A8%20read-patterns%20and%20practices-blue)](https://www.questpdf.com/design-patterns.html)
Everything that may help you designing great reports and create reusable code that is easy to maintain.
## QuestPDF Previewer
The QuestPDF Previewer is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task!
[![Learn more](https://img.shields.io/badge/%F0%9F%93%96%20Previewer-learn%20more-blue)](https://www.questpdf.com/document-previewer)
[![Learn more](https://img.shields.io/badge/%F0%9F%93%96%20Previewer-learn%20more-blue)](https://www.questpdf.com/document-previewer.html)
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/previewer/animation.gif?raw=true" width="100%">