Compare commits

...

5 Commits

Author SHA1 Message Date
MarcinZiabek cbaebdd670 Rendering text paragraphs without the Column element (not fully working) 2022-08-20 19:35:24 +02:00
MarcinZiabek 52ad0f5c24 2202.8.2 2022-08-20 18:22:06 +02:00
Marcin Ziąbek 53107073a6 Merge pull request #322 from emanueleguastella/main
Fix DocumentLayoutException when creating a document with more than one page
2022-08-20 18:14:08 +02:00
Emanuele Guastella c71bb3ea59 Fix DocumentLayoutException when creating a document with more than one page 2022-08-20 14:17:52 +02:00
Marcin Ziąbek 6cced3c143 Update readme.md 2022-08-19 14:45:26 +02:00
9 changed files with 133 additions and 59 deletions
+1 -1
View File
@@ -96,7 +96,7 @@ namespace QuestPDF.Elements
var availableHeight = availableSpace.Height - topOffset;
if (availableHeight <= 0)
if (availableHeight < 0)
break;
var itemSpace = new Size(availableSpace.Width, availableHeight);
@@ -19,5 +19,6 @@ namespace QuestPDF.Elements.Text.Calculation
public int TotalIndex { get; set; }
public bool IsLast => EndIndex == TotalIndex;
public bool IsNewLine { get; set; }
}
}
+25 -5
View File
@@ -39,18 +39,34 @@ namespace QuestPDF.Elements.Text.Items
var paint = Style.ToPaint();
var fontMetrics = Style.ToFontMetrics();
var spaceCodepoint = paint.ToFont().Typeface.GetGlyphs(" ")[0];
// if the element is the first one within the line,
// ignore leading spaces and new lines
var spaceCodepoint = paint.ToFont().Typeface.GetGlyph(' ');
var newLineCodepoint = paint.ToFont().Typeface.GetGlyph('\n');
var returnCodepoint = paint.ToFont().Typeface.GetGlyph('\r');
var startIndex = request.StartIndex;
// if the element is the first one within the line,
// ignore leading spaces
if (!request.IsFirstElementInBlock && request.IsFirstElementInLine)
{
while (startIndex < TextShapingResult.Glyphs.Length && Text[startIndex] == spaceCodepoint)
while (startIndex < TextShapingResult.Glyphs.Length && (Text[startIndex] == spaceCodepoint || Text[startIndex] == newLineCodepoint || Text[startIndex] == returnCodepoint))
startIndex++;
}
// calculate max index (new new line)
var newLineIndex = startIndex;
while (newLineIndex < TextShapingResult.Glyphs.Length)
{
var glyphCodepoint = TextShapingResult.Glyphs[newLineIndex].Codepoint;
if (glyphCodepoint == newLineCodepoint || glyphCodepoint == returnCodepoint)
break;
newLineIndex++;
}
if (TextShapingResult.Glyphs.Length == 0 || startIndex == TextShapingResult.Glyphs.Length)
{
return new TextMeasurementResult
@@ -68,6 +84,8 @@ namespace QuestPDF.Elements.Text.Items
if (endIndex < startIndex)
return null;
endIndex = Math.Min(endIndex, newLineIndex);
// break text only on spaces
var wrappedText = WrapText(startIndex, endIndex, request.IsFirstElementInLine);
@@ -90,7 +108,9 @@ namespace QuestPDF.Elements.Text.Items
StartIndex = startIndex,
EndIndex = wrappedText.Value.endIndex,
NextIndex = wrappedText.Value.nextIndex,
TotalIndex = TextShapingResult.Glyphs.Length - 1
TotalIndex = TextShapingResult.Glyphs.Length - 1,
IsNewLine = endIndex == newLineIndex
};
}
+8 -1
View File
@@ -11,7 +11,8 @@ namespace QuestPDF.Elements.Text
internal class TextBlock : Element, IStateResettable
{
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
public List<ITextBlockItem> Items { get; set; } = new();
public float ParagraphSpacing { get; set; } = 0;
public string Text => string.Join(" ", Items.Where(x => x is TextBlockSpan).Cast<TextBlockSpan>().Select(x => x.Text));
@@ -110,6 +111,12 @@ namespace QuestPDF.Elements.Text
Canvas.Translate(new Position(0, line.LineHeight));
heightOffset += line.LineHeight;
if (line.Elements.Last().Measurement.IsNewLine)
{
heightOffset += ParagraphSpacing;
Canvas.Translate(new Position(0, ParagraphSpacing));
}
}
Canvas.Translate(new Position(0, -heightOffset));
+14 -51
View File
@@ -40,10 +40,9 @@ namespace QuestPDF.Fluent
public class TextDescriptor
{
private ICollection<TextBlock> TextBlocks { get; } = new List<TextBlock>();
internal TextBlock TextBlock = new();
private TextStyle DefaultStyle { get; set; } = TextStyle.Default;
internal HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
private float Spacing { get; set; } = 0f;
public void DefaultTextStyle(TextStyle style)
{
@@ -72,17 +71,9 @@ namespace QuestPDF.Fluent
public void ParagraphSpacing(float value, Unit unit = Unit.Point)
{
Spacing = value.ToPoints(unit);
TextBlock.ParagraphSpacing = value.ToPoints(unit);
}
private void AddItemToLastTextBlock(ITextBlockItem item)
{
if (!TextBlocks.Any())
TextBlocks.Add(new TextBlock());
TextBlocks.Last().Items.Add(item);
}
[Obsolete("This element has been renamed since version 2022.3. Please use the overload that returns a TextSpanDescriptor object which allows to specify text style.")]
public void Span(string? text, TextStyle style)
{
@@ -96,28 +87,13 @@ namespace QuestPDF.Fluent
if (text == null)
return descriptor;
var items = text
.Replace("\r", string.Empty)
.Split(new[] { '\n' }, StringSplitOptions.None)
.Select(x => new TextBlockSpan
{
Text = x,
Style = style
})
.ToList();
AddItemToLastTextBlock(items.First());
items
.Skip(1)
.Select(x => new TextBlock
{
Items = new List<ITextBlockItem> { x }
})
.ToList()
.ForEach(TextBlocks.Add);
TextBlock.Items.Add(new TextBlockSpan
{
Text = text,
Style = style
});
return descriptor;
}
@@ -137,7 +113,7 @@ namespace QuestPDF.Fluent
var style = DefaultStyle.Clone();
var descriptor = new TextPageNumberDescriptor(style);
AddItemToLastTextBlock(new TextBlockPageNumber
TextBlock.Items.Add(new TextBlockPageNumber
{
Source = context => descriptor.FormatFunction(pageNumber(context)),
Style = style
@@ -193,7 +169,7 @@ namespace QuestPDF.Fluent
if (IsNullOrEmpty(text))
return descriptor;
AddItemToLastTextBlock(new TextBlockSectionLink
TextBlock.Items.Add(new TextBlockSectionLink
{
Style = style,
Text = text,
@@ -220,7 +196,7 @@ namespace QuestPDF.Fluent
if (IsNullOrEmpty(text))
return descriptor;
AddItemToLastTextBlock(new TextBlockHyperlink
TextBlock.Items.Add(new TextBlockHyperlink
{
Style = style,
Text = text,
@@ -240,26 +216,13 @@ namespace QuestPDF.Fluent
{
var container = new Container();
AddItemToLastTextBlock(new TextBlockElement
TextBlock.Items.Add(new TextBlockElement
{
Element = container
});
return container.AlignBottom().MinimalBox();
}
internal void Compose(IContainer container)
{
TextBlocks.ToList().ForEach(x => x.Alignment = Alignment);
container.DefaultTextStyle(DefaultStyle).Column(column =>
{
column.Spacing(Spacing);
foreach (var textBlock in TextBlocks)
column.Item().Element(textBlock);
});
}
}
public static class TextExtensions
@@ -272,7 +235,7 @@ namespace QuestPDF.Fluent
descriptor.Alignment = alignment.Horizontal;
content?.Invoke(descriptor);
descriptor.Compose(element);
element.Element(descriptor.TextBlock);
}
[Obsolete("This element has been renamed since version 2022.3. Please use the overload that returns a TextSpanDescriptor object which allows to specify text style.")]
+1 -1
View File
@@ -3,7 +3,7 @@
<Authors>MarcinZiabek</Authors>
<Company>CodeFlint</Company>
<PackageId>QuestPDF</PackageId>
<Version>2022.8.1</Version>
<Version>2202.8.2</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>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt"))</PackageReleaseNotes>
<LangVersion>9</LangVersion>
+78
View File
@@ -0,0 +1,78 @@
[![Dotnet](https://img.shields.io/badge/platform-.NET-blue)](https://www.nuget.org/packages/QuestPDF/)
[![GitHub Repo stars](https://img.shields.io/github/stars/QuestPDF/QuestPDF)](https://github.com/QuestPDF/QuestPDF/stargazers)
[![Nuget version](https://img.shields.io/nuget/v/QuestPdf)](https://www.nuget.org/packages/QuestPDF/)
[![Nuget download](https://img.shields.io/nuget/dt/QuestPDF)](https://www.nuget.org/packages/QuestPDF/)
[![License](https://img.shields.io/github/license/QuestPDF/QuestPDF)](https://github.com/QuestPDF/QuestPDF/blob/main/LICENSE)
[![Sponsor project](https://img.shields.io/badge/sponsor-project-red)](https://github.com/sponsors/QuestPDF)
QuestPDF is an open-source .NET library for PDF documents generation.
It offers a layout engine designed with a full paging support in mind. The document consists of many simple elements (e.g. border, background, image, text, padding, table, grid etc.) that are composed together to create more complex structures. This way, as a developer, you can understand the behavior of every element and use them with full confidence. Additionally, the document and all its elements support paging functionality. For example, an element can be moved to the next page (if there is not enough space) or even be split between pages like table's rows.
## Documentation
[![Getting started tutorial]( https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue)](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.
[![API reference](https://img.shields.io/badge/%F0%9F%93%96%20read-API%20reference-blue)](https://www.questpdf.com/api-reference/index.html)
A detailed description of behavior of all available components and how to use them with C# Fluent API.
[![Patterns and Practices](https://img.shields.io/badge/%F0%9F%94%8D%20read-patterns%20and%20practices-blue)](https://www.questpdf.com/design-patterns.html)
Everything that may help you designing great reports and create reusable code that is easy to maintain.
## Simplicity is the key
How easy it is to start and prototype with QuestPDF? Really easy thanks to its minimal API! Please analyse the code below:
```#
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
// code in your main method
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.Background(Colors.White);
page.DefaultTextStyle(x => x.FontSize(20));
page.Header()
.Text("Hello PDF!")
.SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Column(x =>
{
x.Spacing(20);
x.Item().Text(Placeholders.LoremIpsum());
x.Item().Image(Placeholders.Image(200, 100));
});
page.Footer()
.AlignCenter()
.Text(x =>
{
x.Span("Page ");
x.CurrentPageNumber();
});
});
})
.GeneratePdf("hello.pdf");
```
And compare it to the produced PDF file:
![invoice](https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/docs/public/minimal-example-shadow.png)
## Are you ready for more?
The Fluent API of QuestPDF scales really well. It is easy to create and maintain even most complex documents. Read [the Getting started tutorial](https://www.questpdf.com/documentation/getting-started.html) to learn QuestPDF basics and implement an invoice under 200 lines of code. You can also investigate and play with the code from [the example repository](https://github.com/QuestPDF/example-invoice).
![invoice](https://raw.githubusercontent.com/QuestPDF/QuestPDF-Documentation/main/docs/public/invoice-small.png)
+3
View File
@@ -15,3 +15,6 @@
- Stability improvements for text wrapping
- Updated stability of rendering elements in negative space
- Optimization for the Column element: do not measure child when available height is negative
2202.8.2
- Fixed: the Column element incorrectly renders zero-height elements.
+2
View File
@@ -23,6 +23,8 @@ Choosing a project dependency could be difficult. We need to ensure stability an
⭐ Please give this repository a star. It takes seconds and help thousands of developers! ⭐
<img src="https://user-images.githubusercontent.com/9263853/184642026-27dd7567-a46a-45d4-9594-e6a70a7193e9.png" width="700" />
## Please share with the community
As an open-source project without funding, I cannot afford advertising QuestPDF in a typical way. Instead, the library relies on community interactions. Please consider sharing a post about QuestPDF and the value it provides. It really does help!