Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f927ec402 | |||
| 9dc3e2b533 |
@@ -1,70 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '22 16 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -1,54 +0,0 @@
|
||||
name: Build And Create Nuget Package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '3 0 * * 0'
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ${{ matrix.environment }}
|
||||
strategy:
|
||||
matrix:
|
||||
environment:
|
||||
- macos-latest
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||
UNIT_TEST_PROJECT: QuestPDF.UnitTests/QuestPDF.UnitTests.csproj
|
||||
|
||||
steps:
|
||||
- name: 📝 Fetch Sources 📝
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: ⚙ Setup .NET 6.0 SDK ⚙
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- name: 🔄 Restore Nuget Packages 🔄
|
||||
shell: bash
|
||||
run: dotnet restore
|
||||
working-directory: ./Source
|
||||
|
||||
- name: 🛠 Build Solution 🛠
|
||||
shell: bash
|
||||
run: dotnet build -c Release --no-restore
|
||||
working-directory: ./Source
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.environment == 'windows-latest' }}
|
||||
with:
|
||||
name: Build Package
|
||||
path: |
|
||||
**/*.nupkg
|
||||
**/*.snupkg
|
||||
!.nuget
|
||||
@@ -1 +0,0 @@
|
||||
github: QuestPDF
|
||||
@@ -0,0 +1,170 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
//[ShowResult]
|
||||
[ImageSize(300, 200)]
|
||||
public void Debug(IContainer container)
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Debug()
|
||||
.Padding(-5)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeColumn().Padding(5).Extend().Placeholder();
|
||||
row.RelativeColumn().Padding(5).Extend().Placeholder();
|
||||
});
|
||||
}
|
||||
|
||||
[ShowResult]
|
||||
[ImageSize(300, 200)]
|
||||
public void ElementEnd(IContainer container)
|
||||
{
|
||||
var text = "";
|
||||
|
||||
container
|
||||
.Padding(10)
|
||||
.Element(x =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
x.Height(10).Width(50).Background("#DDD");
|
||||
else
|
||||
x.Text(text);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
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.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 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,33 @@
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples.Engine
|
||||
{
|
||||
public class SimpleDocument : IDocument
|
||||
{
|
||||
private IContainer Container { get; }
|
||||
private Size Size { get; }
|
||||
|
||||
public SimpleDocument(IContainer container, Size size)
|
||||
{
|
||||
Container = container;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public DocumentMetadata GetMetadata()
|
||||
{
|
||||
return new DocumentMetadata()
|
||||
{
|
||||
RasterDpi = PageSizes.PointsPerInch * 2,
|
||||
Size = Size
|
||||
};
|
||||
}
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container.Background("#FFF").Element(Container.Child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,19 @@
|
||||
<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\QuestPDF.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -2,11 +2,15 @@ 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
|
||||
@@ -14,9 +18,9 @@ namespace QuestPDF.ReportSample
|
||||
Title = "Sample Report Document",
|
||||
HeaderFields = HeaderFields(),
|
||||
|
||||
LogoData = Helpers.GetImage("Logo.png"),
|
||||
Sections = Enumerable.Range(0, 40).Select(x => GenerateSection()).ToList(),
|
||||
Photos = Enumerable.Range(0, 25).Select(x => GetReportPhotos()).ToList()
|
||||
LogoData = Helpers.GetImage("logo.png"),
|
||||
Sections = Enumerable.Range(0, 8).Select(x => GenerateSection()).ToList(),
|
||||
Photos = Enumerable.Range(0, 8).Select(x => GetReportPhotos()).ToList()
|
||||
};
|
||||
|
||||
List<ReportHeaderField> HeaderFields()
|
||||
@@ -37,11 +41,6 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
Label = "Date",
|
||||
Value = DateTime.Now.ToString("g")
|
||||
},
|
||||
new ReportHeaderField()
|
||||
{
|
||||
Label = "Status",
|
||||
Value = "Completed, found 2 issues"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -49,12 +48,12 @@ namespace QuestPDF.ReportSample
|
||||
ReportSection GenerateSection()
|
||||
{
|
||||
var sectionLength = Helpers.Random.NextDouble() > 0.75
|
||||
? Helpers.Random.Next(20, 40)
|
||||
: Helpers.Random.Next(5, 10);
|
||||
? Helpers.Random.Next(10, 20)
|
||||
: Helpers.Random.Next(3, 11);
|
||||
|
||||
return new ReportSection
|
||||
{
|
||||
Title = Placeholders.Label(),
|
||||
Title = TextPlaceholder.Label(),
|
||||
Parts = Enumerable.Range(0, sectionLength).Select(x => GetRandomElement()).ToList()
|
||||
};
|
||||
}
|
||||
@@ -63,10 +62,10 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
var random = Helpers.Random.NextDouble();
|
||||
|
||||
if (random < 0.9f)
|
||||
if (random < 0.8f)
|
||||
return GetTextElement();
|
||||
|
||||
if (random < 0.95f)
|
||||
if (random < 0.9f)
|
||||
return GetMapElement();
|
||||
|
||||
return GetPhotosElement();
|
||||
@@ -76,16 +75,19 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
return new ReportSectionText
|
||||
{
|
||||
Label = Placeholders.Label(),
|
||||
Text = Placeholders.Paragraph()
|
||||
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()
|
||||
};
|
||||
}
|
||||
@@ -95,17 +97,29 @@ namespace QuestPDF.ReportSample
|
||||
return new ReportSectionPhotos
|
||||
{
|
||||
Label = "Photos",
|
||||
PhotoCount = Helpers.Random.Next(1, 15)
|
||||
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()
|
||||
{
|
||||
Comments = Placeholders.Sentence(),
|
||||
PhotoData = Helpers.GetPhoto($"{photoId}.jpg"),
|
||||
|
||||
Comments = TextPlaceholder.Paragraph(),
|
||||
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
|
||||
Location = Helpers.RandomLocation()
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,27 @@
|
||||
using System;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
public static class Helpers
|
||||
{
|
||||
static IContainer Cell(this IContainer container, bool background)
|
||||
static IContainer Cell(this IContainer container, string color)
|
||||
{
|
||||
return container
|
||||
.Border(0.5f)
|
||||
.BorderColor(Colors.Grey.Lighten1)
|
||||
.Background(background ? Colors.Grey.Lighten4 : Colors.White)
|
||||
.Background(color)
|
||||
.Padding(5);
|
||||
}
|
||||
|
||||
public static IContainer ValueCell(this IContainer container)
|
||||
public static IContainer LightCell(this IContainer container)
|
||||
{
|
||||
return container.Cell(false);
|
||||
return container.Cell("#0000");
|
||||
}
|
||||
|
||||
public static IContainer LabelCell(this IContainer container)
|
||||
public static IContainer DarkCell(this IContainer container)
|
||||
{
|
||||
return container.Cell(true);
|
||||
return container.Cell("#1000");
|
||||
}
|
||||
|
||||
public static string Format(this Location location)
|
||||
@@ -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(stack =>
|
||||
{
|
||||
foreach (var part in Model.Parts)
|
||||
{
|
||||
stack.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(x => MapElement(x, map));
|
||||
|
||||
if (part is ReportSectionPhotos photos)
|
||||
frame.Element(x => PhotosElement(x, photos));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MapElement(IContainer container, ReportSectionMap model)
|
||||
{
|
||||
if (model.ImageSource == null || model.Location == null)
|
||||
{
|
||||
container.Text("No location provided", Typography.Normal);
|
||||
return;
|
||||
}
|
||||
|
||||
container.PageableStack(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.Debug().Padding(-2).PageableStack(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,97 @@
|
||||
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).ExternalLink("https://www.questpdf.com").Image(Model.LogoData);
|
||||
});
|
||||
}
|
||||
|
||||
void ComposeContent(IContainer container)
|
||||
{
|
||||
container.PaddingVertical(20).PageableStack(stack =>
|
||||
{
|
||||
stack.Spacing(20);
|
||||
|
||||
stack.Element().Component(new TableOfContentsTemplate(Model.Sections));
|
||||
|
||||
foreach (var section in Model.Sections)
|
||||
stack.Element().Location(section.Title).Component(new SectionTemplate(section));
|
||||
|
||||
stack.Element().PageBreak();
|
||||
stack.Element().Location("Photos");
|
||||
|
||||
foreach (var photo in Model.Photos)
|
||||
stack.Element().Component(new PhotoTemplate(photo));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
public class TableOfContentsTemplate : IComponent
|
||||
{
|
||||
private List<ReportSection> Sections { get; }
|
||||
|
||||
public TableOfContentsTemplate(List<ReportSection> sections)
|
||||
{
|
||||
Sections = sections;
|
||||
}
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container
|
||||
.Section(section =>
|
||||
{
|
||||
section
|
||||
.Header()
|
||||
.PaddingBottom(5)
|
||||
.Text("Table of contents", Typography.Headline);
|
||||
|
||||
section.Content().PageableStack(stack =>
|
||||
{
|
||||
stack.Spacing(5);
|
||||
|
||||
for (var i = 0; i < Sections.Count; i++)
|
||||
stack.Element(c => DrawLink(c, i+1, Sections[i].Title));
|
||||
|
||||
stack.Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawLink(IContainer container, int number, string locationName)
|
||||
{
|
||||
container
|
||||
.InternalLink(locationName)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
|
||||
row.RelativeColumn().Text(locationName, Typography.Normal);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,16 +44,22 @@ namespace QuestPDF.ReportSample
|
||||
|
||||
public class ReportSectionMap : ReportSectionElement
|
||||
{
|
||||
public Func<Size, byte[]> ImageSource { get; set; }
|
||||
public Location Location { get; set; }
|
||||
}
|
||||
|
||||
public class ReportSectionPhotos : ReportSectionElement
|
||||
{
|
||||
public int PhotoCount { get; set; }
|
||||
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; }
|
||||
@@ -1,21 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>8</LangVersion>
|
||||
<RootNamespace>QuestPDF.ReportSample</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<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>
|
||||
@@ -24,8 +29,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Logo.png" />
|
||||
</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 |