Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97e4572c39 |
@@ -1,70 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '22 16 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -22,35 +20,18 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
foreach (var i in Enumerable.Range(0, 10))
|
||||
column.Item().Element(Block);
|
||||
column.Item().Element(Block);
|
||||
|
||||
static void Block(IContainer container)
|
||||
{
|
||||
container
|
||||
.Width(72)
|
||||
.Height(3.5f, Unit.Inch)
|
||||
.Height(1.5f, Unit.Inch)
|
||||
.Height(3.5f)
|
||||
.Height(1.5f)
|
||||
.Background(Placeholders.BackgroundColor());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability_NoItems()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 150)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Column(column => { });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
internal class MyComponent : IComponent
|
||||
{
|
||||
public ISlot Header { get; set; }
|
||||
public ISlot<string> Content { get; set; }
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Slot(Header);
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 10))
|
||||
column.Item().Slot(Content, i.ToString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ComponentExamples
|
||||
{
|
||||
[Test]
|
||||
public void ComplexLayout()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(content =>
|
||||
{
|
||||
content
|
||||
.Padding(10)
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Component<MyComponent>(component =>
|
||||
{
|
||||
component
|
||||
.Slot(x => x.Header)
|
||||
.Text("This is my text");
|
||||
|
||||
component.Slot(x => x.Content, (input, container) =>
|
||||
{
|
||||
container
|
||||
.Background(Placeholders.BackgroundColor())
|
||||
.Padding(5)
|
||||
.Text(input);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ExecutionOrderExamples
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 300)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.DefaultTextStyle(TextStyle.Default.Size(18))
|
||||
.Padding(25)
|
||||
.Row(row =>
|
||||
{
|
||||
row.Spacing(25);
|
||||
|
||||
row.RelativeItem()
|
||||
.Border(1)
|
||||
.Padding(15)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Text("Lorem ipsum");
|
||||
|
||||
row.RelativeItem()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(15)
|
||||
.Text("dolor sit amet");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Example2()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 200)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Border(2)
|
||||
.Width(150)
|
||||
.Height(150)
|
||||
|
||||
.Background(Colors.Blue.Lighten2)
|
||||
.PaddingTop(50)
|
||||
|
||||
.Background(Colors.Green.Lighten2)
|
||||
.PaddingRight(50)
|
||||
|
||||
.Background(Colors.Red.Lighten2)
|
||||
.PaddingBottom(50)
|
||||
|
||||
.Background(Colors.Amber.Lighten2)
|
||||
.PaddingLeft(50)
|
||||
|
||||
.Background(Colors.Grey.Lighten2);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
@@ -15,20 +14,16 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProduceImages()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(15)
|
||||
.MinimalBox()
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text("Above text");
|
||||
column.Item().PaddingVertical(5).LineHorizontal(1).LineColor(Colors.Grey.Medium);
|
||||
column.Item().Text("Below text");
|
||||
});
|
||||
container.Padding(25).Column(column =>
|
||||
{
|
||||
column.Item().Text("Above text");
|
||||
column.Item().PaddingVertical(10).LineHorizontal(1).LineColor(Colors.Blue.Accent1);
|
||||
column.Item().Text("Below text");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,19 +33,17 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProduceImages()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(15)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Row(row =>
|
||||
{
|
||||
row.AutoItem().Text("Left text");
|
||||
row.AutoItem().PaddingHorizontal(10).LineVertical(1).LineColor(Colors.Grey.Medium);
|
||||
row.AutoItem().Text("Right text");
|
||||
});
|
||||
container.Padding(25).Inlined(inlined =>
|
||||
{
|
||||
inlined.Spacing(5);
|
||||
inlined.Item().Text("Above text");
|
||||
inlined.Item().LineVertical(1).LineColor(Colors.Blue.Accent1);
|
||||
inlined.Item().Text("Below text");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageReference Include="Svg.Skia" Version="0.5.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -29,9 +28,6 @@
|
||||
<None Update="logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="pdf-icon.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -89,21 +89,5 @@ namespace QuestPDF.Examples
|
||||
.ShowOnce();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability_NoItems()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 150)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Row(row => { });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
@@ -23,15 +23,15 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
var text = Placeholders.Paragraph();
|
||||
|
||||
foreach (var i in Enumerable.Range(2, 5))
|
||||
foreach (var i in Enumerable.Range(0, 16))
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Width(i * 40)
|
||||
.Height(i * 20)
|
||||
.Width(50 + i * 25)
|
||||
.Height(25 + i * 5)
|
||||
.ScaleToFit()
|
||||
.Text(text);
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class StopPaging
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 250)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(14))
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Medium));
|
||||
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
});
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
column.Item().StopPaging().Text(Placeholders.LoremIpsum());
|
||||
column.Item().ExtendHorizontal().Height(75).Background(Colors.Grey.Lighten2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
using Svg.Skia;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class SvgImageExample
|
||||
{
|
||||
[Test]
|
||||
public void BorderRadius()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(175, 100)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(25)
|
||||
.Canvas((canvas, space) =>
|
||||
{
|
||||
using var svg = new SKSvg();
|
||||
svg.Load("pdf-icon.svg");
|
||||
|
||||
canvas.DrawPicture(svg.Picture);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,39 +49,6 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PagingSupport()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(420, 220)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
// by using custom 'Element' method, we can reuse visual configuration
|
||||
table.Cell().Element(Block).Text(Placeholders.Label());
|
||||
table.Cell().Element(Block).Text(Placeholders.Label());
|
||||
table.Cell().Element(Block).Text(Placeholders.Paragraph());
|
||||
table.Cell().Element(Block).Text(Placeholders.Label());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultCellStyle()
|
||||
{
|
||||
@@ -250,7 +217,7 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(220, 170)
|
||||
.PageSize(220, 220)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
@@ -413,7 +380,7 @@ namespace QuestPDF.Examples
|
||||
.EnableCaching()
|
||||
.EnableDebugging(false)
|
||||
.ShowResults()
|
||||
.Render(container => GeneratePerformanceStructure(container, 250));
|
||||
.Render(container => GeneratePerformanceStructure(container, 1000));
|
||||
}
|
||||
|
||||
public static void GeneratePerformanceStructure(IContainer container, int repeats)
|
||||
@@ -489,10 +456,8 @@ namespace QuestPDF.Examples
|
||||
return container
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.ShowOnce()
|
||||
.MinWidth(50)
|
||||
.MinHeight(50)
|
||||
.Padding(10)
|
||||
.AlignCenter()
|
||||
.AlignMiddle();
|
||||
}
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="75.320129mm"
|
||||
height="92.604164mm"
|
||||
viewBox="0 0 75.320129 92.604164"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
sodipodi:docname="PDF file icon.svg">
|
||||
<title
|
||||
id="title2682">PDF file icon</title>
|
||||
<defs
|
||||
id="defs2">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath839">
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="rect841"
|
||||
width="68.688263"
|
||||
height="67.886459"
|
||||
x="-74.033661"
|
||||
y="141.44913" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="122.12727"
|
||||
inkscape:cy="167.02823"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g899"
|
||||
showgrid="false"
|
||||
inkscape:object-nodes="false"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="705"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-page="false"
|
||||
inkscape:snap-others="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>PDF file icon</dc:title>
|
||||
<dc:date>08/10/2018</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Adobe Systems</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>CMetalCore</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:description>Fuente del texto "PDF":
|
||||
Franklin Gothic Medium Cond</dc:description>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(36.076172,-93.731774)">
|
||||
<g
|
||||
id="g899"
|
||||
transform="matrix(1.4843054,0,0,1.4843054,17.471885,-90.243502)">
|
||||
<g
|
||||
id="g876">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff2116;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.11666656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m -29.632812,123.94727 c -3.551967,0 -6.44336,2.89347 -6.44336,6.44531 v 49.49804 c 0,3.55185 2.891393,6.44532 6.44336,6.44532 H 8.2167969 c 3.5519661,0 6.4433591,-2.89335 6.4433591,-6.44532 v -40.70117 c 0,0 0.101353,-1.19181 -0.416015,-2.35156 -0.484969,-1.08711 -1.275391,-1.84375 -1.275391,-1.84375 a 1.0584391,1.0584391 0 0 0 -0.0059,-0.008 L 3.5722246,125.7752 a 1.0584391,1.0584391 0 0 0 -0.015625,-0.0156 c 0,0 -0.8017392,-0.76344 -1.9902344,-1.27344 -1.39939552,-0.6005 -2.8417968,-0.53711 -2.8417968,-0.53711 l 0.021484,-0.002 z"
|
||||
id="path890" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.11666656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m -29.632812,126.06445 h 28.3789058 a 1.0584391,1.0584391 0 0 0 0.021484,0 c 0,0 1.13480448,0.011 1.96484378,0.36719 0.79889772,0.34282 1.36536982,0.86176 1.36914062,0.86524 1.25e-5,1e-5 0.00391,0.004 0.00391,0.004 l 9.3671868,9.18945 c 0,0 0.564354,0.59582 0.837891,1.20899 0.220779,0.49491 0.234375,1.40039 0.234375,1.40039 a 1.0584391,1.0584391 0 0 0 -0.002,0.0449 v 40.74609 c 0,2.41592 -1.910258,4.32813 -4.3261717,4.32813 H -29.632812 c -2.415914,0 -4.326172,-1.91209 -4.326172,-4.32813 v -49.49804 c 0,-2.41603 1.910258,-4.32813 4.326172,-4.32813 z"
|
||||
id="rect2684" />
|
||||
<path
|
||||
sodipodi:nodetypes="scccscccscccccssscacccsccccccccccccccccccccaccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m -23.40766,161.09299 c -1.45669,-1.45669 0.11934,-3.45839 4.39648,-5.58397 l 2.69124,-1.33743 1.04845,-2.29399 c 0.57665,-1.26169 1.43729,-3.32036 1.91254,-4.5748 l 0.8641,-2.28082 -0.59546,-1.68793 c -0.73217,-2.07547 -0.99326,-5.19438 -0.52872,-6.31588 0.62923,-1.51909 2.69029,-1.36323 3.50626,0.26515 0.63727,1.27176 0.57212,3.57488 -0.18329,6.47946 l -0.6193,2.38125 0.5455,0.92604 c 0.30003,0.50932 1.1764,1.71867 1.9475,2.68743 l 1.44924,1.80272 1.8033728,-0.23533 c 5.72900399,-0.74758 7.6912472,0.523 7.6912472,2.34476 0,2.29921 -4.4984914,2.48899 -8.2760865,-0.16423 -0.8499666,-0.59698 -1.4336605,-1.19001 -1.4336605,-1.19001 0,0 -2.3665326,0.48178 -3.531704,0.79583 -1.202707,0.32417 -1.80274,0.52719 -3.564509,1.12186 0,0 -0.61814,0.89767 -1.02094,1.55026 -1.49858,2.4279 -3.24833,4.43998 -4.49793,5.1723 -1.3991,0.81993 -2.86584,0.87582 -3.60433,0.13733 z m 2.28605,-0.81668 c 0.81883,-0.50607 2.47616,-2.46625 3.62341,-4.28553 l 0.46449,-0.73658 -2.11497,1.06339 c -3.26655,1.64239 -4.76093,3.19033 -3.98386,4.12664 0.43653,0.52598 0.95874,0.48237 2.01093,-0.16792 z m 21.21809,-5.95578 c 0.80089,-0.56097 0.68463,-1.69142 -0.22082,-2.1472 -0.70466,-0.35471 -1.2726074,-0.42759 -3.1031574,-0.40057 -1.1249,0.0767 -2.9337647,0.3034 -3.2403347,0.37237 0,0 0.993716,0.68678 1.434896,0.93922 0.58731,0.33544 2.0145161,0.95811 3.0565161,1.27706 1.02785,0.31461 1.6224,0.28144 2.0729,-0.0409 z m -8.53152,-3.54594 c -0.4847,-0.50952 -1.30889,-1.57296 -1.83152,-2.3632 -0.68353,-0.89643 -1.02629,-1.52887 -1.02629,-1.52887 0,0 -0.4996,1.60694 -0.90948,2.57394 l -1.27876,3.16076 -0.37075,0.71695 c 0,0 1.971043,-0.64627 2.97389,-0.90822 1.0621668,-0.27744 3.21787,-0.70134 3.21787,-0.70134 z m -2.74938,-11.02573 c 0.12363,-1.0375 0.1761,-2.07346 -0.15724,-2.59587 -0.9246,-1.01077 -2.04057,-0.16787 -1.85154,2.23517 0.0636,0.8084 0.26443,2.19033 0.53292,3.04209 l 0.48817,1.54863 0.34358,-1.16638 c 0.18897,-0.64151 0.47882,-2.02015 0.64411,-3.06364 z"
|
||||
style="fill:#ff2116;fill-opacity:1;stroke-width:0.26458335"
|
||||
id="path2697" />
|
||||
<g
|
||||
id="g858">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path845"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -20.930423,167.83862 h 2.364986 q 1.133514,0 1.840213,0.2169 0.706698,0.20991 1.189489,0.9446 0.482795,0.72769 0.482795,1.75625 0,0.94459 -0.391832,1.6233 -0.391833,0.67871 -1.056548,0.97958 -0.65772,0.30087 -2.02913,0.30087 h -0.818651 v 3.72941 h -1.581322 z m 1.581322,1.22447 v 3.33058 h 0.783664 q 1.049552,0 1.44838,-0.39184 0.405826,-0.39183 0.405826,-1.27345 0,-0.65772 -0.265887,-1.06355 -0.265884,-0.41282 -0.587747,-0.50378 -0.314866,-0.098 -1.000572,-0.098 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path847"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -13.842461,167.83862 h 2.148082 q 1.560333,0 2.4909318,0.55276 0.9375993,0.55276 1.4133973,1.6443 0.482791,1.09153 0.482791,2.42096 0,1.3994 -0.4338151,2.49793 -0.4268149,1.09153 -1.3154348,1.76324 -0.8816233,0.67172 -2.5189212,0.67172 h -2.267031 z m 1.581326,1.26645 v 7.018 h 0.657715 q 1.378411,0 2.001144,-0.9516 0.6227329,-0.95858 0.6227329,-2.5539 0,-3.5125 -2.6238769,-3.5125 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path849"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -5.7889096,167.83862 h 5.30372941 v 1.26645 H -4.2075842 v 2.85478 h 2.9807225 v 1.26646 h -2.9807225 v 4.16322 h -1.5813254 z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@@ -17,30 +17,19 @@ namespace QuestPDF.Drawing
|
||||
{
|
||||
internal static void GeneratePdf(Stream stream, IDocument document)
|
||||
{
|
||||
CheckIfStreamIsCompatible(stream);
|
||||
|
||||
var metadata = document.GetMetadata();
|
||||
var canvas = new PdfCanvas(stream, metadata);
|
||||
var writeOnlyStream = new WriteOnlyStream(stream);
|
||||
var canvas = new PdfCanvas(writeOnlyStream, metadata);
|
||||
RenderDocument(canvas, document);
|
||||
}
|
||||
|
||||
internal static void GenerateXps(Stream stream, IDocument document)
|
||||
{
|
||||
CheckIfStreamIsCompatible(stream);
|
||||
|
||||
var metadata = document.GetMetadata();
|
||||
var canvas = new XpsCanvas(stream, metadata);
|
||||
var writeOnlyStream = new WriteOnlyStream(stream);
|
||||
var canvas = new XpsCanvas(writeOnlyStream, metadata);
|
||||
RenderDocument(canvas, document);
|
||||
}
|
||||
|
||||
private static void CheckIfStreamIsCompatible(Stream stream)
|
||||
{
|
||||
if (!stream.CanWrite)
|
||||
throw new ArgumentException("The library requires a Stream object with the 'write' capability available (the CanWrite flag). Please consider using the MemoryStream class.");
|
||||
|
||||
if (!stream.CanSeek)
|
||||
throw new ArgumentException("The library requires a Stream object with the 'seek' capability available (the CanSeek flag). Please consider using the MemoryStream class.");
|
||||
}
|
||||
|
||||
internal static ICollection<byte[]> GenerateImages(IDocument document)
|
||||
{
|
||||
|
||||
@@ -6,16 +6,16 @@ namespace QuestPDF.Drawing
|
||||
{
|
||||
internal class SkiaDocumentCanvasBase : SkiaCanvasBase
|
||||
{
|
||||
private SKDocument? Document { get; }
|
||||
private SKDocument Document { get; }
|
||||
|
||||
protected SkiaDocumentCanvasBase(SKDocument document)
|
||||
public SkiaDocumentCanvasBase(SKDocument document)
|
||||
{
|
||||
Document = document;
|
||||
}
|
||||
|
||||
~SkiaDocumentCanvasBase()
|
||||
{
|
||||
Document?.Dispose();
|
||||
Document.Dispose();
|
||||
}
|
||||
|
||||
public override void BeginDocument()
|
||||
@@ -25,7 +25,7 @@ namespace QuestPDF.Drawing
|
||||
|
||||
public override void EndDocument()
|
||||
{
|
||||
Canvas?.Dispose();
|
||||
Canvas.Dispose();
|
||||
|
||||
Document.Close();
|
||||
Document.Dispose();
|
||||
|
||||
@@ -41,9 +41,6 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override SpacePlan Measure(Size availableSpace)
|
||||
{
|
||||
if (!Items.Any())
|
||||
return SpacePlan.FullRender(Size.Zero);
|
||||
|
||||
var renderingCommands = PlanLayout(availableSpace);
|
||||
|
||||
if (!renderingCommands.Any())
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace QuestPDF.Elements
|
||||
{
|
||||
return Type switch
|
||||
{
|
||||
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(Size, 0),
|
||||
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(0, Size),
|
||||
LineType.Vertical when availableSpace.Width >= Size => SpacePlan.FullRender(Size, 0),
|
||||
LineType.Horizontal when availableSpace.Height >= Size => SpacePlan.FullRender(0, Size),
|
||||
_ => SpacePlan.Wrap()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,9 +53,6 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override SpacePlan Measure(Size availableSpace)
|
||||
{
|
||||
if (!Items.Any())
|
||||
return SpacePlan.FullRender(Size.Zero);
|
||||
|
||||
UpdateItemsWidth(availableSpace.Width);
|
||||
var renderingCommands = PlanLayout(availableSpace);
|
||||
|
||||
@@ -77,9 +74,6 @@ namespace QuestPDF.Elements
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
if (!Items.Any())
|
||||
return;
|
||||
|
||||
UpdateItemsWidth(availableSpace.Width);
|
||||
var renderingCommands = PlanLayout(availableSpace);
|
||||
|
||||
@@ -149,12 +143,8 @@ namespace QuestPDF.Elements
|
||||
renderingCommands.Add(command);
|
||||
leftOffset += item.Width + Spacing;
|
||||
}
|
||||
|
||||
var rowHeight = renderingCommands
|
||||
.Where(x => !x.RowItem.IsRendered)
|
||||
.Select(x => x.Measurement.Height)
|
||||
.DefaultIfEmpty(0)
|
||||
.Max();
|
||||
|
||||
var rowHeight = renderingCommands.Where(x => !x.RowItem.IsRendered).Max(x => x.Measurement.Height);
|
||||
|
||||
foreach (var command in renderingCommands)
|
||||
{
|
||||
|
||||
@@ -17,10 +17,8 @@ namespace QuestPDF.Elements
|
||||
if (perfectScale == null)
|
||||
return SpacePlan.Wrap();
|
||||
|
||||
var scaledSpace = ScaleSize(availableSpace, 1 / perfectScale.Value);
|
||||
var childSizeInScale = Child.Measure(scaledSpace);
|
||||
var childSizeInOriginalScale = ScaleSize(childSizeInScale, perfectScale.Value);
|
||||
return SpacePlan.FullRender(childSizeInOriginalScale);
|
||||
var targetSpace = ScaleSize(availableSpace, perfectScale.Value);
|
||||
return SpacePlan.FullRender(targetSpace);
|
||||
}
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
|
||||
@@ -15,19 +15,15 @@ namespace QuestPDF.Elements.Table
|
||||
public List<TableCell> Cells { get; set; } = new List<TableCell>();
|
||||
public bool ExtendLastCellsToTableBottom { get; set; }
|
||||
|
||||
// cache for efficient cell finding
|
||||
// index of first array - number of row
|
||||
// nested array - collection of all cells starting at given row
|
||||
private TableCell[][] OrderedCells { get; set; }
|
||||
|
||||
private int StartingRowsCount { get; set; }
|
||||
private int RowsCount { get; set; }
|
||||
private int CurrentRow { get; set; }
|
||||
|
||||
internal override void Initialize(IPageContext pageContext, ICanvas canvas)
|
||||
{
|
||||
StartingRowsCount = Cells.Select(x => x.Row).DefaultIfEmpty(0).Max();
|
||||
RowsCount = Cells.Select(x => x.Row + x.RowSpan - 1).DefaultIfEmpty(0).Max();
|
||||
Cells = Cells.OrderBy(x => x.Row).ThenBy(x => x.Column).ToList();
|
||||
|
||||
base.Initialize(pageContext, canvas);
|
||||
}
|
||||
|
||||
internal override IEnumerable<Element?> GetChildren()
|
||||
{
|
||||
return Cells;
|
||||
@@ -35,6 +31,24 @@ namespace QuestPDF.Elements.Table
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
if (StartingRowsCount == default)
|
||||
StartingRowsCount = Cells.Select(x => x.Row).DefaultIfEmpty(0).Max();
|
||||
|
||||
if (RowsCount == default)
|
||||
RowsCount = Cells.Select(x => x.Row + x.RowSpan - 1).DefaultIfEmpty(0).Max();
|
||||
|
||||
if (OrderedCells == default)
|
||||
{
|
||||
var groups = Cells
|
||||
.GroupBy(x => x.Row)
|
||||
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Column).ToArray());
|
||||
|
||||
OrderedCells = Enumerable
|
||||
.Range(0, RowsCount + 1)
|
||||
.Select(x => groups.TryGetValue(x, out var output) ? output : Array.Empty<TableCell>())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
Cells.ForEach(x => x.IsRendered = false);
|
||||
CurrentRow = 1;
|
||||
}
|
||||
@@ -71,9 +85,6 @@ namespace QuestPDF.Elements.Table
|
||||
{
|
||||
if (command.Measurement.Type == SpacePlanType.FullRender)
|
||||
command.Cell.IsRendered = true;
|
||||
|
||||
if (command.Measurement.Type == SpacePlanType.Wrap)
|
||||
continue;
|
||||
|
||||
Canvas.Translate(command.Offset);
|
||||
command.Cell.Draw(command.Size);
|
||||
@@ -92,7 +103,7 @@ namespace QuestPDF.Elements.Table
|
||||
{
|
||||
return commands
|
||||
.GroupBy(x => x.Cell.Row)
|
||||
.Where(x => x.All(y => y.Cell.IsRendered || y.Measurement.Type == SpacePlanType.FullRender))
|
||||
.Where(x => x.All(y => y.Measurement.Type == SpacePlanType.FullRender))
|
||||
.Select(x => x.Key)
|
||||
.DefaultIfEmpty(0)
|
||||
.Max();
|
||||
@@ -141,12 +152,16 @@ namespace QuestPDF.Elements.Table
|
||||
ICollection<TableCellRenderingCommand> GetRenderingCommands()
|
||||
{
|
||||
var rowBottomOffsets = new DynamicDictionary<int, float>();
|
||||
var commands = new List<TableCellRenderingCommand>();
|
||||
|
||||
var cellsToTry = Cells.Where(x => x.Row + x.RowSpan - 1 >= CurrentRow);
|
||||
var cellsToTry = Enumerable
|
||||
.Range(CurrentRow, RowsCount - CurrentRow + 1)
|
||||
.SelectMany(x => OrderedCells[x]);
|
||||
|
||||
var currentRow = CurrentRow;
|
||||
var maxRenderingRow = RowsCount;
|
||||
|
||||
var commands = new List<TableCellRenderingCommand>();
|
||||
|
||||
foreach (var cell in cellsToTry)
|
||||
{
|
||||
// update position of previous row
|
||||
@@ -166,7 +181,10 @@ namespace QuestPDF.Elements.Table
|
||||
// cell visibility optimizations
|
||||
if (cell.Row > maxRenderingRow)
|
||||
break;
|
||||
|
||||
|
||||
if (cell.IsRendered)
|
||||
continue;
|
||||
|
||||
// calculate cell position / size
|
||||
var topOffset = rowBottomOffsets[cell.Row - 1];
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements.Text.Calculation;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -12,7 +11,8 @@ namespace QuestPDF.Elements.Text.Items
|
||||
public string Text { get; set; }
|
||||
public TextStyle Style { get; set; } = new TextStyle();
|
||||
|
||||
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new ();
|
||||
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache =
|
||||
new Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?>();
|
||||
|
||||
public virtual TextMeasurementResult? Measure(TextMeasurementRequest request)
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace QuestPDF.Elements.Text.Items
|
||||
// start breaking text from requested position
|
||||
var text = Text.Substring(startIndex);
|
||||
|
||||
var textLength = (int)paint.BreakText(text, request.AvailableWidth + Size.Epsilon);
|
||||
var textLength = (int)paint.BreakText(text, request.AvailableWidth);
|
||||
|
||||
if (textLength <= 0)
|
||||
return null;
|
||||
|
||||
@@ -1,41 +1,53 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Fluent
|
||||
{
|
||||
class ComponentDescriptor<T> where T : IComponent
|
||||
internal class ComponentDescriptor<TComponent> where TComponent : IComponent
|
||||
{
|
||||
public T Component { get; }
|
||||
public TComponent Component { get; }
|
||||
|
||||
public ComponentDescriptor(T component)
|
||||
public ComponentDescriptor(TComponent component)
|
||||
{
|
||||
Component = component;
|
||||
}
|
||||
|
||||
public IContainer Slot(Expression<Func<T, ISlot>> selector)
|
||||
public IContainer Slot(Expression<Func<TComponent, ISlot>> selector)
|
||||
{
|
||||
try
|
||||
AssureThatTheSlotIsNotConfiguredYet(selector);
|
||||
|
||||
var slot = new Slot();
|
||||
Component.SetPropertyValue(selector, slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
public void Slot<TArgument>(Expression<Func<TComponent, ISlot<TArgument>>> selector, Action<TArgument, IContainer> handler)
|
||||
{
|
||||
AssureThatTheSlotIsNotConfiguredYet(selector);
|
||||
|
||||
var slot = new Slot<TArgument>
|
||||
{
|
||||
var existingValue = Component.GetPropertyValue(selector);
|
||||
GetContent = argument =>
|
||||
{
|
||||
var container = new Container();
|
||||
handler(argument, container);
|
||||
return container;
|
||||
}
|
||||
};
|
||||
|
||||
Component.SetPropertyValue(selector, slot);
|
||||
}
|
||||
|
||||
if (existingValue != null)
|
||||
throw new DocumentComposeException($"The slot {selector.GetPropertyName()} of the component {(typeof(T).Name)} was already used.");
|
||||
private void AssureThatTheSlotIsNotConfiguredYet<TSlot>(Expression<Func<TComponent, TSlot>> selector) where TSlot : class
|
||||
{
|
||||
var existingValue = Component.GetPropertyValue(selector);
|
||||
|
||||
var slot = new Slot();
|
||||
Component.SetPropertyValue(selector, slot);
|
||||
return slot;
|
||||
}
|
||||
catch (DocumentComposeException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new DocumentComposeException("Every slot in a component should be a public property with getter and setter.");
|
||||
}
|
||||
if (existingValue != null)
|
||||
throw new DocumentComposeException($"The slot {selector.GetPropertyName()} of the component {(typeof(TComponent).Name)} was already used.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +63,7 @@ namespace QuestPDF.Fluent
|
||||
element.Component(new T(), null);
|
||||
}
|
||||
|
||||
static void Component<T>(this IContainer element, T component, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent
|
||||
internal static void Component<T>(this IContainer element, T component, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent
|
||||
{
|
||||
var descriptor = new ComponentDescriptor<T>(component);
|
||||
handler?.Invoke(descriptor);
|
||||
@@ -62,15 +74,21 @@ namespace QuestPDF.Fluent
|
||||
component.Compose(element.Container());
|
||||
}
|
||||
|
||||
static void Component<T>(this IContainer element, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent, new()
|
||||
internal static void Component<T>(this IContainer element, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent, new()
|
||||
{
|
||||
element.Component(new T(), handler);
|
||||
}
|
||||
|
||||
static void Slot(this IContainer element, ISlot slot)
|
||||
internal static void Slot(this IContainer element, ISlot slot)
|
||||
{
|
||||
var child = (slot as Slot)?.Child;
|
||||
element.Element(child);
|
||||
}
|
||||
|
||||
internal static void Slot<TArgument>(this IContainer element, ISlot<TArgument> slot, TArgument argument)
|
||||
{
|
||||
var child = (slot as Slot<TArgument>)?.GetContent(argument) ?? Empty.Instance;
|
||||
element.Element(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,6 @@ namespace QuestPDF.Fluent
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XPS
|
||||
|
||||
public static byte[] GenerateXps(this IDocument document)
|
||||
{
|
||||
@@ -50,6 +48,8 @@ namespace QuestPDF.Fluent
|
||||
DocumentGenerator.GenerateXps(stream, document);
|
||||
}
|
||||
|
||||
#region XPS
|
||||
|
||||
#endregion
|
||||
|
||||
#region Images
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace QuestPDF.Helpers
|
||||
{
|
||||
public static class Placeholders
|
||||
{
|
||||
public static readonly Random Random = new();
|
||||
public static readonly Random Random = new Random(3); // 3
|
||||
|
||||
#region Word Cache
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace QuestPDF.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// SkiaSharp calls the Position property when generating target document file.
|
||||
/// If the output stream does not support the Position property, the NullReferenceException is thrown.
|
||||
/// This wrapper fixes this issue by providing cached Position value (always the end of the stream / current length).
|
||||
/// Example stream affected: HttpContext.Response.Body
|
||||
/// </summary>
|
||||
internal class WriteOnlyStream : Stream
|
||||
{
|
||||
private readonly Stream InnerStream;
|
||||
private long StreamLength { get; set; }
|
||||
|
||||
public WriteOnlyStream(Stream stream)
|
||||
{
|
||||
if (!stream.CanWrite)
|
||||
throw new NotSupportedException("Stream cannot be written");
|
||||
|
||||
InnerStream = stream;
|
||||
}
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => StreamLength;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => StreamLength;
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Flush() => InnerStream.Flush();
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public override void SetLength(long value)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
InnerStream.Write(buffer, offset, count);
|
||||
StreamLength += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace QuestPDF.Infrastructure
|
||||
yield break;
|
||||
}
|
||||
|
||||
internal virtual void Initialize(IPageContext pageContext, ICanvas canvas)
|
||||
internal void Initialize(IPageContext pageContext, ICanvas canvas)
|
||||
{
|
||||
PageContext = pageContext;
|
||||
Canvas = canvas;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using QuestPDF.Elements;
|
||||
|
||||
namespace QuestPDF.Infrastructure
|
||||
@@ -11,7 +12,17 @@ namespace QuestPDF.Infrastructure
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
interface ISlot<T>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Slot<T> : ISlot<T>
|
||||
{
|
||||
public Func<T, IElement> GetContent { get; set; }
|
||||
}
|
||||
|
||||
public interface IComponent
|
||||
{
|
||||
void Compose(IContainer container);
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace QuestPDF.Infrastructure
|
||||
Point => 1,
|
||||
Meter => 100 / InchToCentimetre * InchToPoints,
|
||||
Centimetre => 1 / InchToCentimetre * InchToPoints,
|
||||
Millimetre => 0.1f / InchToCentimetre * InchToPoints,
|
||||
Millimetre => 10 / InchToCentimetre * InchToPoints,
|
||||
Feet => 12 * InchToPoints,
|
||||
Inch => InchToPoints,
|
||||
Mill => InchToPoints / 1000f,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Authors>MarcinZiabek</Authors>
|
||||
<Company>CodeFlint</Company>
|
||||
<PackageId>QuestPDF</PackageId>
|
||||
<Version>2022.2.5</Version>
|
||||
<Version>2022.2.0-beta2</Version>
|
||||
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
||||
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
|
||||
<LangVersion>9</LangVersion>
|
||||
|
||||
@@ -132,42 +132,41 @@ void ComposeTable(IContainer container)
|
||||
{
|
||||
var headerStyle = TextStyle.Default.SemiBold();
|
||||
|
||||
container.Table(table =>
|
||||
container.Decoration(decoration =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
// header
|
||||
decoration.Header().BorderBottom(1).Padding(5).Row(row =>
|
||||
{
|
||||
columns.ConstantColumn(25);
|
||||
columns.RelativeColumn(3);
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
row.ConstantColumn(25).Text("#", headerStyle);
|
||||
row.RelativeColumn(3).Text("Product", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Unit price", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Quantity", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Total", headerStyle);
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().Text("#", headerStyle);
|
||||
header.Cell().Text("Product", headerStyle);
|
||||
header.Cell().AlignRight().Text("Unit price", headerStyle);
|
||||
header.Cell().AlignRight().Text("Quantity", headerStyle);
|
||||
header.Cell().AlignRight().Text("Total", headerStyle);
|
||||
|
||||
header.Cell().ColumnSpan(5)
|
||||
.PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
|
||||
});
|
||||
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
table.Cell().Element(CellStyle).Text(Model.Items.IndexOf(item) + 1);
|
||||
table.Cell().Element(CellStyle).Text(item.Name);
|
||||
table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price}$");
|
||||
table.Cell().Element(CellStyle).AlignRight().Text(item.Quantity);
|
||||
table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price * item.Quantity}$");
|
||||
|
||||
static IContainer CellStyle(IContainer container)
|
||||
|
||||
// content
|
||||
decoration
|
||||
.Content()
|
||||
.column(column =>
|
||||
{
|
||||
container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5);
|
||||
}
|
||||
}
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.ShowEntire()
|
||||
.BorderBottom(1)
|
||||
.BorderColor(Colors.Grey.Lighten2)
|
||||
.Padding(5)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25).Text(Model.Items.IndexOf(item) + 1);
|
||||
row.RelativeColumn(3).Text(item.Name);
|
||||
row.RelativeColumn().AlignRight().Text($"{item.Price}$");
|
||||
row.RelativeColumn().AlignRight().Text(item.Quantity);
|
||||
row.RelativeColumn().AlignRight().Text($"{item.Price * item.Quantity}$");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
- Added a `ScaleToFit` element - scales its child down so it fits in the provided space,
|
||||
- Added a `ScaleToFit` element - scales it child down so it does fit in the provided space,
|
||||
- Added a `StopPaging` element - when its child requires more than one page to fully render, only the first page is shown,
|
||||
- Added a 'LineVertical' and a 'LineHorizontal' elements - those will simplify your code a lot, there is no need to use the `Border` element anymore!
|
||||
- Renaming: the `Stack` element was renamed to the `Column` element,
|
||||
- Renaming: children of the `Row` element are now called `items` instead of `columns`, e.g. `RelativeItem` instead of `RelativeColumn`,
|
||||
- Added a 'LineVertical' and a 'LineHorizontal' elements - those will simplify your code a lot, no need to use the `Border` element anymore!
|
||||
- Renaming: the `Stack` element has been renamed to the `Column` element,
|
||||
- Renaming: children of the `Row` elements are not called `items` instead of `columns`, e.g. `ConstantItem`, `RelativeItem`, `AutoItem`,
|
||||
- Added support of the `AutoItem` to the `Row` element - those items take as little width as possible,
|
||||
- Improved default Fluent configuration behavior for elements: Scale, Padding, Translate,
|
||||
- Improved integration support with the HttpContext.Response.Body. This improvement was introduced by schulz3000, thank you!
|
||||
- Improved integration support with the HttpContext.Response.Body. This improvement was introduced by schulz3000, thank you!
|
||||
@@ -2,21 +2,37 @@
|
||||
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/logo.svg" width="400px">
|
||||
</a>
|
||||
|
||||
QuestPDF presents a new approach to PDF document generation. Unlike other libraries, it does not rely on the HTML-to-PDF conversion which in many cases is not reliable. Instead, it implements its own layouting engine that is optimized to cover all paging-related requirements. Then, everything is rendered using the SkiaSharp library (a Skia port for .NET, used in Chrome, Android, MAUI, etc.).
|
||||
## Overview
|
||||
|
||||
I have designed this layouting engine with full paging support in mind. The document consists of many simple elements (e.g. border, background, image, text, padding, table, grid etc.) that are composed together to create more complex structures. This way, as a developer, you can understand the behaviour of every element and use them with full confidence. Additionally, the document and all its elements support paging functionality. For example, an element can be moved to the next page (if there is not enough space) or even be split between pages like table's rows.
|
||||
**Rely on solid fundamentals** - This library is created specifically for designing and arranging document layouts, with full paging support. Alternative solutions, such as HTML-based converters, are not designed for this purpose and therefore are often unpredictable and do not produce desired results.
|
||||
|
||||
**Work with organized self-explanatory code** - The entire process of implementing PDF document, takes place in your code. Free yourself from slow visual designers and strange technological limitations. Follow simple yet highly effective approaches to create maintainable, high-quality code.
|
||||
|
||||
**Compose simple components into complex documents** - Do you remember the feeling when your code just works? When your ideas are becoming real without any effort? Working with simple, easy to understand, self-explanatory and highly composable layout elements is the key here!
|
||||
|
||||
**Create and reuse components** - Feel no fear of complex documents! Create custom, reusable components and divide the document's layout into easy to maintain pieces. Inject data to customize content and use slots to enhance composability. Decide how complex approaches your solution needs and follow the best path.
|
||||
|
||||
**Prototype with ease** - We understand that document generation is often tricky and require multiple iterations. The library offers additional prototyping tools such as random text generator or image placeholder element. By following best practices, you can develop a document without having data.
|
||||
|
||||
**Enjoy fast PDF generation** - QuestPDF is created upon SkiaSharp, a well-known graphical library, and converts your data into PDF documents. It offers a highly optimized layouting engine capable of generating over 1000 PDF files per minute per core. The entire process is thread-safe.
|
||||
|
||||
## Support QuestPDF
|
||||
|
||||
All great frameworks and libraries started from zero. Please help me make QuestPDF a commonly known library and an obvious choice in case of generating PDF documents. Please give it a start ⭐ and share with your colleagues 💬👨💻.
|
||||
All great frameworks and libraries started from zero. Please help us to make QuestPDF a commonly known library and an obvious choice in case of generating PDF documents. It can be as easy as:
|
||||
- Giving this repository a star ⭐ so more people will know about it,
|
||||
- Observing 🤩 the library to know about each new realease,
|
||||
- Trying our sample project to see how easy it is to create an invoice 📊,
|
||||
- Sharing your thoughts 💬 with us and your colleagues,
|
||||
- Simply using it 👨💻 and suggesting new features,
|
||||
- Creating new features 🆕 for everybody.
|
||||
|
||||
## Installation
|
||||
|
||||
The library is available as a nuget package. You can install it as any other nuget package from your IDE, try to search by `QuestPDF`. You can find package details [on this webpage](https://www.nuget.org/packages/QuestPDF/).
|
||||
|
||||
```
|
||||
Install-Package QuestPDF
|
||||
```
|
||||
<a href="https://www.nuget.org/packages/QuestPDF/">
|
||||
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/nuget.svg" width="200px">
|
||||
</a>
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -133,42 +149,41 @@ void ComposeTable(IContainer container)
|
||||
{
|
||||
var headerStyle = TextStyle.Default.SemiBold();
|
||||
|
||||
container.Table(table =>
|
||||
container.Decoration(decoration =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
// header
|
||||
decoration.Header().BorderBottom(1).Padding(5).Row(row =>
|
||||
{
|
||||
columns.ConstantColumn(25);
|
||||
columns.RelativeColumn(3);
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
row.ConstantColumn(25).Text("#", headerStyle);
|
||||
row.RelativeColumn(3).Text("Product", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Unit price", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Quantity", headerStyle);
|
||||
row.RelativeColumn().AlignRight().Text("Total", headerStyle);
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().Text("#", headerStyle);
|
||||
header.Cell().Text("Product", headerStyle);
|
||||
header.Cell().AlignRight().Text("Unit price", headerStyle);
|
||||
header.Cell().AlignRight().Text("Quantity", headerStyle);
|
||||
header.Cell().AlignRight().Text("Total", headerStyle);
|
||||
|
||||
header.Cell().ColumnSpan(5)
|
||||
.PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
|
||||
});
|
||||
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
table.Cell().Element(CellStyle).Text(Model.Items.IndexOf(item) + 1);
|
||||
table.Cell().Element(CellStyle).Text(item.Name);
|
||||
table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price}$");
|
||||
table.Cell().Element(CellStyle).AlignRight().Text(item.Quantity);
|
||||
table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price * item.Quantity}$");
|
||||
|
||||
static IContainer CellStyle(IContainer container)
|
||||
|
||||
// content
|
||||
decoration
|
||||
.Content()
|
||||
.Stack(column =>
|
||||
{
|
||||
container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5);
|
||||
}
|
||||
}
|
||||
foreach (var item in Model.Items)
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.ShowEntire()
|
||||
.BorderBottom(1)
|
||||
.BorderColor(Colors.Grey.Lighten2)
|
||||
.Padding(5)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25).Text(Model.Items.IndexOf(item) + 1);
|
||||
row.RelativeColumn(3).Text(item.Name);
|
||||
row.RelativeColumn().AlignRight().Text($"{item.Price}$");
|
||||
row.RelativeColumn().AlignRight().Text(item.Quantity);
|
||||
row.RelativeColumn().AlignRight().Text($"{item.Price * item.Quantity}$");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user