Compare commits

...

9 Commits

Author SHA1 Message Date
Marcin Ziąbek 9f927ec402 Updated package version + added release notes 2021-04-03 00:53:09 +02:00
MarcinZiabek 9dc3e2b533 Updated tests for the Constrained element 2021-03-28 17:43:52 +02:00
Laurence Mingle 3369da42ed Additional page sizes (#6)
* Update PageSizes.cs

Added/Updated page sizes using details found in the Adobe PostScript Printer Description File Format Specification version 4.3.

* Update PageSizes.cs

Rounded values

* Update PageSizes.cs

Using float

* Update PageSizes.cs

Formatting

* Update PageSizes.cs

Fewer PageSizes

* Update PageSizes.cs

Addition of PageSize class, dimensions using point values from papersizes.io and extension methods for portrait & landscape orientation.
2021-03-27 11:08:00 +01:00
MarcinZiabek c5cb588857 Fixed: AspectRatio behaviour does not take into account child measurement. Added tests for AspectRatio, Image and DynamicImage elements. Small TestPlan framework improvements. 2021-03-23 18:05:39 +01:00
Marcin Ziąbek 08126b2fa4 simplified AspectRatio code, better exception handling for QuestPdf.Examples, fixed Placeholder image 2021-03-23 14:43:03 +01:00
Marcin Ziąbek 5e9ff7ec1e Added scaling options for AspectRatio and Image component. Added Debug component to the fluent API 2021-03-22 11:49:27 +01:00
Marcin Ziąbek 9c05c182b8 Fix: GeneratePdf closes provided stream 2021-03-21 17:43:55 +01:00
Marcin Ziąbek dee97b22e4 Improved readme 2021-03-10 19:55:58 +01:00
Marcin Ziąbek 1d1d969b5a 2021.3.1 2021-03-01 17:06:37 +01:00
39 changed files with 919 additions and 287 deletions
+32
View File
@@ -134,5 +134,37 @@ namespace QuestPDF.Examples
.Height(150);
});
}
//[ShowResult]
[ImageSize(300, 200)]
public void Debug(IContainer container)
{
container
.Padding(25)
.Debug()
.Padding(-5)
.Row(row =>
{
row.RelativeColumn().Padding(5).Extend().Placeholder();
row.RelativeColumn().Padding(5).Extend().Placeholder();
});
}
[ShowResult]
[ImageSize(300, 200)]
public void ElementEnd(IContainer container)
{
var text = "";
container
.Padding(10)
.Element(x =>
{
if (string.IsNullOrWhiteSpace(text))
x.Height(10).Width(50).Background("#DDD");
else
x.Text(text);
});
}
}
}
+11 -4
View File
@@ -62,10 +62,17 @@ namespace QuestPDF.Examples.Engine
methodInfo.Invoke(this, new object[] {container});
Func<int, string> fileNameSchema = i => $"{fileName.ToLower()}-${i}.png";
var document = new SimpleDocument(container, size);
document.GenerateImages(fileNameSchema);
try
{
var document = new SimpleDocument(container, size);
document.GenerateImages(fileNameSchema);
}
catch (Exception e)
{
throw new Exception($"Cannot perform test ${fileName}", e);
}
if (showResult)
Process.Start("explorer", fileNameSchema(0));
}
+1 -1
View File
@@ -27,7 +27,7 @@ namespace QuestPDF.Examples.Engine
public void Compose(IContainer container)
{
container.Element(Container.Child);
container.Background("#FFF").Element(Container.Child);
}
}
}
@@ -13,7 +13,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF.InvoiceSample\QuestPDF.InvoiceSample.csproj" />
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
+2 -2
View File
@@ -19,8 +19,8 @@ namespace QuestPDF.ReportSample
HeaderFields = HeaderFields(),
LogoData = Helpers.GetImage("logo.png"),
Sections = Enumerable.Range(0, 10).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(0, 10).Select(x => GetReportPhotos()).ToList()
Sections = Enumerable.Range(0, 8).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(0, 8).Select(x => GetReportPhotos()).ToList()
};
List<ReportHeaderField> HeaderFields()
@@ -38,10 +38,10 @@ namespace QuestPDF.ReportSample.Layouts
frame.Text(text.Text, Typography.Normal);
if (part is ReportSectionMap map)
frame.Element(container => MapElement(container, map));
frame.Element(x => MapElement(x, map));
if (part is ReportSectionPhotos photos)
frame.Element(container => PhotosElement(container, photos));
frame.Element(x => PhotosElement(x, photos));
});
}
});
@@ -56,7 +56,7 @@ namespace QuestPDF.ReportSample.Layouts
return;
}
container.Stack(stack =>
container.PageableStack(stack =>
{
stack.Spacing(5);
@@ -75,7 +75,7 @@ namespace QuestPDF.ReportSample.Layouts
var rowCount = (int) Math.Ceiling(model.Photos.Count / 3f);
container.Padding(-2).Stack(stack =>
container.Debug().Padding(-2).PageableStack(stack =>
{
foreach (var rowId in Enumerable.Range(0, rowCount))
{
+3 -4
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -18,8 +18,7 @@ namespace QuestPDF.ReportSample
var report = new StandardReport(reportModel);
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
using var stream = new FileStream(path, FileMode.Create);
report.GeneratePdf(stream);
report.GeneratePdf(path);
Process.Start("explorer.exe", path);
}
@@ -28,7 +27,7 @@ namespace QuestPDF.ReportSample
public void PerformanceBenchmark()
{
// test size
const int testSize = 1000;
const int testSize = 250;
const decimal performanceTarget = 5; // documents per second
// create report models
+2 -2
View File
@@ -5,8 +5,8 @@ namespace QuestPDF.ReportSample
{
public static class Typography
{
public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(20).Bold();
public static TextStyle Headline => TextStyle.Default.FontType("Helvetica").Color("#047AED").Size(14);
public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(22).SemiBold();
public static TextStyle Headline => TextStyle.Default.FontType("Helvetica").Color("#047AED").Size(14).SemiBold();
public static TextStyle Normal => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(10).LineHeight(1.25f).AlignLeft();
}
}
+13 -26
View File
@@ -14,20 +14,7 @@ namespace QuestPDF.UnitTests
[Test]
public void Draw_ShouldHandleNullChild() => new Alignment().DrawWithoutChild();
[Test]
public void Measure_ShouldReturnWrap_WhenChildReturnsWrap()
{
TestPlan
.For(x => new Alignment
{
Child = x.CreateChild("child")
})
.MeasureElement(new Size(1000, 500))
.ExpectChildMeasure("child", expectedInput: new Size(1000, 500), returns: new Wrap())
.CheckMeasureResult(new Wrap());
}
[Test]
public void Draw_HorizontalCenter_VerticalCenter()
{
@@ -37,12 +24,12 @@ namespace QuestPDF.UnitTests
Horizontal = HorizontalAlignment.Center,
Vertical = VerticalAlignment.Middle,
Child = x.CreateChild("child")
Child = x.CreateChild()
})
.DrawElement(new Size(1000, 500))
.ExpectChildMeasure("child", expectedInput: new Size(1000, 500), returns: new PartialRender(new Size(400, 200)))
.ExpectChildMeasure(expectedInput: new Size(1000, 500), returns: new PartialRender(new Size(400, 200)))
.ExpectCanvasTranslate(new Position(300, 150))
.ExpectChildDraw("child", new Size(400, 200))
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(new Position(-300, -150))
.CheckDrawResult();
}
@@ -56,12 +43,12 @@ namespace QuestPDF.UnitTests
Horizontal = HorizontalAlignment.Left,
Vertical = VerticalAlignment.Middle,
Child = x.CreateChild("child")
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("child", expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(0, 125))
.ExpectChildDraw("child", new Size(100, 50))
.ExpectChildDraw(new Size(100, 50))
.ExpectCanvasTranslate(new Position(0, -125))
.CheckDrawResult();
}
@@ -75,12 +62,12 @@ namespace QuestPDF.UnitTests
Horizontal = HorizontalAlignment.Center,
Vertical = VerticalAlignment.Bottom,
Child = x.CreateChild("child")
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("child", expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(150, 250))
.ExpectChildDraw("child", new Size(100, 50))
.ExpectChildDraw(new Size(100, 50))
.ExpectCanvasTranslate(new Position(-150, -250))
.CheckDrawResult();
}
@@ -94,12 +81,12 @@ namespace QuestPDF.UnitTests
Horizontal = HorizontalAlignment.Right,
Vertical = VerticalAlignment.Top,
Child = x.CreateChild("child")
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("child", expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(300, 0))
.ExpectChildDraw("child", new Size(100, 50))
.ExpectChildDraw(new Size(100, 50))
.ExpectCanvasTranslate(new Position(-300, 0))
.CheckDrawResult();
}
+163
View File
@@ -15,5 +15,168 @@ namespace QuestPDF.UnitTests
[Test]
public void Draw_ShouldHandleNullChild() => new AspectRatio().DrawWithoutChild();
[Test]
public void Measure_FitWidth_EnoughSpace_FullRender()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.MeasureElement(new Size(400, 201))
.ExpectChildMeasure(new Size(400, 200), new FullRender(100, 50))
.CheckMeasureResult(new FullRender(400, 200));
}
[Test]
public void Measure_FitWidth_EnoughSpace_PartialRender()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.MeasureElement(new Size(400, 201))
.ExpectChildMeasure(new Size(400, 200), new PartialRender(100, 50))
.CheckMeasureResult(new PartialRender(400, 200));
}
[Test]
public void Measure_FitWidth_EnoughSpace_Wrap()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.MeasureElement(new Size(400, 201))
.ExpectChildMeasure(new Size(400, 200), new Wrap())
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_FitWidth_EnoughSpace()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitWidth,
Ratio = 2f
})
.MeasureElement(new Size(400, 201))
.ExpectChildMeasure(new Size(400, 200), new FullRender(100, 50))
.CheckMeasureResult(new FullRender(400, 200));
}
[Test]
public void Measure_FitWidth_NotEnoughSpace()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitWidth,
Ratio = 2f
})
.MeasureElement(new Size(400, 199))
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_FitHeight_EnoughSpace()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitHeight,
Ratio = 2f
})
.MeasureElement(new Size(401, 200))
.ExpectChildMeasure(new Size(400, 200), new FullRender(100, 50))
.CheckMeasureResult(new FullRender(400, 200));
}
[Test]
public void Measure_FitHeight_NotEnoughSpace()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitHeight,
Ratio = 2f
})
.MeasureElement(new Size(399, 200))
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_FitArea_ToWidth()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(new Size(400, 200), new FullRender(100, 50))
.CheckMeasureResult(new FullRender(400, 200));
}
[Test]
public void Measure_FitArea_ToHeight()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.MeasureElement(new Size(500, 200))
.ExpectChildMeasure(new Size(400, 200), new FullRender(100, 50))
.CheckMeasureResult(new FullRender(400, 200));
}
[Test]
public void DrawChild_PerWidth()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.DrawElement(new Size(500, 200))
.ExpectChildDraw(new Size(400, 200))
.CheckDrawResult();
}
[Test]
public void DrawChild_PerHeight()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f
})
.DrawElement(new Size(400, 300))
.ExpectChildDraw(new Size(400, 200))
.CheckDrawResult();
}
}
}
+2 -2
View File
@@ -31,11 +31,11 @@ namespace QuestPDF.UnitTests
.For(x => new Background
{
Color = "#F00",
Child = x.CreateChild("a")
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectCanvasDrawRectangle(new Position(0, 0), new Size(400, 300), "#F00")
.ExpectChildDraw("a", new Size(400, 300))
.ExpectChildDraw(new Size(400, 300))
.CheckDrawResult();
}
}
+5 -5
View File
@@ -23,10 +23,10 @@ namespace QuestPDF.UnitTests
Bottom = 30,
Left = 40,
Child = x.CreateChild("child")
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("child", expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new FullRender(new Size(100, 50)))
.CheckMeasureResult( new FullRender(new Size(100, 50)));
}
@@ -43,15 +43,15 @@ namespace QuestPDF.UnitTests
Color = "#FF0000",
Child = x.CreateChild("child")
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildDraw("child", new Size(400, 300))
.ExpectChildDraw(new Size(400, 300))
.ExpectCanvasDrawRectangle(new Position(-20, -5), new Size(430, 10), "#FF0000") // top
.ExpectCanvasDrawRectangle(new Position(-20, -5), new Size(40, 320), "#FF0000") // left
.ExpectCanvasDrawRectangle(new Position(-20, 285), new Size(430, 30), "#FF0000") // bottom
.ExpectCanvasDrawRectangle(new Position(390, -5), new Size(20, 320), "#FF0000") // right
.ExpectChildDraw("child", new Size(400, 300))
.ExpectChildDraw(new Size(400, 300))
.CheckDrawResult();
}
}
+185 -14
View File
@@ -15,14 +15,29 @@ namespace QuestPDF.UnitTests
[Test]
public void Draw_ShouldHandleNullChild() => new Constrained().DrawWithoutChild();
#region Min Height
[Test]
public void Measure_MinHeight_ExpectWrap()
public void Measure_MinHeight_Empty()
{
TestPlan
.For(x => new Constrained
{
MinHeight = 100
})
.MeasureElement(new Size(400, 150))
.CheckMeasureResult(new FullRender(0, 100));
}
[Test]
public void Measure_MinHeight_NotEnoughHeight()
{
TestPlan
.For(x => new Constrained
{
MinHeight = 100,
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 50))
.CheckMeasureResult(new Wrap());
}
@@ -34,10 +49,10 @@ namespace QuestPDF.UnitTests
.For(x => new Constrained
{
MinHeight = 100,
Child = x.CreateChild("a")
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure("a", new Size(400, 200), new FullRender(400, 50))
.ExpectChildMeasure(new Size(400, 200), new FullRender(400, 50))
.CheckMeasureResult(new FullRender(400, 100));
}
@@ -48,13 +63,17 @@ namespace QuestPDF.UnitTests
.For(x => new Constrained
{
MinHeight = 100,
Child = x.CreateChild("a")
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure("a", new Size(400, 200), new FullRender(400, 150))
.ExpectChildMeasure(new Size(400, 200), new FullRender(400, 150))
.CheckMeasureResult(new FullRender(400, 150));
}
#endregion
#region Max Height
[Test]
public void Measure_MaxHeight_Empty()
{
@@ -68,31 +87,183 @@ namespace QuestPDF.UnitTests
}
[Test]
public void Measure_MaxHeight_PartialRender()
public void Measure_MaxHeight_PassHeight()
{
TestPlan
.For(x => new Constrained
{
MaxHeight = 100,
Child = x.CreateChild("a")
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure("a", new Size(400, 100), new PartialRender(400, 75))
.CheckMeasureResult(new PartialRender(400, 75));
.ExpectChildMeasure(new Size(400, 100), new FullRender(400, 75))
.CheckMeasureResult(new FullRender(400, 75));
}
#endregion
#region Min Width
[Test]
public void Measure_MinWidth_Empty()
{
TestPlan
.For(x => new Constrained
{
MinWidth = 100
})
.MeasureElement(new Size(150, 400))
.CheckMeasureResult(new FullRender(100, 0));
}
[Test]
public void Measure_MaxHeight_ExpectWrap()
public void Measure_MinWidth_NotEnoughWidth()
{
TestPlan
.For(x => new Constrained
{
MaxHeight = 100,
Child = x.CreateChild("a")
MinWidth = 100,
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure("a", new Size(400, 100), new Wrap())
.MeasureElement(new Size(50, 400))
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_MinWidth_ExtendWidth()
{
TestPlan
.For(x => new Constrained
{
MinWidth = 100,
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure(new Size(400, 200), new FullRender(50, 200))
.CheckMeasureResult(new FullRender(100, 200));
}
[Test]
public void Measure_MinWidth_PassWidth()
{
TestPlan
.For(x => new Constrained
{
MinWidth = 100,
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure(new Size(400, 200), new FullRender(150, 200))
.CheckMeasureResult(new FullRender(150, 200));
}
#endregion
#region Max Width
[Test]
public void Measure_MaxWidth_Empty()
{
TestPlan
.For(x => new Constrained
{
MaxWidth = 100
})
.MeasureElement(new Size(400, 150))
.CheckMeasureResult(new FullRender(0, 0));
}
[Test]
public void Measure_MaxWidth_PassWidth()
{
TestPlan
.For(x => new Constrained
{
MaxWidth = 100,
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure(new Size(100, 200), new FullRender(75, 200))
.CheckMeasureResult(new FullRender(75, 200));
}
#endregion
#region Space Plans
[Test]
public void Measure_AcceptsFullRender()
{
TestPlan
.For(x => new Constrained
{
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure(new Size(400, 200), new FullRender(300, 200))
.CheckMeasureResult(new FullRender(300, 200));
}
[Test]
public void Measure_AcceptsPartialRender()
{
TestPlan
.For(x => new Constrained
{
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure(new Size(400, 200), new PartialRender(300, 200))
.CheckMeasureResult(new PartialRender(300, 200));
}
[Test]
public void Measure_AcceptsWrap()
{
TestPlan
.For(x => new Constrained
{
Child = x.CreateChild()
})
.MeasureElement(new Size(400, 200))
.ExpectChildMeasure(new Size(400, 200), new Wrap())
.CheckMeasureResult(new Wrap());
}
#endregion
#region Drawing
[Test]
public void Draw_Normal()
{
TestPlan
.For(x => new Constrained
{
MaxWidth = 500,
MaxHeight = 300,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 200))
.ExpectChildDraw(new Size(400, 200))
.CheckDrawResult();
}
[Test]
public void Draw_Shrink()
{
TestPlan
.For(x => new Constrained
{
MaxWidth = 300,
MaxHeight = 150,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 200))
.ExpectChildDraw(new Size(300, 150))
.CheckDrawResult();
}
#endregion
}
}
+77 -1
View File
@@ -1,10 +1,86 @@
using NUnit.Framework;
using System;
using FluentAssertions;
using NUnit.Framework;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
using SkiaSharp;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class DynamicImageTests
{
[Test]
public void Measure_TakesAvailableSpaceRegardlessOfSize()
{
TestPlan
.For(x => new DynamicImage
{
Source = GenerateImage
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(new FullRender(300, 200));
}
[Test]
public void Draw_HandlesNull()
{
TestPlan
.For(x => new DynamicImage
{
Source = size => null
})
.DrawElement(new Size(300, 200))
.CheckDrawResult();
}
[Test]
public void Draw_PreservesSize()
{
TestPlan
.For(x => new DynamicImage
{
Source = GenerateImage
})
.DrawElement(new Size(300, 200))
.ExpectCanvasDrawImage(Position.Zero, new Size(300, 200))
.CheckDrawResult();
}
[Test]
public void Draw_PassesCorrectSizeToSource()
{
Size passedSize = null;
TestPlan
.For(x => new DynamicImage
{
Source = size =>
{
passedSize = size;
return GenerateImage(size);
}
})
.DrawElement(new Size(400, 300))
.ExpectCanvasDrawImage(Position.Zero, new Size(400, 300))
.CheckDrawResult();
passedSize.Should().Be(new Size(400, 300));
}
byte[] GenerateImage(Size size)
{
var image = GenerateImage((int) size.Width, (int) size.Height);
return image.Encode(SKEncodedImageFormat.Png, 100).ToArray();
}
SKImage GenerateImage(int width, int height)
{
var imageInfo = new SKImageInfo(width, height);
using var surface = SKSurface.Create(imageInfo);
return surface.Snapshot();
}
}
}
-10
View File
@@ -1,10 +0,0 @@
using NUnit.Framework;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class EmptyTests
{
}
}
+54 -9
View File
@@ -1,7 +1,12 @@
using Moq;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
using SkiaSharp;
namespace QuestPDF.UnitTests
{
@@ -9,17 +14,57 @@ namespace QuestPDF.UnitTests
public class ImageTests
{
[Test]
public void Draw_ShouldHandleNullChild()
public void Measure_ShouldHandleNullChild() => new AspectRatio().MeasureWithoutChild();
[Test]
public void Draw_ShouldHandleNullChild() => new AspectRatio().DrawWithoutChild();
[Test]
public void Measure_TakesAvailableSpaceRegardlessOfSize()
{
Assert.DoesNotThrow(() =>
{
var image = new Image()
TestPlan
.For(x => new Image
{
Data = null
};
image.Draw(It.IsAny<ICanvas>(), Size.Zero);
});
InternalImage = GenerateImage(400, 300)
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(new FullRender(300, 200));
}
[Test]
public void Draw_TakesAvailableSpaceRegardlessOfSize()
{
TestPlan
.For(x => new Image
{
InternalImage = GenerateImage(400, 300)
})
.DrawElement(new Size(300, 200))
.ExpectCanvasDrawImage(new Position(0, 0), new Size(300, 200))
.CheckDrawResult();
}
[Test]
public void Fluent_RecognizesImageProportions()
{
var image = GenerateImage(600, 200).Encode(SKEncodedImageFormat.Png, 100).ToArray();
TestPlan
.For(x =>
{
var container = new Container();
container.Image(image);
return container;
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(new FullRender(300, 100));;
}
SKImage GenerateImage(int width, int height)
{
var imageInfo = new SKImageInfo(width, height);
using var surface = SKSurface.Create(imageInfo);
return surface.Snapshot();
}
}
}
+54 -39
View File
@@ -17,62 +17,77 @@ namespace QuestPDF.UnitTests
[Test]
public void Draw_ShouldHandleNullChild() => new Padding().DrawWithoutChild();
private Padding GetPadding(Element child)
private Padding GetPadding(TestPlan plan)
{
return new Padding()
{
Top = 5,
Right = 10,
Bottom = 15,
Left = 20,
Top = 10,
Right = 20,
Bottom = 30,
Left = 40,
Child = child
Child = plan.CreateChild()
};
}
[Test]
public void Measure_WhenChildReturnsWrap_ReturnsWrap()
public void Measure_General_EnoughSpace()
{
var child = new Mock<Element>();
child
.Setup(x => x.Measure(It.IsAny<Size>()))
.Returns(() => new Wrap());
GetPadding(child.Object)
.Measure(Size.Zero)
.Should()
.BeOfType<Wrap>();
TestPlan
.For(GetPadding)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(new Size(340, 260), new FullRender(140, 60))
.CheckMeasureResult(new FullRender(200, 100));
}
[Test]
public void Measure_NotEnoughWidth()
{
TestPlan
.For(GetPadding)
.MeasureElement(new Size(50, 300))
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_WhenChildReturnsFullRender_ReturnsFullRender()
public void Measure_NotEnoughHeight()
{
var child = new Mock<Element>();
child
.Setup(x => x.Measure(It.IsAny<Size>()))
.Returns(() => new FullRender(Size.Zero));
GetPadding(child.Object)
.Measure(Size.Zero)
.Should()
.BeOfType<FullRender>();
TestPlan
.For(GetPadding)
.MeasureElement(new Size(20, 300))
.CheckMeasureResult(new Wrap());
}
[Test]
public void Measure_WhenChildReturnsPartialRender_ReturnsPartialRender()
public void Measure_AcceptsPartialRender()
{
var child = new Mock<Element>();
child
.Setup(x => x.Measure(It.IsAny<Size>()))
.Returns(() => new PartialRender(Size.Zero));
GetPadding(child.Object)
.Measure(Size.Zero)
.Should()
.BeOfType<PartialRender>();
TestPlan
.For(GetPadding)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(new Size(340, 260), new PartialRender(40, 160))
.CheckMeasureResult(new PartialRender(100, 200));
}
[Test]
public void Measure_AcceptsWrap()
{
TestPlan
.For(GetPadding)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure(new Size(340, 260), new Wrap())
.CheckMeasureResult(new Wrap());
}
[Test]
public void Draw_General()
{
TestPlan
.For(GetPadding)
.DrawElement(new Size(400, 300))
.ExpectCanvasTranslate(new Position(40, 10))
.ExpectChildDraw(new Size(340, 260))
.ExpectCanvasTranslate(new Position(-40, -10))
.CheckDrawResult();
}
}
}
-10
View File
@@ -1,10 +0,0 @@
using NUnit.Framework;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class PageableColumnTests
{
}
}
-10
View File
@@ -1,10 +0,0 @@
using NUnit.Framework;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class PlaceholderTests
{
}
}
+6 -6
View File
@@ -42,7 +42,7 @@ namespace QuestPDF.UnitTests
TestPlan
.For(x => new ShowOnce()
{
Child = x.CreateChild("child")
Child = x.CreateChild()
})
// Measure the element and return result
@@ -52,21 +52,21 @@ namespace QuestPDF.UnitTests
// Draw element partially
.DrawElement(new Size(200, 200))
.ExpectChildMeasure("child", new Size(200, 200), new PartialRender(new Size(200, 200)))
.ExpectChildDraw("child", new Size(200, 200))
.ExpectChildMeasure(new Size(200, 200), new PartialRender(new Size(200, 200)))
.ExpectChildDraw(new Size(200, 200))
.CheckDrawResult()
// Element was not fully drawn
// It should be measured again for rendering on next page
.MeasureElement(new Size(800, 200))
.ExpectChildMeasure("child", new Size(800, 200), new FullRender(new Size(400, 200)))
.ExpectChildMeasure(new Size(800, 200), new FullRender(new Size(400, 200)))
.CheckMeasureResult(new FullRender(new Size(400, 200)))
// Draw element on next page
// Element was fully drawn at this point
.DrawElement(new Size(400, 200))
.ExpectChildMeasure("child", new Size(400, 200), new FullRender(new Size(400, 200)))
.ExpectChildDraw("child", new Size(400, 200))
.ExpectChildMeasure(new Size(400, 200), new FullRender(new Size(400, 200)))
.ExpectChildDraw(new Size(400, 200))
.CheckDrawResult()
// In the next attempt of measuring element, it should behave like empty parent.
+14
View File
@@ -10,6 +10,8 @@ namespace QuestPDF.UnitTests.TestEngine
{
internal class TestPlan
{
private const string DefaultChildName = "child";
private Element Element { get; set; }
private ICanvas Canvas { get; }
@@ -100,6 +102,8 @@ namespace QuestPDF.UnitTests.TestEngine
}
};
}
public Element CreateChild() => CreateChild(DefaultChildName);
public Element CreateChild(string id)
{
@@ -153,11 +157,21 @@ namespace QuestPDF.UnitTests.TestEngine
return this;
}
public TestPlan ExpectChildMeasure(Size expectedInput, ISpacePlan returns)
{
return ExpectChildMeasure(DefaultChildName, expectedInput, returns);
}
public TestPlan ExpectChildMeasure(string child, Size expectedInput, ISpacePlan returns)
{
return AddOperation(new ChildMeasureOperationBase(child, expectedInput, returns));
}
public TestPlan ExpectChildDraw(Size expectedInput)
{
return ExpectChildDraw(DefaultChildName, expectedInput);
}
public TestPlan ExpectChildDraw(string child, Size expectedInput)
{
return AddOperation(new ChildDrawOperationBase(child, expectedInput));
-5
View File
@@ -35,8 +35,6 @@ namespace QuestPDF.Drawing
catch (Exception exception)
{
pdf.Close();
stream.Close();
throw new DocumentDrawingException("An exception occured during document drawing.", exception);
}
@@ -45,8 +43,6 @@ namespace QuestPDF.Drawing
if (totalPages >= metadata.DocumentLayoutExceptionThreshold)
{
pdf.Close();
stream.Close();
throw new DocumentLayoutException("Composed layout generates infinite document.");
}
@@ -57,7 +53,6 @@ namespace QuestPDF.Drawing
}
pdf.Close();
stream.Dispose();
}
internal static IEnumerable<byte[]> GenerateImages(IDocument document)
+34 -11
View File
@@ -1,3 +1,4 @@
using System;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Infrastructure;
@@ -6,18 +7,33 @@ namespace QuestPDF.Elements
internal class AspectRatio : ContainerElement
{
public float Ratio { get; set; } = 1;
public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth;
internal override ISpacePlan Measure(Size availableSpace)
{
if(Child == null)
return new FullRender(Size.Zero);
var size = GetSize(availableSpace);
var targetSize = GetTargetSize(availableSpace);
if (size.Height > availableSpace.Height + Size.Epsilon)
if (targetSize.Height > availableSpace.Height + Size.Epsilon)
return new Wrap();
return new FullRender(size);
if (targetSize.Width > availableSpace.Width + Size.Epsilon)
return new Wrap();
var childSize = Child.Measure(targetSize);
if (childSize is Wrap)
return new Wrap();
if (childSize is PartialRender)
return new PartialRender(targetSize);
if (childSize is FullRender)
return new FullRender(targetSize);
throw new NotSupportedException();
}
internal override void Draw(ICanvas canvas, Size availableSpace)
@@ -25,17 +41,24 @@ namespace QuestPDF.Elements
if (Child == null)
return;
var size = GetSize(availableSpace);
if (size.Height > availableSpace.Height)
return;
Child.Draw(canvas, size);
var size = GetTargetSize(availableSpace);
Child?.Draw(canvas, size);
}
private Size GetSize(Size availableSpace)
private Size GetTargetSize(Size availableSpace)
{
return new Size(availableSpace.Width, (int)(availableSpace.Width / Ratio));
var spaceRatio = availableSpace.Width / availableSpace.Height;
var fitHeight = new Size(availableSpace.Height * Ratio, availableSpace.Height) ;
var fitWidth = new Size(availableSpace.Width, availableSpace.Width / Ratio);
return Option switch
{
AspectRatioOption.FitWidth => fitWidth,
AspectRatioOption.FitHeight => fitHeight,
AspectRatioOption.FitArea => Ratio < spaceRatio ? fitHeight : fitWidth,
_ => throw new ArgumentOutOfRangeException()
};
}
}
}
+3 -3
View File
@@ -31,15 +31,15 @@ namespace QuestPDF.Elements
if (measurement is Wrap)
return new Wrap();
var actualSize = new Size(
var targetSize = new Size(
MathHelpers.Max(MinWidth, size.Width),
MathHelpers.Max(MinHeight, size.Height));
if (size is FullRender)
return new FullRender(actualSize);
return new FullRender(targetSize);
if (size is PartialRender)
return new PartialRender(actualSize);
return new PartialRender(targetSize);
throw new NotSupportedException();
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace QuestPDF.Elements
{
internal Container()
{
Child = new Empty();
}
}
}
+26 -14
View File
@@ -1,25 +1,37 @@
using QuestPDF.Infrastructure;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class Debug : ContainerElement
{
private static readonly TextStyle TextStyle = TextStyle.Default.Color("#FF0000").FontType("Consolas").Size(10);
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var textStyle = new TextStyle
{
Color = "#FF0000",
FontType = "Consolas",
Size = 10
};
Child?.Draw(canvas, availableSpace);
canvas.DrawRectangle(Position.Zero, availableSpace, "#FF0000");
canvas.DrawRectangle(Position.Zero, availableSpace, "#FF00FF");
canvas.DrawText($"W: {availableSpace.Width}", new Position(5, 12), textStyle);
canvas.DrawText($"H: {availableSpace.Height}", new Position(5, 22), textStyle);
DrawBoundingBox();
DrawDimensions();
void DrawBoundingBox()
{
// TODO: when layer element is done, move this code into fluent API
var container = new Container();
container
.Border(1)
.BorderColor("#FF0000")
.Background("#33FF0000");
container.Draw(canvas, availableSpace);
}
void DrawDimensions()
{
canvas.DrawText($"W: {availableSpace.Width:F1}", new Position(5, 12), TextStyle);
canvas.DrawText($"H: {availableSpace.Height:F1}", new Position(5, 22), TextStyle);
}
}
}
}
+8 -5
View File
@@ -1,6 +1,7 @@
using System;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Elements
{
@@ -10,17 +11,19 @@ namespace QuestPDF.Elements
internal override ISpacePlan Measure(Size availableSpace)
{
if (availableSpace.Width < 0 || availableSpace.Height < 0)
return new Wrap();
return new FullRender(availableSpace.Width, availableSpace.Height);
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var imageElement = new Image()
var imageData = Source?.Invoke(availableSpace);
if (imageData == null)
return;
var imageElement = new Image
{
Data = Source?.Invoke(availableSpace)
InternalImage = SKImage.FromEncodedData(imageData)
};
imageElement.Draw(canvas, availableSpace);
+3 -32
View File
@@ -6,53 +6,24 @@ namespace QuestPDF.Elements
{
internal class Image : Element
{
public byte[]? Data { get; set; }
private SKImage? InternalImage { get; set; }
public SKImage? InternalImage { get; set; }
~Image()
{
InternalImage?.Dispose();
}
private void Initialize()
{
if (Data != null)
InternalImage ??= SKImage.FromEncodedData(Data);
}
internal override ISpacePlan Measure(Size availableSpace)
{
Initialize();
if (InternalImage == null)
return new FullRender(Size.Zero);
if (availableSpace.Width < 0 || availableSpace.Height < 0)
return new Wrap();
var size = GetTargetSize(availableSpace);
return new FullRender(size);
return new FullRender(availableSpace);
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
Initialize();
if (InternalImage == null)
return;
var size = GetTargetSize(availableSpace);
canvas.DrawImage(InternalImage, Position.Zero, size);
}
private Size GetTargetSize(Size availableSpace)
{
var imageRatio = InternalImage.Width / (float)InternalImage.Height;
var spaceRatio = availableSpace.Width / (float) availableSpace.Height;
return imageRatio < spaceRatio
? new Size((int)(availableSpace.Height * imageRatio), availableSpace.Height)
: new Size(availableSpace.Width, (int)(availableSpace.Width / imageRatio));
canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
}
}
}
+4
View File
@@ -17,6 +17,10 @@ namespace QuestPDF.Elements
return new FullRender(Size.Zero);
var internalSpace = InternalSpace(availableSpace);
if (internalSpace.Width <= 0 || internalSpace.Height <= 0)
return new Wrap();
var measure = Child.Measure(internalSpace) as Size;
if (measure == null)
+1 -1
View File
@@ -19,7 +19,7 @@ namespace QuestPDF.Elements
.AlignMiddle()
.AlignCenter()
.MaxHeight(32)
.Image(ImageData);
.Image(ImageData, ImageScaling.FitArea);
}
}
}
+1 -4
View File
@@ -8,10 +8,7 @@ namespace QuestPDF.Elements
internal override void Draw(ICanvas canvas, Size availableSpace)
{
if (Child == null)
return;
Child.Draw(canvas, availableSpace);
Child?.Draw(canvas, availableSpace);
}
}
+2 -2
View File
@@ -6,8 +6,8 @@ namespace QuestPDF.Elements
{
internal class Section : Element
{
public Element? Header { get; set; }
public Element? Content { get; set; }
public ContainerElement? Header { get; set; }
public ContainerElement? Content { get; set; }
internal override ISpacePlan Measure(Size availableSpace)
{
+19 -22
View File
@@ -4,6 +4,7 @@ using QuestPDF.Drawing;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Fluent
{
@@ -33,29 +34,21 @@ namespace QuestPDF.Fluent
return child;
}
public static void Element(this IContainer element, Action<IContainer> handler)
public static void Element<TParent>(this TParent parent, Action<IContainer> handler) where TParent : IContainer
{
var container = new Container();
element.Element(container);
handler?.Invoke(container);
handler(parent.Container());
}
public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
{
return handler(parent.Container()).Container();
}
public static IContainer Debug(this IContainer parent)
{
return parent.Element(new Debug());
}
public static void Image(this IContainer element, byte[] data)
{
element.Element(new Image
{
Data = data
});
}
public static void DynamicImage(this IContainer element, Func<Size, byte[]> imageSource)
{
element.Element(new DynamicImage
{
Source = imageSource
});
}
public static void PageNumber(this IContainer element, string textFormat = "{number}", TextStyle? style = null)
{
element.Element(new PageNumber
@@ -65,11 +58,12 @@ namespace QuestPDF.Fluent
});
}
public static IContainer AspectRatio(this IContainer element, float ratio)
public static IContainer AspectRatio(this IContainer element, float ratio, AspectRatioOption option = AspectRatioOption.FitWidth)
{
return element.Element(new AspectRatio
{
Ratio = ratio
Ratio = ratio,
Option = option
});
}
@@ -98,7 +92,10 @@ namespace QuestPDF.Fluent
style ??= TextStyle.Default;
if (element is Alignment alignment)
{
style = style.Clone();
style.Alignment = alignment.Horizontal;
}
element.Element(new Text()
{
+48
View File
@@ -0,0 +1,48 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Fluent
{
public static class ImageExtensions
{
public static void Image(this IContainer parent, byte[] data, ImageScaling scaling = ImageScaling.FitWidth)
{
if (data == null)
return;
var image = SKImage.FromEncodedData(data);
var aspectRatio = image.Width / (float)image.Height;
var imageElement = new Image
{
InternalImage = image
};
if (scaling != ImageScaling.Resize)
parent = parent.AspectRatio(aspectRatio, Map(scaling));
parent.Element(imageElement);
static AspectRatioOption Map(ImageScaling scaling)
{
return scaling switch
{
ImageScaling.FitWidth => AspectRatioOption.FitWidth,
ImageScaling.FitHeight => AspectRatioOption.FitHeight,
ImageScaling.FitArea => AspectRatioOption.FitArea,
_ => throw new ArgumentOutOfRangeException()
};
}
}
public static void DynamicImage(this IContainer element, Func<Size, byte[]> imageSource)
{
element.Element(new DynamicImage
{
Source = imageSource
});
}
}
}
+61 -23
View File
@@ -1,31 +1,69 @@
using QuestPDF.Infrastructure;
using System;
using QuestPDF.Infrastructure;
namespace QuestPDF.Helpers
{
public class PageSize : Size
{
public PageSize(float width, float height) : base(width, height)
{
}
}
public static class PageSizes
{
public const int PointsPerInch = 72;
public static Size A0 => new Size(2384, 3370);
public static Size A1 => new Size(1684, 2384);
public static Size A2 => new Size(1190, 1684);
public static Size A3 => new Size(842, 1190);
public static Size A4 => new Size(595, 842);
public static Size A5 => new Size(420, 595);
public static Size A6 => new Size(298, 420);
public static Size A7 => new Size(210, 298);
public static Size A8 => new Size(148, 210);
public static Size B0 => new Size(2835, 4008);
public static Size B1 => new Size(2004, 2835);
public static Size B2 => new Size(1417, 2004);
public static Size B3 => new Size(1001, 1417);
public static Size B4 => new Size(709, 1001);
public static Size B5 => new Size(499, 709);
public static Size B6 => new Size(354, 499);
public static Size B7 => new Size(249, 354);
public static Size B8 => new Size(176, 249);
public static Size B9 => new Size(125, 176);
public static Size B10 => new Size(88, 125);
public static Size A0 => new PageSize(2384.2f, 3370.8f);
public static Size A1 => new PageSize(1684, 2384.2f);
public static Size A2 => new PageSize(1190.7f, 1684);
public static Size A3 => new PageSize(842, 1190.7f);
public static Size A4 => new PageSize(595.4f, 842);
public static Size A5 => new PageSize(419.6f, 595.4f);
public static Size A6 => new PageSize(297.7f, 419.6f);
public static Size A7 => new PageSize(209.8f, 297.7f);
public static Size A8 => new PageSize(147.4f, 209.8f);
public static Size A9 => new PageSize(104.9f, 147.4f);
public static Size A10 => new PageSize(73.7f, 104.9f);
public static Size B0 => new PageSize(2835, 4008.7f);
public static Size B1 => new PageSize(2004.3f, 2835);
public static Size B2 => new PageSize(1417.5f, 2004.3f);
public static Size B3 => new PageSize(1000.8f, 1417.5f);
public static Size B4 => new PageSize(708.8f, 1000.8f);
public static Size B5 => new PageSize(499, 708.8f);
public static Size B6 => new PageSize(354.4f, 499);
public static Size B7 => new PageSize(249.5f, 354.4f);
public static Size B8 => new PageSize(175.8f, 249.5f);
public static Size B9 => new PageSize(124.7f, 175.8f);
public static Size B10 => new PageSize(87.9f, 124.7f);
public static Size Env10 => new PageSize(683.2f, 294.8f);
public static Size EnvC4 => new PageSize(649.2f, 918.5f);
public static Size EnvDL => new PageSize(311.9f, 623.7f);
public static Size Executive => new PageSize(522, 756);
public static Size Legal => new PageSize(612.4f, 1009.3f);
public static Size Letter => new PageSize(612.4f, 791);
public static Size ARCHA => new PageSize(649.2f, 864.7f);
public static Size ARCHB => new PageSize(864.7f, 1295.6f);
public static Size ARCHC => new PageSize(1295.6f, 1729.3f);
public static Size ARCHD => new PageSize(1729.3f, 2591.2f);
public static Size ARCHE => new PageSize(2591.2f, 3455.9f);
public static Size ARCH_E1 => new PageSize(2160.3f, 3024.9f);
public static Size ARCH_E2 => new PageSize(1871.1f, 2735.8f);
public static Size ARCH_E3 => new PageSize(1944.8f, 2809.5f);
public static PageSize Portrait(this Size size)
{
return new PageSize(Math.Min(size.Width, size.Height), Math.Max(size.Width, size.Height));
}
public static PageSize Landscape(this Size size)
{
return new PageSize(Math.Max(size.Width, size.Height), Math.Min(size.Width, size.Height));
}
}
}
@@ -0,0 +1,9 @@
namespace QuestPDF.Infrastructure
{
public enum AspectRatioOption
{
FitWidth,
FitHeight,
FitArea
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace QuestPDF.Infrastructure
{
public enum ImageScaling
{
FitWidth,
FitHeight,
FitArea,
Resize
}
}
+17 -11
View File
@@ -4,9 +4,16 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2021.3.0</Version>
<Version>2021.4.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>Generating images functionality, ShowIf element, fixed mutating TextStyle</PackageReleaseNotes>
<PackageReleaseNotes>
1. Added new element: `Debug` - this element can be used for inspecting its children and space taken by them,
2. Added new element: `Element` can be used for injecting dynamic elements (usually conditionally) without breaking the fluent API chain. This is practically a syntactic sugar to simplify your code.
3. The `AspectRatio` element allows now to specify scaling rule: fitting width, height or available space.
4. The `Image` element supports now scaling rules: fitting width, height or available space, as well as scaling unproportionally.
5. Added more, commonly used page sizes (thank you for your work [lmingle](https://github.com/lmingle)).
6. Bug fix: the `GeneratePdf(Stream stream)` method does not close the output stream (thank you for your suggestion [tthiery](https://github.com/tthiery)).
</PackageReleaseNotes>
<LangVersion>8</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIcon>Logo.png</PackageIcon>
@@ -22,17 +29,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp" Version="2.80.2" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\ImagePlaceholder.png" />
<None Remove="ImagePlaceholder.png" />
<None Include="Resources\Logo.png">
<Pack>true</Pack>
<Visible>false</Visible>
<PackagePath>\</PackagePath>
</None>
<EmbeddedResource Include="Resources\ImagePlaceholder.png" />
<None Remove="ImagePlaceholder.png" />
<None Include="Resources\Logo.png">
<Pack>true</Pack>
<Visible>false</Visible>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
</Project>
+44 -3
View File
@@ -26,12 +26,53 @@ The library is available as a nuget package. You can install it as any other nug
</a>
## Write less, achieve more
## Documentation
1. [Release notes and roadmap](https://www.questpdf.com/documentation/releases.html) - everything that is planned for future library iterations, description of new features and information about potential breaking changes.
2. [Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html) - a short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
3. [API Reference](https://www.questpdf.com/documentation/api-reference.html) - a detailed description of behavior of all available components and how to use them with C# Fluent API.
4. [Patterns and practices](https://www.questpdf.com/documentation/patterns-and-practices.html#document-metadata) - everything that may help you designing great reports and reusable code that is easy to maintain.
## Example invoice
Do you believe that creating a complete invoice document can take less than 200 lines of code? We have prepared for you a step-by-step instruction that shows every detail of this implementation and describes the best patterns and practices.
For tutorial, documentation and API reference, please visit [the QuestPDF documentation](https://www.questpdf.com/documentation/getting-started.html).
This repository contains sample implementation of the full, page-aware invoice similar to the one below. Let's get started!
<a href="https://github.com/QuestPDF/example-invoice">
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="595px">
</a>
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/invoice.png" width="595px">
Here you can find an example code showing how easy is to write and understand the fluent API:
```csharp
public void Compose(IContainer container)
{
container
.PaddingHorizontal(50)
.PaddingVertical(50)
.Page(page =>
{
page.Header(ComposeHeader);
page.Content(ComposeContent);
page.Footer().AlignCenter().PageNumber("Page {number}");
});
}
void ComposeHeader(IContainer container)
{
container.Row(row =>
{
row.RelativeColumn().Stack(column =>
{
column.Element().Text($"Invoice #{Model.InvoiceNumber}", TextStyle.Default.Size(20).Bold());
column.Element().Text($"Issue date: {Model.IssueDate:d}");
column.Element().Text($"Due date: {Model.DueDate:d}");
});
row.ConstantColumn(100).Height(50).Placeholder();
});
}
// code describing content
```