Added scaling options for AspectRatio and Image component. Added Debug component to the fluent API

This commit is contained in:
Marcin Ziąbek 2021-03-22 11:49:27 +01:00
Родитель 9c05c182b8
Коммит 5e9ff7ec1e
14 изменённых файлов: 133 добавлений и 79 удалений

Просмотреть файл

@ -19,8 +19,8 @@ namespace QuestPDF.ReportSample
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()
Sections = Enumerable.Range(0, 8).Select(x => GenerateSection()).ToList(),
Photos = Enumerable.Range(0, 8).Select(x => GetReportPhotos()).ToList()
};
List<ReportHeaderField> HeaderFields()

Просмотреть файл

@ -38,10 +38,10 @@ namespace QuestPDF.ReportSample.Layouts
frame.Text(text.Text, Typography.Normal);
if (part is ReportSectionMap map)
frame.Element(container => MapElement(container, map));
frame.Element(x => MapElement(x, map));
if (part is ReportSectionPhotos photos)
frame.Element(container => PhotosElement(container, photos));
frame.Element(x => PhotosElement(x, photos));
});
}
});
@ -56,7 +56,7 @@ namespace QuestPDF.ReportSample.Layouts
return;
}
container.Stack(stack =>
container.PageableStack(stack =>
{
stack.Spacing(5);
@ -75,7 +75,7 @@ namespace QuestPDF.ReportSample.Layouts
var rowCount = (int) Math.Ceiling(model.Photos.Count / 3f);
container.Padding(-2).Stack(stack =>
container.Debug().Padding(-2).PageableStack(stack =>
{
foreach (var rowId in Enumerable.Range(0, rowCount))
{

Просмотреть файл

@ -27,7 +27,7 @@ namespace QuestPDF.ReportSample
public void PerformanceBenchmark()
{
// test size
const int testSize = 1000;
const int testSize = 250;
const decimal performanceTarget = 5; // documents per second
// create report models

Просмотреть файл

@ -5,8 +5,8 @@ namespace QuestPDF.ReportSample
{
public static class Typography
{
public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(20).Bold();
public static TextStyle Headline => TextStyle.Default.FontType("Helvetica").Color("#047AED").Size(14);
public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(22).SemiBold();
public static TextStyle Headline => TextStyle.Default.FontType("Helvetica").Color("#047AED").Size(14).SemiBold();
public static TextStyle Normal => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(10).LineHeight(1.25f).AlignLeft();
}
}

Просмотреть файл

@ -15,7 +15,7 @@ namespace QuestPDF.UnitTests
{
var image = new Image()
{
Data = null
InternalImage = null
};
image.Draw(It.IsAny<ICanvas>(), Size.Zero);

Просмотреть файл

@ -1,3 +1,4 @@
using System;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Infrastructure;
@ -6,17 +7,21 @@ namespace QuestPDF.Elements
internal class AspectRatio : ContainerElement
{
public float Ratio { get; set; } = 1;
public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth;
internal override ISpacePlan Measure(Size availableSpace)
{
if(Child == null)
return new FullRender(Size.Zero);
var size = GetSize(availableSpace);
var size = GetTargetSize(availableSpace);
if (size.Height > availableSpace.Height + Size.Epsilon)
return new Wrap();
if (size.Width > availableSpace.Width + Size.Epsilon)
return new Wrap();
return new FullRender(size);
}
@ -25,7 +30,7 @@ namespace QuestPDF.Elements
if (Child == null)
return;
var size = GetSize(availableSpace);
var size = GetTargetSize(availableSpace);
if (size.Height > availableSpace.Height)
return;
@ -33,9 +38,20 @@ namespace QuestPDF.Elements
Child.Draw(canvas, size);
}
private Size GetSize(Size availableSpace)
private Size GetTargetSize(Size availableSpace)
{
return new Size(availableSpace.Width, (int)(availableSpace.Width / Ratio));
var spaceRatio = availableSpace.Width / availableSpace.Height;
var fitHeight = new Size(availableSpace.Height * Ratio, availableSpace.Height) ;
var fitWidth = new Size(availableSpace.Width, availableSpace.Width / Ratio);
return Option switch
{
AspectRatioOption.FitWidth => fitWidth,
AspectRatioOption.FitHeight => fitHeight,
AspectRatioOption.FitArea => Ratio < spaceRatio ? fitHeight : fitWidth,
_ => throw new ArgumentOutOfRangeException()
};
}
}
}

Просмотреть файл

@ -6,7 +6,7 @@ namespace QuestPDF.Elements
{
internal Container()
{
Child = new Empty();
}
}
}

Просмотреть файл

@ -1,25 +1,35 @@
using QuestPDF.Infrastructure;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class Debug : ContainerElement
{
private static readonly TextStyle TextStyle = TextStyle.Default.Color("#FF0000").FontType("Consolas").Size(10);
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var textStyle = new TextStyle
{
Color = "#FF0000",
FontType = "Consolas",
Size = 10
};
Child?.Draw(canvas, availableSpace);
canvas.DrawRectangle(Position.Zero, availableSpace, "#FF0000");
canvas.DrawRectangle(Position.Zero, availableSpace, "#FF00FF");
canvas.DrawText($"W: {availableSpace.Width}", new Position(5, 12), textStyle);
canvas.DrawText($"H: {availableSpace.Height}", new Position(5, 22), textStyle);
DrawBoundingBox();
DrawDimensions();
void DrawBoundingBox()
{
var container = new Container();
container
.Border(1)
.BorderColor("#FF0000")
.Background("#33FF0000");
container.Draw(canvas, availableSpace);
}
void DrawDimensions()
{
canvas.DrawText($"W: {availableSpace.Width:F1}", new Position(5, 12), TextStyle);
canvas.DrawText($"H: {availableSpace.Height:F1}", new Position(5, 22), TextStyle);
}
}
}
}

Просмотреть файл

@ -1,6 +1,7 @@
using System;
using QuestPDF.Drawing.SpacePlan;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Elements
{
@ -18,9 +19,14 @@ namespace QuestPDF.Elements
internal override void Draw(ICanvas canvas, Size availableSpace)
{
var imageElement = new Image()
var imageData = Source?.Invoke(availableSpace);
if (imageData == null)
return;
var imageElement = new Image
{
Data = Source?.Invoke(availableSpace)
InternalImage = SKImage.FromEncodedData(imageData)
};
imageElement.Draw(canvas, availableSpace);

Просмотреть файл

@ -6,53 +6,24 @@ namespace QuestPDF.Elements
{
internal class Image : Element
{
public byte[]? Data { get; set; }
private SKImage? InternalImage { get; set; }
public SKImage? InternalImage { get; set; }
~Image()
{
InternalImage?.Dispose();
}
private void Initialize()
{
if (Data != null)
InternalImage ??= SKImage.FromEncodedData(Data);
}
internal override ISpacePlan Measure(Size availableSpace)
{
Initialize();
if (InternalImage == null)
return new FullRender(Size.Zero);
if (availableSpace.Width < 0 || availableSpace.Height < 0)
return new Wrap();
var size = GetTargetSize(availableSpace);
return new FullRender(size);
return new FullRender(availableSpace);
}
internal override void Draw(ICanvas canvas, Size availableSpace)
{
Initialize();
if (InternalImage == null)
return;
var size = GetTargetSize(availableSpace);
canvas.DrawImage(InternalImage, Position.Zero, size);
}
private Size GetTargetSize(Size availableSpace)
{
var imageRatio = InternalImage.Width / (float)InternalImage.Height;
var spaceRatio = availableSpace.Width / (float) availableSpace.Height;
return imageRatio < spaceRatio
? new Size((int)(availableSpace.Height * imageRatio), availableSpace.Height)
: new Size(availableSpace.Width, (int)(availableSpace.Width / imageRatio));
canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
}
}
}

Просмотреть файл

@ -6,8 +6,8 @@ namespace QuestPDF.Elements
{
internal class Section : Element
{
public Element? Header { get; set; }
public Element? Content { get; set; }
public ContainerElement? Header { get; set; }
public ContainerElement? Content { get; set; }
internal override ISpacePlan Measure(Size availableSpace)
{

Просмотреть файл

@ -4,6 +4,7 @@ using QuestPDF.Drawing;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Fluent
{
@ -33,21 +34,51 @@ namespace QuestPDF.Fluent
return child;
}
public static void Element(this IContainer element, Action<IContainer> handler)
public static void Element<TParent>(this TParent parent, Action<IContainer> handler) where TParent : IContainer
{
var container = new Container();
element.Element(container);
handler?.Invoke(container);
}
public static void Image(this IContainer element, byte[] data)
{
element.Element(new Image
{
Data = data
});
handler(parent.Container());
}
public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
{
return handler(parent.Container()).Container();
}
public static IContainer Debug(this IContainer parent)
{
return parent.Element(new Debug());
}
public static void Image(this IContainer parent, byte[] data, ImageScaling scaling = ImageScaling.FitWidth)
{
if (data == null)
return;
var image = SKImage.FromEncodedData(data);
var aspectRatio = image.Width / (float)image.Height;
var imageElement = new Image
{
InternalImage = image
};
if (scaling != ImageScaling.Resize)
parent = parent.AspectRatio(aspectRatio, Map(scaling));
parent.Element(imageElement);
static AspectRatioOption Map(ImageScaling scaling)
{
return scaling switch
{
ImageScaling.FitWidth => AspectRatioOption.FitWidth,
ImageScaling.FitHeight => AspectRatioOption.FitHeight,
ImageScaling.FitArea => AspectRatioOption.FitArea,
_ => throw new ArgumentOutOfRangeException()
};
}
}
public static void DynamicImage(this IContainer element, Func<Size, byte[]> imageSource)
{
element.Element(new DynamicImage
@ -65,11 +96,12 @@ namespace QuestPDF.Fluent
});
}
public static IContainer AspectRatio(this IContainer element, float ratio)
public static IContainer AspectRatio(this IContainer element, float ratio, AspectRatioOption option = AspectRatioOption.FitWidth)
{
return element.Element(new AspectRatio
{
Ratio = ratio
Ratio = ratio,
Option = option
});
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace QuestPDF.Infrastructure
{
public enum AspectRatioOption
{
FitWidth,
FitHeight,
FitArea
}
}

Просмотреть файл

@ -0,0 +1,10 @@
namespace QuestPDF.Infrastructure
{
public enum ImageScaling
{
FitWidth,
FitHeight,
FitArea,
Resize
}
}