Added scaling options for AspectRatio and Image component. Added Debug component to the fluent API
This commit is contained in:
Родитель
9c05c182b8
Коммит
5e9ff7ec1e
|
@ -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);
|
||||
DrawBoundingBox();
|
||||
DrawDimensions();
|
||||
|
||||
canvas.DrawRectangle(Position.Zero, availableSpace, "#FF0000");
|
||||
canvas.DrawRectangle(Position.Zero, availableSpace, "#FF00FF");
|
||||
void DrawBoundingBox()
|
||||
{
|
||||
var container = new Container();
|
||||
|
||||
canvas.DrawText($"W: {availableSpace.Width}", new Position(5, 12), textStyle);
|
||||
canvas.DrawText($"H: {availableSpace.Height}", new Position(5, 22), textStyle);
|
||||
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,19 +34,49 @@ 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);
|
||||
handler(parent.Container());
|
||||
}
|
||||
|
||||
public static void Image(this IContainer element, byte[] data)
|
||||
public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
|
||||
{
|
||||
element.Element(new Image
|
||||
return handler(parent.Container()).Container();
|
||||
}
|
||||
|
||||
public static IContainer Debug(this IContainer parent)
|
||||
{
|
||||
Data = data
|
||||
});
|
||||
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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче