Library implementation
@@ -0,0 +1,222 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
*.ncrunchsolution
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Project Rider
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class ElementExamples : ExampleTestBase
|
||||||
|
{
|
||||||
|
//[ShowResult]
|
||||||
|
[ImageSize(200, 150)]
|
||||||
|
public void Placeholder(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(25)
|
||||||
|
.Placeholder();
|
||||||
|
}
|
||||||
|
|
||||||
|
//[ShowResult]
|
||||||
|
[ImageSize(300, 300)]
|
||||||
|
public void Section(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(25)
|
||||||
|
.Section(section =>
|
||||||
|
{
|
||||||
|
section
|
||||||
|
.Header()
|
||||||
|
.Background("#888")
|
||||||
|
.Padding(10)
|
||||||
|
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
|
||||||
|
|
||||||
|
section
|
||||||
|
.Content()
|
||||||
|
.Background("#DDD")
|
||||||
|
.Padding(10)
|
||||||
|
.ExtendVertical()
|
||||||
|
.Text(TextPlaceholder.LoremIpsum());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//[ShowResult]
|
||||||
|
[ImageSize(298, 421)]
|
||||||
|
public void Page(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(15)
|
||||||
|
.Page(page =>
|
||||||
|
{
|
||||||
|
page.Header()
|
||||||
|
.Height(60)
|
||||||
|
.Background("#BBB")
|
||||||
|
.AlignCenter()
|
||||||
|
.AlignMiddle()
|
||||||
|
.Text("Header");
|
||||||
|
|
||||||
|
page.Content()
|
||||||
|
.Background("#DDD")
|
||||||
|
.AlignCenter()
|
||||||
|
.AlignMiddle()
|
||||||
|
.Text("Content");
|
||||||
|
|
||||||
|
page.Footer()
|
||||||
|
.Height(30)
|
||||||
|
.Background("#BBB")
|
||||||
|
.AlignCenter()
|
||||||
|
.AlignMiddle()
|
||||||
|
.Text("Footer");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//[ShowResult]
|
||||||
|
[ImageSize(740, 200)]
|
||||||
|
public void Row(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(20)
|
||||||
|
.Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Element()
|
||||||
|
.PaddingBottom(10)
|
||||||
|
.AlignCenter()
|
||||||
|
.Text("This Row element is 700pt wide");
|
||||||
|
|
||||||
|
stack.Element().Row(row =>
|
||||||
|
{
|
||||||
|
row.ConstantColumn(100)
|
||||||
|
.Background("#DDD")
|
||||||
|
.Padding(10)
|
||||||
|
.ExtendVertical()
|
||||||
|
.Text("This column is 100 pt wide");
|
||||||
|
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Background("#BBB")
|
||||||
|
.Padding(10)
|
||||||
|
.Text("This column takes 1/3 of the available space (200pt)");
|
||||||
|
|
||||||
|
row.RelativeColumn(2)
|
||||||
|
.Background("#DDD")
|
||||||
|
.Padding(10)
|
||||||
|
.Text("This column takes 2/3 of the available space (400pt)");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//[ShowResult]
|
||||||
|
[ImageSize(500, 350)]
|
||||||
|
public void Column(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(15)
|
||||||
|
.Stack(column =>
|
||||||
|
{
|
||||||
|
column.Spacing(10);
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.Background("#999")
|
||||||
|
.Height(50);
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.Background("#BBB")
|
||||||
|
.Height(100);
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.Background("#DDD")
|
||||||
|
.Height(150);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
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});
|
||||||
|
|
||||||
|
var iteration = 1;
|
||||||
|
|
||||||
|
while (iteration <= 1)
|
||||||
|
{
|
||||||
|
var imageData = RenderPage(container, size);
|
||||||
|
|
||||||
|
if (imageData == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var path = Path.Combine(ResultPath, $"{fileName.ToLower()}-${iteration}.png");
|
||||||
|
File.WriteAllBytes(path, imageData);
|
||||||
|
|
||||||
|
if (showResult)
|
||||||
|
Process.Start("explorer", path);
|
||||||
|
|
||||||
|
iteration++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Canvas(surface.Canvas);
|
||||||
|
element?.Draw(canvas, size);
|
||||||
|
|
||||||
|
surface.Canvas.Save();
|
||||||
|
return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples.Engine
|
||||||
|
{
|
||||||
|
public class FileNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string FileName { get; }
|
||||||
|
|
||||||
|
public FileNameAttribute(string fileName)
|
||||||
|
{
|
||||||
|
FileName = fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples.Engine
|
||||||
|
{
|
||||||
|
public class IgnoreAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples.Engine
|
||||||
|
{
|
||||||
|
public class ShowResultAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
static class SimpleExtension
|
||||||
|
{
|
||||||
|
private static IContainer Cell(this IContainer container, bool dark)
|
||||||
|
{
|
||||||
|
return container
|
||||||
|
.Border(1)
|
||||||
|
.Background(dark ? "#EEE" : "#FFF")
|
||||||
|
.Padding(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer LabelCell(this IContainer container) => container.Cell(true);
|
||||||
|
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FrameExample: ExampleTestBase
|
||||||
|
{
|
||||||
|
[ImageSize(550, 400)]
|
||||||
|
[ShowResult]
|
||||||
|
public void Frame(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(25)
|
||||||
|
.Stack(stack =>
|
||||||
|
{
|
||||||
|
for(var i=1; i<=4; i++)
|
||||||
|
{
|
||||||
|
stack.Element().Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn(2).LabelCell().Text(TextPlaceholder.Label());
|
||||||
|
row.RelativeColumn(3).ValueCell().Text(TextPlaceholder.Paragraph());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Net;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class LoremPicsum : IComponent
|
||||||
|
{
|
||||||
|
public bool Greyscale { get; }
|
||||||
|
|
||||||
|
public LoremPicsum(bool greyscale)
|
||||||
|
{
|
||||||
|
Greyscale = greyscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compose(IContainer container)
|
||||||
|
{
|
||||||
|
var url = "https://picsum.photos/300/200";
|
||||||
|
|
||||||
|
if(Greyscale)
|
||||||
|
url += "?grayscale";
|
||||||
|
|
||||||
|
using var client = new WebClient();
|
||||||
|
var response = client.DownloadData(url);
|
||||||
|
container.Image(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LoremPicsumExample : ExampleTestBase
|
||||||
|
{
|
||||||
|
[ShowResult]
|
||||||
|
[ImageSize(350, 280)]
|
||||||
|
public void LoremPicsum(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FFF")
|
||||||
|
.Padding(25)
|
||||||
|
.Stack(column =>
|
||||||
|
{
|
||||||
|
column.Spacing(10);
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.Component(new LoremPicsum(true));
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.AlignRight()
|
||||||
|
.Text("From Lorem Picsum");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class Examples : ExampleTestBase
|
||||||
|
{
|
||||||
|
public void Padding(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Background("#FDD")
|
||||||
|
.Padding(50)
|
||||||
|
|
||||||
|
.Background("#AFA")
|
||||||
|
.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
|
||||||
|
.Element()
|
||||||
|
.Height(100)
|
||||||
|
.Background("#FFF")
|
||||||
|
|
||||||
|
.AlignLeft()
|
||||||
|
.AlignMiddle()
|
||||||
|
|
||||||
|
.Width(50)
|
||||||
|
.Height(50)
|
||||||
|
.Background("#444");
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.Height(100)
|
||||||
|
.Background("#DDD")
|
||||||
|
|
||||||
|
.AlignCenter()
|
||||||
|
.AlignMiddle()
|
||||||
|
|
||||||
|
.Width(50)
|
||||||
|
.Height(50)
|
||||||
|
.Background("#222");
|
||||||
|
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.Height(100)
|
||||||
|
.Background("#BBB")
|
||||||
|
|
||||||
|
.AlignRight()
|
||||||
|
.AlignMiddle()
|
||||||
|
|
||||||
|
.Width(50)
|
||||||
|
.Height(50)
|
||||||
|
.Background("#000");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Expand(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Stack(column =>
|
||||||
|
{
|
||||||
|
column
|
||||||
|
.Element()
|
||||||
|
.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
|
||||||
|
.Element()
|
||||||
|
.Height(150)
|
||||||
|
.Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Extend()
|
||||||
|
.Background("BBB")
|
||||||
|
|
||||||
|
.ExtendVertical()
|
||||||
|
.Width(50)
|
||||||
|
.Background("444");
|
||||||
|
|
||||||
|
row.RelativeColumn()
|
||||||
|
.Extend()
|
||||||
|
.Background("BBB")
|
||||||
|
|
||||||
|
.ExtendVertical()
|
||||||
|
.ExtendHorizontal()
|
||||||
|
.Background("444");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="nunit" Version="3.12.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||||
|
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\QuestPDF.InvoiceSample\QuestPDF.InvoiceSample.csproj" />
|
||||||
|
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.ReportSample.Layouts;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample
|
||||||
|
{
|
||||||
|
public static class DataSource
|
||||||
|
{
|
||||||
|
public static int SectionCounter { get; set; }
|
||||||
|
public static int FieldCounter { get; set; }
|
||||||
|
|
||||||
|
public static ReportModel GetReport()
|
||||||
|
{
|
||||||
|
return new ReportModel
|
||||||
|
{
|
||||||
|
Title = "Sample Report Document",
|
||||||
|
HeaderFields = HeaderFields(),
|
||||||
|
|
||||||
|
LogoData = Helpers.GetImage("logo.png"),
|
||||||
|
Sections = Enumerable.Range(0, 10).Select(x => GenerateSection()).ToList(),
|
||||||
|
Photos = Enumerable.Range(0, 10).Select(x => GetReportPhotos()).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
List<ReportHeaderField> HeaderFields()
|
||||||
|
{
|
||||||
|
return new List<ReportHeaderField>
|
||||||
|
{
|
||||||
|
new ReportHeaderField()
|
||||||
|
{
|
||||||
|
Label = "Scope",
|
||||||
|
Value = "Internal activities"
|
||||||
|
},
|
||||||
|
new ReportHeaderField()
|
||||||
|
{
|
||||||
|
Label = "Author",
|
||||||
|
Value = "Marcin Ziąbek"
|
||||||
|
},
|
||||||
|
new ReportHeaderField()
|
||||||
|
{
|
||||||
|
Label = "Date",
|
||||||
|
Value = DateTime.Now.ToString("g")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportSection GenerateSection()
|
||||||
|
{
|
||||||
|
var sectionLength = Helpers.Random.NextDouble() > 0.75
|
||||||
|
? Helpers.Random.Next(10, 20)
|
||||||
|
: Helpers.Random.Next(3, 11);
|
||||||
|
|
||||||
|
return new ReportSection
|
||||||
|
{
|
||||||
|
Title = TextPlaceholder.Label(),
|
||||||
|
Parts = Enumerable.Range(0, sectionLength).Select(x => GetRandomElement()).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportSectionElement GetRandomElement()
|
||||||
|
{
|
||||||
|
var random = Helpers.Random.NextDouble();
|
||||||
|
|
||||||
|
if (random < 0.8f)
|
||||||
|
return GetTextElement();
|
||||||
|
|
||||||
|
if (random < 0.9f)
|
||||||
|
return GetMapElement();
|
||||||
|
|
||||||
|
return GetPhotosElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportSectionText GetTextElement()
|
||||||
|
{
|
||||||
|
return new ReportSectionText
|
||||||
|
{
|
||||||
|
Label = TextPlaceholder.Label(),
|
||||||
|
Text = TextPlaceholder.Paragraph()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportSectionMap GetMapElement()
|
||||||
|
{
|
||||||
|
var rnd = Helpers.Random.Next(0, 64);
|
||||||
|
|
||||||
|
return new ReportSectionMap
|
||||||
|
{
|
||||||
|
Label = "Location",
|
||||||
|
ImageSource = x => Helpers.GetDocumentMap($"{rnd}.jpg"),
|
||||||
|
Location = Helpers.RandomLocation()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportSectionPhotos GetPhotosElement()
|
||||||
|
{
|
||||||
|
return new ReportSectionPhotos
|
||||||
|
{
|
||||||
|
Label = "Photos",
|
||||||
|
Photos = Enumerable
|
||||||
|
.Range(0, Helpers.Random.Next(1, 10))
|
||||||
|
.Select(x => Helpers.Random.Next(0, 128))
|
||||||
|
.Select(x => Helpers.GetPhoto($"{x}.jpg"))
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportPhoto GetReportPhotos()
|
||||||
|
{
|
||||||
|
var photoId = Helpers.Random.Next(0, 128);
|
||||||
|
var mapId = Helpers.Random.Next(0, 64);
|
||||||
|
|
||||||
|
return new ReportPhoto()
|
||||||
|
{
|
||||||
|
PhotoData = Helpers.GetPhoto($"{photoId}.jpg"),
|
||||||
|
|
||||||
|
Comments = TextPlaceholder.Paragraph(),
|
||||||
|
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
|
||||||
|
Location = Helpers.RandomLocation(),
|
||||||
|
|
||||||
|
MapContextSource = x => Helpers.GetContextMap($"{mapId}.jpg"),
|
||||||
|
MapDetailsSource = x => Helpers.GetDetailsMap($"{mapId}.jpg")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using QuestPDF.ReportSample.Layouts;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample
|
||||||
|
{
|
||||||
|
public static class Helpers
|
||||||
|
{
|
||||||
|
public static Random Random { get; } = new Random();
|
||||||
|
|
||||||
|
public static string GetTestItem(string path) => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", path);
|
||||||
|
|
||||||
|
public static byte[] GetDocumentMap(string name) => GetImage(Path.Combine("Maps", "Document", name));
|
||||||
|
public static byte[] GetDetailsMap(string name) => GetImage(Path.Combine("Maps", "Details", name));
|
||||||
|
public static byte[] GetContextMap(string name) => GetImage(Path.Combine("Maps", "Context", name));
|
||||||
|
|
||||||
|
public static byte[] GetPhoto(string name) => GetImage(Path.Combine("Photos", name));
|
||||||
|
|
||||||
|
public static byte[] GetImage(string name)
|
||||||
|
{
|
||||||
|
var photoPath = GetTestItem(name);
|
||||||
|
return SKImage.FromEncodedData(photoPath).EncodedData.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Location RandomLocation()
|
||||||
|
{
|
||||||
|
return new Location
|
||||||
|
{
|
||||||
|
Longitude = Helpers.Random.NextDouble() * 360f - 180f,
|
||||||
|
Latitude = Helpers.Random.NextDouble() * 180f - 90f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample.Layouts
|
||||||
|
{
|
||||||
|
public static class Helpers
|
||||||
|
{
|
||||||
|
static IContainer Cell(this IContainer container, string color)
|
||||||
|
{
|
||||||
|
return container
|
||||||
|
.Border(0.5f)
|
||||||
|
.Background(color)
|
||||||
|
.Padding(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer LightCell(this IContainer container)
|
||||||
|
{
|
||||||
|
return container.Cell("#0000");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer DarkCell(this IContainer container)
|
||||||
|
{
|
||||||
|
return container.Cell("#1000");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Format(this Location location)
|
||||||
|
{
|
||||||
|
if (location == null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var lon = location.Longitude;
|
||||||
|
var lat = location.Latitude;
|
||||||
|
|
||||||
|
var typeLon = lon > 0 ? "E" : "W";
|
||||||
|
lon = Math.Abs(lon);
|
||||||
|
|
||||||
|
var typeLat = lat > 0 ? "N" : "S";
|
||||||
|
lat = Math.Abs(lat);
|
||||||
|
|
||||||
|
return $"{lat:F5}° {typeLat} {lon:F5}° {typeLon}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample.Layouts
|
||||||
|
{
|
||||||
|
public class ImageTemplate : IComponent
|
||||||
|
{
|
||||||
|
private Func<Size, byte[]> Source { get; }
|
||||||
|
private float AspectRatio { get; }
|
||||||
|
|
||||||
|
public ImageTemplate(byte[] source, float aspectRatio = 1.333333f) : this(_ => source, aspectRatio)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageTemplate(Func<Size, byte[]> source, float aspectRatio = 1.333333f)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
AspectRatio = aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compose(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.AspectRatio(AspectRatio)
|
||||||
|
.Background("#EEEEEE")
|
||||||
|
.Image(Source(Size.Zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample.Layouts
|
||||||
|
{
|
||||||
|
public class PhotoTemplate : IComponent
|
||||||
|
{
|
||||||
|
public ReportPhoto Model { get; set; }
|
||||||
|
|
||||||
|
public PhotoTemplate(ReportPhoto model)
|
||||||
|
{
|
||||||
|
Model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compose(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Element(PhotoWithMaps);
|
||||||
|
stack.Element(PhotoDetails);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhotoWithMaps(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(-3)
|
||||||
|
.PaddingBottom(3)
|
||||||
|
.Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn(2).Padding(3).Component(new ImageTemplate(Model.PhotoData));
|
||||||
|
|
||||||
|
row.RelativeColumn().Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Element().Padding(3).Component(new ImageTemplate(Model.MapDetailsSource));
|
||||||
|
stack.Element().Padding(3).Component(new ImageTemplate(Model.MapContextSource));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhotoDetails(IContainer container)
|
||||||
|
{
|
||||||
|
container.Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Element().Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn().DarkCell().Text("Date", Typography.Normal);
|
||||||
|
row.RelativeColumn(2).LightCell().Text(Model.Date?.ToString("g") ?? string.Empty, Typography.Normal);
|
||||||
|
row.RelativeColumn().DarkCell().Text("Location", Typography.Normal);
|
||||||
|
row.RelativeColumn(2).LightCell().Text(Model.Location.Format(), Typography.Normal);
|
||||||
|
});
|
||||||
|
|
||||||
|
stack.Element().Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn().DarkCell().Text("Comments", Typography.Normal);
|
||||||
|
row.RelativeColumn(5).LightCell().Text(Model.Comments, Typography.Normal);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample.Layouts
|
||||||
|
{
|
||||||
|
public class SectionTemplate : IComponent
|
||||||
|
{
|
||||||
|
public ReportSection Model { get; set; }
|
||||||
|
|
||||||
|
public SectionTemplate(ReportSection model)
|
||||||
|
{
|
||||||
|
Model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compose(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.MinHeight(100)
|
||||||
|
.Section(section =>
|
||||||
|
{
|
||||||
|
section
|
||||||
|
.Header()
|
||||||
|
.PaddingBottom(5)
|
||||||
|
.Text(Model.Title, Typography.Headline);
|
||||||
|
|
||||||
|
section.Content().PageableStack(column =>
|
||||||
|
{
|
||||||
|
foreach (var part in Model.Parts)
|
||||||
|
{
|
||||||
|
column.Element().Row(row =>
|
||||||
|
{
|
||||||
|
row.ConstantColumn(150).DarkCell().Text(part.Label, Typography.Normal);
|
||||||
|
var frame = row.RelativeColumn().LightCell();
|
||||||
|
|
||||||
|
if (part is ReportSectionText text)
|
||||||
|
frame.Text(text.Text, Typography.Normal);
|
||||||
|
|
||||||
|
if (part is ReportSectionMap map)
|
||||||
|
frame.Element(container => MapElement(container, map));
|
||||||
|
|
||||||
|
if (part is ReportSectionPhotos photos)
|
||||||
|
frame.Element(container => PhotosElement(container, photos));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapElement(IContainer container, ReportSectionMap model)
|
||||||
|
{
|
||||||
|
if (model.ImageSource == null || model.Location == null)
|
||||||
|
{
|
||||||
|
container.Text("No location provided", Typography.Normal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Spacing(5);
|
||||||
|
|
||||||
|
stack.Element().Component(new ImageTemplate(model.ImageSource));
|
||||||
|
stack.Element().Text(model.Location.Format(), Typography.Normal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhotosElement(IContainer container, ReportSectionPhotos model)
|
||||||
|
{
|
||||||
|
if (model.Photos.Count == 0)
|
||||||
|
{
|
||||||
|
container.Text("No photos", Typography.Normal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rowCount = (int) Math.Ceiling(model.Photos.Count / 3f);
|
||||||
|
|
||||||
|
container.Padding(-2).Stack(stack =>
|
||||||
|
{
|
||||||
|
foreach (var rowId in Enumerable.Range(0, rowCount))
|
||||||
|
{
|
||||||
|
stack.Element().Row(row =>
|
||||||
|
{
|
||||||
|
foreach (var id in Enumerable.Range(0, 3))
|
||||||
|
{
|
||||||
|
var data = model.Photos.ElementAtOrDefault(rowId + id);
|
||||||
|
row.RelativeColumn().Padding(2).Component(new ImageTemplate(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using QuestPDF.Drawing;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample.Layouts
|
||||||
|
{
|
||||||
|
public class StandardReport : IDocument
|
||||||
|
{
|
||||||
|
private ReportModel Model { get; }
|
||||||
|
|
||||||
|
public StandardReport(ReportModel model)
|
||||||
|
{
|
||||||
|
Model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DocumentMetadata GetMetadata()
|
||||||
|
{
|
||||||
|
return new DocumentMetadata()
|
||||||
|
{
|
||||||
|
Title = Model.Title,
|
||||||
|
Size = PageSizes.A4
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Compose(IContainer container)
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.PaddingVertical(40)
|
||||||
|
.PaddingHorizontal(50)
|
||||||
|
|
||||||
|
.Page(page =>
|
||||||
|
{
|
||||||
|
page.Header(ComposeHeader);
|
||||||
|
page.Content(ComposeContent);
|
||||||
|
page.Footer().AlignCenter().PageNumber("Page {number}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ComposeHeader(IContainer container)
|
||||||
|
{
|
||||||
|
container.Row(row =>
|
||||||
|
{
|
||||||
|
row.RelativeColumn().MaxWidth(300).Stack(stack =>
|
||||||
|
{
|
||||||
|
stack.Spacing(5);
|
||||||
|
|
||||||
|
stack
|
||||||
|
.Element()
|
||||||
|
.PaddingBottom(5)
|
||||||
|
.Text(Model.Title, Typography.Title);
|
||||||
|
|
||||||
|
stack.Element().ShowOnce().Stack(table =>
|
||||||
|
{
|
||||||
|
table.Spacing(5);
|
||||||
|
|
||||||
|
foreach (var field in Model.HeaderFields)
|
||||||
|
{
|
||||||
|
table.Element().Row(row =>
|
||||||
|
{
|
||||||
|
row.ConstantColumn(50)
|
||||||
|
.AlignLeft()
|
||||||
|
.Text($"{field.Label}:", Typography.Normal);
|
||||||
|
|
||||||
|
row.RelativeColumn()
|
||||||
|
.PaddingLeft(10)
|
||||||
|
.Text(field.Value, Typography.Normal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
row.ConstantColumn(150).Image(Model.LogoData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeContent(IContainer container)
|
||||||
|
{
|
||||||
|
container.PaddingVertical(20).PageableStack(stack =>
|
||||||
|
{
|
||||||
|
stack.Spacing(20);
|
||||||
|
|
||||||
|
foreach (var section in Model.Sections)
|
||||||
|
stack.Element().Component(new SectionTemplate(section));
|
||||||
|
|
||||||
|
stack.Element().PageBreak();
|
||||||
|
|
||||||
|
foreach (var photo in Model.Photos)
|
||||||
|
stack.Element().Component(new PhotoTemplate(photo));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.ReportSample
|
||||||
|
{
|
||||||
|
public class ReportModel
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public byte[] LogoData { get; set; }
|
||||||
|
public List<ReportHeaderField> HeaderFields { get; set; }
|
||||||
|
|
||||||
|
public List<ReportSection> Sections { get; set; }
|
||||||
|
public List<ReportPhoto> Photos { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReportHeaderField
|
||||||
|
{
|
||||||
|
public string Label { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Location
|
||||||
|
{
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReportSection
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public List<ReportSectionElement> Parts { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ReportSectionElement
|
||||||
|
{
|
||||||
|
public string Label { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReportSectionText : ReportSectionElement
|
||||||
|
{
|
||||||
|
public string Text { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReportSectionMap : ReportSectionElement
|
||||||
|
{
|
||||||
|
public Func<Size, byte[]> ImageSource { get; set; }
|
||||||
|
public Location Location { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReportSectionPhotos : ReportSectionElement
|
||||||
|
{
|
||||||
|
public List<byte[]> Photos { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReportPhoto
|
||||||
|
{
|
||||||
|
public byte[] PhotoData { get; set; }
|
||||||
|
|
||||||
|
public Func<Size, byte[]> MapDetailsSource { get; set; }
|
||||||
|
public Func<Size, byte[]> MapContextSource { get; set; }
|
||||||
|
|
||||||
|
public Location Location { get; set; }
|
||||||
|
|
||||||
|
public DateTime? Date { get; set; }
|
||||||
|
public string Comments { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<LangVersion>8</LangVersion>
|
||||||
|
<RootNamespace>QuestPDF.ReportSample</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DeepCloner" Version="0.10.2" />
|
||||||
|
<PackageReference Include="nunit" Version="3.12.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Resources" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Resources\Photos\readme.txt" />
|
||||||
|
<Content Include="Resources\**\**">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 14 KiB |