From 657bbe2aacce1e2469865b193939ad3c4e768b97 Mon Sep 17 00:00:00 2001 From: MarcinZiabek Date: Fri, 18 Feb 2022 21:48:57 +0100 Subject: [PATCH] 2022.2.4 --- QuestPDF/Drawing/DocumentGenerator.cs | 19 +++++-- QuestPDF/Drawing/SkiaDocumentCanvasBase.cs | 8 +-- QuestPDF/Elements/Line.cs | 4 +- QuestPDF/Elements/ScaleToFit.cs | 6 +- QuestPDF/Elements/Text/Items/TextBlockSpan.cs | 8 +-- QuestPDF/Fluent/GenerateExtensions.cs | 4 +- QuestPDF/Helpers/WriteOnlyStream.cs | 56 ------------------- QuestPDF/QuestPDF.csproj | 2 +- 8 files changed, 32 insertions(+), 75 deletions(-) delete mode 100644 QuestPDF/Helpers/WriteOnlyStream.cs diff --git a/QuestPDF/Drawing/DocumentGenerator.cs b/QuestPDF/Drawing/DocumentGenerator.cs index e253c94..a40c2d8 100644 --- a/QuestPDF/Drawing/DocumentGenerator.cs +++ b/QuestPDF/Drawing/DocumentGenerator.cs @@ -17,19 +17,30 @@ namespace QuestPDF.Drawing { internal static void GeneratePdf(Stream stream, IDocument document) { + CheckIfStreamIsCompatible(stream); + var metadata = document.GetMetadata(); - var writeOnlyStream = new WriteOnlyStream(stream); - var canvas = new PdfCanvas(writeOnlyStream, metadata); + var canvas = new PdfCanvas(stream, metadata); RenderDocument(canvas, document); } internal static void GenerateXps(Stream stream, IDocument document) { + CheckIfStreamIsCompatible(stream); + var metadata = document.GetMetadata(); - var writeOnlyStream = new WriteOnlyStream(stream); - var canvas = new XpsCanvas(writeOnlyStream, metadata); + var canvas = new XpsCanvas(stream, metadata); RenderDocument(canvas, document); } + + private static void CheckIfStreamIsCompatible(Stream stream) + { + if (!stream.CanWrite) + throw new ArgumentException("The library requires a Stream object with the 'write' capability available (the CanWrite flag). Please consider using the MemoryStream class."); + + if (!stream.CanSeek) + throw new ArgumentException("The library requires a Stream object with the 'seek' capability available (the CanSeek flag). Please consider using the MemoryStream class."); + } internal static ICollection GenerateImages(IDocument document) { diff --git a/QuestPDF/Drawing/SkiaDocumentCanvasBase.cs b/QuestPDF/Drawing/SkiaDocumentCanvasBase.cs index 16818c1..549eeba 100644 --- a/QuestPDF/Drawing/SkiaDocumentCanvasBase.cs +++ b/QuestPDF/Drawing/SkiaDocumentCanvasBase.cs @@ -6,16 +6,16 @@ namespace QuestPDF.Drawing { internal class SkiaDocumentCanvasBase : SkiaCanvasBase { - private SKDocument Document { get; } + private SKDocument? Document { get; } - public SkiaDocumentCanvasBase(SKDocument document) + protected SkiaDocumentCanvasBase(SKDocument document) { Document = document; } ~SkiaDocumentCanvasBase() { - Document.Dispose(); + Document?.Dispose(); } public override void BeginDocument() @@ -25,7 +25,7 @@ namespace QuestPDF.Drawing public override void EndDocument() { - Canvas.Dispose(); + Canvas?.Dispose(); Document.Close(); Document.Dispose(); diff --git a/QuestPDF/Elements/Line.cs b/QuestPDF/Elements/Line.cs index 03def38..fedb03b 100644 --- a/QuestPDF/Elements/Line.cs +++ b/QuestPDF/Elements/Line.cs @@ -25,8 +25,8 @@ namespace QuestPDF.Elements { return Type switch { - LineType.Vertical when availableSpace.Width >= Size => SpacePlan.FullRender(Size, 0), - LineType.Horizontal when availableSpace.Height >= Size => SpacePlan.FullRender(0, Size), + LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(Size, 0), + LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(0, Size), _ => SpacePlan.Wrap() }; } diff --git a/QuestPDF/Elements/ScaleToFit.cs b/QuestPDF/Elements/ScaleToFit.cs index 354df6b..6372700 100644 --- a/QuestPDF/Elements/ScaleToFit.cs +++ b/QuestPDF/Elements/ScaleToFit.cs @@ -17,8 +17,10 @@ namespace QuestPDF.Elements if (perfectScale == null) return SpacePlan.Wrap(); - var targetSpace = ScaleSize(availableSpace, perfectScale.Value); - return SpacePlan.FullRender(targetSpace); + var scaledSpace = ScaleSize(availableSpace, 1 / perfectScale.Value); + var childSizeInScale = Child.Measure(scaledSpace); + var childSizeInOriginalScale = ScaleSize(childSizeInScale, perfectScale.Value); + return SpacePlan.FullRender(childSizeInOriginalScale); } internal override void Draw(Size availableSpace) diff --git a/QuestPDF/Elements/Text/Items/TextBlockSpan.cs b/QuestPDF/Elements/Text/Items/TextBlockSpan.cs index a8d0436..525e11f 100644 --- a/QuestPDF/Elements/Text/Items/TextBlockSpan.cs +++ b/QuestPDF/Elements/Text/Items/TextBlockSpan.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using QuestPDF.Drawing; using QuestPDF.Elements.Text.Calculation; using QuestPDF.Infrastructure; @@ -11,8 +12,7 @@ namespace QuestPDF.Elements.Text.Items public string Text { get; set; } public TextStyle Style { get; set; } = new TextStyle(); - private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = - new Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?>(); + private Dictionary<(int startIndex, float availableWidth), TextMeasurementResult?> MeasureCache = new (); public virtual TextMeasurementResult? Measure(TextMeasurementRequest request) { @@ -54,7 +54,7 @@ namespace QuestPDF.Elements.Text.Items // start breaking text from requested position var text = Text.Substring(startIndex); - var textLength = (int)paint.BreakText(text, request.AvailableWidth); + var textLength = (int)paint.BreakText(text, request.AvailableWidth + Size.Epsilon); if (textLength <= 0) return null; diff --git a/QuestPDF/Fluent/GenerateExtensions.cs b/QuestPDF/Fluent/GenerateExtensions.cs index ead590e..0736def 100644 --- a/QuestPDF/Fluent/GenerateExtensions.cs +++ b/QuestPDF/Fluent/GenerateExtensions.cs @@ -29,6 +29,8 @@ namespace QuestPDF.Fluent } #endregion + + #region XPS public static byte[] GenerateXps(this IDocument document) { @@ -48,8 +50,6 @@ namespace QuestPDF.Fluent DocumentGenerator.GenerateXps(stream, document); } - #region XPS - #endregion #region Images diff --git a/QuestPDF/Helpers/WriteOnlyStream.cs b/QuestPDF/Helpers/WriteOnlyStream.cs deleted file mode 100644 index e0c4343..0000000 --- a/QuestPDF/Helpers/WriteOnlyStream.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.IO; - -namespace QuestPDF.Helpers -{ - /// - /// SkiaSharp calls the Position property when generating target document file. - /// If the output stream does not support the Position property, the NullReferenceException is thrown. - /// This wrapper fixes this issue by providing cached Position value (always the end of the stream / current length). - /// Example stream affected: HttpContext.Response.Body - /// - internal class WriteOnlyStream : Stream - { - private readonly Stream InnerStream; - private long StreamLength { get; set; } - - public WriteOnlyStream(Stream stream) - { - if (!stream.CanWrite) - throw new NotSupportedException("Stream cannot be written"); - - InnerStream = stream; - } - - public override bool CanRead => false; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override long Length => StreamLength; - - public override long Position - { - get => StreamLength; - set => throw new NotImplementedException(); - } - - public override void Flush() => InnerStream.Flush(); - - public override int Read(byte[] buffer, int offset, int count) - => throw new NotImplementedException(); - - public override long Seek(long offset, SeekOrigin origin) - => throw new NotImplementedException(); - - public override void SetLength(long value) - => throw new NotImplementedException(); - - public override void Write(byte[] buffer, int offset, int count) - { - InnerStream.Write(buffer, offset, count); - StreamLength += count; - } - } -} diff --git a/QuestPDF/QuestPDF.csproj b/QuestPDF/QuestPDF.csproj index 932b8b3..86fdd03 100644 --- a/QuestPDF/QuestPDF.csproj +++ b/QuestPDF/QuestPDF.csproj @@ -4,7 +4,7 @@ MarcinZiabek CodeFlint QuestPDF - 2022.2.3 + 2022.2.4 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. $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/Resources/ReleaseNotes.txt")) 9