2021.3.0 Generating images functionality, ShowIf element, fixed mutating TextStyle

This commit is contained in:
Marcin Ziąbek
2021-03-01 16:24:59 +01:00
parent 35214892ef
commit 43c4011f91
11 changed files with 167 additions and 43 deletions
+7 -16
View File
@@ -6,6 +6,7 @@ using System.Reflection;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using SkiaSharp;
@@ -60,23 +61,13 @@ namespace QuestPDF.Examples.Engine
var container = new Container();
methodInfo.Invoke(this, new object[] {container});
var iteration = 1;
Func<int, string> fileNameSchema = i => $"{fileName.ToLower()}-${i}.png";
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++;
}
var document = new SimpleDocument(container, size);
document.GenerateImages(fileNameSchema);
if (showResult)
Process.Start("explorer", fileNameSchema(0));
}
private byte[] RenderPage(Element element, Size size)
@@ -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.Element(Container.Child);
}
}
}
+2 -2
View File
@@ -19,7 +19,7 @@ namespace QuestPDF.ReportSample
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
using var stream = new FileStream(path, FileMode.Create);
report.Generate(stream);
report.GeneratePdf(stream);
Process.Start("explorer.exe", path);
}
@@ -45,7 +45,7 @@ namespace QuestPDF.ReportSample
var sw = new Stopwatch();
sw.Start();
var totalSize = reports.Select(x => x.Generate()).Sum(x => (long)x.Length);
var totalSize = reports.Select(x => x.GeneratePdf()).Sum(x => (long)x.Length);
sw.Stop();
// show summary
+55 -4
View File
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
@@ -11,9 +13,7 @@ namespace QuestPDF.Drawing
{
static class DocumentGenerator
{
const int DocumentLayoutExceptionThreshold = 250;
internal static void Generate(Stream stream, IDocument document)
internal static void GeneratePdf(Stream stream, IDocument document)
{
var content = ElementExtensions.Create(document.Compose);
var metadata = document.GetMetadata();
@@ -42,7 +42,7 @@ namespace QuestPDF.Drawing
pdf.EndPage();
if (totalPages >= DocumentLayoutExceptionThreshold)
if (totalPages >= metadata.DocumentLayoutExceptionThreshold)
{
pdf.Close();
stream.Close();
@@ -60,6 +60,57 @@ namespace QuestPDF.Drawing
stream.Dispose();
}
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
+6
View File
@@ -21,6 +21,12 @@ namespace QuestPDF.Drawing
public DateTime CreationDate { get; set; } = DateTime.Now;
public DateTime ModifiedDate { get; set; } = DateTime.Now;
/// <summary>
/// If the number of generated pages exceeds this threshold
/// (likely due to infinite layout), the exception is thrown.
/// </summary>
public int DocumentLayoutExceptionThreshold { get; set; } = 250;
public static DocumentMetadata Default => new DocumentMetadata();
}
}
+5 -18
View File
@@ -16,24 +16,6 @@ namespace QuestPDF.Fluent
return container;
}
public static byte[] Generate(this IDocument document)
{
using var stream = new MemoryStream();
document.Generate(stream);
return stream.ToArray();
}
public static void Generate(this IDocument document, string filePath)
{
using var stream = new FileStream(filePath, FileMode.Create);
document.Generate(stream);
}
public static void Generate(this IDocument document, Stream stream)
{
DocumentGenerator.Generate(stream, document);
}
public static T Element<T>(this IContainer element, T child) where T : IElement
{
if (element?.Child != null && element.Child is Empty == false)
@@ -158,5 +140,10 @@ namespace QuestPDF.Fluent
LocationName = locationName
});
}
public static IContainer ShowIf(this IContainer element, bool condition)
{
return condition ? element : new Container();
}
}
}
+47
View File
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class GenerateExtensions
{
public static byte[] GeneratePdf(this IDocument document)
{
using var stream = new MemoryStream();
document.GeneratePdf(stream);
return stream.ToArray();
}
public static void GeneratePdf(this IDocument document, string filePath)
{
using var stream = new FileStream(filePath, FileMode.Create);
document.GeneratePdf(stream);
}
public static void GeneratePdf(this IDocument document, Stream stream)
{
DocumentGenerator.GeneratePdf(stream, document);
}
public static IEnumerable<byte[]> GenerateImages(this IDocument document)
{
return DocumentGenerator.GenerateImages(document);
}
/// <param name="filePath">Method should return fileName for given index</param>
public static void GenerateImages(this IDocument document, Func<int, string> filePath)
{
var index = 0;
foreach (var imageData in document.GenerateImages())
{
var path = filePath(index);
File.WriteAllBytes(path, imageData);
index++;
}
}
}
}
+2
View File
@@ -7,6 +7,8 @@ namespace QuestPDF.Fluent
{
private static TextStyle Mutate(this TextStyle style, Action<TextStyle> handler)
{
style = style.Clone();
handler(style);
return style;
}
+2
View File
@@ -4,6 +4,8 @@ namespace QuestPDF.Helpers
{
public static class PageSizes
{
public const int PointsPerInch = 72;
public static Size A0 => new Size(2384, 3370);
public static Size A1 => new Size(1684, 2384);
public static Size A2 => new Size(1190, 1684);
+6 -1
View File
@@ -1,4 +1,7 @@
namespace QuestPDF.Infrastructure
using System;
using QuestPDF.Elements;
namespace QuestPDF.Infrastructure
{
public class TextStyle
{
@@ -16,5 +19,7 @@
{
return $"{Color}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
}
internal TextStyle Clone() => (TextStyle)MemberwiseClone();
}
}
+2 -2
View File
@@ -4,9 +4,9 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2021.2.0</Version>
<Version>2021.3.0</Version>
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
<PackageReleaseNotes>2021.2.0 Internal links, external links, dynamic images, font weights</PackageReleaseNotes>
<PackageReleaseNotes>Generating images functionality, ShowIf element, fixed mutating TextStyle</PackageReleaseNotes>
<LangVersion>8</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIcon>Logo.png</PackageIcon>