Compare commits
24 Commits
tree_stack
...
2021.9
| Author | SHA1 | Date | |
|---|---|---|---|
| c2fb68562d | |||
| 683ab362d8 | |||
| 04af33fc31 | |||
| 7f94ae589a | |||
| adfce6204e | |||
| 6d3c8d0708 | |||
| 3b8c300126 | |||
| 239a042615 | |||
| 49fb6dadb3 | |||
| c79a4a0c08 | |||
| 31191efd88 | |||
| 20fe49dd01 | |||
| 7cdc1fcee2 | |||
| 14f11232be | |||
| c05c5e1597 | |||
| 07f1eb99b9 | |||
| 44d7538578 | |||
| b894262f7e | |||
| 4423774648 | |||
| 5d4f1f530a | |||
| 3ba8185871 | |||
| b6e0bd505c | |||
| 4e3ba1d878 | |||
| 6f0c6513da |
@@ -0,0 +1,32 @@
|
|||||||
|
using System.IO;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class BarCode
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Example()
|
||||||
|
{
|
||||||
|
FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||||
|
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(400, 100)
|
||||||
|
.FileName()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background(Colors.White)
|
||||||
|
.AlignCenter()
|
||||||
|
.AlignMiddle()
|
||||||
|
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using QuestPDF.Elements;
|
|
||||||
using QuestPDF.Fluent;
|
|
||||||
using QuestPDF.Infrastructure;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples.Engine
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class ExampleTestBase
|
|
||||||
{
|
|
||||||
private readonly Size DefaultImageSize = new Size(400, 300);
|
|
||||||
private const string ResultPath = "Result";
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
if (Directory.Exists(ResultPath))
|
|
||||||
Directory.Delete(ResultPath, true);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(ResultPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void RunTest()
|
|
||||||
{
|
|
||||||
GetType()
|
|
||||||
.GetMethods()
|
|
||||||
.Where(IsExampleMethod)
|
|
||||||
.ToList()
|
|
||||||
.ForEach(HandleExample);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsExampleMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
var parameters = method.GetParameters();
|
|
||||||
return parameters.Length == 1 && parameters.First().ParameterType == typeof(IContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private T GetAttribute<T>(MethodInfo methodInfo) where T : Attribute
|
|
||||||
{
|
|
||||||
return methodInfo.GetCustomAttributes().FirstOrDefault(y => y is T) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleExample(MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
var size = GetAttribute<ImageSizeAttribute>(methodInfo)?.Size ?? DefaultImageSize;
|
|
||||||
var fileName = GetAttribute<FileNameAttribute>(methodInfo)?.FileName ?? methodInfo.Name;
|
|
||||||
var showResult = GetAttribute<ShowResultAttribute>(methodInfo) != null;
|
|
||||||
var shouldIgnore = GetAttribute<IgnoreAttribute>(methodInfo) != null;
|
|
||||||
|
|
||||||
if (shouldIgnore)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var container = new Container();
|
|
||||||
methodInfo.Invoke(this, new object[] {container});
|
|
||||||
|
|
||||||
Func<int, string> fileNameSchema = i => $"{fileName.ToLower()}-${i}.png";
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] RenderPage(Element element, Size size)
|
|
||||||
{
|
|
||||||
// scale the result so it is more readable
|
|
||||||
const float scalingFactor = 2;
|
|
||||||
|
|
||||||
var imageInfo = new SKImageInfo((int)(size.Width * scalingFactor), (int)(size.Height * scalingFactor));
|
|
||||||
using var surface = SKSurface.Create(imageInfo);
|
|
||||||
surface.Canvas.Scale(scalingFactor);
|
|
||||||
|
|
||||||
var canvas = new Drawing.Canvas(surface.Canvas);
|
|
||||||
element?.Draw(canvas, size);
|
|
||||||
|
|
||||||
surface.Canvas.Save();
|
|
||||||
return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples.Engine
|
|
||||||
{
|
|
||||||
public class FileNameAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string FileName { get; }
|
|
||||||
|
|
||||||
public FileNameAttribute(string fileName)
|
|
||||||
{
|
|
||||||
FileName = fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples.Engine
|
|
||||||
{
|
|
||||||
public class IgnoreAttribute : Attribute
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
using QuestPDF.Infrastructure;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples.Engine
|
|
||||||
{
|
|
||||||
public class ImageSizeAttribute : Attribute
|
|
||||||
{
|
|
||||||
private int Width { get; }
|
|
||||||
private int Height { get; }
|
|
||||||
|
|
||||||
public Size Size => new Size(Width, Height);
|
|
||||||
|
|
||||||
public ImageSizeAttribute(int width, int height)
|
|
||||||
{
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples.Engine
|
||||||
|
{
|
||||||
|
public class RenderingTest
|
||||||
|
{
|
||||||
|
private string FileNamePrefix = "test";
|
||||||
|
private Size Size { get; set; }
|
||||||
|
|
||||||
|
private RenderingTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RenderingTest Create()
|
||||||
|
{
|
||||||
|
return new RenderingTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderingTest FileName([CallerMemberName] string fileName = "test")
|
||||||
|
{
|
||||||
|
FileNamePrefix = fileName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderingTest PageSize(int width, int height)
|
||||||
|
{
|
||||||
|
Size = new Size(width, height);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render(Action<IContainer> content)
|
||||||
|
{
|
||||||
|
var container = new Container();
|
||||||
|
content(container);
|
||||||
|
|
||||||
|
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
|
||||||
|
|
||||||
|
var document = new SimpleDocument(container, Size);
|
||||||
|
document.GenerateImages(fileNameSchema);
|
||||||
|
|
||||||
|
Process.Start("explorer", fileNameSchema(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples.Engine
|
|
||||||
{
|
|
||||||
public class ShowResultAttribute : Attribute
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using QuestPDF.Drawing;
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Elements;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
@@ -7,6 +8,8 @@ namespace QuestPDF.Examples.Engine
|
|||||||
{
|
{
|
||||||
public class SimpleDocument : IDocument
|
public class SimpleDocument : IDocument
|
||||||
{
|
{
|
||||||
|
public const int ImageScalingFactor = 2;
|
||||||
|
|
||||||
private IContainer Container { get; }
|
private IContainer Container { get; }
|
||||||
private Size Size { get; }
|
private Size Size { get; }
|
||||||
|
|
||||||
@@ -20,14 +23,18 @@ namespace QuestPDF.Examples.Engine
|
|||||||
{
|
{
|
||||||
return new DocumentMetadata()
|
return new DocumentMetadata()
|
||||||
{
|
{
|
||||||
RasterDpi = PageSizes.PointsPerInch * 2,
|
RasterDpi = PageSizes.PointsPerInch * ImageScalingFactor,
|
||||||
Size = Size
|
DocumentLayoutExceptionThreshold = 10
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Compose(IContainer container)
|
public void Compose(IDocumentContainer container)
|
||||||
{
|
{
|
||||||
container.Background("#FFF").Element(Container.Child);
|
container.Page(page =>
|
||||||
|
{
|
||||||
|
page.Size(new PageSize(Size.Width, Size.Height));
|
||||||
|
page.Content().Container().Background(Colors.White).Element(Container as Container);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
using QuestPDF.Examples.Engine;
|
using QuestPDF.Examples.Engine;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
@@ -11,33 +12,39 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
return container
|
return container
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.Background(dark ? "#EEE" : "#FFF")
|
.Background(dark ? Colors.Grey.Lighten2 : Colors.White)
|
||||||
.Padding(10);
|
.Padding(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IContainer LabelCell(this IContainer container) => container.Cell(true);
|
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.Medium());
|
||||||
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FrameExample: ExampleTestBase
|
public class FrameExample
|
||||||
{
|
{
|
||||||
[ImageSize(550, 400)]
|
[Test]
|
||||||
[ShowResult]
|
|
||||||
public void Frame(IContainer container)
|
public void Frame(IContainer container)
|
||||||
{
|
{
|
||||||
container
|
RenderingTest
|
||||||
.Background("#FFF")
|
.Create()
|
||||||
.Padding(25)
|
.PageSize(550, 400)
|
||||||
.Stack(stack =>
|
.FileName()
|
||||||
|
.Render(container =>
|
||||||
{
|
{
|
||||||
for(var i=1; i<=4; i++)
|
container
|
||||||
{
|
.Background("#FFF")
|
||||||
stack.Item().Row(row =>
|
.Padding(25)
|
||||||
|
.Stack(stack =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).LabelCell().Text(Placeholders.Label());
|
for(var i=1; i<=4; i++)
|
||||||
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
|
{
|
||||||
|
stack.Item().Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn(2).LabelCell(Placeholders.Label());
|
||||||
|
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,4 +1,5 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
|
using NUnit.Framework;
|
||||||
using QuestPDF.Examples.Engine;
|
using QuestPDF.Examples.Engine;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
@@ -27,27 +28,33 @@ namespace QuestPDF.Examples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LoremPicsumExample : ExampleTestBase
|
public class LoremPicsumExample
|
||||||
{
|
{
|
||||||
[ShowResult]
|
[Test]
|
||||||
[ImageSize(350, 280)]
|
public void LoremPicsum()
|
||||||
public void LoremPicsum(IContainer container)
|
|
||||||
{
|
{
|
||||||
container
|
RenderingTest
|
||||||
.Background("#FFF")
|
.Create()
|
||||||
.Padding(25)
|
.PageSize(350, 280)
|
||||||
.Stack(column =>
|
.FileName()
|
||||||
|
.Render(container =>
|
||||||
{
|
{
|
||||||
column.Spacing(10);
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(25)
|
||||||
|
.Stack(column =>
|
||||||
|
{
|
||||||
|
column.Spacing(10);
|
||||||
|
|
||||||
column
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.Component(new LoremPicsum(true));
|
.Component(new LoremPicsum(true));
|
||||||
|
|
||||||
column
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.AlignRight()
|
.AlignRight()
|
||||||
.Text("From Lorem Picsum");
|
.Text("From Lorem Picsum");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+139
-106
@@ -1,132 +1,165 @@
|
|||||||
using QuestPDF.Examples.Engine;
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
namespace QuestPDF.Examples
|
namespace QuestPDF.Examples
|
||||||
{
|
{
|
||||||
public class Examples : ExampleTestBase
|
public class Examples
|
||||||
{
|
{
|
||||||
public void Padding(IContainer container)
|
[Test]
|
||||||
|
public void Padding()
|
||||||
{
|
{
|
||||||
container
|
RenderingTest
|
||||||
.Background("#FDD")
|
.Create()
|
||||||
.Padding(50)
|
.PageSize(200, 150)
|
||||||
|
.FileName()
|
||||||
.Background("#AFA")
|
.Render(container =>
|
||||||
.PaddingVertical(50)
|
|
||||||
|
|
||||||
.Background("#77F")
|
|
||||||
.PaddingHorizontal(50)
|
|
||||||
|
|
||||||
.Background("#444");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Border(IContainer container)
|
|
||||||
{
|
|
||||||
container
|
|
||||||
.Background("#EEE")
|
|
||||||
.Padding(25)
|
|
||||||
|
|
||||||
.AlignBottom()
|
|
||||||
.AlignCenter()
|
|
||||||
.BorderBottom(2)
|
|
||||||
.BorderColor("#000")
|
|
||||||
|
|
||||||
.Background("FFF")
|
|
||||||
.Padding(5)
|
|
||||||
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji").Alignment(HorizontalAlignment.Center));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Alignment(IContainer container)
|
|
||||||
{
|
|
||||||
container
|
|
||||||
.Stack(column =>
|
|
||||||
{
|
{
|
||||||
column
|
container
|
||||||
.Item()
|
.Background("#FDD")
|
||||||
.Height(100)
|
.Padding(50)
|
||||||
.Background("#FFF")
|
|
||||||
|
.Background("#AFA")
|
||||||
.AlignLeft()
|
.PaddingVertical(50)
|
||||||
.AlignMiddle()
|
|
||||||
|
.Background("#77F")
|
||||||
|
.PaddingHorizontal(50)
|
||||||
|
|
||||||
.Width(50)
|
|
||||||
.Height(50)
|
|
||||||
.Background("#444");
|
.Background("#444");
|
||||||
|
|
||||||
column
|
|
||||||
.Item()
|
|
||||||
.Height(100)
|
|
||||||
.Background("#DDD")
|
|
||||||
|
|
||||||
.AlignCenter()
|
|
||||||
.AlignMiddle()
|
|
||||||
|
|
||||||
.Width(50)
|
|
||||||
.Height(50)
|
|
||||||
.Background("#222");
|
|
||||||
|
|
||||||
column
|
|
||||||
.Item()
|
|
||||||
.Height(100)
|
|
||||||
.Background("#BBB")
|
|
||||||
|
|
||||||
.AlignRight()
|
|
||||||
.AlignMiddle()
|
|
||||||
|
|
||||||
.Width(50)
|
|
||||||
.Height(50)
|
|
||||||
.Background("#000");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Expand(IContainer container)
|
[Test]
|
||||||
|
public void Border()
|
||||||
{
|
{
|
||||||
container
|
RenderingTest
|
||||||
.Stack(column =>
|
.Create()
|
||||||
|
.PageSize(200, 150)
|
||||||
|
.FileName()
|
||||||
|
.Render(container =>
|
||||||
{
|
{
|
||||||
column
|
container
|
||||||
.Item()
|
.Background("#EEE")
|
||||||
.Height(150)
|
.Padding(25)
|
||||||
.Row(row =>
|
|
||||||
|
.AlignBottom()
|
||||||
|
.AlignCenter()
|
||||||
|
.BorderBottom(2)
|
||||||
|
.BorderColor("#000")
|
||||||
|
|
||||||
|
.Background("FFF")
|
||||||
|
.Padding(5)
|
||||||
|
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji").Alignment(HorizontalAlignment.Center));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Alignment()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(200, 150)
|
||||||
|
.FileName()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Stack(column =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn()
|
column
|
||||||
.Extend()
|
.Item()
|
||||||
.Background("FFF")
|
.Height(100)
|
||||||
|
.Background("#FFF")
|
||||||
|
|
||||||
|
.AlignLeft()
|
||||||
|
.AlignMiddle()
|
||||||
|
|
||||||
.Height(50)
|
|
||||||
.Width(50)
|
.Width(50)
|
||||||
.Background("444");
|
|
||||||
|
|
||||||
row.RelativeColumn()
|
|
||||||
.Extend()
|
|
||||||
.Background("BBB")
|
|
||||||
|
|
||||||
.Height(50)
|
.Height(50)
|
||||||
.ExtendHorizontal()
|
.Background("#444");
|
||||||
.Background("444");
|
|
||||||
});
|
|
||||||
|
|
||||||
column
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.Height(150)
|
.Height(100)
|
||||||
.Row(row =>
|
.Background("#DDD")
|
||||||
{
|
|
||||||
row.RelativeColumn()
|
.AlignCenter()
|
||||||
.Extend()
|
.AlignMiddle()
|
||||||
.Background("BBB")
|
|
||||||
|
|
||||||
.ExtendVertical()
|
|
||||||
.Width(50)
|
.Width(50)
|
||||||
.Background("444");
|
.Height(50)
|
||||||
|
.Background("#222");
|
||||||
row.RelativeColumn()
|
|
||||||
.Extend()
|
column
|
||||||
.Background("BBB")
|
.Item()
|
||||||
|
.Height(100)
|
||||||
|
.Background("#BBB")
|
||||||
|
|
||||||
|
.AlignRight()
|
||||||
|
.AlignMiddle()
|
||||||
|
|
||||||
.ExtendVertical()
|
.Width(50)
|
||||||
.ExtendHorizontal()
|
.Height(50)
|
||||||
.Background("444");
|
.Background("#000");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Expand()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(200, 150)
|
||||||
|
.FileName()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Stack(column =>
|
||||||
|
{
|
||||||
|
column
|
||||||
|
.Item()
|
||||||
|
.Height(150)
|
||||||
|
.Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Extend()
|
||||||
|
.Background("FFF")
|
||||||
|
|
||||||
|
.Height(50)
|
||||||
|
.Width(50)
|
||||||
|
.Background("444");
|
||||||
|
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Extend()
|
||||||
|
.Background("BBB")
|
||||||
|
|
||||||
|
.Height(50)
|
||||||
|
.ExtendHorizontal()
|
||||||
|
.Background("444");
|
||||||
|
});
|
||||||
|
|
||||||
|
column
|
||||||
|
.Item()
|
||||||
|
.Height(150)
|
||||||
|
.Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Extend()
|
||||||
|
.Background("BBB")
|
||||||
|
|
||||||
|
.ExtendVertical()
|
||||||
|
.Width(50)
|
||||||
|
.Background("444");
|
||||||
|
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Extend()
|
||||||
|
.Background("BBB")
|
||||||
|
|
||||||
|
.ExtendVertical()
|
||||||
|
.ExtendHorizontal()
|
||||||
|
.Background("444");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,10 @@
|
|||||||
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
|
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="LibreBarcode39-Regular.ttf">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ namespace QuestPDF.ReportSample
|
|||||||
HeaderFields = HeaderFields(),
|
HeaderFields = HeaderFields(),
|
||||||
|
|
||||||
LogoData = Helpers.GetImage("Logo.png"),
|
LogoData = Helpers.GetImage("Logo.png"),
|
||||||
Sections = Enumerable.Range(0, 8).Select(x => GenerateSection()).ToList(),
|
Sections = Enumerable.Range(0, 50).Select(x => GenerateSection()).ToList(),
|
||||||
Photos = Enumerable.Range(2, 6).Select(x => GetReportPhotos()).ToList()
|
Photos = Enumerable.Range(0, 30).Select(x => GetReportPhotos()).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
List<ReportHeaderField> HeaderFields()
|
List<ReportHeaderField> HeaderFields()
|
||||||
@@ -52,8 +52,8 @@ namespace QuestPDF.ReportSample
|
|||||||
ReportSection GenerateSection()
|
ReportSection GenerateSection()
|
||||||
{
|
{
|
||||||
var sectionLength = Helpers.Random.NextDouble() > 0.75
|
var sectionLength = Helpers.Random.NextDouble() > 0.75
|
||||||
? Helpers.Random.Next(10, 20)
|
? Helpers.Random.Next(20, 40)
|
||||||
: Helpers.Random.Next(3, 11);
|
: Helpers.Random.Next(5, 10);
|
||||||
|
|
||||||
return new ReportSection
|
return new ReportSection
|
||||||
{
|
{
|
||||||
@@ -100,8 +100,7 @@ namespace QuestPDF.ReportSample
|
|||||||
{
|
{
|
||||||
Label = "Photos",
|
Label = "Photos",
|
||||||
Photos = Enumerable
|
Photos = Enumerable
|
||||||
.Range(0, Helpers.Random.Next(1, 10))
|
.Range(0, Helpers.Random.Next(1, 15))
|
||||||
.Select(x => Helpers.Random.Next(0, 128))
|
|
||||||
.Select(x => Placeholders.Image(400, 300))
|
.Select(x => Placeholders.Image(400, 300))
|
||||||
.ToList()
|
.ToList()
|
||||||
};
|
};
|
||||||
@@ -111,7 +110,7 @@ namespace QuestPDF.ReportSample
|
|||||||
{
|
{
|
||||||
return new ReportPhoto()
|
return new ReportPhoto()
|
||||||
{
|
{
|
||||||
PhotoData = Placeholders.Image(400, 300),
|
PhotoData = Placeholders.Image(800, 600),
|
||||||
|
|
||||||
Comments = Placeholders.Sentence(),
|
Comments = Placeholders.Sentence(),
|
||||||
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
|
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.Debug("Photo gallery").Grid(grid =>
|
container.Debug("Photos").Grid(grid =>
|
||||||
{
|
{
|
||||||
grid.Spacing(5);
|
grid.Spacing(5);
|
||||||
grid.Columns(3);
|
grid.Columns(3);
|
||||||
|
|||||||
@@ -18,21 +18,23 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
{
|
{
|
||||||
return new DocumentMetadata()
|
return new DocumentMetadata()
|
||||||
{
|
{
|
||||||
Title = Model.Title,
|
Title = Model.Title
|
||||||
Size = PageSizes.A4
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Compose(IContainer container)
|
public void Compose(IDocumentContainer container)
|
||||||
{
|
{
|
||||||
container
|
container
|
||||||
.PaddingVertical(40)
|
|
||||||
.PaddingHorizontal(50)
|
|
||||||
.Page(page =>
|
.Page(page =>
|
||||||
{
|
{
|
||||||
|
page.MarginVertical(40);
|
||||||
|
page.MarginHorizontal(50);
|
||||||
|
|
||||||
|
page.Size(PageSizes.A4);
|
||||||
|
|
||||||
page.Header().Element(ComposeHeader);
|
page.Header().Element(ComposeHeader);
|
||||||
page.Content().Element(ComposeContent);
|
page.Content().Element(ComposeContent);
|
||||||
page.Footer().AlignCenter().PageNumber("Page {number}");
|
page.Footer().AlignCenter().PageNumber();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
{
|
{
|
||||||
row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
|
row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
|
||||||
row.RelativeColumn().Text(locationName, Typography.Normal);
|
row.RelativeColumn().Text(locationName, Typography.Normal);
|
||||||
|
row.ConstantColumn(150).AlignRight().PageNumber($"Page {{pdf:{locationName}}}", Typography.Normal.AlignRight());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ namespace QuestPDF.ReportSample
|
|||||||
[Test]
|
[Test]
|
||||||
public void PerformanceBenchmark()
|
public void PerformanceBenchmark()
|
||||||
{
|
{
|
||||||
|
// target document length should be around 100 pages
|
||||||
|
|
||||||
// test size
|
// test size
|
||||||
const int testSize = 100;
|
const int testSize = 100;
|
||||||
const decimal performanceTarget = 5; // documents per second
|
const decimal performanceTarget = 1; // documents per second
|
||||||
|
|
||||||
// create report models
|
// create report models
|
||||||
var reports = Enumerable
|
var reports = Enumerable
|
||||||
|
|||||||
+105
-34
@@ -1,4 +1,5 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using FluentAssertions.Equivalency;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using QuestPDF.Elements;
|
using QuestPDF.Elements;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
@@ -6,7 +7,7 @@ using QuestPDF.UnitTests.TestEngine;
|
|||||||
|
|
||||||
namespace QuestPDF.UnitTests
|
namespace QuestPDF.UnitTests
|
||||||
{
|
{
|
||||||
/*[TestFixture]
|
[TestFixture]
|
||||||
public class GridTests
|
public class GridTests
|
||||||
{
|
{
|
||||||
#region Alignment
|
#region Alignment
|
||||||
@@ -39,30 +40,30 @@ namespace QuestPDF.UnitTests
|
|||||||
// assert
|
// assert
|
||||||
var expected = new Container();
|
var expected = new Container();
|
||||||
|
|
||||||
expected.Stack(stack =>
|
expected.Container().Stack(stack =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(6).Element(childA);
|
row.RelativeColumn(6).Container().Element(childA);
|
||||||
row.RelativeColumn(4).Element(childB);
|
row.RelativeColumn(4).Container().Element(childB);
|
||||||
row.RelativeColumn(2).Element(Empty.Instance);
|
row.RelativeColumn(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(4).Element(childC);
|
row.RelativeColumn(4).Container().Element(childC);
|
||||||
row.RelativeColumn(2).Element(childD);
|
row.RelativeColumn(2).Container().Element(childD);
|
||||||
row.RelativeColumn(6).Element(Empty.Instance);
|
row.RelativeColumn(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(8).Element(childE);
|
row.RelativeColumn(8).Container().Element(childE);
|
||||||
row.RelativeColumn(4).Element(Empty.Instance);
|
row.RelativeColumn(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
|
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -93,33 +94,33 @@ namespace QuestPDF.UnitTests
|
|||||||
// assert
|
// assert
|
||||||
var expected = new Container();
|
var expected = new Container();
|
||||||
|
|
||||||
expected.Stack(stack =>
|
expected.Container().Stack(stack =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(1).Element(Empty.Instance);
|
row.RelativeColumn(1);
|
||||||
row.RelativeColumn(6).Element(childA);
|
row.RelativeColumn(6).Container().Element(childA);
|
||||||
row.RelativeColumn(4).Element(childB);
|
row.RelativeColumn(4).Container().Element(childB);
|
||||||
row.RelativeColumn(1).Element(Empty.Instance);
|
row.RelativeColumn(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(3).Element(Empty.Instance);
|
row.RelativeColumn(3);
|
||||||
row.RelativeColumn(4).Element(childC);
|
row.RelativeColumn(4).Container().Element(childC);
|
||||||
row.RelativeColumn(2).Element(childD);
|
row.RelativeColumn(2).Container().Element(childD);
|
||||||
row.RelativeColumn(3).Element(Empty.Instance);
|
row.RelativeColumn(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).Element(Empty.Instance);
|
row.RelativeColumn(2);
|
||||||
row.RelativeColumn(8).Element(childE);
|
row.RelativeColumn(8).Container().Element(childE);
|
||||||
row.RelativeColumn(2).Element(Empty.Instance);
|
row.RelativeColumn(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
|
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -154,28 +155,98 @@ namespace QuestPDF.UnitTests
|
|||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).Element(Empty.Instance);
|
row.RelativeColumn(2);
|
||||||
row.RelativeColumn(6).Element(childA);
|
row.RelativeColumn(6).Container().Element(childA);
|
||||||
row.RelativeColumn(4).Element(childB);
|
row.RelativeColumn(4).Container().Element(childB);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(6).Element(Empty.Instance);
|
row.RelativeColumn(6);
|
||||||
row.RelativeColumn(4).Element(childC);
|
row.RelativeColumn(4).Container().Element(childC);
|
||||||
row.RelativeColumn(2).Element(childD);
|
row.RelativeColumn(2).Container().Element(childD);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
stack.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(4).Element(Empty.Instance);
|
row.RelativeColumn(4);
|
||||||
row.RelativeColumn(8).Element(childE);
|
row.RelativeColumn(8).Container().Element(childE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
structure.Child.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering().IncludingAllRuntimeProperties());
|
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}*/
|
|
||||||
|
#region Spacing
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Spacing()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var structure = new Container();
|
||||||
|
|
||||||
|
var childA = TestPlan.CreateUniqueElement();
|
||||||
|
var childB = TestPlan.CreateUniqueElement();
|
||||||
|
var childC = TestPlan.CreateUniqueElement();
|
||||||
|
var childD = TestPlan.CreateUniqueElement();
|
||||||
|
|
||||||
|
// act
|
||||||
|
structure
|
||||||
|
.Grid(grid =>
|
||||||
|
{
|
||||||
|
grid.Columns(16);
|
||||||
|
grid.AlignCenter();
|
||||||
|
|
||||||
|
grid.VerticalSpacing(20);
|
||||||
|
grid.HorizontalSpacing(30);
|
||||||
|
|
||||||
|
grid.Item(5).Element(childA);
|
||||||
|
grid.Item(5).Element(childB);
|
||||||
|
grid.Item(10).Element(childC);
|
||||||
|
grid.Item(12).Element(childD);
|
||||||
|
});
|
||||||
|
|
||||||
|
// assert
|
||||||
|
var expected = new Container();
|
||||||
|
|
||||||
|
expected.Container().Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Spacing(20);
|
||||||
|
|
||||||
|
stack.Item().Row(row =>
|
||||||
|
{
|
||||||
|
row.Spacing(30);
|
||||||
|
|
||||||
|
row.RelativeColumn(3);
|
||||||
|
row.RelativeColumn(5).Container().Element(childA);
|
||||||
|
row.RelativeColumn(5).Container().Element(childB);
|
||||||
|
row.RelativeColumn(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
stack.Item().Row(row =>
|
||||||
|
{
|
||||||
|
row.Spacing(30);
|
||||||
|
|
||||||
|
row.RelativeColumn(3);
|
||||||
|
row.RelativeColumn(10).Container().Element(childC);
|
||||||
|
row.RelativeColumn(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
stack.Item().Row(row =>
|
||||||
|
{
|
||||||
|
row.Spacing(30);
|
||||||
|
|
||||||
|
row.RelativeColumn(2);
|
||||||
|
row.RelativeColumn(12).Container().Element(childD);
|
||||||
|
row.RelativeColumn(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
structure.Should().BeEquivalentTo(expected, o => o.WithTracing().WithAutoConversion().WithStrictOrdering().IncludingAllRuntimeProperties().AllowingInfiniteRecursion());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ namespace QuestPDF.UnitTests
|
|||||||
.For(x => new PageBreak())
|
.For(x => new PageBreak())
|
||||||
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.CheckMeasureResult(new PartialRender(400, 300))
|
.CheckMeasureResult(new PartialRender(Size.Zero))
|
||||||
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.CheckDrawResult()
|
.CheckDrawResult()
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ namespace QuestPDF.UnitTests
|
|||||||
Child = child.Object
|
Child = child.Object
|
||||||
};
|
};
|
||||||
|
|
||||||
element.Draw(null, Size.Zero);
|
element.Draw(Size.Zero);
|
||||||
element.Draw(null, Size.Zero);
|
element.Draw(Size.Zero);
|
||||||
|
|
||||||
child.Verify(x => x.Draw(It.IsAny<ICanvas>(), It.IsAny<Size>()), Times.Once);
|
child.Verify(x => x.Draw(It.IsAny<Size>()), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
|
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
|
||||||
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
|
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
|
||||||
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
||||||
|
|
||||||
public void Translate(Position vector) => TranslateFunc(vector);
|
public void Translate(Position vector) => TranslateFunc(vector);
|
||||||
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
||||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||||
|
|
||||||
public void DrawExternalLink(string url, Size size)
|
public void DrawExternalLink(string url, Size size)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
public Action<Size> DrawFunc { get; set; }
|
public Action<Size> DrawFunc { get; set; }
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace) => MeasureFunc(availableSpace);
|
internal override ISpacePlan Measure(Size availableSpace) => MeasureFunc(availableSpace);
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace) => DrawFunc(availableSpace);
|
internal override void Draw(Size availableSpace) => DrawFunc(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,6 +205,8 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
|
|
||||||
public TestPlan CheckMeasureResult(ISpacePlan expected)
|
public TestPlan CheckMeasureResult(ISpacePlan expected)
|
||||||
{
|
{
|
||||||
|
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
|
||||||
|
|
||||||
var actual = Element.Measure(OperationInput);
|
var actual = Element.Measure(OperationInput);
|
||||||
|
|
||||||
Assert.AreEqual(expected.GetType(), actual.GetType());
|
Assert.AreEqual(expected.GetType(), actual.GetType());
|
||||||
@@ -223,7 +225,8 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
|
|
||||||
public TestPlan CheckDrawResult()
|
public TestPlan CheckDrawResult()
|
||||||
{
|
{
|
||||||
Element.Draw(Canvas, OperationInput);
|
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
|
||||||
|
Element.Draw(OperationInput);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
using QuestPDF.Infrastructure;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace QuestPDF.Drawing
|
|
||||||
{
|
|
||||||
internal class Canvas : ICanvas
|
|
||||||
{
|
|
||||||
internal SKCanvas SkiaCanvas { get; }
|
|
||||||
|
|
||||||
public Canvas(SKCanvas skiaCanvas)
|
|
||||||
{
|
|
||||||
SkiaCanvas = skiaCanvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Canvas()
|
|
||||||
{
|
|
||||||
SkiaCanvas.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Translate(Position vector)
|
|
||||||
{
|
|
||||||
SkiaCanvas.Translate(vector.X, vector.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawRectangle(Position vector, Size size, string color)
|
|
||||||
{
|
|
||||||
if (size.Width < Size.Epsilon || size.Height < Size.Epsilon)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var paint = color.ColorToPaint();
|
|
||||||
SkiaCanvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawText(string text, Position vector, TextStyle style)
|
|
||||||
{
|
|
||||||
SkiaCanvas.DrawText(text, vector.X, vector.Y, style.ToPaint());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawImage(SKImage image, Position vector, Size size)
|
|
||||||
{
|
|
||||||
SkiaCanvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawExternalLink(string url, Size size)
|
|
||||||
{
|
|
||||||
SkiaCanvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawLocationLink(string locationName, Size size)
|
|
||||||
{
|
|
||||||
SkiaCanvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawLocation(string locationName)
|
|
||||||
{
|
|
||||||
SkiaCanvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Drawing
|
||||||
|
{
|
||||||
|
internal class DocumentContainer : IDocumentContainer
|
||||||
|
{
|
||||||
|
internal List<IComponent> Pages { get; set; } = new List<IComponent>();
|
||||||
|
|
||||||
|
internal Container Compose()
|
||||||
|
{
|
||||||
|
var container = new Container();
|
||||||
|
|
||||||
|
container
|
||||||
|
.Stack(stack =>
|
||||||
|
{
|
||||||
|
Pages
|
||||||
|
.SelectMany(x => new List<Action>()
|
||||||
|
{
|
||||||
|
() => stack.Item().PageBreak(),
|
||||||
|
() => stack.Item().Component(x)
|
||||||
|
})
|
||||||
|
.Skip(1)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(x => x());
|
||||||
|
});
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using QuestPDF.Drawing.Exceptions;
|
using QuestPDF.Drawing.Exceptions;
|
||||||
using QuestPDF.Drawing.SpacePlan;
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Elements;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
@@ -14,115 +16,78 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
internal static void GeneratePdf(Stream stream, IDocument document)
|
internal static void GeneratePdf(Stream stream, IDocument document)
|
||||||
{
|
{
|
||||||
var content = ElementExtensions.Create(document.Compose);
|
|
||||||
var metadata = document.GetMetadata();
|
var metadata = document.GetMetadata();
|
||||||
|
var canvas = new PdfCanvas(stream, metadata);
|
||||||
|
RenderDocument(canvas, document);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ICollection<byte[]> GenerateImages(IDocument document)
|
||||||
|
{
|
||||||
|
var metadata = document.GetMetadata();
|
||||||
|
var canvas = new ImageCanvas(metadata);
|
||||||
|
RenderDocument(canvas, document);
|
||||||
|
|
||||||
|
return canvas.Images;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
|
||||||
|
where TCanvas : ICanvas, IRenderingCanvas
|
||||||
|
{
|
||||||
|
var container = new DocumentContainer();
|
||||||
|
document.Compose(container);
|
||||||
|
var content = container.Compose();
|
||||||
|
|
||||||
using var pdf = SKDocument.CreatePdf(stream, MapMetadata(metadata));
|
var metadata = document.GetMetadata();
|
||||||
var totalPages = 1;
|
var pageContext = new PageContext();
|
||||||
|
|
||||||
|
RenderPass(pageContext, new FreeCanvas(), content, metadata);
|
||||||
|
RenderPass(pageContext, canvas, content, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata)
|
||||||
|
where TCanvas : ICanvas, IRenderingCanvas
|
||||||
|
{
|
||||||
|
content.HandleVisitor(x => x?.Initialize(pageContext, canvas));
|
||||||
|
content.HandleVisitor(x => (x as IStateResettable)?.ResetState());
|
||||||
|
|
||||||
|
canvas.BeginDocument();
|
||||||
|
|
||||||
|
var currentPage = 1;
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
var spacePlan = content.Measure(metadata.Size);
|
pageContext.SetPageNumber(currentPage);
|
||||||
|
var spacePlan = content.Measure(Size.Max) as Size;
|
||||||
|
|
||||||
using var skiaCanvas = pdf.BeginPage(metadata.Size.Width, metadata.Size.Height);
|
if (spacePlan == null)
|
||||||
var canvas = new Canvas(skiaCanvas);
|
break;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
content.Draw(canvas, metadata.Size);
|
canvas.BeginPage(spacePlan);
|
||||||
|
content.Draw(spacePlan);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
pdf.Close();
|
canvas.EndDocument();
|
||||||
throw new DocumentDrawingException("An exception occured during document drawing.", exception);
|
throw new DocumentDrawingException("An exception occured during document drawing.", exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf.EndPage();
|
canvas.EndPage();
|
||||||
|
|
||||||
if (totalPages >= metadata.DocumentLayoutExceptionThreshold)
|
if (currentPage >= documentMetadata.DocumentLayoutExceptionThreshold)
|
||||||
{
|
{
|
||||||
pdf.Close();
|
canvas.EndDocument();
|
||||||
throw new DocumentLayoutException("Composed layout generates infinite document.");
|
throw new DocumentLayoutException("Composed layout generates infinite document.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spacePlan is FullRender)
|
if (spacePlan is FullRender)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
totalPages++;
|
currentPage++;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf.Close();
|
canvas.EndDocument();
|
||||||
}
|
|
||||||
|
|
||||||
internal static IEnumerable<byte[]> GenerateImages(IDocument document)
|
|
||||||
{
|
|
||||||
var content = ElementExtensions.Create(document.Compose);
|
|
||||||
var metadata = document.GetMetadata();
|
|
||||||
|
|
||||||
var totalPages = 1;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var spacePlan = content.Measure(metadata.Size);
|
|
||||||
byte[] result;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = RenderPage(content);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new DocumentDrawingException("An exception occured during document drawing.", exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return result;
|
|
||||||
|
|
||||||
if (totalPages >= metadata.DocumentLayoutExceptionThreshold)
|
|
||||||
{
|
|
||||||
throw new DocumentLayoutException("Composed layout generates infinite document.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spacePlan is FullRender)
|
|
||||||
break;
|
|
||||||
|
|
||||||
totalPages++;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] RenderPage(Element element)
|
|
||||||
{
|
|
||||||
// scale the result so it is more readable
|
|
||||||
var scalingFactor = metadata.RasterDpi / (float) PageSizes.PointsPerInch;
|
|
||||||
|
|
||||||
var imageInfo = new SKImageInfo((int) (metadata.Size.Width * scalingFactor), (int) (metadata.Size.Height * scalingFactor));
|
|
||||||
using var surface = SKSurface.Create(imageInfo);
|
|
||||||
surface.Canvas.Scale(scalingFactor);
|
|
||||||
|
|
||||||
var canvas = new Canvas(surface.Canvas);
|
|
||||||
element?.Draw(canvas, metadata.Size);
|
|
||||||
|
|
||||||
surface.Canvas.Save();
|
|
||||||
return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
|
|
||||||
{
|
|
||||||
return new SKDocumentPdfMetadata
|
|
||||||
{
|
|
||||||
Title = metadata.Title,
|
|
||||||
Author = metadata.Author,
|
|
||||||
Subject = metadata.Subject,
|
|
||||||
Keywords = metadata.Keywords,
|
|
||||||
Creator = metadata.Creator,
|
|
||||||
Producer = metadata.Producer,
|
|
||||||
|
|
||||||
Creation = metadata.CreationDate,
|
|
||||||
Modified = metadata.ModifiedDate,
|
|
||||||
|
|
||||||
RasterDpi = metadata.RasterDpi,
|
|
||||||
EncodingQuality = metadata.ImageQuality,
|
|
||||||
PdfA = metadata.PdfA
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,6 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
public class DocumentMetadata
|
public class DocumentMetadata
|
||||||
{
|
{
|
||||||
public Size Size { get; set; } = PageSizes.A4;
|
|
||||||
public int ImageQuality { get; set; } = 101;
|
public int ImageQuality { get; set; } = 101;
|
||||||
public int RasterDpi { get; set; } = 72;
|
public int RasterDpi { get; set; } = 72;
|
||||||
public bool PdfA { get; set; }
|
public bool PdfA { get; set; }
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
using System.Collections.Concurrent;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.IO;
|
||||||
using QuestPDF.Drawing.SpacePlan;
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace QuestPDF.Drawing
|
namespace QuestPDF.Drawing
|
||||||
{
|
{
|
||||||
internal static class CanvasCache
|
public static class FontManager
|
||||||
{
|
{
|
||||||
|
private static ConcurrentDictionary<string, SKTypeface> Typefaces = new ConcurrentDictionary<string, SKTypeface>();
|
||||||
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
||||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
||||||
|
|
||||||
|
public static void RegisterFontType(string fontName, Stream stream)
|
||||||
|
{
|
||||||
|
Typefaces.TryAdd(fontName, SKTypeface.FromStream(stream));
|
||||||
|
}
|
||||||
|
|
||||||
internal static SKPaint ColorToPaint(this string color)
|
internal static SKPaint ColorToPaint(this string color)
|
||||||
{
|
{
|
||||||
return ColorPaint.GetOrAdd(color, Convert);
|
return ColorPaint.GetOrAdd(color, Convert);
|
||||||
@@ -29,12 +37,10 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
static SKPaint Convert(TextStyle style)
|
static SKPaint Convert(TextStyle style)
|
||||||
{
|
{
|
||||||
var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
|
||||||
|
|
||||||
return new SKPaint
|
return new SKPaint
|
||||||
{
|
{
|
||||||
Color = SKColor.Parse(style.Color),
|
Color = SKColor.Parse(style.Color),
|
||||||
Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
|
Typeface = GetTypeface(style),
|
||||||
TextSize = style.Size,
|
TextSize = style.Size,
|
||||||
TextEncoding = SKTextEncoding.Utf32,
|
TextEncoding = SKTextEncoding.Utf32,
|
||||||
|
|
||||||
@@ -47,6 +53,17 @@ namespace QuestPDF.Drawing
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SKTypeface GetTypeface(TextStyle style)
|
||||||
|
{
|
||||||
|
if (Typefaces.TryGetValue(style.FontType, out var result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||||
|
|
||||||
|
return SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant)
|
||||||
|
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static TextMeasurement BreakText(this TextStyle style, string text, float availableWidth)
|
internal static TextMeasurement BreakText(this TextStyle style, string text, float availableWidth)
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Drawing
|
||||||
|
{
|
||||||
|
internal class FreeCanvas : ICanvas, IRenderingCanvas
|
||||||
|
{
|
||||||
|
#region IRenderingCanvas
|
||||||
|
|
||||||
|
public void BeginDocument()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndDocument()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginPage(Size size)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndPage()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ICanvas
|
||||||
|
|
||||||
|
public void Translate(Position vector)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawRectangle(Position vector, Size size, string color)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawText(string text, Position position, TextStyle style)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawImage(SKImage image, Position position, Size size)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawExternalLink(string url, Size size)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawLocationLink(string locationName, Size size)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawLocation(string locationName)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Drawing
|
||||||
|
{
|
||||||
|
internal class ImageCanvas : SkiaCanvasBase
|
||||||
|
{
|
||||||
|
private DocumentMetadata Metadata { get; }
|
||||||
|
private SKSurface Surface { get; set; }
|
||||||
|
|
||||||
|
internal ICollection<byte[]> Images { get; } = new List<byte[]>();
|
||||||
|
|
||||||
|
public ImageCanvas(DocumentMetadata metadata)
|
||||||
|
{
|
||||||
|
Metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BeginDocument()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void EndDocument()
|
||||||
|
{
|
||||||
|
Canvas?.Dispose();
|
||||||
|
Surface?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BeginPage(Size size)
|
||||||
|
{
|
||||||
|
var scalingFactor = Metadata.RasterDpi / (float) PageSizes.PointsPerInch;
|
||||||
|
var imageInfo = new SKImageInfo((int) (size.Width * scalingFactor), (int) (size.Height * scalingFactor));
|
||||||
|
|
||||||
|
Surface = SKSurface.Create(imageInfo);
|
||||||
|
Canvas = Surface.Canvas;
|
||||||
|
|
||||||
|
Canvas.Scale(scalingFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void EndPage()
|
||||||
|
{
|
||||||
|
Canvas.Save();
|
||||||
|
var image = Surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
|
||||||
|
Images.Add(image);
|
||||||
|
|
||||||
|
Canvas.Dispose();
|
||||||
|
Surface.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System.IO;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Drawing
|
||||||
|
{
|
||||||
|
internal class PdfCanvas : SkiaCanvasBase
|
||||||
|
{
|
||||||
|
private SKDocument Document { get; }
|
||||||
|
|
||||||
|
public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
|
||||||
|
{
|
||||||
|
Document = SKDocument.CreatePdf(stream, MapMetadata(documentMetadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
~PdfCanvas()
|
||||||
|
{
|
||||||
|
Document.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BeginDocument()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void EndDocument()
|
||||||
|
{
|
||||||
|
Canvas.Dispose();
|
||||||
|
|
||||||
|
Document.Close();
|
||||||
|
Document.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BeginPage(Size size)
|
||||||
|
{
|
||||||
|
Canvas = Document.BeginPage(size.Width, size.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void EndPage()
|
||||||
|
{
|
||||||
|
Document.EndPage();
|
||||||
|
Canvas.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
|
||||||
|
{
|
||||||
|
return new SKDocumentPdfMetadata
|
||||||
|
{
|
||||||
|
Title = metadata.Title,
|
||||||
|
Author = metadata.Author,
|
||||||
|
Subject = metadata.Subject,
|
||||||
|
Keywords = metadata.Keywords,
|
||||||
|
Creator = metadata.Creator,
|
||||||
|
Producer = metadata.Producer,
|
||||||
|
|
||||||
|
Creation = metadata.CreationDate,
|
||||||
|
Modified = metadata.ModifiedDate,
|
||||||
|
|
||||||
|
RasterDpi = metadata.RasterDpi,
|
||||||
|
EncodingQuality = metadata.ImageQuality,
|
||||||
|
PdfA = metadata.PdfA
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Drawing
|
||||||
|
{
|
||||||
|
internal abstract class SkiaCanvasBase : ICanvas, IRenderingCanvas
|
||||||
|
{
|
||||||
|
internal SKCanvas Canvas { get; set; }
|
||||||
|
|
||||||
|
public abstract void BeginDocument();
|
||||||
|
public abstract void EndDocument();
|
||||||
|
|
||||||
|
public abstract void BeginPage(Size size);
|
||||||
|
public abstract void EndPage();
|
||||||
|
|
||||||
|
public void Translate(Position vector)
|
||||||
|
{
|
||||||
|
Canvas.Translate(vector.X, vector.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawRectangle(Position vector, Size size, string color)
|
||||||
|
{
|
||||||
|
if (size.Width < Size.Epsilon || size.Height < Size.Epsilon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var paint = color.ColorToPaint();
|
||||||
|
Canvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawText(string text, Position vector, TextStyle style)
|
||||||
|
{
|
||||||
|
Canvas.DrawText(text, vector.X, vector.Y, style.ToPaint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawImage(SKImage image, Position vector, Size size)
|
||||||
|
{
|
||||||
|
Canvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawExternalLink(string url, Size size)
|
||||||
|
{
|
||||||
|
Canvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawLocationLink(string locationName, Size size)
|
||||||
|
{
|
||||||
|
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawLocation(string locationName)
|
||||||
|
{
|
||||||
|
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace QuestPDF.Elements
|
|||||||
public VerticalAlignment Vertical { get; set; } = VerticalAlignment.Top;
|
public VerticalAlignment Vertical { get; set; } = VerticalAlignment.Top;
|
||||||
public HorizontalAlignment Horizontal { get; set; } = HorizontalAlignment.Left;
|
public HorizontalAlignment Horizontal { get; set; } = HorizontalAlignment.Left;
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (Child == null)
|
if (Child == null)
|
||||||
return;
|
return;
|
||||||
@@ -21,9 +21,9 @@ namespace QuestPDF.Elements
|
|||||||
var top = GetTopOffset(availableSpace, childSize);
|
var top = GetTopOffset(availableSpace, childSize);
|
||||||
var left = GetLeftOffset(availableSpace, childSize);
|
var left = GetLeftOffset(availableSpace, childSize);
|
||||||
|
|
||||||
canvas.Translate(new Position(left, top));
|
Canvas.Translate(new Position(left, top));
|
||||||
Child.Draw(canvas, childSize);
|
Child.Draw(childSize);
|
||||||
canvas.Translate(new Position(-left, -top));
|
Canvas.Translate(new Position(-left, -top));
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetTopOffset(Size availableSpace, Size childSize)
|
private float GetTopOffset(Size availableSpace, Size childSize)
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ namespace QuestPDF.Elements
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (Child == null)
|
if (Child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var size = GetTargetSize(availableSpace);
|
var size = GetTargetSize(availableSpace);
|
||||||
Child?.Draw(canvas, size);
|
Child?.Draw(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Size GetTargetSize(Size availableSpace)
|
private Size GetTargetSize(Size availableSpace)
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public string Color { get; set; } = Colors.Black;
|
public string Color { get; set; } = Colors.Black;
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
canvas.DrawRectangle(Position.Zero, availableSpace, Color);
|
Canvas.DrawRectangle(Position.Zero, availableSpace, Color);
|
||||||
Child?.Draw(canvas, availableSpace);
|
Child?.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,26 +12,26 @@ namespace QuestPDF.Elements
|
|||||||
public float Bottom { get; set; }
|
public float Bottom { get; set; }
|
||||||
public float Left { get; set; }
|
public float Left { get; set; }
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
Child?.Draw(canvas, availableSpace);
|
Child?.Draw(availableSpace);
|
||||||
|
|
||||||
canvas.DrawRectangle(
|
Canvas.DrawRectangle(
|
||||||
new Position(-Left/2, -Top/2),
|
new Position(-Left/2, -Top/2),
|
||||||
new Size(availableSpace.Width + Left/2 + Right/2, Top),
|
new Size(availableSpace.Width + Left/2 + Right/2, Top),
|
||||||
Color);
|
Color);
|
||||||
|
|
||||||
canvas.DrawRectangle(
|
Canvas.DrawRectangle(
|
||||||
new Position(-Left/2, -Top/2),
|
new Position(-Left/2, -Top/2),
|
||||||
new Size(Left, availableSpace.Height + Top/2 + Bottom/2),
|
new Size(Left, availableSpace.Height + Top/2 + Bottom/2),
|
||||||
Color);
|
Color);
|
||||||
|
|
||||||
canvas.DrawRectangle(
|
Canvas.DrawRectangle(
|
||||||
new Position(-Left/2, availableSpace.Height-Bottom/2),
|
new Position(-Left/2, availableSpace.Height-Bottom/2),
|
||||||
new Size(availableSpace.Width + Left/2 + Right/2, Bottom),
|
new Size(availableSpace.Width + Left/2 + Right/2, Bottom),
|
||||||
Color);
|
Color);
|
||||||
|
|
||||||
canvas.DrawRectangle(
|
Canvas.DrawRectangle(
|
||||||
new Position(availableSpace.Width-Right/2, -Top/2),
|
new Position(availableSpace.Width-Right/2, -Top/2),
|
||||||
new Size(Right, availableSpace.Height + Top/2 + Bottom/2),
|
new Size(Right, availableSpace.Height + Top/2 + Bottom/2),
|
||||||
Color);
|
Color);
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
internal class Box : ContainerElement
|
internal class Box : ContainerElement
|
||||||
{
|
{
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var targetSize = Child?.Measure(availableSpace) as Size;
|
var targetSize = Child?.Measure(availableSpace) as Size;
|
||||||
|
|
||||||
if (targetSize == null)
|
if (targetSize == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Child?.Draw(canvas, targetSize);
|
Child?.Draw(targetSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,9 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(availableSpace);
|
return new FullRender(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var skiaCanvas = (canvas as Drawing.Canvas)?.SkiaCanvas;
|
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||||
|
|
||||||
if (Handler == null || skiaCanvas == null)
|
if (Handler == null || skiaCanvas == null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ namespace QuestPDF.Elements
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var available = new Size(
|
var available = new Size(
|
||||||
MathHelpers.Min(MaxWidth, availableSpace.Width),
|
MathHelpers.Min(MaxWidth, availableSpace.Width),
|
||||||
MathHelpers.Min(MaxHeight, availableSpace.Height));
|
MathHelpers.Min(MaxHeight, availableSpace.Height));
|
||||||
|
|
||||||
Child?.Draw(canvas, available);
|
Child?.Draw(available);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,15 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public Element DecorationElement { get; set; } = Empty.Instance;
|
public Element DecorationElement { get; set; } = Empty.Instance;
|
||||||
public Element ContentElement { get; set; } = Empty.Instance;
|
public Element ContentElement { get; set; } = Empty.Instance;
|
||||||
public DecorationType Type { get; set; }
|
public DecorationType Type { get; set; }
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
DecorationElement?.HandleVisitor(visit);
|
||||||
|
ContentElement?.HandleVisitor(visit);
|
||||||
|
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
@@ -42,22 +50,22 @@ namespace QuestPDF.Elements
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var decorationSize = DecorationElement?.Measure(availableSpace) as Size ?? Size.Zero;
|
var decorationSize = DecorationElement?.Measure(availableSpace) as Size ?? Size.Zero;
|
||||||
var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
|
var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
|
||||||
|
|
||||||
var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
|
var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
|
||||||
Action drawDecoration = () => DecorationElement?.Draw(canvas, new Size(availableSpace.Width, decorationSize.Height));
|
Action drawDecoration = () => DecorationElement?.Draw(new Size(availableSpace.Width, decorationSize.Height));
|
||||||
Action drawContent = () => ContentElement?.Draw(canvas, new Size (availableSpace.Width, contentSize.Height));
|
Action drawContent = () => ContentElement?.Draw(new Size (availableSpace.Width, contentSize.Height));
|
||||||
|
|
||||||
var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
|
var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
|
||||||
var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
|
var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
|
||||||
|
|
||||||
first();
|
first();
|
||||||
canvas.Translate(new Position(0, translateHeight));
|
Canvas.Translate(new Position(0, translateHeight));
|
||||||
second();
|
second();
|
||||||
canvas.Translate(new Position(0, -translateHeight));
|
Canvas.Translate(new Position(0, -translateHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(availableSpace.Width, availableSpace.Height);
|
return new FullRender(availableSpace.Width, availableSpace.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var imageData = Source?.Invoke(availableSpace);
|
var imageData = Source?.Invoke(availableSpace);
|
||||||
|
|
||||||
@@ -26,7 +26,8 @@ namespace QuestPDF.Elements
|
|||||||
InternalImage = SKImage.FromEncodedData(imageData)
|
InternalImage = SKImage.FromEncodedData(imageData)
|
||||||
};
|
};
|
||||||
|
|
||||||
imageElement.Draw(canvas, availableSpace);
|
imageElement.Initialize(PageContext, Canvas);
|
||||||
|
imageElement.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(Size.Zero);
|
return new FullRender(Size.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public string Url { get; set; } = "https://www.questpdf.com";
|
public string Url { get; set; } = "https://www.questpdf.com";
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var targetSize = Child?.Measure(availableSpace) as Size;
|
var targetSize = Child?.Measure(availableSpace) as Size;
|
||||||
|
|
||||||
if (targetSize == null)
|
if (targetSize == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
canvas.DrawExternalLink(Url, targetSize);
|
Canvas.DrawExternalLink(Url, targetSize);
|
||||||
Child?.Draw(canvas, availableSpace);
|
Child?.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ namespace QuestPDF.Elements
|
|||||||
|
|
||||||
container.Stack(stack =>
|
container.Stack(stack =>
|
||||||
{
|
{
|
||||||
stack.Spacing(HorizontalSpacing);
|
stack.Spacing(VerticalSpacing);
|
||||||
|
|
||||||
while (ChildrenQueue.Any())
|
while (ChildrenQueue.Any())
|
||||||
stack.Item().Row(BuildRow);
|
stack.Item().Row(BuildRow);
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(availableSpace);
|
return new FullRender(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (InternalImage == null)
|
if (InternalImage == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
|
Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,15 +6,15 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public string LocationName { get; set; }
|
public string LocationName { get; set; }
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var targetSize = Child?.Measure(availableSpace) as Size;
|
var targetSize = Child?.Measure(availableSpace) as Size;
|
||||||
|
|
||||||
if (targetSize == null)
|
if (targetSize == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
canvas.DrawLocationLink(LocationName, targetSize);
|
Canvas.DrawLocationLink(LocationName, targetSize);
|
||||||
Child?.Draw(canvas, availableSpace);
|
Child?.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,14 +2,27 @@
|
|||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class InternalLocation : ContainerElement
|
internal class InternalLocation : ContainerElement, IStateResettable
|
||||||
{
|
{
|
||||||
public string LocationName { get; set; }
|
public string LocationName { get; set; }
|
||||||
|
private bool IsRendered { get; set; }
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
public void ResetState()
|
||||||
{
|
{
|
||||||
canvas.DrawLocation(LocationName);
|
IsRendered = false;
|
||||||
Child?.Draw(canvas, availableSpace);
|
}
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
if (!IsRendered)
|
||||||
|
{
|
||||||
|
PageContext.SetLocationPage(LocationName);
|
||||||
|
|
||||||
|
Canvas.DrawLocation(LocationName);
|
||||||
|
IsRendered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Child?.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using QuestPDF.Drawing.SpacePlan;
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
@@ -14,6 +15,12 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public List<Layer> Children { get; set; } = new List<Layer>();
|
public List<Layer> Children { get; set; } = new List<Layer>();
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
Children.ForEach(x => x.HandleVisitor(visit));
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
return Children
|
return Children
|
||||||
@@ -21,12 +28,12 @@ namespace QuestPDF.Elements
|
|||||||
.Measure(availableSpace);
|
.Measure(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
Children
|
Children
|
||||||
.Where(x => x.Measure(availableSpace) is Size)
|
.Where(x => x.Measure(availableSpace) is Size)
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(x => x.Draw(canvas, availableSpace));
|
.ForEach(x => x.Draw(availableSpace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,16 +39,16 @@ namespace QuestPDF.Elements
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (Child == null)
|
if (Child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var internalSpace = InternalSpace(availableSpace);
|
var internalSpace = InternalSpace(availableSpace);
|
||||||
|
|
||||||
canvas.Translate(new Position(Left, Top));
|
Canvas.Translate(new Position(Left, Top));
|
||||||
Child?.Draw(canvas, internalSpace);
|
Child?.Draw(internalSpace);
|
||||||
canvas.Translate(new Position(-Left, -Top));
|
Canvas.Translate(new Position(-Left, -Top));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Size InternalSpace(Size availableSpace)
|
private Size InternalSpace(Size availableSpace)
|
||||||
|
|||||||
@@ -1,22 +1,44 @@
|
|||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class Page : IComponent
|
internal class Page : IComponent
|
||||||
{
|
{
|
||||||
|
public Size MinSize { get; set; } = PageSizes.A4;
|
||||||
|
public Size MaxSize { get; set; } = PageSizes.A4;
|
||||||
|
|
||||||
|
public float MarginLeft { get; set; }
|
||||||
|
public float MarginRight { get; set; }
|
||||||
|
public float MarginTop { get; set; }
|
||||||
|
public float MarginBottom { get; set; }
|
||||||
|
|
||||||
public Element Header { get; set; } = Empty.Instance;
|
public Element Header { get; set; } = Empty.Instance;
|
||||||
public Element Content { get; set; } = Empty.Instance;
|
public Element Content { get; set; } = Empty.Instance;
|
||||||
public Element Footer { get; set; } = Empty.Instance;
|
public Element Footer { get; set; } = Empty.Instance;
|
||||||
|
|
||||||
public void Compose(IContainer container)
|
public void Compose(IContainer container)
|
||||||
{
|
{
|
||||||
container.Decoration(decoration =>
|
container
|
||||||
{
|
|
||||||
decoration.Header().Element(Header);
|
.MinWidth(MinSize.Width)
|
||||||
decoration.Content().Extend().Element(Content);
|
.MinHeight(MinSize.Height)
|
||||||
decoration.Footer().Element(Footer);
|
|
||||||
});
|
.MaxWidth(MaxSize.Width)
|
||||||
|
.MaxHeight(MaxSize.Height)
|
||||||
|
|
||||||
|
.PaddingLeft(MarginLeft)
|
||||||
|
.PaddingRight(MarginRight)
|
||||||
|
.PaddingTop(MarginTop)
|
||||||
|
.PaddingBottom(MarginBottom)
|
||||||
|
|
||||||
|
.Decoration(decoration =>
|
||||||
|
{
|
||||||
|
decoration.Header().Element(Header);
|
||||||
|
decoration.Content().Extend().Element(Content);
|
||||||
|
decoration.Footer().Element(Footer);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,27 @@
|
|||||||
using QuestPDF.Drawing.SpacePlan;
|
using System;
|
||||||
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class PageBreak : Element
|
internal class PageBreak : Element, IStateResettable
|
||||||
{
|
{
|
||||||
private bool IsRendered { get; set; }
|
private bool IsRendered { get; set; }
|
||||||
|
|
||||||
|
public void ResetState()
|
||||||
|
{
|
||||||
|
IsRendered = false;
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (IsRendered)
|
if (IsRendered)
|
||||||
return new FullRender(Size.Zero);
|
return new FullRender(Size.Zero);
|
||||||
|
|
||||||
return new PartialRender(availableSpace.Width, availableSpace.Height);
|
return new PartialRender(Size.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
IsRendered = true;
|
IsRendered = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using QuestPDF.Drawing.SpacePlan;
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using Size = QuestPDF.Infrastructure.Size;
|
using Size = QuestPDF.Infrastructure.Size;
|
||||||
@@ -7,33 +9,44 @@ namespace QuestPDF.Elements
|
|||||||
internal class PageNumber : Element
|
internal class PageNumber : Element
|
||||||
{
|
{
|
||||||
public string TextFormat { get; set; } = "";
|
public string TextFormat { get; set; } = "";
|
||||||
public TextStyle? TextStyle { get; set; }
|
private Text TextElement { get; set; } = new Text();
|
||||||
private Text? TextElement { get; set; }
|
|
||||||
|
public TextStyle? TextStyle
|
||||||
private int Number { get; set; } = 1;
|
{
|
||||||
|
get => TextElement?.Style;
|
||||||
|
set => TextElement.Style = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
TextElement?.HandleVisitor(visit);
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
InitializeTextElement();
|
TextElement.Value = GetText();
|
||||||
|
|
||||||
TextElement.Value = TextFormat.Replace("{number}", Number.ToString());
|
|
||||||
return TextElement.Measure(availableSpace);
|
return TextElement.Measure(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
InitializeTextElement();
|
TextElement.Value = GetText();
|
||||||
|
TextElement.Draw(availableSpace);
|
||||||
TextElement.Draw(canvas, availableSpace);
|
|
||||||
Number++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeTextElement()
|
private string GetText()
|
||||||
{
|
{
|
||||||
TextElement ??= new Text()
|
var result = TextFormat;
|
||||||
{
|
|
||||||
Style = TextStyle
|
// replace known locations
|
||||||
};
|
foreach (var location in PageContext.GetRegisteredLocations())
|
||||||
|
result = result.Replace($"{{pdf:{location}}}", PageContext.GetLocationPage(location).ToString());
|
||||||
|
|
||||||
|
// placeholder unknown locations
|
||||||
|
result = Regex.Replace(result, @"{pdf:[ \w]+}", "123");
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class Rotate : ContainerElement
|
||||||
|
{
|
||||||
|
public float Angle { get; set; } = 0;
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||||
|
|
||||||
|
if (skiaCanvas == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var currentMatrix = skiaCanvas.TotalMatrix;
|
||||||
|
|
||||||
|
skiaCanvas.RotateDegrees(Angle);
|
||||||
|
Child?.Draw(availableSpace);
|
||||||
|
skiaCanvas.SetMatrix(currentMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
-11
@@ -11,9 +11,9 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public float Width { get; set; } = 1;
|
public float Width { get; set; } = 1;
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
Child?.Draw(canvas, availableSpace);
|
Child?.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +38,14 @@ namespace QuestPDF.Elements
|
|||||||
internal Element Left { get; set; }
|
internal Element Left { get; set; }
|
||||||
internal Element Right { get; set; }
|
internal Element Right { get; set; }
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
Left.HandleVisitor(visit);
|
||||||
|
Right.HandleVisitor(visit);
|
||||||
|
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height)) as Size;
|
var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height)) as Size;
|
||||||
@@ -61,16 +69,16 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(targetSize);
|
return new FullRender(targetSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height));
|
var leftMeasurement = Left.Measure(new Size(availableSpace.Width, availableSpace.Height));
|
||||||
var leftWidth = (leftMeasurement as Size)?.Width ?? 0;
|
var leftWidth = (leftMeasurement as Size)?.Width ?? 0;
|
||||||
|
|
||||||
Left.Draw(canvas, new Size(leftWidth, availableSpace.Height));
|
Left.Draw(new Size(leftWidth, availableSpace.Height));
|
||||||
|
|
||||||
canvas.Translate(new Position(leftWidth, 0));
|
Canvas.Translate(new Position(leftWidth, 0));
|
||||||
Right.Draw(canvas, new Size(availableSpace.Width - leftWidth, availableSpace.Height));
|
Right.Draw(new Size(availableSpace.Width - leftWidth, availableSpace.Height));
|
||||||
canvas.Translate(new Position(-leftWidth, 0));
|
Canvas.Translate(new Position(-leftWidth, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,15 +86,21 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public ICollection<RowElement> Children { get; internal set; } = new List<RowElement>();
|
public ICollection<RowElement> Children { get; internal set; } = new List<RowElement>();
|
||||||
public float Spacing { get; set; } = 0;
|
public float Spacing { get; set; } = 0;
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
Children.ToList().ForEach(x => x.HandleVisitor(visit));
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
return Compose(availableSpace.Width).Measure(availableSpace);
|
return Compose(availableSpace.Width).Measure(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
Compose(availableSpace.Width).Draw(canvas, availableSpace);
|
Compose(availableSpace.Width).Draw(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region structure
|
#region structure
|
||||||
@@ -95,7 +109,10 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
var elements = AddSpacing(Children, Spacing);
|
var elements = AddSpacing(Children, Spacing);
|
||||||
var rowElements = ReduceRows(elements, availableWidth);
|
var rowElements = ReduceRows(elements, availableWidth);
|
||||||
return BuildTree(rowElements.ToArray());
|
var tree = BuildTree(rowElements.ToArray());
|
||||||
|
tree.HandleVisitor(x => x.Initialize(PageContext, Canvas));
|
||||||
|
|
||||||
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ICollection<Element> ReduceRows(ICollection<RowElement> elements, float availableWidth)
|
private static ICollection<Element> ReduceRows(ICollection<RowElement> elements, float availableWidth)
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class Scale : ContainerElement
|
||||||
|
{
|
||||||
|
public float ScaleX { get; set; } = 1;
|
||||||
|
public float ScaleY { get; set; } = 1;
|
||||||
|
|
||||||
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
|
{
|
||||||
|
var targetSpace = new Size(
|
||||||
|
Math.Abs(availableSpace.Width / ScaleX),
|
||||||
|
Math.Abs(availableSpace.Height / ScaleY));
|
||||||
|
|
||||||
|
var measure = base.Measure(targetSpace) as Size;
|
||||||
|
|
||||||
|
if (measure == null)
|
||||||
|
return new Wrap();
|
||||||
|
|
||||||
|
var targetSize = new Size(
|
||||||
|
Math.Abs(measure.Width * ScaleX),
|
||||||
|
Math.Abs(measure.Height * ScaleY));
|
||||||
|
|
||||||
|
if (measure is PartialRender)
|
||||||
|
return new PartialRender(targetSize);
|
||||||
|
|
||||||
|
if (measure is FullRender)
|
||||||
|
return new FullRender(targetSize);
|
||||||
|
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||||
|
|
||||||
|
if (skiaCanvas == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var targetSpace = new Size(
|
||||||
|
Math.Abs(availableSpace.Width / ScaleX),
|
||||||
|
Math.Abs(availableSpace.Height / ScaleY));
|
||||||
|
|
||||||
|
var currentMatrix = skiaCanvas.TotalMatrix;
|
||||||
|
|
||||||
|
if (ScaleX < 0)
|
||||||
|
skiaCanvas.Translate(availableSpace.Width, 0);
|
||||||
|
|
||||||
|
if (ScaleY < 0)
|
||||||
|
skiaCanvas.Translate(0, availableSpace.Height);
|
||||||
|
|
||||||
|
skiaCanvas.Scale(ScaleX, ScaleY);
|
||||||
|
Child?.Draw(targetSpace);
|
||||||
|
skiaCanvas.SetMatrix(currentMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,15 @@ using QuestPDF.Infrastructure;
|
|||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class ShowOnce : ContainerElement
|
internal class ShowOnce : ContainerElement, IStateResettable
|
||||||
{
|
{
|
||||||
private bool IsRendered { get; set; }
|
private bool IsRendered { get; set; }
|
||||||
|
|
||||||
|
public void ResetState()
|
||||||
|
{
|
||||||
|
IsRendered = false;
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (Child == null || IsRendered)
|
if (Child == null || IsRendered)
|
||||||
@@ -15,7 +20,7 @@ namespace QuestPDF.Elements
|
|||||||
return Child.Measure(availableSpace);
|
return Child.Measure(availableSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (Child == null || IsRendered)
|
if (Child == null || IsRendered)
|
||||||
return;
|
return;
|
||||||
@@ -23,7 +28,7 @@ namespace QuestPDF.Elements
|
|||||||
if (Child.Measure(availableSpace) is FullRender)
|
if (Child.Measure(availableSpace) is FullRender)
|
||||||
IsRendered = true;
|
IsRendered = true;
|
||||||
|
|
||||||
Child.Draw(canvas, availableSpace);
|
Child.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class SimpleRotate : ContainerElement
|
||||||
|
{
|
||||||
|
public int TurnCount { get; set; }
|
||||||
|
public int NormalizedTurnCount => (TurnCount % 4 + 4) % 4;
|
||||||
|
|
||||||
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
|
{
|
||||||
|
if (NormalizedTurnCount == 0 || NormalizedTurnCount == 2)
|
||||||
|
return base.Measure(availableSpace);
|
||||||
|
|
||||||
|
availableSpace = new Size(availableSpace.Height, availableSpace.Width);
|
||||||
|
var childSpace = base.Measure(availableSpace) as Size;
|
||||||
|
|
||||||
|
if (childSpace == null)
|
||||||
|
return new Wrap();
|
||||||
|
|
||||||
|
var targetSpace = new Size(childSpace.Height, childSpace.Width);
|
||||||
|
|
||||||
|
if (childSpace is FullRender)
|
||||||
|
return new FullRender(targetSpace);
|
||||||
|
|
||||||
|
if (childSpace is PartialRender)
|
||||||
|
return new PartialRender(targetSpace);
|
||||||
|
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||||
|
|
||||||
|
if (skiaCanvas == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var currentMatrix = skiaCanvas.TotalMatrix;
|
||||||
|
|
||||||
|
if (NormalizedTurnCount == 1 || NormalizedTurnCount == 2)
|
||||||
|
skiaCanvas.Translate(availableSpace.Width, 0);
|
||||||
|
|
||||||
|
if (NormalizedTurnCount == 2 || NormalizedTurnCount == 3)
|
||||||
|
skiaCanvas.Translate(0, availableSpace.Height);
|
||||||
|
|
||||||
|
skiaCanvas.RotateDegrees(NormalizedTurnCount * 90);
|
||||||
|
|
||||||
|
if (NormalizedTurnCount == 1 || NormalizedTurnCount == 3)
|
||||||
|
availableSpace = new Size(availableSpace.Height, availableSpace.Width);
|
||||||
|
|
||||||
|
Child?.Draw(availableSpace);
|
||||||
|
skiaCanvas.SetMatrix(currentMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,13 +10,26 @@ using IContainer = QuestPDF.Infrastructure.IContainer;
|
|||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class SimpleStack : Element
|
internal class SimpleStack : Element, IStateResettable
|
||||||
{
|
{
|
||||||
internal Element First { get; set; } = Empty.Instance;
|
internal Element First { get; set; } = Empty.Instance;
|
||||||
internal Element Second { get; set; } = Empty.Instance;
|
internal Element Second { get; set; } = Empty.Instance;
|
||||||
|
|
||||||
internal bool IsFirstRendered { get; set; } = false;
|
internal bool IsFirstRendered { get; set; } = false;
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
First?.HandleVisitor(visit);
|
||||||
|
Second?.HandleVisitor(visit);
|
||||||
|
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetState()
|
||||||
|
{
|
||||||
|
IsFirstRendered = false;
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
var firstElement = IsFirstRendered ? Empty.Instance : First;
|
var firstElement = IsFirstRendered ? Empty.Instance : First;
|
||||||
@@ -44,7 +57,7 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(targetSize);
|
return new FullRender(targetSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var firstElement = IsFirstRendered ? Empty.Instance : First;
|
var firstElement = IsFirstRendered ? Empty.Instance : First;
|
||||||
|
|
||||||
@@ -56,7 +69,7 @@ namespace QuestPDF.Elements
|
|||||||
var firstSize = firstMeasurement as Size;
|
var firstSize = firstMeasurement as Size;
|
||||||
|
|
||||||
if (firstSize != null)
|
if (firstSize != null)
|
||||||
firstElement.Draw(canvas, new Size(availableSpace.Width, firstSize.Height));
|
firstElement.Draw(new Size(availableSpace.Width, firstSize.Height));
|
||||||
|
|
||||||
if (firstMeasurement is Wrap || firstMeasurement is PartialRender)
|
if (firstMeasurement is Wrap || firstMeasurement is PartialRender)
|
||||||
return;
|
return;
|
||||||
@@ -68,9 +81,9 @@ namespace QuestPDF.Elements
|
|||||||
if (secondMeasurement == null)
|
if (secondMeasurement == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
canvas.Translate(new Position(0, firstHeight));
|
Canvas.Translate(new Position(0, firstHeight));
|
||||||
Second.Draw(canvas, new Size(availableSpace.Width, secondMeasurement.Height));
|
Second.Draw(new Size(availableSpace.Width, secondMeasurement.Height));
|
||||||
canvas.Translate(new Position(0, -firstHeight));
|
Canvas.Translate(new Position(0, -firstHeight));
|
||||||
|
|
||||||
if (secondMeasurement is FullRender)
|
if (secondMeasurement is FullRender)
|
||||||
IsFirstRendered = false;
|
IsFirstRendered = false;
|
||||||
@@ -97,6 +110,7 @@ namespace QuestPDF.Elements
|
|||||||
return elements;
|
return elements;
|
||||||
|
|
||||||
return elements
|
return elements
|
||||||
|
.Where(x => !(x is Empty))
|
||||||
.Select(x => new Padding
|
.Select(x => new Padding
|
||||||
{
|
{
|
||||||
Bottom = spacing,
|
Bottom = spacing,
|
||||||
|
|||||||
@@ -32,22 +32,22 @@ namespace QuestPDF.Elements
|
|||||||
return new FullRender(realWidth, realHeight);
|
return new FullRender(realWidth, realHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
var lines = BreakLines(availableSpace.Width);
|
var lines = BreakLines(availableSpace.Width);
|
||||||
|
|
||||||
var offsetTop = 0f;
|
var offsetTop = 0f;
|
||||||
var offsetLeft = GetLeftOffset();
|
var offsetLeft = GetLeftOffset();
|
||||||
|
|
||||||
canvas.Translate(new Position(0, Style.Size));
|
Canvas.Translate(new Position(0, Style.Size));
|
||||||
|
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
{
|
{
|
||||||
canvas.DrawText(line, new Position(offsetLeft, offsetTop), Style);
|
Canvas.DrawText(line, new Position(offsetLeft, offsetTop), Style);
|
||||||
offsetTop += LineHeight;
|
offsetTop += LineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.Translate(new Position(0, -Style.Size));
|
Canvas.Translate(new Position(0, -Style.Size));
|
||||||
|
|
||||||
float GetLeftOffset()
|
float GetLeftOffset()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class Translate : ContainerElement
|
||||||
|
{
|
||||||
|
public float TranslateX { get; set; } = 0;
|
||||||
|
public float TranslateY { get; set; } = 0;
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||||
|
|
||||||
|
if (skiaCanvas == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
skiaCanvas.Translate(TranslateX, TranslateY);
|
||||||
|
base.Draw(availableSpace);
|
||||||
|
skiaCanvas.Translate(-TranslateX, -TranslateY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class Unconstrained : ContainerElement
|
||||||
|
{
|
||||||
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
|
{
|
||||||
|
var childSize = Child?.Measure(Size.Max) ?? new FullRender(Size.Zero);
|
||||||
|
|
||||||
|
if (childSize is PartialRender)
|
||||||
|
return new PartialRender(Size.Zero);
|
||||||
|
|
||||||
|
if (childSize is FullRender)
|
||||||
|
return new FullRender(Size.Zero);
|
||||||
|
|
||||||
|
return childSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Draw(Size availableSpace)
|
||||||
|
{
|
||||||
|
var measurement = Child?.Measure(Size.Max) as Size;
|
||||||
|
|
||||||
|
if (measurement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Child?.Draw(measurement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,7 +56,7 @@ namespace QuestPDF.Fluent
|
|||||||
var descriptor = new ComponentDescriptor<T>(component);
|
var descriptor = new ComponentDescriptor<T>(component);
|
||||||
handler?.Invoke(descriptor);
|
handler?.Invoke(descriptor);
|
||||||
|
|
||||||
component.Compose(element);
|
component.Compose(element.Container());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Component<T>(this IContainer element, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent, new()
|
static void Component<T>(this IContainer element, Action<ComponentDescriptor<T>>? handler = null) where T : IComponent, new()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace QuestPDF.Fluent
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Element<T>(this IContainer element, T child) where T : IElement
|
internal static T Element<T>(this IContainer element, T child) where T : IElement
|
||||||
{
|
{
|
||||||
if (element?.Child != null && element.Child is Empty == false)
|
if (element?.Child != null && element.Child is Empty == false)
|
||||||
{
|
{
|
||||||
@@ -33,15 +33,15 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static void Element<TParent>(this TParent parent, Action<IContainer> handler) where TParent : IContainer
|
public static void Element<TParent>(this TParent parent, Action<IContainer> handler) where TParent : IContainer
|
||||||
{
|
{
|
||||||
handler(parent);
|
handler(parent.Container());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
|
public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
|
||||||
{
|
{
|
||||||
return handler(parent);
|
return handler(parent.Container()).Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PageNumber(this IContainer element, string textFormat = "{number}", TextStyle? style = null)
|
public static void PageNumber(this IContainer element, string textFormat = "{pdf:currentPage} / {pdf:totalPages}", TextStyle? style = null)
|
||||||
{
|
{
|
||||||
element.Element(new PageNumber
|
element.Element(new PageNumber
|
||||||
{
|
{
|
||||||
@@ -104,7 +104,7 @@ namespace QuestPDF.Fluent
|
|||||||
style.Alignment = alignment.Horizontal;
|
style.Alignment = alignment.Horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
element.Element(new Text()
|
element.Element(new Text
|
||||||
{
|
{
|
||||||
Value = text.ToString(),
|
Value = text.ToString(),
|
||||||
Style = style
|
Style = style
|
||||||
@@ -152,7 +152,7 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static void Canvas(this IContainer element, DrawOnCanvas handler)
|
public static void Canvas(this IContainer element, DrawOnCanvas handler)
|
||||||
{
|
{
|
||||||
element.Element(new Elements.Canvas
|
element.Element(new Canvas
|
||||||
{
|
{
|
||||||
Handler = handler
|
Handler = handler
|
||||||
});
|
});
|
||||||
@@ -162,5 +162,10 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
return element.Element(new Box());
|
return element.Element(new Box());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IContainer Unconstrained(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.Element(new Unconstrained());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ namespace QuestPDF.Fluent
|
|||||||
foreach (var imageData in document.GenerateImages())
|
foreach (var imageData in document.GenerateImages())
|
||||||
{
|
{
|
||||||
var path = filePath(index);
|
var path = filePath(index);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
|
||||||
File.WriteAllBytes(path, imageData);
|
File.WriteAllBytes(path, imageData);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
using QuestPDF.Elements;
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
namespace QuestPDF.Fluent
|
namespace QuestPDF.Fluent
|
||||||
@@ -8,6 +10,56 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
internal Page Page { get; } = new Page();
|
internal Page Page { get; } = new Page();
|
||||||
|
|
||||||
|
public void Size(PageSize pageSize)
|
||||||
|
{
|
||||||
|
Page.MinSize = pageSize;
|
||||||
|
Page.MaxSize = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ContinuousSize(float width)
|
||||||
|
{
|
||||||
|
Page.MinSize = new PageSize(width, 0);
|
||||||
|
Page.MaxSize = new PageSize(width, Infrastructure.Size.Max.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarginLeft(float value)
|
||||||
|
{
|
||||||
|
Page.MarginLeft = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarginRight(float value)
|
||||||
|
{
|
||||||
|
Page.MarginRight = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarginTop(float value)
|
||||||
|
{
|
||||||
|
Page.MarginTop = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarginBottom(float value)
|
||||||
|
{
|
||||||
|
Page.MarginBottom = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarginVertical(float value)
|
||||||
|
{
|
||||||
|
MarginTop(value);
|
||||||
|
MarginBottom(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarginHorizontal(float value)
|
||||||
|
{
|
||||||
|
MarginLeft(value);
|
||||||
|
MarginRight(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Margin(float value)
|
||||||
|
{
|
||||||
|
MarginVertical(value);
|
||||||
|
MarginHorizontal(value);
|
||||||
|
}
|
||||||
|
|
||||||
public IContainer Header()
|
public IContainer Header()
|
||||||
{
|
{
|
||||||
var container = new Container();
|
var container = new Container();
|
||||||
@@ -32,11 +84,14 @@ namespace QuestPDF.Fluent
|
|||||||
|
|
||||||
public static class PageExtensions
|
public static class PageExtensions
|
||||||
{
|
{
|
||||||
public static void Page(this IContainer document, Action<PageDescriptor> handler)
|
public static IDocumentContainer Page(this IDocumentContainer document, Action<PageDescriptor> handler)
|
||||||
{
|
{
|
||||||
var descriptor = new PageDescriptor();
|
var descriptor = new PageDescriptor();
|
||||||
handler(descriptor);
|
handler(descriptor);
|
||||||
document.Component(descriptor.Page);
|
|
||||||
|
(document as DocumentContainer).Pages.Add(descriptor.Page);
|
||||||
|
|
||||||
|
return document;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class RotateExtensions
|
||||||
|
{
|
||||||
|
private static IContainer SimpleRotate(this IContainer element, Action<SimpleRotate> handler)
|
||||||
|
{
|
||||||
|
var scale = element as SimpleRotate ?? new SimpleRotate();
|
||||||
|
handler(scale);
|
||||||
|
|
||||||
|
return element.Element(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RotateLeft(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.SimpleRotate(x => x.TurnCount--);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer RotateRight(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.SimpleRotate(x => x.TurnCount++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="angle">In degrees</param>
|
||||||
|
public static IContainer Rotate(this IContainer element, float angle)
|
||||||
|
{
|
||||||
|
return element.Element(new Rotate
|
||||||
|
{
|
||||||
|
Angle = angle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class ScaleExtensions
|
||||||
|
{
|
||||||
|
private static IContainer Scale(this IContainer element, Action<Scale> handler)
|
||||||
|
{
|
||||||
|
var scale = element as Scale ?? new Scale();
|
||||||
|
handler(scale);
|
||||||
|
|
||||||
|
return element.Element(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer Scale(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.ScaleHorizontal(value).ScaleVertical(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer ScaleHorizontal(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.Scale(x => x.ScaleX = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer FlipHorizontal(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.ScaleHorizontal(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer ScaleVertical(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.Scale(x => x.ScaleY = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer FlipVertical(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.ScaleVertical(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer FlipOver(this IContainer element)
|
||||||
|
{
|
||||||
|
return element.FlipHorizontal().FlipVertical();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Fluent
|
||||||
|
{
|
||||||
|
public static class TranslateExtensions
|
||||||
|
{
|
||||||
|
private static IContainer Translate(this IContainer element, Action<Translate> handler)
|
||||||
|
{
|
||||||
|
var translate = element as Translate ?? new Translate();
|
||||||
|
handler(translate);
|
||||||
|
|
||||||
|
return element.Element(translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer TranslateX(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.Translate(x => x.TranslateX = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer TranslateY(this IContainer element, float value)
|
||||||
|
{
|
||||||
|
return element.Translate(x => x.TranslateY = value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,46 +15,46 @@ namespace QuestPDF.Helpers
|
|||||||
{
|
{
|
||||||
public const int PointsPerInch = 72;
|
public const int PointsPerInch = 72;
|
||||||
|
|
||||||
public static Size A0 => new PageSize(2384.2f, 3370.8f);
|
public static PageSize A0 => new PageSize(2384.2f, 3370.8f);
|
||||||
public static Size A1 => new PageSize(1684, 2384.2f);
|
public static PageSize A1 => new PageSize(1684, 2384.2f);
|
||||||
public static Size A2 => new PageSize(1190.7f, 1684);
|
public static PageSize A2 => new PageSize(1190.7f, 1684);
|
||||||
public static Size A3 => new PageSize(842, 1190.7f);
|
public static PageSize A3 => new PageSize(842, 1190.7f);
|
||||||
public static Size A4 => new PageSize(595.4f, 842);
|
public static PageSize A4 => new PageSize(595.4f, 842);
|
||||||
public static Size A5 => new PageSize(419.6f, 595.4f);
|
public static PageSize A5 => new PageSize(419.6f, 595.4f);
|
||||||
public static Size A6 => new PageSize(297.7f, 419.6f);
|
public static PageSize A6 => new PageSize(297.7f, 419.6f);
|
||||||
public static Size A7 => new PageSize(209.8f, 297.7f);
|
public static PageSize A7 => new PageSize(209.8f, 297.7f);
|
||||||
public static Size A8 => new PageSize(147.4f, 209.8f);
|
public static PageSize A8 => new PageSize(147.4f, 209.8f);
|
||||||
public static Size A9 => new PageSize(104.9f, 147.4f);
|
public static PageSize A9 => new PageSize(104.9f, 147.4f);
|
||||||
public static Size A10 => new PageSize(73.7f, 104.9f);
|
public static PageSize A10 => new PageSize(73.7f, 104.9f);
|
||||||
|
|
||||||
public static Size B0 => new PageSize(2835, 4008.7f);
|
public static PageSize B0 => new PageSize(2835, 4008.7f);
|
||||||
public static Size B1 => new PageSize(2004.3f, 2835);
|
public static PageSize B1 => new PageSize(2004.3f, 2835);
|
||||||
public static Size B2 => new PageSize(1417.5f, 2004.3f);
|
public static PageSize B2 => new PageSize(1417.5f, 2004.3f);
|
||||||
public static Size B3 => new PageSize(1000.8f, 1417.5f);
|
public static PageSize B3 => new PageSize(1000.8f, 1417.5f);
|
||||||
public static Size B4 => new PageSize(708.8f, 1000.8f);
|
public static PageSize B4 => new PageSize(708.8f, 1000.8f);
|
||||||
public static Size B5 => new PageSize(499, 708.8f);
|
public static PageSize B5 => new PageSize(499, 708.8f);
|
||||||
public static Size B6 => new PageSize(354.4f, 499);
|
public static PageSize B6 => new PageSize(354.4f, 499);
|
||||||
public static Size B7 => new PageSize(249.5f, 354.4f);
|
public static PageSize B7 => new PageSize(249.5f, 354.4f);
|
||||||
public static Size B8 => new PageSize(175.8f, 249.5f);
|
public static PageSize B8 => new PageSize(175.8f, 249.5f);
|
||||||
public static Size B9 => new PageSize(124.7f, 175.8f);
|
public static PageSize B9 => new PageSize(124.7f, 175.8f);
|
||||||
public static Size B10 => new PageSize(87.9f, 124.7f);
|
public static PageSize B10 => new PageSize(87.9f, 124.7f);
|
||||||
|
|
||||||
public static Size Env10 => new PageSize(683.2f, 294.8f);
|
public static PageSize Env10 => new PageSize(683.2f, 294.8f);
|
||||||
public static Size EnvC4 => new PageSize(649.2f, 918.5f);
|
public static PageSize EnvC4 => new PageSize(649.2f, 918.5f);
|
||||||
public static Size EnvDL => new PageSize(311.9f, 623.7f);
|
public static PageSize EnvDL => new PageSize(311.9f, 623.7f);
|
||||||
|
|
||||||
public static Size Executive => new PageSize(522, 756);
|
public static PageSize Executive => new PageSize(522, 756);
|
||||||
public static Size Legal => new PageSize(612.4f, 1009.3f);
|
public static PageSize Legal => new PageSize(612.4f, 1009.3f);
|
||||||
public static Size Letter => new PageSize(612.4f, 791);
|
public static PageSize Letter => new PageSize(612.4f, 791);
|
||||||
|
|
||||||
public static Size ARCH_A => new PageSize(649.2f, 864.7f);
|
public static PageSize ARCH_A => new PageSize(649.2f, 864.7f);
|
||||||
public static Size ARCH_B => new PageSize(864.7f, 1295.6f);
|
public static PageSize ARCH_B => new PageSize(864.7f, 1295.6f);
|
||||||
public static Size ARCH_C => new PageSize(1295.6f, 1729.3f);
|
public static PageSize ARCH_C => new PageSize(1295.6f, 1729.3f);
|
||||||
public static Size ARCH_D => new PageSize(1729.3f, 2591.2f);
|
public static PageSize ARCH_D => new PageSize(1729.3f, 2591.2f);
|
||||||
public static Size ARCH_E => new PageSize(2591.2f, 3455.9f);
|
public static PageSize ARCH_E => new PageSize(2591.2f, 3455.9f);
|
||||||
public static Size ARCH_E1 => new PageSize(2160.3f, 3024.9f);
|
public static PageSize ARCH_E1 => new PageSize(2160.3f, 3024.9f);
|
||||||
public static Size ARCH_E2 => new PageSize(1871.1f, 2735.8f);
|
public static PageSize ARCH_E2 => new PageSize(1871.1f, 2735.8f);
|
||||||
public static Size ARCH_E3 => new PageSize(1944.8f, 2809.5f);
|
public static PageSize ARCH_E3 => new PageSize(1944.8f, 2809.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PageSizeExtensions
|
public static class PageSizeExtensions
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using QuestPDF.Drawing.SpacePlan;
|
using System;
|
||||||
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
using QuestPDF.Elements;
|
using QuestPDF.Elements;
|
||||||
|
|
||||||
namespace QuestPDF.Infrastructure
|
namespace QuestPDF.Infrastructure
|
||||||
@@ -13,14 +14,20 @@ namespace QuestPDF.Infrastructure
|
|||||||
set => Child = value as Element;
|
set => Child = value as Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
base.HandleVisitor(visit);
|
||||||
|
Child?.HandleVisitor(visit);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ISpacePlan Measure(Size availableSpace)
|
internal override ISpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
return Child?.Measure(availableSpace) ?? new FullRender(Size.Zero);
|
return Child?.Measure(availableSpace) ?? new FullRender(Size.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
Child?.Draw(canvas, availableSpace);
|
Child?.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,26 @@
|
|||||||
|
using System;
|
||||||
using QuestPDF.Drawing.SpacePlan;
|
using QuestPDF.Drawing.SpacePlan;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
|
||||||
namespace QuestPDF.Infrastructure
|
namespace QuestPDF.Infrastructure
|
||||||
{
|
{
|
||||||
internal abstract class Element : IElement
|
internal abstract class Element : IElement
|
||||||
{
|
{
|
||||||
|
internal IPageContext PageContext { get; set; }
|
||||||
|
internal ICanvas Canvas { get; set; }
|
||||||
|
|
||||||
|
internal virtual void HandleVisitor(Action<Element?> visit)
|
||||||
|
{
|
||||||
|
visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Initialize(IPageContext pageContext, ICanvas canvas)
|
||||||
|
{
|
||||||
|
PageContext = pageContext;
|
||||||
|
Canvas = canvas;
|
||||||
|
}
|
||||||
|
|
||||||
internal abstract ISpacePlan Measure(Size availableSpace);
|
internal abstract ISpacePlan Measure(Size availableSpace);
|
||||||
internal abstract void Draw(ICanvas canvas, Size availableSpace);
|
internal abstract void Draw(Size availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace QuestPDF.Infrastructure
|
namespace QuestPDF.Infrastructure
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ namespace QuestPDF.Infrastructure
|
|||||||
public interface IDocument
|
public interface IDocument
|
||||||
{
|
{
|
||||||
DocumentMetadata GetMetadata();
|
DocumentMetadata GetMetadata();
|
||||||
void Compose(IContainer container);
|
void Compose(IDocumentContainer container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IDocumentContainer
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IPageContext
|
||||||
|
{
|
||||||
|
void SetLocationPage(string key);
|
||||||
|
int GetLocationPage(string key);
|
||||||
|
ICollection<string> GetRegisteredLocations();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IRenderingCanvas
|
||||||
|
{
|
||||||
|
void BeginDocument();
|
||||||
|
void EndDocument();
|
||||||
|
|
||||||
|
void BeginPage(Size size);
|
||||||
|
void EndPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
internal interface IStateResettable
|
||||||
|
{
|
||||||
|
void ResetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
|
||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
public class PageContext : IPageContext
|
||||||
|
{
|
||||||
|
public const string CurrentPageSlot = "currentPage";
|
||||||
|
public const string TotalPagesSlot = "totalPages";
|
||||||
|
|
||||||
|
private Dictionary<string, int> Locations { get; } = new Dictionary<string, int>();
|
||||||
|
private int PageNumber { get; set; }
|
||||||
|
|
||||||
|
internal void SetPageNumber(int number)
|
||||||
|
{
|
||||||
|
PageNumber = number;
|
||||||
|
Locations[CurrentPageSlot] = number;
|
||||||
|
|
||||||
|
if (!Locations.ContainsKey(TotalPagesSlot) || Locations[TotalPagesSlot] < number)
|
||||||
|
Locations[TotalPagesSlot] = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLocationPage(string key)
|
||||||
|
{
|
||||||
|
if (Locations.ContainsKey(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Locations[key] = PageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetLocationPage(string key)
|
||||||
|
{
|
||||||
|
if (!Locations.ContainsKey(key))
|
||||||
|
throw new ArgumentException($"The location '{key}' does not exists.");
|
||||||
|
|
||||||
|
return Locations[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<string> GetRegisteredLocations()
|
||||||
|
{
|
||||||
|
return Locations.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
public float Height { get; }
|
public float Height { get; }
|
||||||
|
|
||||||
public static Size Zero => new Size(0, 0);
|
public static Size Zero => new Size(0, 0);
|
||||||
|
public static Size Max => new Size(14_400, 14_400);
|
||||||
|
|
||||||
public Size(float width, float height)
|
public Size(float width, float height)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
<Authors>MarcinZiabek</Authors>
|
<Authors>MarcinZiabek</Authors>
|
||||||
<Company>CodeFlint</Company>
|
<Company>CodeFlint</Company>
|
||||||
<PackageId>QuestPDF</PackageId>
|
<PackageId>QuestPDF</PackageId>
|
||||||
<Version>2021.5-alpha2</Version>
|
<Version>2021.9.0</Version>
|
||||||
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
<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>New elements: Grid, Canvas, EnsureSpace, Layers. Predefined colors and fonts.</PackageReleaseNotes>
|
<PackageReleaseNotes>Added support for registering custom fonts from a stream.</PackageReleaseNotes>
|
||||||
<LangVersion>8</LangVersion>
|
<LangVersion>8</LangVersion>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<PackageIcon>Logo.png</PackageIcon>
|
<PackageIcon>Logo.png</PackageIcon>
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>
|
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/QuestPDF/library.git</RepositoryUrl>
|
<RepositoryUrl>https://github.com/QuestPDF/library.git</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<Copyright>QuestPDF contributors</Copyright>
|
<Copyright>Marcin Ziąbek, QuestPDF contributors</Copyright>
|
||||||
<PackageTags>PDF file export generate create render portable document format quest free</PackageTags>
|
<PackageTags>pdf file export generate generation tool create creation render portable document format quest html library converter free</PackageTags>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>net462;netstandard2.0;netcoreapp2.0;netcoreapp3.0</TargetFrameworks>
|
<TargetFrameworks>net462;netstandard2.0;netcoreapp2.0;netcoreapp3.0</TargetFrameworks>
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 2021.5.2 | :white_check_mark: |
|
||||||
|
| Older | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Please report any code vulnerabilities using GitHub Issues. We are trying to respond within 1 day and analyze reported situation as soon as possible.
|
||||||
|
Please collaborate with us to find best solutation available.
|
||||||
|
Once the vulnerability is recognized and fixed, the issue is closed with appropraite message.
|
||||||
@@ -16,6 +16,15 @@
|
|||||||
|
|
||||||
6) **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.
|
6) **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 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
|
## Installation
|
||||||
|
|
||||||
@@ -25,7 +34,6 @@ The library is available as a nuget package. You can install it as any other nug
|
|||||||
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/nuget.svg" width="200px">
|
<img src="https://github.com/QuestPDF/example-invoice/raw/main/images/nuget.svg" width="200px">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## 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.
|
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.
|
||||||
@@ -46,16 +54,19 @@ For tutorial, documentation and API reference, please visit [the QuestPDF docume
|
|||||||
Here you can find an example code showing how easy is to write and understand the fluent API:
|
Here you can find an example code showing how easy is to write and understand the fluent API:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public void Compose(IContainer container)
|
public void Compose(IDocumentContainer container)
|
||||||
{
|
{
|
||||||
container
|
container
|
||||||
.PaddingHorizontal(50)
|
|
||||||
.PaddingVertical(50)
|
|
||||||
.Page(page =>
|
.Page(page =>
|
||||||
{
|
{
|
||||||
page.Header(ComposeHeader);
|
page.MarginVertical(60);
|
||||||
page.Content(ComposeContent);
|
page.MarginHorizontal(40);
|
||||||
page.Footer().AlignCenter().PageNumber("Page {number}");
|
|
||||||
|
page.Size(PageSizes.A4);
|
||||||
|
|
||||||
|
page.Header().Element(ComposeHeader);
|
||||||
|
page.Content().Element(ComposeContent);
|
||||||
|
page.Footer().AlignCenter().PageNumber();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,11 +74,11 @@ void ComposeHeader(IContainer container)
|
|||||||
{
|
{
|
||||||
container.Row(row =>
|
container.Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().Stack(column =>
|
row.RelativeColumn().Stack(stack =>
|
||||||
{
|
{
|
||||||
column.Element().Text($"Invoice #{Model.InvoiceNumber}", TextStyle.Default.Size(20).Bold());
|
stack.Item().Text($"Invoice #{Model.InvoiceNumber}", TextStyle.Default.Size(20).Bold());
|
||||||
column.Element().Text($"Issue date: {Model.IssueDate:d}");
|
stack.Item().Text($"Issue date: {Model.IssueDate:d}");
|
||||||
column.Element().Text($"Due date: {Model.DueDate:d}");
|
stack.Item().Text($"Due date: {Model.DueDate:d}");
|
||||||
});
|
});
|
||||||
|
|
||||||
row.ConstantColumn(100).Height(50).Placeholder();
|
row.ConstantColumn(100).Height(50).Placeholder();
|
||||||
|
|||||||
Reference in New Issue
Block a user