2021.2.0 Internal links, external links, dynamic images, font weights
This commit is contained in:
Родитель
8b98c4b1ac
Коммит
35214892ef
|
@ -25,11 +25,11 @@ namespace QuestPDF.ReportSample.Layouts
|
|||
.PaddingBottom(5)
|
||||
.Text(Model.Title, Typography.Headline);
|
||||
|
||||
section.Content().PageableStack(column =>
|
||||
section.Content().PageableStack(stack =>
|
||||
{
|
||||
foreach (var part in Model.Parts)
|
||||
{
|
||||
column.Element().Row(row =>
|
||||
stack.Element().Row(row =>
|
||||
{
|
||||
row.ConstantColumn(150).DarkCell().Text(part.Label, Typography.Normal);
|
||||
var frame = row.RelativeColumn().LightCell();
|
||||
|
|
|
@ -71,7 +71,7 @@ namespace QuestPDF.ReportSample.Layouts
|
|||
|
||||
});
|
||||
|
||||
row.ConstantColumn(150).Image(Model.LogoData);
|
||||
row.ConstantColumn(150).ExternalLink("https://www.questpdf.com").Image(Model.LogoData);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -81,10 +81,13 @@ namespace QuestPDF.ReportSample.Layouts
|
|||
{
|
||||
stack.Spacing(20);
|
||||
|
||||
stack.Element().Component(new TableOfContentsTemplate(Model.Sections));
|
||||
|
||||
foreach (var section in Model.Sections)
|
||||
stack.Element().Component(new SectionTemplate(section));
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace QuestPDF.ReportSample
|
|||
{
|
||||
public static class Typography
|
||||
{
|
||||
public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(20);
|
||||
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 Normal => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(10).LineHeight(1.25f).AlignLeft();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using NUnit.Framework;
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace QuestPDF.UnitTests
|
|||
|
||||
public static Size RandomSize => new Size(Random.Next(200, 400), Random.Next(100, 200));
|
||||
|
||||
public static void ShouldEqual(this IEnumerable<Operation> commands, IEnumerable<Operation> expected)
|
||||
public static void ShouldEqual(this IEnumerable<OperationBase> commands, IEnumerable<OperationBase> expected)
|
||||
{
|
||||
commands.Should().HaveSameCount(expected);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ using NUnit.Framework;
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using NUnit.Framework;
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.MeasureTest;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
|
@ -220,6 +220,9 @@ namespace QuestPDF.UnitTests
|
|||
.ExpectCanvasTranslate(0, -250)
|
||||
|
||||
.ExpectChildMeasure("c", expectedInput: new Size(500, 400), returns: new FullRender(Size.Zero))
|
||||
.ExpectCanvasTranslate(0, 600)
|
||||
.ExpectChildDraw("c", new Size(500, 0))
|
||||
.ExpectCanvasTranslate(0, -600)
|
||||
|
||||
.ExpectChildMeasure("d", expectedInput: new Size(500, 400), returns: new FullRender(200, 400))
|
||||
.ExpectCanvasTranslate(0, 600)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
internal class CanvasMock : ICanvas
|
||||
{
|
||||
public Action<Position> TranslateFunc { get; set; }
|
||||
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
|
||||
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
|
||||
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
||||
|
||||
public void Translate(Position vector) => TranslateFunc(vector);
|
||||
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||
public void DrawExternalLink(string url, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawLocationLink(string locationName, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawLocation(string locationName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawLink(string url, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Size MeasureText(string text, TextStyle style)
|
||||
{
|
||||
return new Size(text.Length * style.Size, style.Size);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
internal class ElementMock : Element
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public Func<Size, ISpacePlan> MeasureFunc { get; set; }
|
||||
public Action<Size> DrawFunc { get; set; }
|
||||
|
||||
internal override ISpacePlan Measure(Size availableSpace) => MeasureFunc(availableSpace);
|
||||
internal override void Draw(ICanvas canvas, Size availableSpace) => DrawFunc(availableSpace);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
public abstract class OperationBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawImageOperationBase : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
|
||||
public CanvasDrawImageOperationBase(Position position, Size size)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawRectangleOperationBase : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
public string Color { get; }
|
||||
|
||||
public CanvasDrawRectangleOperationBase(Position position, Size size, string color)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawTextOperationBase : OperationBase
|
||||
{
|
||||
public string Text { get; }
|
||||
public Position Position { get; }
|
||||
public TextStyle Style { get; }
|
||||
|
||||
public CanvasDrawTextOperationBase(string text, Position position, TextStyle style)
|
||||
{
|
||||
Text = text;
|
||||
Position = position;
|
||||
Style = style;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasTranslateOperationBase : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
|
||||
public CanvasTranslateOperationBase(Position position)
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
public class ChildDrawOperationBase : OperationBase
|
||||
{
|
||||
public string ChildId { get; }
|
||||
public Size Input { get; }
|
||||
|
||||
public ChildDrawOperationBase(string childId, Size input)
|
||||
{
|
||||
ChildId = childId;
|
||||
Input = input;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class ChildMeasureOperationBase : OperationBase
|
||||
{
|
||||
public string ChildId { get; }
|
||||
public Size Input { get; }
|
||||
public ISpacePlan Output { get; }
|
||||
|
||||
public ChildMeasureOperationBase(string childId, Size input, ISpacePlan output)
|
||||
{
|
||||
ChildId = childId;
|
||||
Input = input;
|
||||
Output = output;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
public class ElementMeasureOperationBase : OperationBase
|
||||
{
|
||||
public ElementMeasureOperationBase(Size input)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
public static class SingleChildTests
|
||||
{
|
||||
internal static void MeasureWithoutChild<T>(this T element) where T : ContainerElement
|
||||
{
|
||||
element.Child = null;
|
||||
element.Measure(Size.Zero).Should().BeEquivalentTo(new FullRender(Size.Zero));
|
||||
}
|
||||
|
||||
internal static void DrawWithoutChild<T>(this T element) where T : ContainerElement
|
||||
{
|
||||
// component does not throw an exception when called with null child
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
element.Child = null;
|
||||
|
||||
// component does not perform any canvas operation when called with null child
|
||||
TestPlan
|
||||
.For(x => element)
|
||||
.DrawElement(new Size(200, 100))
|
||||
.CheckDrawResult();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
using QuestPDF.UnitTests.TestEngine.Operations;
|
||||
|
||||
namespace QuestPDF.UnitTests.MeasureTest
|
||||
namespace QuestPDF.UnitTests.TestEngine
|
||||
{
|
||||
public abstract class Operation
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class ElementMeasureOperation : Operation
|
||||
{
|
||||
public ElementMeasureOperation(Size input)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal class ChildMeasureOperation : Operation
|
||||
{
|
||||
public string ChildId { get; }
|
||||
public Size Input { get; }
|
||||
public ISpacePlan Output { get; }
|
||||
|
||||
public ChildMeasureOperation(string childId, Size input, ISpacePlan output)
|
||||
{
|
||||
ChildId = childId;
|
||||
Input = input;
|
||||
Output = output;
|
||||
}
|
||||
}
|
||||
|
||||
public class ChildDrawOperation : Operation
|
||||
{
|
||||
public string ChildId { get; }
|
||||
public Size Input { get; }
|
||||
|
||||
public ChildDrawOperation(string childId, Size input)
|
||||
{
|
||||
ChildId = childId;
|
||||
Input = input;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CanvasTranslateOperation : Operation
|
||||
{
|
||||
public Position Position { get; }
|
||||
|
||||
public CanvasTranslateOperation(Position position)
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CanvasDrawRectangleOperation : Operation
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
public string Color { get; }
|
||||
|
||||
public CanvasDrawRectangleOperation(Position position, Size size, string color)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CanvasDrawTextOperation : Operation
|
||||
{
|
||||
public string Text { get; }
|
||||
public Position Position { get; }
|
||||
public TextStyle Style { get; }
|
||||
|
||||
public CanvasDrawTextOperation(string text, Position position, TextStyle style)
|
||||
{
|
||||
Text = text;
|
||||
Position = position;
|
||||
Style = style;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CanvasDrawImageOperation : Operation
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
|
||||
public CanvasDrawImageOperation(Position position, Size size)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ElementMock : Element
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public Func<Size, ISpacePlan> MeasureFunc { get; set; }
|
||||
public Action<Size> DrawFunc { get; set; }
|
||||
|
||||
internal override ISpacePlan Measure(Size availableSpace) => MeasureFunc(availableSpace);
|
||||
internal override void Draw(ICanvas canvas, Size availableSpace) => DrawFunc(availableSpace);
|
||||
}
|
||||
|
||||
internal class CanvasMock : ICanvas
|
||||
{
|
||||
public Action<Position> TranslateFunc { get; set; }
|
||||
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
|
||||
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
|
||||
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
||||
|
||||
public void Translate(Position vector) => TranslateFunc(vector);
|
||||
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||
|
||||
public void DrawLink(string url, Size size)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Size MeasureText(string text, TextStyle style)
|
||||
{
|
||||
return new Size(text.Length * style.Size, style.Size);
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestPlan
|
||||
{
|
||||
private Element Element { get; set; }
|
||||
private ICanvas Canvas { get; }
|
||||
|
||||
private Size OperationInput { get; set; }
|
||||
private Queue<Operation> Operations { get; } = new Queue<Operation>();
|
||||
private Queue<OperationBase> Operations { get; } = new Queue<OperationBase>();
|
||||
|
||||
public TestPlan()
|
||||
{
|
||||
|
@ -153,7 +29,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
return plan;
|
||||
}
|
||||
|
||||
private T GetExpected<T>() where T : Operation
|
||||
private T GetExpected<T>() where T : OperationBase
|
||||
{
|
||||
if (Operations.TryDequeue(out var value) && value is T result)
|
||||
return result;
|
||||
|
@ -169,7 +45,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
{
|
||||
TranslateFunc = position =>
|
||||
{
|
||||
var expected = GetExpected<CanvasTranslateOperation>();
|
||||
var expected = GetExpected<CanvasTranslateOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X);
|
||||
Assert.AreEqual(expected.Position.Y, position.Y);
|
||||
|
@ -178,7 +54,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
},
|
||||
DrawRectFunc = (position, size, color) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawRectangleOperation>();
|
||||
var expected = GetExpected<CanvasDrawRectangleOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X);
|
||||
Assert.AreEqual(expected.Position.Y, position.Y);
|
||||
|
@ -194,7 +70,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
},
|
||||
DrawTextFunc = (text, position, style) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawTextOperation>();
|
||||
var expected = GetExpected<CanvasDrawTextOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Text, text);
|
||||
|
||||
|
@ -211,7 +87,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
},
|
||||
DrawImageFunc = (image, position, size) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawImageOperation>();
|
||||
var expected = GetExpected<CanvasDrawImageOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X);
|
||||
Assert.AreEqual(expected.Position.Y, position.Y);
|
||||
|
@ -232,7 +108,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
Id = id,
|
||||
MeasureFunc = availableSpace =>
|
||||
{
|
||||
var expected = GetExpected<ChildMeasureOperation>();
|
||||
var expected = GetExpected<ChildMeasureOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.ChildId, id);
|
||||
|
||||
|
@ -246,7 +122,7 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
},
|
||||
DrawFunc = availableSpace =>
|
||||
{
|
||||
var expected = GetExpected<ChildDrawOperation>();
|
||||
var expected = GetExpected<ChildDrawOperationBase>();
|
||||
|
||||
Assert.AreEqual(expected.ChildId, id);
|
||||
|
||||
|
@ -271,45 +147,45 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
return this;
|
||||
}
|
||||
|
||||
private TestPlan AddOperation(Operation operation)
|
||||
private TestPlan AddOperation(OperationBase operationBase)
|
||||
{
|
||||
Operations.Enqueue(operation);
|
||||
Operations.Enqueue(operationBase);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestPlan ExpectChildMeasure(string child, Size expectedInput, ISpacePlan returns)
|
||||
{
|
||||
return AddOperation(new ChildMeasureOperation(child, expectedInput, returns));
|
||||
return AddOperation(new ChildMeasureOperationBase(child, expectedInput, returns));
|
||||
}
|
||||
|
||||
public TestPlan ExpectChildDraw(string child, Size expectedInput)
|
||||
{
|
||||
return AddOperation(new ChildDrawOperation(child, expectedInput));
|
||||
return AddOperation(new ChildDrawOperationBase(child, expectedInput));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasTranslate(Position position)
|
||||
{
|
||||
return AddOperation(new CanvasTranslateOperation(position));
|
||||
return AddOperation(new CanvasTranslateOperationBase(position));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasTranslate(float left, float top)
|
||||
{
|
||||
return AddOperation(new CanvasTranslateOperation(new Position(left, top)));
|
||||
return AddOperation(new CanvasTranslateOperationBase(new Position(left, top)));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawRectangle(Position position, Size size, string color)
|
||||
{
|
||||
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
|
||||
return AddOperation(new CanvasDrawRectangleOperationBase(position, size, color));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawText(string text, Position position, TextStyle style)
|
||||
{
|
||||
return AddOperation(new CanvasDrawTextOperation(text, position, style));
|
||||
return AddOperation(new CanvasDrawTextOperationBase(text, position, style));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawImage(Position position, Size size)
|
||||
{
|
||||
return AddOperation(new CanvasDrawImageOperation(position, size));
|
||||
return AddOperation(new CanvasDrawImageOperationBase(position, size));
|
||||
}
|
||||
|
||||
public TestPlan CheckMeasureResult(ISpacePlan expected)
|
||||
|
@ -336,70 +212,4 @@ namespace QuestPDF.UnitTests.MeasureTest
|
|||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SingleChildTests
|
||||
{
|
||||
internal static void MeasureWithoutChild<T>(this T element) where T : ContainerElement
|
||||
{
|
||||
element.Child = null;
|
||||
element.Measure(Size.Zero).Should().BeEquivalentTo(new FullRender(Size.Zero));
|
||||
}
|
||||
|
||||
internal static void DrawWithoutChild<T>(this T element) where T : ContainerElement
|
||||
{
|
||||
// component does not throw an exception when called with null child
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
element.Child = null;
|
||||
|
||||
// component does not perform any canvas operation when called with null child
|
||||
TestPlan
|
||||
.For(x => element)
|
||||
.DrawElement(new Size(200, 100))
|
||||
.CheckDrawResult();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class LetsTest
|
||||
{
|
||||
[Test]
|
||||
public void TestExample()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Padding
|
||||
{
|
||||
Top = 5,
|
||||
Right = 10,
|
||||
Bottom = 15,
|
||||
Left = 20,
|
||||
|
||||
Child = x.CreateChild("child")
|
||||
})
|
||||
.MeasureElement(new Size(200, 100))
|
||||
.ExpectChildMeasure("child", expectedInput: new Size(170, 80), returns: new FullRender(new Size(100, 50)))
|
||||
.CheckMeasureResult(new FullRender(130, 70));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExample2()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Padding
|
||||
{
|
||||
Top = 5,
|
||||
Right = 10,
|
||||
Bottom = 15,
|
||||
Left = 20,
|
||||
|
||||
Child = x.CreateChild("child")
|
||||
})
|
||||
.DrawElement(new Size(200, 100))
|
||||
.ExpectCanvasTranslate(new Position(20, 5))
|
||||
.ExpectChildDraw("child", expectedInput: new Size(170, 80))
|
||||
.ExpectCanvasTranslate(new Position(-20, -5))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,9 +41,19 @@ namespace QuestPDF.Drawing
|
|||
SkiaCanvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
|
||||
}
|
||||
|
||||
public void DrawLink(string url, Size size)
|
||||
public void DrawExternalLink(string url, Size size)
|
||||
{
|
||||
SkiaCanvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
|
||||
}
|
||||
|
||||
public void DrawLocationLink(string locationName, Size size)
|
||||
{
|
||||
SkiaCanvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
|
||||
}
|
||||
|
||||
public void DrawLocation(string locationName)
|
||||
{
|
||||
SkiaCanvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,12 +30,14 @@ namespace QuestPDF.Drawing
|
|||
|
||||
static SKPaint Convert(TextStyle style)
|
||||
{
|
||||
var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(style.Color),
|
||||
Typeface = Fonts.GetOrAdd(style.FontType, SKTypeface.FromFamilyName),
|
||||
Typeface = SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant),
|
||||
TextSize = style.Size,
|
||||
IsLinearText = true,
|
||||
TextEncoding = SKTextEncoding.Utf32,
|
||||
|
||||
TextAlign = style.Alignment switch
|
||||
{
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace QuestPDF.Drawing
|
|||
static class DocumentGenerator
|
||||
{
|
||||
const int DocumentLayoutExceptionThreshold = 250;
|
||||
private static readonly Watermark Watermark = new Watermark();
|
||||
|
||||
internal static void Generate(Stream stream, IDocument document)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Elements
|
||||
{
|
||||
internal class ExternalLink : ContainerElement
|
||||
{
|
||||
public string Url { get; set; } = "https://www.questpdf.com";
|
||||
|
||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
||||
{
|
||||
var targetSize = Child?.Measure(availableSpace) as Size;
|
||||
|
||||
if (targetSize == null)
|
||||
return;
|
||||
|
||||
canvas.DrawExternalLink(Url, targetSize);
|
||||
Child?.Draw(canvas, availableSpace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Elements
|
||||
{
|
||||
internal class InternalLink : ContainerElement
|
||||
{
|
||||
public string LocationName { get; set; }
|
||||
|
||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
||||
{
|
||||
var targetSize = Child?.Measure(availableSpace) as Size;
|
||||
|
||||
if (targetSize == null)
|
||||
return;
|
||||
|
||||
canvas.DrawLocationLink(LocationName, targetSize);
|
||||
Child?.Draw(canvas, availableSpace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Elements
|
||||
{
|
||||
internal class InternalLocation : ContainerElement
|
||||
{
|
||||
public string LocationName { get; set; }
|
||||
|
||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
||||
{
|
||||
canvas.DrawLocation(LocationName);
|
||||
Child?.Draw(canvas, availableSpace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,9 +47,7 @@ namespace QuestPDF.Elements
|
|||
|
||||
var size = space as Size;
|
||||
|
||||
if (size.Height < Size.Epsilon)
|
||||
continue;
|
||||
|
||||
if (size.Height > Size.Epsilon)
|
||||
heightOnCurrentPage += size.Height + Spacing;
|
||||
|
||||
if (space is PartialRender)
|
||||
|
@ -82,16 +80,11 @@ namespace QuestPDF.Elements
|
|||
|
||||
var size = space as Size;
|
||||
|
||||
if (size.Height < Size.Epsilon)
|
||||
{
|
||||
ChildrenQueue.Dequeue();
|
||||
continue;
|
||||
}
|
||||
|
||||
canvas.Translate(new Position(0, topOffset));
|
||||
child.Draw(canvas, new Size(availableSpace.Width, size.Height));
|
||||
canvas.Translate(new Position(0, -topOffset));
|
||||
|
||||
if (size.Height > Size.Epsilon)
|
||||
topOffset += size.Height + Spacing;
|
||||
|
||||
if (space is PartialRender)
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
using QuestPDF.Drawing.SpacePlan;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Elements
|
||||
{
|
||||
internal class Watermark : Element
|
||||
{
|
||||
private Position Offset { get; set; } = new Position(36, 36);
|
||||
private const float ImageHeight = 28;
|
||||
private const string TargetUrl = "https://www.questpdf.com";
|
||||
|
||||
private Image Image { get; set; }
|
||||
private static readonly byte[] ImageData;
|
||||
|
||||
static Watermark()
|
||||
{
|
||||
ImageData = Helpers.Helpers.LoadEmbeddedResource("QuestPDF.Resources.Watermark.png");
|
||||
}
|
||||
|
||||
public Watermark()
|
||||
{
|
||||
Image = new Image()
|
||||
{
|
||||
Data = ImageData
|
||||
};
|
||||
}
|
||||
|
||||
internal void AdjustPosition(Element? element)
|
||||
{
|
||||
while (element != null)
|
||||
{
|
||||
if (element is Padding padding)
|
||||
{
|
||||
if (padding.Left > 0 && padding.Bottom > 0)
|
||||
Offset = new Position(padding.Left, padding.Bottom);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
element = (element as ContainerElement)?.Child;
|
||||
}
|
||||
}
|
||||
|
||||
internal override ISpacePlan Measure(Size availableSpace)
|
||||
{
|
||||
return Image.Measure(availableSpace);
|
||||
}
|
||||
|
||||
internal override void Draw(ICanvas canvas, Size availableSpace)
|
||||
{
|
||||
var offset = new Position(Offset.X, availableSpace.Height - Offset.Y - ImageHeight);
|
||||
canvas.Translate(offset);
|
||||
|
||||
availableSpace = new Size(availableSpace.Width, ImageHeight);
|
||||
var targetSize = Image.Measure(availableSpace) as FullRender;
|
||||
Image.Draw(canvas, targetSize);
|
||||
canvas.DrawLink(TargetUrl, targetSize);
|
||||
|
||||
canvas.Translate(offset.Reverse());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,6 +66,14 @@ namespace QuestPDF.Fluent
|
|||
});
|
||||
}
|
||||
|
||||
public static void DynamicImage(this IContainer element, Func<Size, byte[]> imageSource)
|
||||
{
|
||||
element.Element(new DynamicImage
|
||||
{
|
||||
Source = imageSource
|
||||
});
|
||||
}
|
||||
|
||||
public static void PageNumber(this IContainer element, string textFormat = "{number}", TextStyle? style = null)
|
||||
{
|
||||
element.Element(new PageNumber
|
||||
|
@ -127,11 +135,27 @@ namespace QuestPDF.Fluent
|
|||
return element.Element(new Container());
|
||||
}
|
||||
|
||||
private static void DynamicImage(this IContainer element, Func<Size, byte[]> handler)
|
||||
public static IContainer ExternalLink(this IContainer element, string url)
|
||||
{
|
||||
element.Element(new DynamicImage()
|
||||
return element.Element(new ExternalLink
|
||||
{
|
||||
Source = handler
|
||||
Url = url
|
||||
});
|
||||
}
|
||||
|
||||
public static IContainer Location(this IContainer element, string locationName)
|
||||
{
|
||||
return element.Element(new InternalLocation
|
||||
{
|
||||
LocationName = locationName
|
||||
});
|
||||
}
|
||||
|
||||
public static IContainer InternalLink(this IContainer element, string locationName)
|
||||
{
|
||||
return element.Element(new InternalLink
|
||||
{
|
||||
LocationName = locationName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,13 @@ namespace QuestPDF.Fluent
|
|||
return style.Mutate(x => x.LineHeight = value);
|
||||
}
|
||||
|
||||
public static TextStyle Italic(this TextStyle style, bool value = true)
|
||||
{
|
||||
return style.Mutate(x => x.IsItalic = value);
|
||||
}
|
||||
|
||||
#region Alignmnet
|
||||
|
||||
public static TextStyle Alignment(this TextStyle style, HorizontalAlignment value)
|
||||
{
|
||||
return style.Mutate(x => x.Alignment = value);
|
||||
|
@ -38,17 +45,78 @@ namespace QuestPDF.Fluent
|
|||
|
||||
public static TextStyle AlignLeft(this TextStyle style)
|
||||
{
|
||||
return style.Mutate(x => x.Alignment = HorizontalAlignment.Left);
|
||||
return style.Alignment(HorizontalAlignment.Left);
|
||||
}
|
||||
|
||||
public static TextStyle AlignCenter(this TextStyle style)
|
||||
{
|
||||
return style.Mutate(x => x.Alignment = HorizontalAlignment.Center);
|
||||
return style.Alignment(HorizontalAlignment.Center);
|
||||
}
|
||||
|
||||
public static TextStyle AlignRight(this TextStyle style)
|
||||
{
|
||||
return style.Mutate(x => x.Alignment = HorizontalAlignment.Right);
|
||||
return style.Alignment(HorizontalAlignment.Right);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Weight
|
||||
|
||||
public static TextStyle Weight(this TextStyle style, FontWeight weight)
|
||||
{
|
||||
return style.Mutate(x => x.FontWeight = weight);
|
||||
}
|
||||
|
||||
public static TextStyle Thin(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.Thin);
|
||||
}
|
||||
|
||||
public static TextStyle ExtraLight(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.ExtraLight);
|
||||
}
|
||||
|
||||
public static TextStyle Light(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.Light);
|
||||
}
|
||||
|
||||
public static TextStyle NormalWeight(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.Normal);
|
||||
}
|
||||
|
||||
public static TextStyle Medium(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.Medium);
|
||||
}
|
||||
|
||||
public static TextStyle SemiBold(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.SemiBold);
|
||||
}
|
||||
|
||||
public static TextStyle Bold(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.Bold);
|
||||
}
|
||||
|
||||
public static TextStyle ExtraBold(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.ExtraBold);
|
||||
}
|
||||
|
||||
public static TextStyle Black(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.Black);
|
||||
}
|
||||
|
||||
public static TextStyle ExtraBlack(this TextStyle style)
|
||||
{
|
||||
return style.Weight(FontWeight.ExtraBlack);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
namespace QuestPDF.Infrastructure
|
||||
{
|
||||
public enum FontWeight
|
||||
{
|
||||
Thin = 100,
|
||||
ExtraLight = 200,
|
||||
Light = 300,
|
||||
Normal = 400,
|
||||
Medium = 500,
|
||||
SemiBold = 600,
|
||||
Bold = 700,
|
||||
ExtraBold = 800,
|
||||
Black = 900,
|
||||
ExtraBlack = 1000
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ namespace QuestPDF.Infrastructure
|
|||
void DrawText(string text, Position position, TextStyle style);
|
||||
void DrawImage(SKImage image, Position position, Size size);
|
||||
|
||||
void DrawLink(string url, Size size);
|
||||
void DrawExternalLink(string url, Size size);
|
||||
void DrawLocationLink(string locationName, Size size);
|
||||
void DrawLocation(string locationName);
|
||||
}
|
||||
}
|
|
@ -2,17 +2,19 @@
|
|||
{
|
||||
public class TextStyle
|
||||
{
|
||||
public string Color { get; set; } = "#000000";
|
||||
public string FontType { get; set; } = "Helvetica";
|
||||
public float Size { get; set; } = 12;
|
||||
public float LineHeight { get; set; } = 1.2f;
|
||||
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
||||
internal string Color { get; set; } = "#000000";
|
||||
internal string FontType { get; set; } = "Helvetica";
|
||||
internal float Size { get; set; } = 12;
|
||||
internal float LineHeight { get; set; } = 1.2f;
|
||||
internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
||||
internal FontWeight FontWeight { get; set; } = FontWeight.Normal;
|
||||
internal bool IsItalic { get; set; } = false;
|
||||
|
||||
public static TextStyle Default => new TextStyle();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Color}|{FontType}|{Size}|{LineHeight}|{Alignment}";
|
||||
return $"{Color}|{FontType}|{Size}|{LineHeight}|{Alignment}|{FontWeight}|{IsItalic}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
<Authors>MarcinZiabek</Authors>
|
||||
<Company>CodeFlint</Company>
|
||||
<PackageId>QuestPDF</PackageId>
|
||||
<Version>2021.1.0</Version>
|
||||
<Version>2021.2.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>The library is open-source and totally free now. Removed watermak.</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>2021.2.0 Internal links, external links, dynamic images, font weights</PackageReleaseNotes>
|
||||
<LangVersion>8</LangVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageIcon>Logo.png</PackageIcon>
|
||||
|
|
Загрузка…
Ссылка в новой задаче