Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b658344a46 |
@@ -1,146 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using QuestPDF.Elements;
|
|
||||||
using QuestPDF.Fluent;
|
|
||||||
using QuestPDF.Helpers;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples
|
|
||||||
{
|
|
||||||
public class ProcessRunningTime
|
|
||||||
{
|
|
||||||
public TimeSpan FluentTime { get; set; }
|
|
||||||
public TimeSpan GenerationTime { get; set; }
|
|
||||||
public float Size { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GenerationBenchmark
|
|
||||||
{
|
|
||||||
public const int TestSize = 4096;
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void BenchmarkAsync()
|
|
||||||
{
|
|
||||||
RunTest(() => Enumerable
|
|
||||||
.Range(0, TestSize)
|
|
||||||
.AsParallel() // difference
|
|
||||||
.Select(GenerateAndCollect)
|
|
||||||
.ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void BenchmarkSync()
|
|
||||||
{
|
|
||||||
RunTest(() => Enumerable
|
|
||||||
.Range(0, TestSize)
|
|
||||||
.Select(GenerateAndCollect)
|
|
||||||
.ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RunTest(Func<IEnumerable<ProcessRunningTime>> handler)
|
|
||||||
{
|
|
||||||
var totalFluentTime = TimeSpan.Zero;
|
|
||||||
var totalGenerationTime = TimeSpan.Zero;
|
|
||||||
|
|
||||||
var stopWatch = new Stopwatch();
|
|
||||||
|
|
||||||
stopWatch.Start();
|
|
||||||
var results = handler();
|
|
||||||
stopWatch.Stop();
|
|
||||||
|
|
||||||
foreach (var result in results)
|
|
||||||
{
|
|
||||||
totalFluentTime += result.FluentTime;
|
|
||||||
totalGenerationTime += result.GenerationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"Fluent: {totalFluentTime:g}");
|
|
||||||
Console.WriteLine($"Generation: {totalGenerationTime:g}");
|
|
||||||
Console.WriteLine($"Total: {stopWatch.Elapsed:g}");
|
|
||||||
}
|
|
||||||
|
|
||||||
static ProcessRunningTime GenerateAndCollect(int attemptNumber)
|
|
||||||
{
|
|
||||||
var stopwatch = new Stopwatch();
|
|
||||||
stopwatch.Start();
|
|
||||||
|
|
||||||
var container = new Container();
|
|
||||||
|
|
||||||
container
|
|
||||||
.Padding(10)
|
|
||||||
.MinimalBox()
|
|
||||||
.Border(1)
|
|
||||||
.Column(column =>
|
|
||||||
{
|
|
||||||
column.Item().Text($"Attempts {attemptNumber}");
|
|
||||||
|
|
||||||
const int numberOfRows = 100;
|
|
||||||
const int numberOfColumns = 10;
|
|
||||||
|
|
||||||
for (var y = 0; y < numberOfRows; y++)
|
|
||||||
{
|
|
||||||
column.Item().Row(row =>
|
|
||||||
{
|
|
||||||
for (var x = 0; x < numberOfColumns; x++)
|
|
||||||
{
|
|
||||||
row.RelativeItem()
|
|
||||||
|
|
||||||
.Background(Colors.Red.Lighten5)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Lighten4)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Lighten3)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Lighten2)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Lighten1)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Medium)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Darken1)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Darken2)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Darken3)
|
|
||||||
.Padding(3)
|
|
||||||
|
|
||||||
.Background(Colors.Red.Darken4)
|
|
||||||
.Height(3);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var fluentTime = stopwatch.Elapsed;
|
|
||||||
|
|
||||||
stopwatch.Reset();
|
|
||||||
stopwatch.Start();
|
|
||||||
|
|
||||||
var size = Document
|
|
||||||
.Create(x => x.Page(page => page.Content().Element(container)))
|
|
||||||
.GeneratePdf()
|
|
||||||
.Length;
|
|
||||||
|
|
||||||
var generationTime = stopwatch.Elapsed;
|
|
||||||
|
|
||||||
return new ProcessRunningTime
|
|
||||||
{
|
|
||||||
FluentTime = fluentTime,
|
|
||||||
GenerationTime = generationTime,
|
|
||||||
Size = size
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using QuestPDF.Drawing.Exceptions;
|
using QuestPDF.Drawing.Exceptions;
|
||||||
using QuestPDF.Examples.Engine;
|
using QuestPDF.Examples.Engine;
|
||||||
@@ -35,21 +34,6 @@ namespace QuestPDF.Examples
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void DynamicImage()
|
|
||||||
{
|
|
||||||
RenderingTest
|
|
||||||
.Create()
|
|
||||||
.PageSize(450, 350)
|
|
||||||
.ProducePdf()
|
|
||||||
.ShowResults()
|
|
||||||
.Render(page =>
|
|
||||||
{
|
|
||||||
page.Padding(25)
|
|
||||||
.Image(Placeholders.Image);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Exception()
|
public void Exception()
|
||||||
{
|
{
|
||||||
@@ -63,43 +47,5 @@ namespace QuestPDF.Examples
|
|||||||
.Render(page => page.Image("non_existent.png"));
|
.Render(page => page.Image("non_existent.png"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void ReusingTheSameImageFileShouldBePossible()
|
|
||||||
{
|
|
||||||
var fileName = Path.GetTempFileName() + ".jpg";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var image = Placeholders.Image(300, 100);
|
|
||||||
|
|
||||||
using var file = File.Create(fileName);
|
|
||||||
file.Write(image);
|
|
||||||
file.Dispose();
|
|
||||||
|
|
||||||
RenderingTest
|
|
||||||
.Create()
|
|
||||||
.ProducePdf()
|
|
||||||
.PageSize(PageSizes.A4)
|
|
||||||
.ShowResults()
|
|
||||||
.Render(container =>
|
|
||||||
{
|
|
||||||
container
|
|
||||||
.Padding(20)
|
|
||||||
.Column(column =>
|
|
||||||
{
|
|
||||||
column.Spacing(20);
|
|
||||||
|
|
||||||
column.Item().Image(fileName);
|
|
||||||
column.Item().Image(fileName);
|
|
||||||
column.Item().Image(fileName);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
File.Delete(fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using QuestPDF.Examples.Engine;
|
|
||||||
using QuestPDF.Fluent;
|
|
||||||
using QuestPDF.Helpers;
|
|
||||||
|
|
||||||
namespace QuestPDF.Examples
|
|
||||||
{
|
|
||||||
public class RepeatContentExamples
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void ItemTypes()
|
|
||||||
{
|
|
||||||
RenderingTest
|
|
||||||
.Create()
|
|
||||||
.ProducePdf()
|
|
||||||
.PageSize(PageSizes.A4)
|
|
||||||
.ShowResults()
|
|
||||||
.Render(container =>
|
|
||||||
{
|
|
||||||
container
|
|
||||||
.Padding(25)
|
|
||||||
.Decoration(decoration =>
|
|
||||||
{
|
|
||||||
decoration.Before().Text("Test").FontSize(22);
|
|
||||||
|
|
||||||
decoration.Content().Column(column =>
|
|
||||||
{
|
|
||||||
column.Spacing(20);
|
|
||||||
|
|
||||||
foreach (var _ in Enumerable.Range(0, 10))
|
|
||||||
column.Item().Background(Colors.Grey.Medium).ExtendHorizontal().Height(80);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -105,41 +105,5 @@ namespace QuestPDF.Examples
|
|||||||
.Row(row => { });
|
.Row(row => { });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void RowElementForRelativeHeightDivision()
|
|
||||||
{
|
|
||||||
RenderingTest
|
|
||||||
.Create()
|
|
||||||
.ProduceImages()
|
|
||||||
.ShowResults()
|
|
||||||
.MaxPages(100)
|
|
||||||
.PageSize(250, 400)
|
|
||||||
.Render(container =>
|
|
||||||
{
|
|
||||||
container
|
|
||||||
.Padding(25)
|
|
||||||
.AlignLeft()
|
|
||||||
.RotateRight()
|
|
||||||
.Row(row =>
|
|
||||||
{
|
|
||||||
row.Spacing(20);
|
|
||||||
|
|
||||||
row.RelativeItem(1).Element(Content);
|
|
||||||
row.RelativeItem(2).Element(Content);
|
|
||||||
row.RelativeItem(3).Element(Content);
|
|
||||||
|
|
||||||
void Content(IContainer container)
|
|
||||||
{
|
|
||||||
container
|
|
||||||
.RotateLeft()
|
|
||||||
.Border(1)
|
|
||||||
.Background(Placeholders.BackgroundColor())
|
|
||||||
.Padding(5)
|
|
||||||
.Text(Placeholders.Label());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -657,37 +657,5 @@ namespace QuestPDF.Examples
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void WordWrappingStability()
|
|
||||||
{
|
|
||||||
// instruction: check if any characters repeat when performing the word-wrapping algorithm
|
|
||||||
|
|
||||||
RenderingTest
|
|
||||||
.Create()
|
|
||||||
.PageSize(PageSizes.A4)
|
|
||||||
.ProducePdf()
|
|
||||||
.ShowResults()
|
|
||||||
.Render(container =>
|
|
||||||
{
|
|
||||||
var text = "Lorem ipsum dolor sit amet consectetuer";
|
|
||||||
|
|
||||||
container
|
|
||||||
.Padding(20)
|
|
||||||
.Column(column =>
|
|
||||||
{
|
|
||||||
column.Spacing(10);
|
|
||||||
|
|
||||||
foreach (var width in Enumerable.Range(25, 200))
|
|
||||||
{
|
|
||||||
column
|
|
||||||
.Item()
|
|
||||||
.MaxWidth(width)
|
|
||||||
.Background(Colors.Grey.Lighten3)
|
|
||||||
.Text(text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,8 +64,9 @@ namespace QuestPDF.Drawing
|
|||||||
var container = new DocumentContainer();
|
var container = new DocumentContainer();
|
||||||
document.Compose(container);
|
document.Compose(container);
|
||||||
var content = container.Compose();
|
var content = container.Compose();
|
||||||
|
|
||||||
|
ApplyRepeatContent(content);
|
||||||
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
||||||
ApplyContentRepeatState(content, false);
|
|
||||||
|
|
||||||
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
|
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
|
||||||
|
|
||||||
@@ -83,8 +84,6 @@ namespace QuestPDF.Drawing
|
|||||||
content.VisitChildren(x => x?.Initialize(pageContext, canvas));
|
content.VisitChildren(x => x?.Initialize(pageContext, canvas));
|
||||||
content.VisitChildren(x => (x as IStateResettable)?.ResetState());
|
content.VisitChildren(x => (x as IStateResettable)?.ResetState());
|
||||||
|
|
||||||
ResetIsRenderedState(content);
|
|
||||||
|
|
||||||
canvas.BeginDocument();
|
canvas.BeginDocument();
|
||||||
|
|
||||||
var currentPage = 1;
|
var currentPage = 1;
|
||||||
@@ -164,50 +163,6 @@ namespace QuestPDF.Drawing
|
|||||||
return debuggingState;
|
return debuggingState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApplyContentRepeatState(Element? content, bool repeatContent)
|
|
||||||
{
|
|
||||||
if (content == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (content is IVisual visual)
|
|
||||||
visual.RepeatContent = repeatContent;
|
|
||||||
|
|
||||||
if (content is TextBlock textBlock)
|
|
||||||
{
|
|
||||||
foreach (var textBlockItem in textBlock.Items)
|
|
||||||
{
|
|
||||||
if (textBlockItem is TextBlockElement textElement)
|
|
||||||
{
|
|
||||||
ApplyContentRepeatState(textElement.Element, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: apply RepeatState in dynamic content
|
|
||||||
//if (content is DynamicHost dynamicHost)
|
|
||||||
// dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
|
||||||
|
|
||||||
if (content is RepeatContentSetter repeatContentSetter)
|
|
||||||
repeatContent = repeatContentSetter.RepeatContent;
|
|
||||||
|
|
||||||
foreach (var child in content.GetChildren())
|
|
||||||
ApplyContentRepeatState(child, repeatContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ResetIsRenderedState(Element? content)
|
|
||||||
{
|
|
||||||
if (content == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (content is IVisual visual)
|
|
||||||
visual.IsRendered = false;
|
|
||||||
|
|
||||||
foreach (var child in content.GetChildren())
|
|
||||||
ResetIsRenderedState(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
||||||
{
|
{
|
||||||
if (content == null)
|
if (content == null)
|
||||||
@@ -239,5 +194,23 @@ namespace QuestPDF.Drawing
|
|||||||
foreach (var child in content.GetChildren())
|
foreach (var child in content.GetChildren())
|
||||||
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ApplyRepeatContent(this Element? content, bool enabled = false)
|
||||||
|
{
|
||||||
|
if (content == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (content is RepeatContent repeatContent)
|
||||||
|
{
|
||||||
|
ApplyRepeatContent(repeatContent.Child, repeatContent.Repeat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in content.GetChildren())
|
||||||
|
ApplyRepeatContent(child, enabled);
|
||||||
|
|
||||||
|
if (!enabled)
|
||||||
|
content.CreateProxy(y => y is IContent ? new ShowOnce { Child = y } : y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,28 +7,19 @@ namespace QuestPDF.Elements
|
|||||||
{
|
{
|
||||||
public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace);
|
public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace);
|
||||||
|
|
||||||
internal class Canvas : Element, IVisual, ICacheable
|
internal class Canvas : Element, ICacheable, IContent
|
||||||
{
|
{
|
||||||
public bool IsRendered { get; set; }
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
|
|
||||||
public DrawOnCanvas Handler { get; set; }
|
public DrawOnCanvas Handler { get; set; }
|
||||||
|
|
||||||
internal override SpacePlan Measure(Size availableSpace)
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (availableSpace.IsNegative())
|
return availableSpace.IsNegative()
|
||||||
return SpacePlan.Wrap();
|
? SpacePlan.Wrap()
|
||||||
|
: SpacePlan.FullRender(availableSpace);
|
||||||
if (IsRendered && !RepeatContent)
|
|
||||||
return SpacePlan.FullRender(Size.Zero);
|
|
||||||
|
|
||||||
return SpacePlan.FullRender(availableSpace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
IsRendered = true;
|
|
||||||
|
|
||||||
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
|
||||||
|
|
||||||
if (Handler == null || skiaCanvas == null)
|
if (Handler == null || skiaCanvas == null)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using QuestPDF.Infrastructure;
|
|||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class DynamicHost : Element, IStateResettable
|
internal class DynamicHost : Element, IStateResettable, IContent
|
||||||
{
|
{
|
||||||
private DynamicComponentProxy Child { get; }
|
private DynamicComponentProxy Child { get; }
|
||||||
private object InitialComponentState { get; set; }
|
private object InitialComponentState { get; set; }
|
||||||
|
|||||||
@@ -6,38 +6,31 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class DynamicImage : Element, IVisual
|
internal class DynamicImage : Element, IContent
|
||||||
{
|
{
|
||||||
public bool IsRendered { get; set; }
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
|
|
||||||
public Func<Size, byte[]>? Source { get; set; }
|
public Func<Size, byte[]>? Source { get; set; }
|
||||||
|
|
||||||
internal override SpacePlan Measure(Size availableSpace)
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (availableSpace.IsNegative())
|
return availableSpace.IsNegative()
|
||||||
return SpacePlan.Wrap();
|
? SpacePlan.Wrap()
|
||||||
|
: SpacePlan.FullRender(availableSpace);
|
||||||
if (IsRendered && !RepeatContent)
|
|
||||||
return SpacePlan.FullRender(Size.Zero);
|
|
||||||
|
|
||||||
return SpacePlan.FullRender(availableSpace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
IsRendered = true;
|
|
||||||
|
|
||||||
if (availableSpace.Width < Size.Epsilon || availableSpace.Height < Size.Epsilon)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var imageData = Source?.Invoke(availableSpace);
|
var imageData = Source?.Invoke(availableSpace);
|
||||||
|
|
||||||
if (imageData == null)
|
if (imageData == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using var image = SKImage.FromEncodedData(imageData);
|
var imageElement = new Image
|
||||||
Canvas.DrawImage(image, Position.Zero, availableSpace);
|
{
|
||||||
|
InternalImage = SKImage.FromEncodedData(imageData)
|
||||||
|
};
|
||||||
|
|
||||||
|
imageElement.Initialize(PageContext, Canvas);
|
||||||
|
imageElement.Draw(availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,8 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace QuestPDF.Elements
|
namespace QuestPDF.Elements
|
||||||
{
|
{
|
||||||
internal class Image : Element, IVisual, ICacheable
|
internal class Image : Element, ICacheable, IContent
|
||||||
{
|
{
|
||||||
public bool IsRendered { get; set; }
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
|
|
||||||
public SKImage? InternalImage { get; set; }
|
public SKImage? InternalImage { get; set; }
|
||||||
|
|
||||||
~Image()
|
~Image()
|
||||||
@@ -19,13 +16,9 @@ namespace QuestPDF.Elements
|
|||||||
|
|
||||||
internal override SpacePlan Measure(Size availableSpace)
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (availableSpace.IsNegative())
|
return availableSpace.IsNegative()
|
||||||
return SpacePlan.Wrap();
|
? SpacePlan.Wrap()
|
||||||
|
: SpacePlan.FullRender(availableSpace);
|
||||||
if (IsRendered && !RepeatContent)
|
|
||||||
return SpacePlan.FullRender(Size.Zero);
|
|
||||||
|
|
||||||
return SpacePlan.FullRender(availableSpace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
@@ -33,7 +26,6 @@ namespace QuestPDF.Elements
|
|||||||
if (InternalImage == null)
|
if (InternalImage == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IsRendered = true;
|
|
||||||
Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
|
Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ namespace QuestPDF.Elements
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
var element = queue.Peek();
|
var element = queue.Peek();
|
||||||
var size = element.Measure(new Size(availableSize.Width, Size.Max.Height));
|
var size = element.Measure(Size.Max);
|
||||||
|
|
||||||
if (size.Type == SpacePlanType.Wrap)
|
if (size.Type == SpacePlanType.Wrap)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -15,42 +15,34 @@ namespace QuestPDF.Elements
|
|||||||
Horizontal
|
Horizontal
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Line : Element, ILine, IVisual, ICacheable
|
internal class Line : Element, ILine, ICacheable, IContent
|
||||||
{
|
{
|
||||||
public bool IsRendered { get; set; }
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
|
|
||||||
public LineType Type { get; set; } = LineType.Vertical;
|
public LineType Type { get; set; } = LineType.Vertical;
|
||||||
public string Color { get; set; } = Colors.Black;
|
public string Color { get; set; } = Colors.Black;
|
||||||
public float Thickness { get; set; } = 1;
|
public float Size { get; set; } = 1;
|
||||||
|
|
||||||
internal override SpacePlan Measure(Size availableSpace)
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (availableSpace.IsNegative())
|
if (availableSpace.IsNegative())
|
||||||
return SpacePlan.Wrap();
|
return SpacePlan.Wrap();
|
||||||
|
|
||||||
if (IsRendered && !RepeatContent)
|
|
||||||
return SpacePlan.FullRender(Size.Zero);
|
|
||||||
|
|
||||||
return Type switch
|
return Type switch
|
||||||
{
|
{
|
||||||
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(Thickness, 0),
|
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(Size, 0),
|
||||||
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(0, Thickness),
|
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(0, Size),
|
||||||
_ => SpacePlan.Wrap()
|
_ => SpacePlan.Wrap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Draw(Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
IsRendered = true;
|
|
||||||
|
|
||||||
if (Type == LineType.Vertical)
|
if (Type == LineType.Vertical)
|
||||||
{
|
{
|
||||||
Canvas.DrawRectangle(new Position(-Thickness/2, 0), new Size(Thickness, availableSpace.Height), Color);
|
Canvas.DrawRectangle(new Position(-Size/2, 0), new Size(Size, availableSpace.Height), Color);
|
||||||
}
|
}
|
||||||
else if (Type == LineType.Horizontal)
|
else if (Type == LineType.Horizontal)
|
||||||
{
|
{
|
||||||
Canvas.DrawRectangle(new Position(0, -Thickness/2), new Size(availableSpace.Width, Thickness), Color);
|
Canvas.DrawRectangle(new Position(0, -Size/2), new Size(availableSpace.Width, Size), Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Elements
|
||||||
|
{
|
||||||
|
internal class RepeatContent : ContainerElement
|
||||||
|
{
|
||||||
|
public bool Repeat { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using QuestPDF.Infrastructure;
|
|
||||||
|
|
||||||
namespace QuestPDF.Elements
|
|
||||||
{
|
|
||||||
internal class RepeatContentSetter : ContainerElement
|
|
||||||
{
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,6 @@ namespace QuestPDF.Elements.Table
|
|||||||
// inner table: list of all cells that ends at the corresponding row
|
// inner table: list of all cells that ends at the corresponding row
|
||||||
private TableCell[][] CellsCache { get; set; }
|
private TableCell[][] CellsCache { get; set; }
|
||||||
private int MaxRow { get; set; }
|
private int MaxRow { get; set; }
|
||||||
private int MaxRowSpan { get; set; }
|
|
||||||
|
|
||||||
internal override void Initialize(IPageContext pageContext, ICanvas canvas)
|
internal override void Initialize(IPageContext pageContext, ICanvas canvas)
|
||||||
{
|
{
|
||||||
@@ -57,7 +56,6 @@ namespace QuestPDF.Elements.Table
|
|||||||
if (Cells.Count == 0)
|
if (Cells.Count == 0)
|
||||||
{
|
{
|
||||||
MaxRow = 0;
|
MaxRow = 0;
|
||||||
MaxRowSpan = 1;
|
|
||||||
CellsCache = Array.Empty<TableCell[]>();
|
CellsCache = Array.Empty<TableCell[]>();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -68,7 +66,6 @@ namespace QuestPDF.Elements.Table
|
|||||||
.ToDictionary(x => x.Key, x => x.OrderBy(x => x.Column).ToArray());
|
.ToDictionary(x => x.Key, x => x.OrderBy(x => x.Column).ToArray());
|
||||||
|
|
||||||
MaxRow = groups.Max(x => x.Key);
|
MaxRow = groups.Max(x => x.Key);
|
||||||
MaxRowSpan = Cells.Max(x => x.RowSpan);
|
|
||||||
|
|
||||||
CellsCache = Enumerable
|
CellsCache = Enumerable
|
||||||
.Range(0, MaxRow + 1)
|
.Range(0, MaxRow + 1)
|
||||||
@@ -204,7 +201,7 @@ namespace QuestPDF.Elements.Table
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cell visibility optimizations
|
// cell visibility optimizations
|
||||||
if (cell.Row > maxRenderingRow + MaxRowSpan)
|
if (cell.Row > maxRenderingRow)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// calculate cell position / size
|
// calculate cell position / size
|
||||||
|
|||||||
@@ -121,11 +121,7 @@ namespace QuestPDF.Elements.Text
|
|||||||
{
|
{
|
||||||
foreach (var textBlockItem in textBlockItems)
|
foreach (var textBlockItem in textBlockItems)
|
||||||
{
|
{
|
||||||
if (textBlockItem is TextBlockPageNumber or TextBlockElement)
|
if (textBlockItem is TextBlockSpan textBlockSpan and not TextBlockPageNumber)
|
||||||
{
|
|
||||||
yield return textBlockItem;
|
|
||||||
}
|
|
||||||
else if (textBlockItem is TextBlockSpan textBlockSpan)
|
|
||||||
{
|
{
|
||||||
if (!Settings.CheckIfAllTextGlyphsAreAvailable && textBlockSpan.Style.Fallback == null)
|
if (!Settings.CheckIfAllTextGlyphsAreAvailable && textBlockSpan.Style.Fallback == null)
|
||||||
{
|
{
|
||||||
@@ -137,22 +133,16 @@ namespace QuestPDF.Elements.Text
|
|||||||
|
|
||||||
foreach (var textRun in textRuns)
|
foreach (var textRun in textRuns)
|
||||||
{
|
{
|
||||||
var newElement = textBlockSpan switch
|
yield return new TextBlockSpan
|
||||||
{
|
{
|
||||||
TextBlockHyperlink hyperlink => new TextBlockHyperlink { Url = hyperlink.Url },
|
Text = textRun.Content,
|
||||||
TextBlockSectionLink sectionLink => new TextBlockSectionLink { SectionName = sectionLink.SectionName },
|
Style = textRun.Style
|
||||||
TextBlockSpan => new TextBlockSpan()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
newElement.Text = textRun.Content;
|
|
||||||
newElement.Style = textRun.Style;
|
|
||||||
|
|
||||||
yield return newElement;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
yield return textBlockItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace QuestPDF.Elements.Text.Items
|
|||||||
{
|
{
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
public TextStyle Style { get; set; } = TextStyle.Default;
|
public TextStyle Style { get; set; } = TextStyle.Default;
|
||||||
private TextShapingResult? TextShapingResult { get; set; }
|
public TextShapingResult? TextShapingResult { get; set; }
|
||||||
|
|
||||||
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new ();
|
private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new ();
|
||||||
protected virtual bool EnableTextCache => true;
|
protected virtual bool EnableTextCache => true;
|
||||||
|
|||||||
@@ -4,16 +4,12 @@ using System.Linq;
|
|||||||
using QuestPDF.Drawing;
|
using QuestPDF.Drawing;
|
||||||
using QuestPDF.Elements.Text.Calculation;
|
using QuestPDF.Elements.Text.Calculation;
|
||||||
using QuestPDF.Elements.Text.Items;
|
using QuestPDF.Elements.Text.Items;
|
||||||
using QuestPDF.Helpers;
|
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
namespace QuestPDF.Elements.Text
|
namespace QuestPDF.Elements.Text
|
||||||
{
|
{
|
||||||
internal class TextBlock : Element, IVisual, IStateResettable
|
internal class TextBlock : Element, IStateResettable, IContent
|
||||||
{
|
{
|
||||||
public bool IsRendered { get; set; }
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
|
|
||||||
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
|
||||||
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
|
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
|
||||||
|
|
||||||
@@ -57,12 +53,6 @@ namespace QuestPDF.Elements.Text
|
|||||||
|
|
||||||
internal override SpacePlan Measure(Size availableSpace)
|
internal override SpacePlan Measure(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (availableSpace.IsNegative())
|
|
||||||
return SpacePlan.Wrap();
|
|
||||||
|
|
||||||
if (IsRendered && !RepeatContent)
|
|
||||||
return SpacePlan.FullRender(Size.Zero);
|
|
||||||
|
|
||||||
if (!RenderingQueue.Any())
|
if (!RenderingQueue.Any())
|
||||||
return SpacePlan.FullRender(Size.Zero);
|
return SpacePlan.FullRender(Size.Zero);
|
||||||
|
|
||||||
@@ -90,9 +80,6 @@ namespace QuestPDF.Elements.Text
|
|||||||
|
|
||||||
internal override void Draw(Size availableSpace)
|
internal override void Draw(Size availableSpace)
|
||||||
{
|
{
|
||||||
if (IsRendered && !RepeatContent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList();
|
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList();
|
||||||
|
|
||||||
if (!lines.Any())
|
if (!lines.Any())
|
||||||
@@ -151,10 +138,7 @@ namespace QuestPDF.Elements.Text
|
|||||||
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex;
|
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex;
|
||||||
|
|
||||||
if (!RenderingQueue.Any())
|
if (!RenderingQueue.Any())
|
||||||
{
|
|
||||||
ResetState();
|
ResetState();
|
||||||
IsRendered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GetAlignmentOffset(float lineWidth)
|
float GetAlignmentOffset(float lineWidth)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
var container = new Container();
|
var container = new Container();
|
||||||
Decoration.Before = container;
|
Decoration.Before = container;
|
||||||
return container.RepeatContentWhenPaging();
|
return container.RepeatContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Before(Action<IContainer> handler)
|
public void Before(Action<IContainer> handler)
|
||||||
@@ -36,7 +36,7 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
var container = new Container();
|
var container = new Container();
|
||||||
Decoration.After = container;
|
Decoration.After = container;
|
||||||
return container.RepeatContentWhenPaging();
|
return container.RepeatContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void After(Action<IContainer> handler)
|
public void After(Action<IContainer> handler)
|
||||||
@@ -49,7 +49,9 @@ namespace QuestPDF.Fluent
|
|||||||
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
|
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
|
||||||
public IContainer Header()
|
public IContainer Header()
|
||||||
{
|
{
|
||||||
return Before();
|
var container = new Container();
|
||||||
|
Decoration.Before = container;
|
||||||
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
|
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
|
||||||
@@ -61,7 +63,9 @@ namespace QuestPDF.Fluent
|
|||||||
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
|
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
|
||||||
public IContainer Footer()
|
public IContainer Footer()
|
||||||
{
|
{
|
||||||
return After();
|
var container = new Container();
|
||||||
|
Decoration.After = container;
|
||||||
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
|
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
|
||||||
|
|||||||
@@ -195,5 +195,13 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
return element.Element(new ScaleToFit());
|
return element.Element(new ScaleToFit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IContainer RepeatContent(this IContainer element, bool enabled = true)
|
||||||
|
{
|
||||||
|
return element.Element(new RepeatContent
|
||||||
|
{
|
||||||
|
Repeat = enabled
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace QuestPDF.Fluent
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IContainer Layer() => Layer(false).RepeatContentWhenPaging();
|
public IContainer Layer() => Layer(false);
|
||||||
public IContainer PrimaryLayer() => Layer(true);
|
public IContainer PrimaryLayer() => Layer(true);
|
||||||
|
|
||||||
internal void Validate()
|
internal void Validate()
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ namespace QuestPDF.Fluent
|
|||||||
{
|
{
|
||||||
public static class LineExtensions
|
public static class LineExtensions
|
||||||
{
|
{
|
||||||
private static ILine Line(this IContainer element, LineType type, float thickness)
|
private static ILine Line(this IContainer element, LineType type, float size)
|
||||||
{
|
{
|
||||||
var line = new Line
|
var line = new Line
|
||||||
{
|
{
|
||||||
Thickness = thickness,
|
Size = size,
|
||||||
Type = type
|
Type = type
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -18,14 +18,14 @@ namespace QuestPDF.Fluent
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ILine LineVertical(this IContainer element, float thickness, Unit unit = Unit.Point)
|
public static ILine LineVertical(this IContainer element, float size, Unit unit = Unit.Point)
|
||||||
{
|
{
|
||||||
return element.Line(LineType.Vertical, thickness.ToPoints(unit));
|
return element.Line(LineType.Vertical, size.ToPoints(unit));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ILine LineHorizontal(this IContainer element, float thickness, Unit unit = Unit.Point)
|
public static ILine LineHorizontal(this IContainer element, float size, Unit unit = Unit.Point)
|
||||||
{
|
{
|
||||||
return element.Line(LineType.Horizontal, thickness.ToPoints(unit));
|
return element.Line(LineType.Horizontal, size.ToPoints(unit));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LineColor(this ILine descriptor, string value)
|
public static void LineColor(this ILine descriptor, string value)
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using QuestPDF.Elements;
|
|
||||||
using QuestPDF.Infrastructure;
|
|
||||||
|
|
||||||
namespace QuestPDF.Fluent
|
|
||||||
{
|
|
||||||
public static class RepeatContentExtensions
|
|
||||||
{
|
|
||||||
private static IContainer RepeatContent(this IContainer element, Action<RepeatContentSetter> handler)
|
|
||||||
{
|
|
||||||
var repeatContentSetter = element as RepeatContentSetter ?? new RepeatContentSetter();
|
|
||||||
handler(repeatContentSetter);
|
|
||||||
|
|
||||||
return element.Element(repeatContentSetter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IContainer RepeatContentWhenPaging(this IContainer element)
|
|
||||||
{
|
|
||||||
return element.RepeatContent(x => x.RepeatContent = true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IContainer DoNotRepeatContentWhenPaging(this IContainer element)
|
|
||||||
{
|
|
||||||
return element.RepeatContent(x => x.RepeatContent = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace QuestPDF.Infrastructure
|
||||||
|
{
|
||||||
|
internal interface IContent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace QuestPDF.Infrastructure
|
|
||||||
{
|
|
||||||
internal interface IVisual
|
|
||||||
{
|
|
||||||
public bool IsRendered { get; set; }
|
|
||||||
public bool RepeatContent { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<Authors>MarcinZiabek</Authors>
|
<Authors>MarcinZiabek</Authors>
|
||||||
<Company>CodeFlint</Company>
|
<Company>CodeFlint</Company>
|
||||||
<PackageId>QuestPDF</PackageId>
|
<PackageId>QuestPDF</PackageId>
|
||||||
<Version>2022.9.1</Version>
|
<Version>2022.9.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>
|
<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>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
|
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
|
||||||
<LangVersion>9</LangVersion>
|
<LangVersion>9</LangVersion>
|
||||||
|
|||||||
@@ -4,8 +4,3 @@
|
|||||||
- Significantly reduced memory allocation cost for TextStyle objects,
|
- Significantly reduced memory allocation cost for TextStyle objects,
|
||||||
- Implemented optional checking if all font glyphs are available,
|
- Implemented optional checking if all font glyphs are available,
|
||||||
- Minor text-rendering optimizations.
|
- Minor text-rendering optimizations.
|
||||||
|
|
||||||
2022.9.1
|
|
||||||
- Fixed: text hyperlinks do not work when the CheckIfAllTextGlyphsAreAvailable option or text fallback are used,
|
|
||||||
- Fixed: cells with RowSpan (greater than 1) are not always displayed properly,
|
|
||||||
- Improved predictability of the Inlined element when measuring its children.
|
|
||||||
@@ -17,7 +17,7 @@ It offers a layouting engine designed with a full paging support in mind. The do
|
|||||||
|
|
||||||
Unlike other libraries, it does not rely on the HTML-to-PDF conversion which in many cases is not reliable. Instead, it implements its own layouting engine that is optimized to cover all paging-related requirements.
|
Unlike other libraries, it does not rely on the HTML-to-PDF conversion which in many cases is not reliable. Instead, it implements its own layouting engine that is optimized to cover all paging-related requirements.
|
||||||
|
|
||||||
## Please help by giving a star
|
## Please show the value
|
||||||
|
|
||||||
Choosing a project dependency could be difficult. We need to ensure stability and maintainability of our projects. Surveys show that GitHub stars count play an important factor when assessing library quality.
|
Choosing a project dependency could be difficult. We need to ensure stability and maintainability of our projects. Surveys show that GitHub stars count play an important factor when assessing library quality.
|
||||||
|
|
||||||
@@ -47,7 +47,6 @@ Special thanks to all companies that decided to sponsor QuestPDF development. Th
|
|||||||
| Company | Description |
|
| Company | Description |
|
||||||
|--------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| <img src="Resources/jetbrains-logo.svg" width="100px"> | [JetBrains](https://www.jetbrains.com/) supports this project as part of the OSS Power-Ups program. Thank you!<br/>100$ / month |
|
| <img src="Resources/jetbrains-logo.svg" width="100px"> | [JetBrains](https://www.jetbrains.com/) supports this project as part of the OSS Power-Ups program. Thank you!<br/>100$ / month |
|
||||||
| <img src="https://avatars.githubusercontent.com/u/2712328?v=4" width="100px"> | [Mark Gould](https://github.com/markgould) supports this project. Thank you!<br/>100$ / month |
|
|
||||||
|
|
||||||
[](https://github.com/sponsors/QuestPDF)
|
[](https://github.com/sponsors/QuestPDF)
|
||||||
|
|
||||||
@@ -63,14 +62,14 @@ Install-Package QuestPDF
|
|||||||
dotnet add package QuestPDF
|
dotnet add package QuestPDF
|
||||||
|
|
||||||
// Package reference in .csproj file
|
// Package reference in .csproj file
|
||||||
<PackageReference Include="QuestPDF" Version="2022.9.0" />
|
<PackageReference Include="QuestPDF" Version="2022.6.0" />
|
||||||
```
|
```
|
||||||
|
|
||||||
[](https://www.nuget.org/packages/QuestPDF/)
|
[](https://www.nuget.org/packages/QuestPDF/)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
[](https://www.questpdf.com/getting-started)
|
[](https://www.questpdf.com/getting-started.html)
|
||||||
A short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
|
A short and easy to follow tutorial showing how to design an invoice document under 200 lines of code.
|
||||||
|
|
||||||
|
|
||||||
@@ -78,14 +77,14 @@ A short and easy to follow tutorial showing how to design an invoice document un
|
|||||||
A detailed description of behavior of all available components and how to use them with C# Fluent API.
|
A detailed description of behavior of all available components and how to use them with C# Fluent API.
|
||||||
|
|
||||||
|
|
||||||
[](https://www.questpdf.com/design-patterns)
|
[](https://www.questpdf.com/design-patterns.html)
|
||||||
Everything that may help you designing great reports and create reusable code that is easy to maintain.
|
Everything that may help you designing great reports and create reusable code that is easy to maintain.
|
||||||
|
|
||||||
## QuestPDF Previewer
|
## QuestPDF Previewer
|
||||||
|
|
||||||
The QuestPDF Previewer is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task!
|
The QuestPDF Previewer is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task!
|
||||||
|
|
||||||
[](https://www.questpdf.com/document-previewer)
|
[](https://www.questpdf.com/document-previewer.html)
|
||||||
|
|
||||||
|
|
||||||
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/previewer/animation.gif?raw=true" width="100%">
|
<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/previewer/animation.gif?raw=true" width="100%">
|
||||||
|
|||||||
Reference in New Issue
Block a user