2021.2.0 Internal links, external links, dynamic images, font weights

This commit is contained in:
Marcin Ziąbek 2021-02-08 14:36:12 +01:00
Родитель 8b98c4b1ac
Коммит 35214892ef
41 изменённых файлов: 512 добавлений и 322 удалений

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

@ -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,11 +81,14 @@ 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,13 +30,15 @@ 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
{
HorizontalAlignment.Left => SKTextAlign.Left,

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

@ -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,10 +47,8 @@ namespace QuestPDF.Elements
var size = space as Size;
if (size.Height < Size.Epsilon)
continue;
heightOnCurrentPage += size.Height + Spacing;
if (size.Height > Size.Epsilon)
heightOnCurrentPage += size.Height + Spacing;
if (space is PartialRender)
{
@ -81,18 +79,13 @@ namespace QuestPDF.Elements
break;
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));
topOffset += size.Height + Spacing;
if (size.Height > Size.Epsilon)
topOffset += size.Height + Spacing;
if (space is PartialRender)
break;

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

@ -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>