Compare commits

...

12 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
Marcin Ziąbek 190b1e7dee Update issue templates 2021-03-01 16:49:27 +01:00
Marcin Ziąbek 98be1abe74 Merge pull request #1 from QuestPDF/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2021-03-01 16:41:17 +01:00
Marcin Ziąbek 6b6d2d7fa3 Create CODE_OF_CONDUCT.md 2021-03-01 16:40:03 +01:00
42 changed files with 1038 additions and 287 deletions
+23
View File
@@ -0,0 +1,23 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Please provide an example, minimalistic code that shows the problem.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+76
View File
@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at marcin@ziabek.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
+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
```