From 2ab559d90116f6a2dd68b986e5ee619583f4a5dc Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 1 May 2024 05:35:01 +0800 Subject: [PATCH 1/8] Improve the PDF creation --- binding/SkiaSharp/SkiaApi.generated.cs | 350 +++++++++++++++++------- binding/libSkiaSharp.json | 16 +- externals/skia | 2 +- tests/Tests/SkiaSharp/SKDocumentTest.cs | 60 +++- 4 files changed, 327 insertions(+), 101 deletions(-) diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index 3dd311d4dc..76b6deca36 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -467,6 +467,34 @@ internal static void gr_direct_context_flush_and_submit (gr_direct_context_t con (gr_direct_context_flush_and_submit_delegate ??= GetSymbol ("gr_direct_context_flush_and_submit")).Invoke (context, syncCpu); #endif + // void gr_direct_context_flush_image(gr_direct_context_t* context, const sk_image_t* image) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void gr_direct_context_flush_image (gr_direct_context_t context, sk_image_t image); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void gr_direct_context_flush_image (gr_direct_context_t context, sk_image_t image); + } + private static Delegates.gr_direct_context_flush_image gr_direct_context_flush_image_delegate; + internal static void gr_direct_context_flush_image (gr_direct_context_t context, sk_image_t image) => + (gr_direct_context_flush_image_delegate ??= GetSymbol ("gr_direct_context_flush_image")).Invoke (context, image); + #endif + + // void gr_direct_context_flush_surface(gr_direct_context_t* context, sk_surface_t* surface) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void gr_direct_context_flush_surface (gr_direct_context_t context, sk_surface_t surface); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void gr_direct_context_flush_surface (gr_direct_context_t context, sk_surface_t surface); + } + private static Delegates.gr_direct_context_flush_surface gr_direct_context_flush_surface_delegate; + internal static void gr_direct_context_flush_surface (gr_direct_context_t context, sk_surface_t surface) => + (gr_direct_context_flush_surface_delegate ??= GetSymbol ("gr_direct_context_flush_surface")).Invoke (context, surface); + #endif + // void gr_direct_context_free_gpu_resources(gr_direct_context_t* context) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -3691,17 +3719,17 @@ internal static sk_document_t sk_document_create_pdf_from_stream (sk_wstream_t s (sk_document_create_pdf_from_stream_delegate ??= GetSymbol ("sk_document_create_pdf_from_stream")).Invoke (stream); #endif - // sk_document_t* sk_document_create_pdf_from_stream_with_metadata(sk_wstream_t* stream, const sk_document_pdf_metadata_t* metadata) + // sk_document_t* sk_document_create_pdf_from_stream_with_metadata(sk_wstream_t* stream, const sk_pdf_metadata_t* metadata) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKDocumentPdfMetadataInternal* metadata); + internal static extern sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKPdfMetadataInternal* metadata); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKDocumentPdfMetadataInternal* metadata); + internal delegate sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKPdfMetadataInternal* metadata); } private static Delegates.sk_document_create_pdf_from_stream_with_metadata sk_document_create_pdf_from_stream_with_metadata_delegate; - internal static sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKDocumentPdfMetadataInternal* metadata) => + internal static sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKPdfMetadataInternal* metadata) => (sk_document_create_pdf_from_stream_with_metadata_delegate ??= GetSymbol ("sk_document_create_pdf_from_stream_with_metadata")).Invoke (stream, metadata); #endif @@ -5011,32 +5039,32 @@ internal static sk_image_t sk_image_make_raster_image (sk_image_t cimage) => (sk_image_make_raster_image_delegate ??= GetSymbol ("sk_image_make_raster_image")).Invoke (cimage); #endif - // sk_shader_t* sk_image_make_shader(const sk_image_t* image, sk_shader_tilemode_t tileX, sk_shader_tilemode_t tileY, const sk_sampling_options_t* sampling, const sk_matrix_t* cmatrix) + // sk_shader_t* sk_image_make_raw_shader(const sk_image_t* image, sk_shader_tilemode_t tileX, sk_shader_tilemode_t tileY, const sk_sampling_options_t* sampling, const sk_matrix_t* cmatrix) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern sk_shader_t sk_image_make_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); + internal static extern sk_shader_t sk_image_make_raw_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate sk_shader_t sk_image_make_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); + internal delegate sk_shader_t sk_image_make_raw_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); } - private static Delegates.sk_image_make_shader sk_image_make_shader_delegate; - internal static sk_shader_t sk_image_make_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix) => - (sk_image_make_shader_delegate ??= GetSymbol ("sk_image_make_shader")).Invoke (image, tileX, tileY, sampling, cmatrix); + private static Delegates.sk_image_make_raw_shader sk_image_make_raw_shader_delegate; + internal static sk_shader_t sk_image_make_raw_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix) => + (sk_image_make_raw_shader_delegate ??= GetSymbol ("sk_image_make_raw_shader")).Invoke (image, tileX, tileY, sampling, cmatrix); #endif - // sk_shader_t* sk_image_make_raw_shader(const sk_image_t* image, sk_shader_tilemode_t tileX, sk_shader_tilemode_t tileY, const sk_sampling_options_t* sampling, const sk_matrix_t* cmatrix) + // sk_shader_t* sk_image_make_shader(const sk_image_t* image, sk_shader_tilemode_t tileX, sk_shader_tilemode_t tileY, const sk_sampling_options_t* sampling, const sk_matrix_t* cmatrix) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern sk_shader_t sk_image_make_raw_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); + internal static extern sk_shader_t sk_image_make_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate sk_shader_t sk_image_make_raw_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); + internal delegate sk_shader_t sk_image_make_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix); } - private static Delegates.sk_image_make_raw_shader sk_image_make_raw_shader_delegate; - internal static sk_shader_t sk_image_make_raw_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix) => - (sk_image_make_raw_shader_delegate ??= GetSymbol ("sk_image_make_raw_shader")).Invoke (image, tileX, tileY, sampling, cmatrix); + private static Delegates.sk_image_make_shader sk_image_make_shader_delegate; + internal static sk_shader_t sk_image_make_shader (sk_image_t image, SKShaderTileMode tileX, SKShaderTileMode tileY, SKSamplingOptions* sampling, SKMatrix* cmatrix) => + (sk_image_make_shader_delegate ??= GetSymbol ("sk_image_make_shader")).Invoke (image, tileX, tileY, sampling, cmatrix); #endif // sk_image_t* sk_image_make_subset(const sk_image_t* cimage, gr_direct_context_t* context, const sk_irect_t* subset) @@ -5345,20 +5373,6 @@ internal static void sk_image_unref (sk_image_t cimage) => #region sk_imagefilter.h - // sk_imagefilter_t* sk_imagefilter_new_alpha_threshold(const sk_region_t* region, float innerThreshold, float outerThreshold, const sk_imagefilter_t* input) - #if !USE_DELEGATES - [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern sk_imagefilter_t sk_imagefilter_new_alpha_threshold (sk_region_t region, Single innerThreshold, Single outerThreshold, sk_imagefilter_t input); - #else - private partial class Delegates { - [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate sk_imagefilter_t sk_imagefilter_new_alpha_threshold (sk_region_t region, Single innerThreshold, Single outerThreshold, sk_imagefilter_t input); - } - private static Delegates.sk_imagefilter_new_alpha_threshold sk_imagefilter_new_alpha_threshold_delegate; - internal static sk_imagefilter_t sk_imagefilter_new_alpha_threshold (sk_region_t region, Single innerThreshold, Single outerThreshold, sk_imagefilter_t input) => - (sk_imagefilter_new_alpha_threshold_delegate ??= GetSymbol ("sk_imagefilter_new_alpha_threshold")).Invoke (region, innerThreshold, outerThreshold, input); - #endif - // sk_imagefilter_t* sk_imagefilter_new_arithmetic(float k1, float k2, float k3, float k4, bool enforcePMColor, const sk_imagefilter_t* background, const sk_imagefilter_t* foreground, const sk_rect_t* cropRect) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -13733,73 +13747,6 @@ public readonly override int GetHashCode () } - // sk_document_pdf_metadata_t - [StructLayout (LayoutKind.Sequential)] - internal unsafe partial struct SKDocumentPdfMetadataInternal : IEquatable { - // public sk_string_t* fTitle - public sk_string_t fTitle; - - // public sk_string_t* fAuthor - public sk_string_t fAuthor; - - // public sk_string_t* fSubject - public sk_string_t fSubject; - - // public sk_string_t* fKeywords - public sk_string_t fKeywords; - - // public sk_string_t* fCreator - public sk_string_t fCreator; - - // public sk_string_t* fProducer - public sk_string_t fProducer; - - // public sk_time_datetime_t* fCreation - public SKTimeDateTimeInternal* fCreation; - - // public sk_time_datetime_t* fModified - public SKTimeDateTimeInternal* fModified; - - // public float fRasterDPI - public Single fRasterDPI; - - // public bool fPDFA - public Byte fPDFA; - - // public int fEncodingQuality - public Int32 fEncodingQuality; - - public readonly bool Equals (SKDocumentPdfMetadataInternal obj) => - fTitle == obj.fTitle && fAuthor == obj.fAuthor && fSubject == obj.fSubject && fKeywords == obj.fKeywords && fCreator == obj.fCreator && fProducer == obj.fProducer && fCreation == obj.fCreation && fModified == obj.fModified && fRasterDPI == obj.fRasterDPI && fPDFA == obj.fPDFA && fEncodingQuality == obj.fEncodingQuality; - - public readonly override bool Equals (object obj) => - obj is SKDocumentPdfMetadataInternal f && Equals (f); - - public static bool operator == (SKDocumentPdfMetadataInternal left, SKDocumentPdfMetadataInternal right) => - left.Equals (right); - - public static bool operator != (SKDocumentPdfMetadataInternal left, SKDocumentPdfMetadataInternal right) => - !left.Equals (right); - - public readonly override int GetHashCode () - { - var hash = new HashCode (); - hash.Add (fTitle); - hash.Add (fAuthor); - hash.Add (fSubject); - hash.Add (fKeywords); - hash.Add (fCreator); - hash.Add (fProducer); - hash.Add (fCreation); - hash.Add (fModified); - hash.Add (fRasterDPI); - hash.Add (fPDFA); - hash.Add (fEncodingQuality); - return hash.ToHashCode (); - } - - } - // sk_fontmetrics_t [StructLayout (LayoutKind.Sequential)] public unsafe partial struct SKFontMetrics : IEquatable { @@ -14642,6 +14589,187 @@ public readonly override int GetHashCode () } + // sk_pdf_attribute_item_t + [StructLayout (LayoutKind.Sequential)] + internal unsafe partial struct SKPdfAttributeItemInternal : IEquatable { + // public sk_string_t* fOwner + public sk_string_t fOwner; + + // public sk_string_t* fName + public sk_string_t fName; + + // public void* fValue + public void* fValue; + + // public int fValueSize + public Int32 fValueSize; + + // public int fValueType + public Int32 fValueType; + + public readonly bool Equals (SKPdfAttributeItemInternal obj) => + fOwner == obj.fOwner && fName == obj.fName && fValue == obj.fValue && fValueSize == obj.fValueSize && fValueType == obj.fValueType; + + public readonly override bool Equals (object obj) => + obj is SKPdfAttributeItemInternal f && Equals (f); + + public static bool operator == (SKPdfAttributeItemInternal left, SKPdfAttributeItemInternal right) => + left.Equals (right); + + public static bool operator != (SKPdfAttributeItemInternal left, SKPdfAttributeItemInternal right) => + !left.Equals (right); + + public readonly override int GetHashCode () + { + var hash = new HashCode (); + hash.Add (fOwner); + hash.Add (fName); + hash.Add (fValue); + hash.Add (fValueSize); + hash.Add (fValueType); + return hash.ToHashCode (); + } + + } + + // sk_pdf_metadata_t + [StructLayout (LayoutKind.Sequential)] + internal unsafe partial struct SKPdfMetadataInternal : IEquatable { + // public sk_string_t* fTitle + public sk_string_t fTitle; + + // public sk_string_t* fAuthor + public sk_string_t fAuthor; + + // public sk_string_t* fSubject + public sk_string_t fSubject; + + // public sk_string_t* fKeywords + public sk_string_t fKeywords; + + // public sk_string_t* fCreator + public sk_string_t fCreator; + + // public sk_string_t* fProducer + public sk_string_t fProducer; + + // public sk_time_datetime_t* fCreation + public SKTimeDateTimeInternal* fCreation; + + // public sk_time_datetime_t* fModified + public SKTimeDateTimeInternal* fModified; + + // public float fRasterDPI + public Single fRasterDPI; + + // public bool fPDFA + public Byte fPDFA; + + // public int fEncodingQuality + public Int32 fEncodingQuality; + + // public sk_pdf_compression_t fCompression + public SKPdfCompression fCompression; + + // public sk_pdf_structure_element_t* fStructureElementTreeRoot + public SKPdfStructureElementInternal* fStructureElementTreeRoot; + + public readonly bool Equals (SKPdfMetadataInternal obj) => + fTitle == obj.fTitle && fAuthor == obj.fAuthor && fSubject == obj.fSubject && fKeywords == obj.fKeywords && fCreator == obj.fCreator && fProducer == obj.fProducer && fCreation == obj.fCreation && fModified == obj.fModified && fRasterDPI == obj.fRasterDPI && fPDFA == obj.fPDFA && fEncodingQuality == obj.fEncodingQuality && fCompression == obj.fCompression && fStructureElementTreeRoot == obj.fStructureElementTreeRoot; + + public readonly override bool Equals (object obj) => + obj is SKPdfMetadataInternal f && Equals (f); + + public static bool operator == (SKPdfMetadataInternal left, SKPdfMetadataInternal right) => + left.Equals (right); + + public static bool operator != (SKPdfMetadataInternal left, SKPdfMetadataInternal right) => + !left.Equals (right); + + public readonly override int GetHashCode () + { + var hash = new HashCode (); + hash.Add (fTitle); + hash.Add (fAuthor); + hash.Add (fSubject); + hash.Add (fKeywords); + hash.Add (fCreator); + hash.Add (fProducer); + hash.Add (fCreation); + hash.Add (fModified); + hash.Add (fRasterDPI); + hash.Add (fPDFA); + hash.Add (fEncodingQuality); + hash.Add (fCompression); + hash.Add (fStructureElementTreeRoot); + return hash.ToHashCode (); + } + + } + + // sk_pdf_structure_element_t + [StructLayout (LayoutKind.Sequential)] + internal unsafe partial struct SKPdfStructureElementInternal : IEquatable { + // public sk_string_t* fTypeString + public sk_string_t fTypeString; + + // public sk_pdf_structure_element_t* fChildren + public SKPdfStructureElementInternal* fChildren; + + // public int fChildrenSize + public Int32 fChildrenSize; + + // public int fNodeId + public Int32 fNodeId; + + // public int* fAdditionalNodeIds + public Int32* fAdditionalNodeIds; + + // public int fAdditionalNodeIdsSize + public Int32 fAdditionalNodeIdsSize; + + // public sk_pdf_attribute_item_t* fAttributes + public SKPdfAttributeItemInternal* fAttributes; + + // public int fAttributesSize + public Int32 fAttributesSize; + + // public sk_string_t* fAlt + public sk_string_t fAlt; + + // public sk_string_t* fLang + public sk_string_t fLang; + + public readonly bool Equals (SKPdfStructureElementInternal obj) => + fTypeString == obj.fTypeString && fChildren == obj.fChildren && fChildrenSize == obj.fChildrenSize && fNodeId == obj.fNodeId && fAdditionalNodeIds == obj.fAdditionalNodeIds && fAdditionalNodeIdsSize == obj.fAdditionalNodeIdsSize && fAttributes == obj.fAttributes && fAttributesSize == obj.fAttributesSize && fAlt == obj.fAlt && fLang == obj.fLang; + + public readonly override bool Equals (object obj) => + obj is SKPdfStructureElementInternal f && Equals (f); + + public static bool operator == (SKPdfStructureElementInternal left, SKPdfStructureElementInternal right) => + left.Equals (right); + + public static bool operator != (SKPdfStructureElementInternal left, SKPdfStructureElementInternal right) => + !left.Equals (right); + + public readonly override int GetHashCode () + { + var hash = new HashCode (); + hash.Add (fTypeString); + hash.Add (fChildren); + hash.Add (fChildrenSize); + hash.Add (fNodeId); + hash.Add (fAdditionalNodeIds); + hash.Add (fAdditionalNodeIdsSize); + hash.Add (fAttributes); + hash.Add (fAttributesSize); + hash.Add (fAlt); + hash.Add (fLang); + return hash.ToHashCode (); + } + + } + // sk_pngencoder_options_t [StructLayout (LayoutKind.Sequential)] public readonly unsafe partial struct SKPngEncoderOptions : IEquatable { @@ -15711,6 +15839,34 @@ public enum SKPathOp { ReverseDifference = 4, } + // sk_pdf_attribute_item_type_t + internal enum SKPdfAttributeItemTypeInternal { + // INT_SK_PDF_ATTRIBUTE_ITEM_TYPE = 0 + Int = 0, + // FLOAT_SK_PDF_ATTRIBUTE_ITEM_TYPE = 1 + Float = 1, + // NAME_SK_PDF_ATTRIBUTE_ITEM_TYPE = 2 + Name = 2, + // FLOAT_ARRAY_SK_PDF_ATTRIBUTE_ITEM_TYPE = 3 + FloatArray = 3, + // NODE_ID_ARRAY_SK_PDF_ATTRIBUTE_ITEM_TYPE = 4 + NodeIdArray = 4, + } + + // sk_pdf_compression_t + public enum SKPdfCompression { + // DEFAULT_SK_PDF_COMPRESSION = -1 + Default = -1, + // NONE_SK_PDF_COMPRESSION = 0 + None = 0, + // LOW_SK_PDF_COMPRESSION = 1 + Low = 1, + // AVERAGE_SK_PDF_COMPRESSION = 6 + Average = 6, + // HIGH_SK_PDF_COMPRESSION = 9 + High = 9, + } + // sk_pixelgeometry_t public enum SKPixelGeometry { // UNKNOWN_SK_PIXELGEOMETRY = 0 diff --git a/binding/libSkiaSharp.json b/binding/libSkiaSharp.json index a67a69969c..9222044183 100644 --- a/binding/libSkiaSharp.json +++ b/binding/libSkiaSharp.json @@ -373,8 +373,20 @@ "cs": "SKCodecOptionsInternal", "internal": true }, - "sk_document_pdf_metadata_t": { - "cs": "SKDocumentPdfMetadataInternal", + "sk_pdf_metadata_t": { + "cs": "SKPdfMetadataInternal", + "internal": true + }, + "sk_pdf_attribute_item_type_t": { + "cs": "SKPdfAttributeItemTypeInternal", + "internal": true + }, + "sk_pdf_attribute_item_t": { + "cs": "SKPdfAttributeItemInternal", + "internal": true + }, + "sk_pdf_structure_element_t": { + "cs": "SKPdfStructureElementInternal", "internal": true }, "sk_lattice_t": { diff --git a/externals/skia b/externals/skia index f1c2f7b424..5fbeafefbe 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit f1c2f7b4246141c6037820dc75d89496ac4aa8b3 +Subproject commit 5fbeafefbeb00d8cbcdd677aa1a1a629359d1065 diff --git a/tests/Tests/SkiaSharp/SKDocumentTest.cs b/tests/Tests/SkiaSharp/SKDocumentTest.cs index 717fb03ac6..31cd3256c7 100644 --- a/tests/Tests/SkiaSharp/SKDocumentTest.cs +++ b/tests/Tests/SkiaSharp/SKDocumentTest.cs @@ -167,7 +167,6 @@ public void CanCreateXps() } } - [SkippableFact] public void StreamIsNotCollectedPrematurely() { @@ -204,5 +203,64 @@ static SKDocument CreateDocument(out IntPtr streamHandle) return SKDocument.CreatePdf(stream, new SKDocumentPdfMetadata()); } } + + [SkippableFact] + public void CanCreateTaggedPdf() + { + var metadata = new SKPdfMetadata(); + metadata.Title = "Example Tagged PDF With Links"; + metadata.Creator = "Skia"; + metadata.Creation = DateTime.Now; + metadata.Modified = DateTime.Now; + + // The document tag. + var root = new SKPdfStructureElement(); + root.NodeId = 1; + root.Type = "Document"; + root.Language = "en-US"; + + // A link. + var l1 = new SKPdfStructureElement(); + l1.NodeId = 2; + l1.Type = "Link"; + root.Children.Add(l1); + + metadata.Structure = root; + + using var stream = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(stream, metadata)) + { + Assert.NotNull(doc); + + var canvas = doc.BeginPage(612, 792); // U.S. Letter + Assert.NotNull(canvas); + + var paint = new SKPaint(); + paint.Color = SKColors.Blue; + + var font = new SKFont(); + font.Size = 20; + + // The node ID should cover both the text and the annotation. + SkPDF::SetNodeId(canvas, 2); + canvas->drawString("Click to visit Google.com", 72, 72, font, paint); + SkRect linkRect = SkRect::MakeXYWH( + SkIntToScalar(72), SkIntToScalar(54), + SkIntToScalar(218), SkIntToScalar(24)); + sk_sp url(SkData::MakeWithCString("http://www.google.com")); + SkAnnotateRectWithURL(canvas, linkRect, url.get()); + + document->endPage(); + document->close(); + outputStream.flush(); + + + doc.EndPage(); + doc.Close(); + } + + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); + } } } From 0f5008187c8c3eb9ea665a0b810cee40cf9b55b1 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 2 May 2024 00:21:08 +0800 Subject: [PATCH 2/8] more --- binding/SkiaSharp/SKDocument.cs | 18 +++---- binding/SkiaSharp/SKPdfMetadata.cs | 64 ++++++++++++++++++++++++ tests/Tests/SkiaSharp/SKDocumentTest.cs | 65 ++++++++++++++++++++----- 3 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 binding/SkiaSharp/SKPdfMetadata.cs diff --git a/binding/SkiaSharp/SKDocument.cs b/binding/SkiaSharp/SKDocument.cs index 2330964be8..1d6d52f1d8 100644 --- a/binding/SkiaSharp/SKDocument.cs +++ b/binding/SkiaSharp/SKDocument.cs @@ -1,12 +1,10 @@ -#nullable disable - -using System; +using System; using System.ComponentModel; using System.IO; namespace SkiaSharp { - public unsafe class SKDocument : SKObject, ISKReferenceCounted, ISKSkipObjectRegistration + public unsafe class SKDocument : SKObject, ISKReferenceCounted, ISKSkipObjectRegistration { public const float DefaultRasterDpi = 72.0f; @@ -35,16 +33,16 @@ public void Close () => // CreateXps - public static SKDocument CreateXps (string path) => + public static SKDocument? CreateXps (string path) => CreateXps (path, DefaultRasterDpi); - public static SKDocument CreateXps (Stream stream) => + public static SKDocument? CreateXps (Stream stream) => CreateXps (stream, DefaultRasterDpi); - public static SKDocument CreateXps (SKWStream stream) => + public static SKDocument? CreateXps (SKWStream stream) => CreateXps (stream, DefaultRasterDpi); - public static SKDocument CreateXps (string path, float dpi) + public static SKDocument? CreateXps (string path, float dpi) { if (path == null) { throw new ArgumentNullException (nameof (path)); @@ -54,7 +52,7 @@ public static SKDocument CreateXps (string path, float dpi) return Owned (CreateXps (stream, dpi), stream); } - public static SKDocument CreateXps (Stream stream, float dpi) + public static SKDocument? CreateXps (Stream stream, float dpi) { if (stream == null) { throw new ArgumentNullException (nameof (stream)); @@ -64,7 +62,7 @@ public static SKDocument CreateXps (Stream stream, float dpi) return Owned (CreateXps (managed, dpi), managed); } - public static SKDocument CreateXps (SKWStream stream, float dpi) + public static SKDocument? CreateXps (SKWStream stream, float dpi) { if (stream == null) { throw new ArgumentNullException (nameof (stream)); diff --git a/binding/SkiaSharp/SKPdfMetadata.cs b/binding/SkiaSharp/SKPdfMetadata.cs new file mode 100644 index 0000000000..7f0cb04deb --- /dev/null +++ b/binding/SkiaSharp/SKPdfMetadata.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; + +namespace SkiaSharp; + +public class SKPdfMetadata +{ + public const float DefaultRasterDpi = SKDocument.DefaultRasterDpi; + + public const int DefaultEncodingQuality = 101; + + public SKPdfMetadata () + { + } + + public string? Title { get; set; } + + public string? Author { get; set; } + + public string? Subject { get; set; } + + public string? Keywords { get; set; } + + public string? Creator { get; set; } + + public string? Producer { get; set; } + + public DateTime? Creation { get; set; } + + public DateTime? Modified { get; set; } + + public float RasterDpi { get; set; } = DefaultRasterDpi; + + public bool PdfA { get; set; } + + public int EncodingQuality { get; set; } = DefaultEncodingQuality; + + public SKPdfCompression Compression { get; set; } = SKPdfCompression.Default; + + public SKPdfStructure? Structure { get; set; } +} + +public class SKPdfStructure +{ + public SKPdfStructure(int id, string type) + { + Id = id; + Type = type; + } + + public int Id { get; } + + public string Type { get; } + + public string? Alt { get; } + + public string? Language { get; } + + public IList Children { get; set; } + + public IList AdditionalNodeIds { get; set; } + + public IList Attributes { get; set; } +} diff --git a/tests/Tests/SkiaSharp/SKDocumentTest.cs b/tests/Tests/SkiaSharp/SKDocumentTest.cs index 31cd3256c7..40dba1d607 100644 --- a/tests/Tests/SkiaSharp/SKDocumentTest.cs +++ b/tests/Tests/SkiaSharp/SKDocumentTest.cs @@ -242,21 +242,62 @@ public void CanCreateTaggedPdf() font.Size = 20; // The node ID should cover both the text and the annotation. - SkPDF::SetNodeId(canvas, 2); - canvas->drawString("Click to visit Google.com", 72, 72, font, paint); - SkRect linkRect = SkRect::MakeXYWH( - SkIntToScalar(72), SkIntToScalar(54), - SkIntToScalar(218), SkIntToScalar(24)); - sk_sp url(SkData::MakeWithCString("http://www.google.com")); - SkAnnotateRectWithURL(canvas, linkRect, url.get()); + canvas.SetPdfNodeId(2); + canvasDrawText("Click to visit Google.com", 72, 72, font, paint); + var linkRect = SKRect.Create(72, 54, 218, 24); + canvas.DrawUrlAnnotation(linkRect, "http://www.google.com"); - document->endPage(); - document->close(); - outputStream.flush(); + document.EndPage(); + document.Close(); + } + + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); + } + + [SkippableFact] + public void CanCreateTaggedPdfWithConstructorStructures() + { + var metadata = new SKPdfMetadata + { + Title = "Example Tagged PDF With Links", + Creator = "Skia", + Creation = DateTime.Now, + Modified = DateTime.Now, + // The document tag. + Structure = new SKPdfStructureElement(1, "Document") + { + Language = "en-US", + Children = + { + // A link. + new SKPdfStructureElement(2, "Link") + } + } + }; + + using var stream = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(stream, metadata)) + { + Assert.NotNull(doc); + + var canvas = doc.BeginPage(612, 792); // U.S. Letter + Assert.NotNull(canvas); + var paint = new SKPaint(); + paint.Color = SKColors.Blue; + + var font = new SKFont(); + font.Size = 20; + + // The node ID should cover both the text and the annotation. + canvas.SetPdfNodeId(2); + canvasDrawText("Click to visit Google.com", 72, 72, font, paint); + var linkRect = SKRect.Create(72, 54, 218, 24); + canvas.DrawUrlAnnotation(linkRect, "http://www.google.com"); - doc.EndPage(); - doc.Close(); + document.EndPage(); + document.Close(); } Assert.True(stream.Length > 0); From c37c1d6d243b2709186f3db47a39422d00ab0821 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 20 May 2024 20:58:47 +0800 Subject: [PATCH 3/8] So far --- binding/SkiaSharp/SKDocument.cs | 75 ++- binding/SkiaSharp/SKPdfMetadata.cs | 84 +++- binding/SkiaSharp/SKString.cs | 18 +- binding/SkiaSharp/SkiaApi.generated.cs | 609 +++++++++++++++++-------- 4 files changed, 538 insertions(+), 248 deletions(-) diff --git a/binding/SkiaSharp/SKDocument.cs b/binding/SkiaSharp/SKDocument.cs index 1d6d52f1d8..ce54b2d059 100644 --- a/binding/SkiaSharp/SKDocument.cs +++ b/binding/SkiaSharp/SKDocument.cs @@ -1,6 +1,9 @@ using System; using System.ComponentModel; using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace SkiaSharp { @@ -111,7 +114,16 @@ public static SKDocument CreatePdf (Stream stream, float dpi) => public static SKDocument CreatePdf (SKWStream stream, float dpi) => CreatePdf (stream, new SKDocumentPdfMetadata (dpi)); - public static SKDocument CreatePdf (string path, SKDocumentPdfMetadata metadata) + public static SKDocument CreatePdf (string path, SKDocumentPdfMetadata metadata) => + CreatePdf (path, new SKPdfMetadata (metadata)); + + public static SKDocument CreatePdf (Stream stream, SKDocumentPdfMetadata metadata) => + CreatePdf (stream, new SKPdfMetadata (metadata)); + + public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata) => + CreatePdf (stream, new SKPdfMetadata (metadata)); + + public static SKDocument CreatePdf (string path, SKPdfMetadata metadata) { if (path == null) { throw new ArgumentNullException (nameof (path)); @@ -121,7 +133,7 @@ public static SKDocument CreatePdf (string path, SKDocumentPdfMetadata metadata) return Owned (CreatePdf (stream, metadata), stream); } - public static SKDocument CreatePdf (Stream stream, SKDocumentPdfMetadata metadata) + public static SKDocument CreatePdf (Stream stream, SKPdfMetadata metadata) { if (stream == null) { throw new ArgumentNullException (nameof (stream)); @@ -131,7 +143,7 @@ public static SKDocument CreatePdf (Stream stream, SKDocumentPdfMetadata metadat return Owned (CreatePdf (managed, metadata), managed); } - public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata) + public static SKDocument CreatePdf (SKWStream stream, SKPdfMetadata metadata) { if (stream == null) { throw new ArgumentNullException (nameof (stream)); @@ -144,33 +156,52 @@ public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata meta using var creator = SKString.Create (metadata.Creator); using var producer = SKString.Create (metadata.Producer); - var cmetadata = new SKDocumentPdfMetadataInternal { - fTitle = title?.Handle ?? IntPtr.Zero, - fAuthor = author?.Handle ?? IntPtr.Zero, - fSubject = subject?.Handle ?? IntPtr.Zero, - fKeywords = keywords?.Handle ?? IntPtr.Zero, - fCreator = creator?.Handle ?? IntPtr.Zero, - fProducer = producer?.Handle ?? IntPtr.Zero, - fRasterDPI = metadata.RasterDpi, - fPDFA = metadata.PdfA ? (byte)1 : (byte)0, - fEncodingQuality = metadata.EncodingQuality, - }; - SKTimeDateTimeInternal creation; - if (metadata.Creation != null) { + if (metadata.Creation is not null) creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value); - cmetadata.fCreation = &creation; - } + SKTimeDateTimeInternal modified; - if (metadata.Modified != null) { + if (metadata.Modified is not null) modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value); - cmetadata.fModified = &modified; - } - return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, &cmetadata)), stream); + var metadataHandle = SkiaApi.sk_pdf_metadata_new (); + + try { + SkiaApi.sk_pdf_metadata_set_title(metadataHandle, title?.Handle ?? IntPtr.Zero); + SkiaApi.sk_pdf_metadata_set_author(metadataHandle, author?.Handle ?? IntPtr.Zero); + SkiaApi.sk_pdf_metadata_set_subject(metadataHandle, subject?.Handle ?? IntPtr.Zero); + SkiaApi.sk_pdf_metadata_set_keywords(metadataHandle, keywords?.Handle ?? IntPtr.Zero); + SkiaApi.sk_pdf_metadata_set_creator(metadataHandle, creator?.Handle ?? IntPtr.Zero); + SkiaApi.sk_pdf_metadata_set_producer(metadataHandle, producer?.Handle ?? IntPtr.Zero); + + SkiaApi.sk_pdf_metadata_set_creation(metadataHandle, &creation); + SkiaApi.sk_pdf_metadata_set_modified(metadataHandle, &modified); + + SkiaApi.sk_pdf_metadata_set_raster_dpi(metadataHandle, metadata.RasterDpi); + SkiaApi.sk_pdf_metadata_set_pdfa(metadataHandle, metadata.PdfA); + SkiaApi.sk_pdf_metadata_set_encoding_quality(metadataHandle, metadata.EncodingQuality); + SkiaApi.sk_pdf_metadata_set_compression_level(metadataHandle, metadata.Compression); + + return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, &cmetadata)), stream); + } finally { + if (metadataHandle != IntPtr.Zero) + SkiaApi.sk_pdf_metadata_delete(metadataHandle); + } } internal static SKDocument GetObject (IntPtr handle) => handle == IntPtr.Zero ? null : new SKDocument (handle, true); + + static SKPdfStructureElementNative ToNative(SKPdfStructureElement structure) + { + var t = new Memory (); + var x = t.Pin (); + int* tt = (int*)x.Pointer; + + new SKPdfStructureElementInternal { + fAdditionalNodeIds = tt, + fAdditionalNodeIdsSize = structure.AdditionalNodeIds.Count, + }; + } } } diff --git a/binding/SkiaSharp/SKPdfMetadata.cs b/binding/SkiaSharp/SKPdfMetadata.cs index 7f0cb04deb..8b9e92b1ab 100644 --- a/binding/SkiaSharp/SKPdfMetadata.cs +++ b/binding/SkiaSharp/SKPdfMetadata.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Linq; namespace SkiaSharp; @@ -13,52 +14,93 @@ public SKPdfMetadata () { } + internal SKPdfMetadata (SKDocumentPdfMetadata metadata) + { + Title = metadata.Title; + Author = metadata.Author; + Subject = metadata.Subject; + Keywords = metadata.Keywords; + Creator = metadata.Creator; + Producer = metadata.Producer; + Creation = metadata.Creation; + Modified = metadata.Modified; + RasterDpi = metadata.RasterDpi; + PdfA = metadata.PdfA; + EncodingQuality = metadata.EncodingQuality; + } + public string? Title { get; set; } public string? Author { get; set; } - + public string? Subject { get; set; } - + public string? Keywords { get; set; } - + public string? Creator { get; set; } - + public string? Producer { get; set; } - + public DateTime? Creation { get; set; } - + public DateTime? Modified { get; set; } - + public float RasterDpi { get; set; } = DefaultRasterDpi; - + public bool PdfA { get; set; } - + public int EncodingQuality { get; set; } = DefaultEncodingQuality; - + public SKPdfCompression Compression { get; set; } = SKPdfCompression.Default; - public SKPdfStructure? Structure { get; set; } + public SKPdfStructureElement? Structure { get; set; } } -public class SKPdfStructure +public class SKPdfStructureElement { - public SKPdfStructure(int id, string type) + private List? children; + private List? additionalNodeIds; + private SKPdfAttributeList? attributes; + + public SKPdfStructureElement (int id, string type) { Id = id; Type = type; } - + public int Id { get; } public string Type { get; } - - public string? Alt { get; } - public string? Language { get; } + public string? Alt { get; set; } + + public string? Language { get; set; } + + public IList Children => children ??= new (); + + public IList AdditionalNodeIds => additionalNodeIds ??= new (); + + public SKPdfAttributeList Attributes => attributes ??= new (); +} + +public class SKPdfAttributeList +{ + private List<(string Owner, string Name, object Value)> attributes = new (); + + internal IReadOnlyList<(string Owner, string Name, object Value)> Inner => attributes; + + public void Add (string owner, string name, int value) => + attributes.Add ((owner, name, value)); + + public void Add (string owner, string name, float value) => + attributes.Add ((owner, name, value)); - public IList Children { get; set; } + public void Add (string owner, string name, string value) => + attributes.Add ((owner, name, value)); - public IList AdditionalNodeIds { get; set; } + public void Add (string owner, string name, IEnumerable value) => + attributes.Add ((owner, name, value)); - public IList Attributes { get; set; } + public void Add (string owner, string name, IEnumerable value) => + attributes.Add ((owner, name, value)); } diff --git a/binding/SkiaSharp/SKString.cs b/binding/SkiaSharp/SKString.cs index b48a84f23f..c735fb0bb2 100644 --- a/binding/SkiaSharp/SKString.cs +++ b/binding/SkiaSharp/SKString.cs @@ -1,6 +1,4 @@ -#nullable disable - -using System; +using System; using System.Runtime.InteropServices; namespace SkiaSharp @@ -20,7 +18,7 @@ public SKString () } } - public SKString (byte [] src, long length) + public SKString (ReadOnlySpan src, long length) : base (CreateCopy (src, length), true) { if (Handle == IntPtr.Zero) { @@ -28,14 +26,19 @@ public SKString (byte [] src, long length) } } - private static IntPtr CreateCopy (byte [] src, long length) + private static IntPtr CreateCopy (ReadOnlySpan src, long length) { + if (src is null) + throw new ArgumentNullException (nameof (src)); + if (length > src.Length) + throw new ArgumentOutOfRangeException (nameof (length)); + fixed (byte* s = src) { return SkiaApi.sk_string_new_with_copy (s, (IntPtr)length); } } - public SKString (byte [] src) + public SKString (ReadOnlySpan src) : this (src, src.Length) { } @@ -57,7 +60,8 @@ public static explicit operator string (SKString skString) return skString.ToString (); } - internal static SKString Create (string str) + [return: NotNullIfNotNull(nameof(str))] + internal static SKString? Create (string? str) { if (str == null) { return null; diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index 76b6deca36..093c2d4dad 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -48,6 +48,8 @@ using sk_path_rawiterator_t = System.IntPtr; using sk_path_t = System.IntPtr; using sk_pathmeasure_t = System.IntPtr; +using sk_pdf_metadata_t = System.IntPtr; +using sk_pdf_structure_element_t = System.IntPtr; using sk_picture_recorder_t = System.IntPtr; using sk_picture_t = System.IntPtr; using sk_pixelref_factory_t = System.IntPtr; @@ -3722,14 +3724,14 @@ internal static sk_document_t sk_document_create_pdf_from_stream (sk_wstream_t s // sk_document_t* sk_document_create_pdf_from_stream_with_metadata(sk_wstream_t* stream, const sk_pdf_metadata_t* metadata) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKPdfMetadataInternal* metadata); + internal static extern sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, sk_pdf_metadata_t metadata); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKPdfMetadataInternal* metadata); + internal delegate sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, sk_pdf_metadata_t metadata); } private static Delegates.sk_document_create_pdf_from_stream_with_metadata sk_document_create_pdf_from_stream_with_metadata_delegate; - internal static sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKPdfMetadataInternal* metadata) => + internal static sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, sk_pdf_metadata_t metadata) => (sk_document_create_pdf_from_stream_with_metadata_delegate ??= GetSymbol ("sk_document_create_pdf_from_stream_with_metadata")).Invoke (stream, metadata); #endif @@ -3775,6 +3777,412 @@ internal static void sk_document_unref (sk_document_t document) => (sk_document_unref_delegate ??= GetSymbol ("sk_document_unref")).Invoke (document); #endif + // void sk_pdf_metadata_delete(sk_pdf_metadata_t* metadata) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_delete (sk_pdf_metadata_t metadata); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_delete (sk_pdf_metadata_t metadata); + } + private static Delegates.sk_pdf_metadata_delete sk_pdf_metadata_delete_delegate; + internal static void sk_pdf_metadata_delete (sk_pdf_metadata_t metadata) => + (sk_pdf_metadata_delete_delegate ??= GetSymbol ("sk_pdf_metadata_delete")).Invoke (metadata); + #endif + + // sk_pdf_metadata_t* sk_pdf_metadata_new() + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern sk_pdf_metadata_t sk_pdf_metadata_new (); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate sk_pdf_metadata_t sk_pdf_metadata_new (); + } + private static Delegates.sk_pdf_metadata_new sk_pdf_metadata_new_delegate; + internal static sk_pdf_metadata_t sk_pdf_metadata_new () => + (sk_pdf_metadata_new_delegate ??= GetSymbol ("sk_pdf_metadata_new")).Invoke (); + #endif + + // void sk_pdf_metadata_set_author(sk_pdf_metadata_t* metadata, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_author (sk_pdf_metadata_t metadata, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_author (sk_pdf_metadata_t metadata, sk_string_t value); + } + private static Delegates.sk_pdf_metadata_set_author sk_pdf_metadata_set_author_delegate; + internal static void sk_pdf_metadata_set_author (sk_pdf_metadata_t metadata, sk_string_t value) => + (sk_pdf_metadata_set_author_delegate ??= GetSymbol ("sk_pdf_metadata_set_author")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_compression_level(sk_pdf_metadata_t* metadata, sk_pdf_compression_t value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_compression_level (sk_pdf_metadata_t metadata, SKPdfCompression value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_compression_level (sk_pdf_metadata_t metadata, SKPdfCompression value); + } + private static Delegates.sk_pdf_metadata_set_compression_level sk_pdf_metadata_set_compression_level_delegate; + internal static void sk_pdf_metadata_set_compression_level (sk_pdf_metadata_t metadata, SKPdfCompression value) => + (sk_pdf_metadata_set_compression_level_delegate ??= GetSymbol ("sk_pdf_metadata_set_compression_level")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_creation(sk_pdf_metadata_t* metadata, sk_time_datetime_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_creation (sk_pdf_metadata_t metadata, SKTimeDateTimeInternal* value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_creation (sk_pdf_metadata_t metadata, SKTimeDateTimeInternal* value); + } + private static Delegates.sk_pdf_metadata_set_creation sk_pdf_metadata_set_creation_delegate; + internal static void sk_pdf_metadata_set_creation (sk_pdf_metadata_t metadata, SKTimeDateTimeInternal* value) => + (sk_pdf_metadata_set_creation_delegate ??= GetSymbol ("sk_pdf_metadata_set_creation")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_creator(sk_pdf_metadata_t* metadata, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_creator (sk_pdf_metadata_t metadata, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_creator (sk_pdf_metadata_t metadata, sk_string_t value); + } + private static Delegates.sk_pdf_metadata_set_creator sk_pdf_metadata_set_creator_delegate; + internal static void sk_pdf_metadata_set_creator (sk_pdf_metadata_t metadata, sk_string_t value) => + (sk_pdf_metadata_set_creator_delegate ??= GetSymbol ("sk_pdf_metadata_set_creator")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_encoding_quality(sk_pdf_metadata_t* metadata, int value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_encoding_quality (sk_pdf_metadata_t metadata, Int32 value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_encoding_quality (sk_pdf_metadata_t metadata, Int32 value); + } + private static Delegates.sk_pdf_metadata_set_encoding_quality sk_pdf_metadata_set_encoding_quality_delegate; + internal static void sk_pdf_metadata_set_encoding_quality (sk_pdf_metadata_t metadata, Int32 value) => + (sk_pdf_metadata_set_encoding_quality_delegate ??= GetSymbol ("sk_pdf_metadata_set_encoding_quality")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_keywords(sk_pdf_metadata_t* metadata, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_keywords (sk_pdf_metadata_t metadata, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_keywords (sk_pdf_metadata_t metadata, sk_string_t value); + } + private static Delegates.sk_pdf_metadata_set_keywords sk_pdf_metadata_set_keywords_delegate; + internal static void sk_pdf_metadata_set_keywords (sk_pdf_metadata_t metadata, sk_string_t value) => + (sk_pdf_metadata_set_keywords_delegate ??= GetSymbol ("sk_pdf_metadata_set_keywords")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_modified(sk_pdf_metadata_t* metadata, sk_time_datetime_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_modified (sk_pdf_metadata_t metadata, SKTimeDateTimeInternal* value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_modified (sk_pdf_metadata_t metadata, SKTimeDateTimeInternal* value); + } + private static Delegates.sk_pdf_metadata_set_modified sk_pdf_metadata_set_modified_delegate; + internal static void sk_pdf_metadata_set_modified (sk_pdf_metadata_t metadata, SKTimeDateTimeInternal* value) => + (sk_pdf_metadata_set_modified_delegate ??= GetSymbol ("sk_pdf_metadata_set_modified")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_pdfa(sk_pdf_metadata_t* metadata, bool value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_pdfa (sk_pdf_metadata_t metadata, [MarshalAs (UnmanagedType.I1)] bool value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_pdfa (sk_pdf_metadata_t metadata, [MarshalAs (UnmanagedType.I1)] bool value); + } + private static Delegates.sk_pdf_metadata_set_pdfa sk_pdf_metadata_set_pdfa_delegate; + internal static void sk_pdf_metadata_set_pdfa (sk_pdf_metadata_t metadata, [MarshalAs (UnmanagedType.I1)] bool value) => + (sk_pdf_metadata_set_pdfa_delegate ??= GetSymbol ("sk_pdf_metadata_set_pdfa")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_producer(sk_pdf_metadata_t* metadata, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_producer (sk_pdf_metadata_t metadata, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_producer (sk_pdf_metadata_t metadata, sk_string_t value); + } + private static Delegates.sk_pdf_metadata_set_producer sk_pdf_metadata_set_producer_delegate; + internal static void sk_pdf_metadata_set_producer (sk_pdf_metadata_t metadata, sk_string_t value) => + (sk_pdf_metadata_set_producer_delegate ??= GetSymbol ("sk_pdf_metadata_set_producer")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_raster_dpi(sk_pdf_metadata_t* metadata, float value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_raster_dpi (sk_pdf_metadata_t metadata, Single value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_raster_dpi (sk_pdf_metadata_t metadata, Single value); + } + private static Delegates.sk_pdf_metadata_set_raster_dpi sk_pdf_metadata_set_raster_dpi_delegate; + internal static void sk_pdf_metadata_set_raster_dpi (sk_pdf_metadata_t metadata, Single value) => + (sk_pdf_metadata_set_raster_dpi_delegate ??= GetSymbol ("sk_pdf_metadata_set_raster_dpi")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_structure_element_tree_root(sk_pdf_metadata_t* metadata, sk_pdf_structure_element_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_structure_element_tree_root (sk_pdf_metadata_t metadata, sk_pdf_structure_element_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_structure_element_tree_root (sk_pdf_metadata_t metadata, sk_pdf_structure_element_t value); + } + private static Delegates.sk_pdf_metadata_set_structure_element_tree_root sk_pdf_metadata_set_structure_element_tree_root_delegate; + internal static void sk_pdf_metadata_set_structure_element_tree_root (sk_pdf_metadata_t metadata, sk_pdf_structure_element_t value) => + (sk_pdf_metadata_set_structure_element_tree_root_delegate ??= GetSymbol ("sk_pdf_metadata_set_structure_element_tree_root")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_subject(sk_pdf_metadata_t* metadata, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_subject (sk_pdf_metadata_t metadata, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_subject (sk_pdf_metadata_t metadata, sk_string_t value); + } + private static Delegates.sk_pdf_metadata_set_subject sk_pdf_metadata_set_subject_delegate; + internal static void sk_pdf_metadata_set_subject (sk_pdf_metadata_t metadata, sk_string_t value) => + (sk_pdf_metadata_set_subject_delegate ??= GetSymbol ("sk_pdf_metadata_set_subject")).Invoke (metadata, value); + #endif + + // void sk_pdf_metadata_set_title(sk_pdf_metadata_t* metadata, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_metadata_set_title (sk_pdf_metadata_t metadata, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_metadata_set_title (sk_pdf_metadata_t metadata, sk_string_t value); + } + private static Delegates.sk_pdf_metadata_set_title sk_pdf_metadata_set_title_delegate; + internal static void sk_pdf_metadata_set_title (sk_pdf_metadata_t metadata, sk_string_t value) => + (sk_pdf_metadata_set_title_delegate ??= GetSymbol ("sk_pdf_metadata_set_title")).Invoke (metadata, value); + #endif + + // void sk_pdf_structure_element_add_additional_node_id(sk_pdf_structure_element_t* element, int value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_additional_node_id (sk_pdf_structure_element_t element, Int32 value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_additional_node_id (sk_pdf_structure_element_t element, Int32 value); + } + private static Delegates.sk_pdf_structure_element_add_additional_node_id sk_pdf_structure_element_add_additional_node_id_delegate; + internal static void sk_pdf_structure_element_add_additional_node_id (sk_pdf_structure_element_t element, Int32 value) => + (sk_pdf_structure_element_add_additional_node_id_delegate ??= GetSymbol ("sk_pdf_structure_element_add_additional_node_id")).Invoke (element, value); + #endif + + // void sk_pdf_structure_element_add_additional_node_ids(sk_pdf_structure_element_t* element, int* value, size_t count) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_additional_node_ids (sk_pdf_structure_element_t element, Int32* value, /* size_t */ IntPtr count); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_additional_node_ids (sk_pdf_structure_element_t element, Int32* value, /* size_t */ IntPtr count); + } + private static Delegates.sk_pdf_structure_element_add_additional_node_ids sk_pdf_structure_element_add_additional_node_ids_delegate; + internal static void sk_pdf_structure_element_add_additional_node_ids (sk_pdf_structure_element_t element, Int32* value, /* size_t */ IntPtr count) => + (sk_pdf_structure_element_add_additional_node_ids_delegate ??= GetSymbol ("sk_pdf_structure_element_add_additional_node_ids")).Invoke (element, value, count); + #endif + + // void sk_pdf_structure_element_add_child(sk_pdf_structure_element_t* element, sk_pdf_structure_element_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_child (sk_pdf_structure_element_t element, sk_pdf_structure_element_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_child (sk_pdf_structure_element_t element, sk_pdf_structure_element_t value); + } + private static Delegates.sk_pdf_structure_element_add_child sk_pdf_structure_element_add_child_delegate; + internal static void sk_pdf_structure_element_add_child (sk_pdf_structure_element_t element, sk_pdf_structure_element_t value) => + (sk_pdf_structure_element_add_child_delegate ??= GetSymbol ("sk_pdf_structure_element_add_child")).Invoke (element, value); + #endif + + // void sk_pdf_structure_element_add_float_array_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, float* values, size_t count) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single* values, /* size_t */ IntPtr count); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single* values, /* size_t */ IntPtr count); + } + private static Delegates.sk_pdf_structure_element_add_float_array_attribute sk_pdf_structure_element_add_float_array_attribute_delegate; + internal static void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single* values, /* size_t */ IntPtr count) => + (sk_pdf_structure_element_add_float_array_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_float_array_attribute")).Invoke (element, owner, name, values, count); + #endif + + // void sk_pdf_structure_element_add_float_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, float value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single value); + } + private static Delegates.sk_pdf_structure_element_add_float_attribute sk_pdf_structure_element_add_float_attribute_delegate; + internal static void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single value) => + (sk_pdf_structure_element_add_float_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_float_attribute")).Invoke (element, owner, name, value); + #endif + + // void sk_pdf_structure_element_add_int_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, int value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32 value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32 value); + } + private static Delegates.sk_pdf_structure_element_add_int_attribute sk_pdf_structure_element_add_int_attribute_delegate; + internal static void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32 value) => + (sk_pdf_structure_element_add_int_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_int_attribute")).Invoke (element, owner, name, value); + #endif + + // void sk_pdf_structure_element_add_name_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, const char* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, /* char */ void* value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, /* char */ void* value); + } + private static Delegates.sk_pdf_structure_element_add_name_attribute sk_pdf_structure_element_add_name_attribute_delegate; + internal static void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, /* char */ void* value) => + (sk_pdf_structure_element_add_name_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_name_attribute")).Invoke (element, owner, name, value); + #endif + + // void sk_pdf_structure_element_add_node_id_array_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, int* values, size_t count) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32* values, /* size_t */ IntPtr count); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32* values, /* size_t */ IntPtr count); + } + private static Delegates.sk_pdf_structure_element_add_node_id_array_attribute sk_pdf_structure_element_add_node_id_array_attribute_delegate; + internal static void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32* values, /* size_t */ IntPtr count) => + (sk_pdf_structure_element_add_node_id_array_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_node_id_array_attribute")).Invoke (element, owner, name, values, count); + #endif + + // void sk_pdf_structure_element_delete(sk_pdf_structure_element_t* element) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_delete (sk_pdf_structure_element_t element); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_delete (sk_pdf_structure_element_t element); + } + private static Delegates.sk_pdf_structure_element_delete sk_pdf_structure_element_delete_delegate; + internal static void sk_pdf_structure_element_delete (sk_pdf_structure_element_t element) => + (sk_pdf_structure_element_delete_delegate ??= GetSymbol ("sk_pdf_structure_element_delete")).Invoke (element); + #endif + + // sk_pdf_structure_element_t* sk_pdf_structure_element_new() + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern sk_pdf_structure_element_t sk_pdf_structure_element_new (); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate sk_pdf_structure_element_t sk_pdf_structure_element_new (); + } + private static Delegates.sk_pdf_structure_element_new sk_pdf_structure_element_new_delegate; + internal static sk_pdf_structure_element_t sk_pdf_structure_element_new () => + (sk_pdf_structure_element_new_delegate ??= GetSymbol ("sk_pdf_structure_element_new")).Invoke (); + #endif + + // void sk_pdf_structure_element_set_alt(sk_pdf_structure_element_t* element, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_set_alt (sk_pdf_structure_element_t element, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_set_alt (sk_pdf_structure_element_t element, sk_string_t value); + } + private static Delegates.sk_pdf_structure_element_set_alt sk_pdf_structure_element_set_alt_delegate; + internal static void sk_pdf_structure_element_set_alt (sk_pdf_structure_element_t element, sk_string_t value) => + (sk_pdf_structure_element_set_alt_delegate ??= GetSymbol ("sk_pdf_structure_element_set_alt")).Invoke (element, value); + #endif + + // void sk_pdf_structure_element_set_lang(sk_pdf_structure_element_t* element, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_set_lang (sk_pdf_structure_element_t element, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_set_lang (sk_pdf_structure_element_t element, sk_string_t value); + } + private static Delegates.sk_pdf_structure_element_set_lang sk_pdf_structure_element_set_lang_delegate; + internal static void sk_pdf_structure_element_set_lang (sk_pdf_structure_element_t element, sk_string_t value) => + (sk_pdf_structure_element_set_lang_delegate ??= GetSymbol ("sk_pdf_structure_element_set_lang")).Invoke (element, value); + #endif + + // void sk_pdf_structure_element_set_node_id(sk_pdf_structure_element_t* element, int value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_set_node_id (sk_pdf_structure_element_t element, Int32 value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_set_node_id (sk_pdf_structure_element_t element, Int32 value); + } + private static Delegates.sk_pdf_structure_element_set_node_id sk_pdf_structure_element_set_node_id_delegate; + internal static void sk_pdf_structure_element_set_node_id (sk_pdf_structure_element_t element, Int32 value) => + (sk_pdf_structure_element_set_node_id_delegate ??= GetSymbol ("sk_pdf_structure_element_set_node_id")).Invoke (element, value); + #endif + + // void sk_pdf_structure_element_set_type_string(sk_pdf_structure_element_t* element, sk_string_t* value) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_pdf_structure_element_set_type_string (sk_pdf_structure_element_t element, sk_string_t value); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_pdf_structure_element_set_type_string (sk_pdf_structure_element_t element, sk_string_t value); + } + private static Delegates.sk_pdf_structure_element_set_type_string sk_pdf_structure_element_set_type_string_delegate; + internal static void sk_pdf_structure_element_set_type_string (sk_pdf_structure_element_t element, sk_string_t value) => + (sk_pdf_structure_element_set_type_string_delegate ??= GetSymbol ("sk_pdf_structure_element_set_type_string")).Invoke (element, value); + #endif + #endregion #region sk_drawable.h @@ -14589,187 +14997,6 @@ public readonly override int GetHashCode () } - // sk_pdf_attribute_item_t - [StructLayout (LayoutKind.Sequential)] - internal unsafe partial struct SKPdfAttributeItemInternal : IEquatable { - // public sk_string_t* fOwner - public sk_string_t fOwner; - - // public sk_string_t* fName - public sk_string_t fName; - - // public void* fValue - public void* fValue; - - // public int fValueSize - public Int32 fValueSize; - - // public int fValueType - public Int32 fValueType; - - public readonly bool Equals (SKPdfAttributeItemInternal obj) => - fOwner == obj.fOwner && fName == obj.fName && fValue == obj.fValue && fValueSize == obj.fValueSize && fValueType == obj.fValueType; - - public readonly override bool Equals (object obj) => - obj is SKPdfAttributeItemInternal f && Equals (f); - - public static bool operator == (SKPdfAttributeItemInternal left, SKPdfAttributeItemInternal right) => - left.Equals (right); - - public static bool operator != (SKPdfAttributeItemInternal left, SKPdfAttributeItemInternal right) => - !left.Equals (right); - - public readonly override int GetHashCode () - { - var hash = new HashCode (); - hash.Add (fOwner); - hash.Add (fName); - hash.Add (fValue); - hash.Add (fValueSize); - hash.Add (fValueType); - return hash.ToHashCode (); - } - - } - - // sk_pdf_metadata_t - [StructLayout (LayoutKind.Sequential)] - internal unsafe partial struct SKPdfMetadataInternal : IEquatable { - // public sk_string_t* fTitle - public sk_string_t fTitle; - - // public sk_string_t* fAuthor - public sk_string_t fAuthor; - - // public sk_string_t* fSubject - public sk_string_t fSubject; - - // public sk_string_t* fKeywords - public sk_string_t fKeywords; - - // public sk_string_t* fCreator - public sk_string_t fCreator; - - // public sk_string_t* fProducer - public sk_string_t fProducer; - - // public sk_time_datetime_t* fCreation - public SKTimeDateTimeInternal* fCreation; - - // public sk_time_datetime_t* fModified - public SKTimeDateTimeInternal* fModified; - - // public float fRasterDPI - public Single fRasterDPI; - - // public bool fPDFA - public Byte fPDFA; - - // public int fEncodingQuality - public Int32 fEncodingQuality; - - // public sk_pdf_compression_t fCompression - public SKPdfCompression fCompression; - - // public sk_pdf_structure_element_t* fStructureElementTreeRoot - public SKPdfStructureElementInternal* fStructureElementTreeRoot; - - public readonly bool Equals (SKPdfMetadataInternal obj) => - fTitle == obj.fTitle && fAuthor == obj.fAuthor && fSubject == obj.fSubject && fKeywords == obj.fKeywords && fCreator == obj.fCreator && fProducer == obj.fProducer && fCreation == obj.fCreation && fModified == obj.fModified && fRasterDPI == obj.fRasterDPI && fPDFA == obj.fPDFA && fEncodingQuality == obj.fEncodingQuality && fCompression == obj.fCompression && fStructureElementTreeRoot == obj.fStructureElementTreeRoot; - - public readonly override bool Equals (object obj) => - obj is SKPdfMetadataInternal f && Equals (f); - - public static bool operator == (SKPdfMetadataInternal left, SKPdfMetadataInternal right) => - left.Equals (right); - - public static bool operator != (SKPdfMetadataInternal left, SKPdfMetadataInternal right) => - !left.Equals (right); - - public readonly override int GetHashCode () - { - var hash = new HashCode (); - hash.Add (fTitle); - hash.Add (fAuthor); - hash.Add (fSubject); - hash.Add (fKeywords); - hash.Add (fCreator); - hash.Add (fProducer); - hash.Add (fCreation); - hash.Add (fModified); - hash.Add (fRasterDPI); - hash.Add (fPDFA); - hash.Add (fEncodingQuality); - hash.Add (fCompression); - hash.Add (fStructureElementTreeRoot); - return hash.ToHashCode (); - } - - } - - // sk_pdf_structure_element_t - [StructLayout (LayoutKind.Sequential)] - internal unsafe partial struct SKPdfStructureElementInternal : IEquatable { - // public sk_string_t* fTypeString - public sk_string_t fTypeString; - - // public sk_pdf_structure_element_t* fChildren - public SKPdfStructureElementInternal* fChildren; - - // public int fChildrenSize - public Int32 fChildrenSize; - - // public int fNodeId - public Int32 fNodeId; - - // public int* fAdditionalNodeIds - public Int32* fAdditionalNodeIds; - - // public int fAdditionalNodeIdsSize - public Int32 fAdditionalNodeIdsSize; - - // public sk_pdf_attribute_item_t* fAttributes - public SKPdfAttributeItemInternal* fAttributes; - - // public int fAttributesSize - public Int32 fAttributesSize; - - // public sk_string_t* fAlt - public sk_string_t fAlt; - - // public sk_string_t* fLang - public sk_string_t fLang; - - public readonly bool Equals (SKPdfStructureElementInternal obj) => - fTypeString == obj.fTypeString && fChildren == obj.fChildren && fChildrenSize == obj.fChildrenSize && fNodeId == obj.fNodeId && fAdditionalNodeIds == obj.fAdditionalNodeIds && fAdditionalNodeIdsSize == obj.fAdditionalNodeIdsSize && fAttributes == obj.fAttributes && fAttributesSize == obj.fAttributesSize && fAlt == obj.fAlt && fLang == obj.fLang; - - public readonly override bool Equals (object obj) => - obj is SKPdfStructureElementInternal f && Equals (f); - - public static bool operator == (SKPdfStructureElementInternal left, SKPdfStructureElementInternal right) => - left.Equals (right); - - public static bool operator != (SKPdfStructureElementInternal left, SKPdfStructureElementInternal right) => - !left.Equals (right); - - public readonly override int GetHashCode () - { - var hash = new HashCode (); - hash.Add (fTypeString); - hash.Add (fChildren); - hash.Add (fChildrenSize); - hash.Add (fNodeId); - hash.Add (fAdditionalNodeIds); - hash.Add (fAdditionalNodeIdsSize); - hash.Add (fAttributes); - hash.Add (fAttributesSize); - hash.Add (fAlt); - hash.Add (fLang); - return hash.ToHashCode (); - } - - } - // sk_pngencoder_options_t [StructLayout (LayoutKind.Sequential)] public readonly unsafe partial struct SKPngEncoderOptions : IEquatable { @@ -15839,20 +16066,6 @@ public enum SKPathOp { ReverseDifference = 4, } - // sk_pdf_attribute_item_type_t - internal enum SKPdfAttributeItemTypeInternal { - // INT_SK_PDF_ATTRIBUTE_ITEM_TYPE = 0 - Int = 0, - // FLOAT_SK_PDF_ATTRIBUTE_ITEM_TYPE = 1 - Float = 1, - // NAME_SK_PDF_ATTRIBUTE_ITEM_TYPE = 2 - Name = 2, - // FLOAT_ARRAY_SK_PDF_ATTRIBUTE_ITEM_TYPE = 3 - FloatArray = 3, - // NODE_ID_ARRAY_SK_PDF_ATTRIBUTE_ITEM_TYPE = 4 - NodeIdArray = 4, - } - // sk_pdf_compression_t public enum SKPdfCompression { // DEFAULT_SK_PDF_COMPRESSION = -1 From d8b0f51ddd68414c9330ebe96e68335b7156c264 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 20 May 2024 23:01:38 +0800 Subject: [PATCH 4/8] more --- binding/SkiaSharp/SKDocument.cs | 138 +++++++++++++++++------- binding/SkiaSharp/SKPdfMetadata.cs | 4 +- binding/SkiaSharp/SKString.cs | 41 ++++++- binding/SkiaSharp/Util.cs | 27 +++++ externals/skia | 2 +- tests/Tests/SkiaSharp/SKDocumentTest.cs | 4 +- 6 files changed, 172 insertions(+), 44 deletions(-) diff --git a/binding/SkiaSharp/SKDocument.cs b/binding/SkiaSharp/SKDocument.cs index ce54b2d059..b0847da816 100644 --- a/binding/SkiaSharp/SKDocument.cs +++ b/binding/SkiaSharp/SKDocument.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.IO; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -149,59 +150,120 @@ public static SKDocument CreatePdf (SKWStream stream, SKPdfMetadata metadata) throw new ArgumentNullException (nameof (stream)); } - using var title = SKString.Create (metadata.Title); - using var author = SKString.Create (metadata.Author); - using var subject = SKString.Create (metadata.Subject); - using var keywords = SKString.Create (metadata.Keywords); - using var creator = SKString.Create (metadata.Creator); - using var producer = SKString.Create (metadata.Producer); - - SKTimeDateTimeInternal creation; - if (metadata.Creation is not null) - creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value); - - SKTimeDateTimeInternal modified; - if (metadata.Modified is not null) - modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value); + var structureHandle = ToNative (metadata.Structure); var metadataHandle = SkiaApi.sk_pdf_metadata_new (); try { - SkiaApi.sk_pdf_metadata_set_title(metadataHandle, title?.Handle ?? IntPtr.Zero); - SkiaApi.sk_pdf_metadata_set_author(metadataHandle, author?.Handle ?? IntPtr.Zero); - SkiaApi.sk_pdf_metadata_set_subject(metadataHandle, subject?.Handle ?? IntPtr.Zero); - SkiaApi.sk_pdf_metadata_set_keywords(metadataHandle, keywords?.Handle ?? IntPtr.Zero); - SkiaApi.sk_pdf_metadata_set_creator(metadataHandle, creator?.Handle ?? IntPtr.Zero); - SkiaApi.sk_pdf_metadata_set_producer(metadataHandle, producer?.Handle ?? IntPtr.Zero); - - SkiaApi.sk_pdf_metadata_set_creation(metadataHandle, &creation); - SkiaApi.sk_pdf_metadata_set_modified(metadataHandle, &modified); - - SkiaApi.sk_pdf_metadata_set_raster_dpi(metadataHandle, metadata.RasterDpi); - SkiaApi.sk_pdf_metadata_set_pdfa(metadataHandle, metadata.PdfA); - SkiaApi.sk_pdf_metadata_set_encoding_quality(metadataHandle, metadata.EncodingQuality); - SkiaApi.sk_pdf_metadata_set_compression_level(metadataHandle, metadata.Compression); + if (metadata.Title is not null) { + using var title = SKString.CreateRaw (metadata.Title); + SkiaApi.sk_pdf_metadata_set_title (metadataHandle, title.Handle); + } + using var author = SKString.CreateRaw (metadata.Author); + SkiaApi.sk_pdf_metadata_set_author (metadataHandle, author.Handle); + using var subject = SKString.CreateRaw (metadata.Subject); + SkiaApi.sk_pdf_metadata_set_subject (metadataHandle, subject.Handle); + using var keywords = SKString.CreateRaw (metadata.Keywords); + SkiaApi.sk_pdf_metadata_set_keywords (metadataHandle, keywords.Handle); + using var creator = SKString.CreateRaw (metadata.Creator); + SkiaApi.sk_pdf_metadata_set_creator (metadataHandle, creator.Handle); + using var producer = SKString.CreateRaw (metadata.Producer); + SkiaApi.sk_pdf_metadata_set_producer (metadataHandle, producer.Handle); + + if (metadata.Creation is not null) { + var creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value); + SkiaApi.sk_pdf_metadata_set_creation (metadataHandle, &creation); + } + + if (metadata.Modified is not null) { + var modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value); + SkiaApi.sk_pdf_metadata_set_modified (metadataHandle, &modified); + } + + SkiaApi.sk_pdf_metadata_set_raster_dpi (metadataHandle, metadata.RasterDpi); + SkiaApi.sk_pdf_metadata_set_pdfa (metadataHandle, metadata.PdfA); + SkiaApi.sk_pdf_metadata_set_encoding_quality (metadataHandle, metadata.EncodingQuality); + SkiaApi.sk_pdf_metadata_set_compression_level (metadataHandle, metadata.Compression); + + SkiaApi.sk_pdf_metadata_set_structure_element_tree_root (metadataHandle, structureHandle); return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, &cmetadata)), stream); } finally { if (metadataHandle != IntPtr.Zero) - SkiaApi.sk_pdf_metadata_delete(metadataHandle); + SkiaApi.sk_pdf_metadata_delete (metadataHandle); + + // dispose because the root node is not owned + if (structureHandle != IntPtr.Zero) + SkiaApi.sk_pdf_structure_element_delete (structureHandle); } } internal static SKDocument GetObject (IntPtr handle) => handle == IntPtr.Zero ? null : new SKDocument (handle, true); - static SKPdfStructureElementNative ToNative(SKPdfStructureElement structure) + static IntPtr ToNative(SKPdfStructureElement? structure) { - var t = new Memory (); - var x = t.Pin (); - int* tt = (int*)x.Pointer; - - new SKPdfStructureElementInternal { - fAdditionalNodeIds = tt, - fAdditionalNodeIdsSize = structure.AdditionalNodeIds.Count, - }; + if (structure is null) + return IntPtr.Zero; + + var handle = SkiaApi.sk_pdf_structure_element_new (); + + SkiaApi.sk_pdf_structure_element_set_node_id (handle, structure.Id); + +#if NET5_0_OR_GREATER + if (structure.AdditionalNodeIds is List nodes) { + var span = CollectionsMarshal.AsSpan (nodes); + SkiaApi.sk_pdf_structure_element_add_additional_node_ids (handle, &span, span.Length); + } else +#endif + { + using var nodes = Utils.ToRentedArray (structure.AdditionalNodeIds); + SkiaApi.sk_pdf_structure_element_add_additional_node_ids (handle, &nodes, nodes.Length); + } + + using var type = SKString.CreateRaw (structure.Type); + SkiaApi.sk_pdf_structure_element_set_type_string (handle, type.Handle); + + using var alt = SKString.CreateRaw (structure.Alt); + SkiaApi.sk_pdf_structure_element_set_alt (handle, alt.Handle); + + using var lang = SKString.CreateRaw (structure.Language); + SkiaApi.sk_pdf_structure_element_set_lang (handle, lang.Handle); + + if (structure.Attributes.Inner.Count > 0) { + foreach (var attr in structure.Attributes.Inner) { + switch (attr.Value) { + case int intValue: + SkiaApi.sk_pdf_structure_element_add_int_attribute (handle, attr.Owner, attr.Name, intValue); + break; + case float floatValue: + SkiaApi.sk_pdf_structure_element_add_float_attribute (handle, attr.Owner, attr.Name, floatValue); + break; + case string stringValue: + SkiaApi.sk_pdf_structure_element_add_int_attribute (handle, attr.Owner, attr.Name, stringValue); + break; + case int[] intArray: + SkiaApi.sk_pdf_structure_element_add_node_id_array_attribute (handle, attr.Owner, attr.Name, &intArray, intArray.Length); + break; + case float[] floatArray: + SkiaApi.sk_pdf_structure_element_add_float_array_attribute (handle, attr.Owner, attr.Name, &floatArray, floatArray.Length); + break; + default: + // TODO + break; + } + } + } + + if (structure.Children.Count > 0) { + foreach (var child in structure.Children) { + // do not dispose as the parent takes ownership + var childHandle = ToNative (child); + if (childHandle == IntPtr.Zero) + continue; + SkiaApi.sk_pdf_structure_element_add_child (handle, childHandle); + } + } } } } diff --git a/binding/SkiaSharp/SKPdfMetadata.cs b/binding/SkiaSharp/SKPdfMetadata.cs index 8b9e92b1ab..dd9f358690 100644 --- a/binding/SkiaSharp/SKPdfMetadata.cs +++ b/binding/SkiaSharp/SKPdfMetadata.cs @@ -99,8 +99,8 @@ public void Add (string owner, string name, string value) => attributes.Add ((owner, name, value)); public void Add (string owner, string name, IEnumerable value) => - attributes.Add ((owner, name, value)); + attributes.Add ((owner, name, value.ToArray ())); public void Add (string owner, string name, IEnumerable value) => - attributes.Add ((owner, name, value)); + attributes.Add ((owner, name, value.ToArray ())); } diff --git a/binding/SkiaSharp/SKString.cs b/binding/SkiaSharp/SKString.cs index c735fb0bb2..b2a860a60f 100644 --- a/binding/SkiaSharp/SKString.cs +++ b/binding/SkiaSharp/SKString.cs @@ -63,12 +63,15 @@ public static explicit operator string (SKString skString) [return: NotNullIfNotNull(nameof(str))] internal static SKString? Create (string? str) { - if (str == null) { + if (str is null) { return null; } return new SKString (str); } + internal SKStringRaw CreateRaw (string? str) => + new SKStringRaw (str); + protected override void Dispose (bool disposing) => base.Dispose (disposing); @@ -77,6 +80,42 @@ protected override void DisposeNative () => internal static SKString GetObject (IntPtr handle) => handle == IntPtr.Zero ? null : new SKString (handle, true); + + internal readonly ref struct SKStringRaw + { + internal SKStringRaw () + { + Handle = IntPtr.Zero; + } + + internal SKStringRaw (string? text) + : this (text.AsSpan ()) + { + } + + internal SKStringRaw (ReadOnlySpan text) + { + if (text.Length == 0) { + Handle = IntPtr.Zero; + return; + } + + var bufferSize = StringUtilities.GetMaxByteCount (text, SKTextEncoding.Utf8); + var buffer = stackalloc byte [bufferSize]; + + var bytesSize = StringUtilities.GetEncodedText (text, buffer, SKTextEncoding.Utf8); + + Handle = SkiaApi.sk_string_new_with_copy (buffer, bytesSize); + } + + public readonly IntPtr Handle; + + public void Dispose () + { + if (Handle != IntPtr.Zero) + SkiaApi.sk_string_destructor (Handle); + } + } } } diff --git a/binding/SkiaSharp/Util.cs b/binding/SkiaSharp/Util.cs index 3846d58dcb..2c00cc71f2 100644 --- a/binding/SkiaSharp/Util.cs +++ b/binding/SkiaSharp/Util.cs @@ -74,6 +74,15 @@ public static RentedArray RentHandlesArray (SKObject[] objects, bool nul return handles; } + public static RentedArray ToRentedArray (IList list) + { + var rented = new RentedArray (list.Count); + for (var idx = 0; idx < list.Count; idx++) { + rented [idx] = list [idx]; + } + return rented; + } + internal readonly ref struct RentedArray { internal RentedArray (int length) @@ -177,6 +186,24 @@ public static byte[] GetEncodedText (ReadOnlySpan text, SKTextEncoding enc _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."), }; + public static int GetEncodedText (ReadOnlySpan text, Span bytes, SKTextEncoding encoding) => + encoding switch { + SKTextEncoding.Utf8 => Encoding.UTF8.GetBytes (text, bytes), + SKTextEncoding.Utf16 => Encoding.Unicode.GetBytes (text, bytes), + SKTextEncoding.Utf32 => Encoding.UTF32.GetBytes (text, bytes), + _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."), + }; + + // GetMaxByteCount + + public static int GetMaxByteCount (ReadOnlySpan text, SKTextEncoding encoding) => + encoding switch { + SKTextEncoding.Utf8 => Encoding.UTF8.GetMaxByteCount (text.Length), + SKTextEncoding.Utf16 => Encoding.Unicode.GetMaxByteCount (text.Length), + SKTextEncoding.Utf32 => Encoding.UTF32.GetMaxByteCount (text.Length), + _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."), + }; + // GetString public static string GetString (IntPtr data, int dataLength, SKTextEncoding encoding) => diff --git a/externals/skia b/externals/skia index 5fbeafefbe..0f5ff1ce30 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit 5fbeafefbeb00d8cbcdd677aa1a1a629359d1065 +Subproject commit 0f5ff1ce3015a7e49b912513045c4d439ecb8e5b diff --git a/tests/Tests/SkiaSharp/SKDocumentTest.cs b/tests/Tests/SkiaSharp/SKDocumentTest.cs index 40dba1d607..31bccd06e7 100644 --- a/tests/Tests/SkiaSharp/SKDocumentTest.cs +++ b/tests/Tests/SkiaSharp/SKDocumentTest.cs @@ -215,13 +215,13 @@ public void CanCreateTaggedPdf() // The document tag. var root = new SKPdfStructureElement(); - root.NodeId = 1; + root.Id = 1; root.Type = "Document"; root.Language = "en-US"; // A link. var l1 = new SKPdfStructureElement(); - l1.NodeId = 2; + l1.Id = 2; l1.Type = "Link"; root.Children.Add(l1); From 88403af1a444e541d552f425a21853c3985e116e Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 20 May 2024 23:42:55 +0800 Subject: [PATCH 5/8] this --- binding/SkiaSharp/SKDocument.cs | 424 +++++++++++++------------ binding/SkiaSharp/SKString.cs | 36 ++- binding/SkiaSharp/SkiaApi.generated.cs | 30 +- binding/SkiaSharp/Util.cs | 15 +- binding/libSkiaSharp.json | 31 ++ 5 files changed, 304 insertions(+), 232 deletions(-) diff --git a/binding/SkiaSharp/SKDocument.cs b/binding/SkiaSharp/SKDocument.cs index b0847da816..75f95a86a3 100644 --- a/binding/SkiaSharp/SKDocument.cs +++ b/binding/SkiaSharp/SKDocument.cs @@ -1,269 +1,297 @@ using System; -using System.ComponentModel; -using System.IO; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -namespace SkiaSharp -{ - public unsafe class SKDocument : SKObject, ISKReferenceCounted, ISKSkipObjectRegistration - { - public const float DefaultRasterDpi = 72.0f; +namespace SkiaSharp; - internal SKDocument (IntPtr handle, bool owns) - : base (handle, owns) - { - } +public unsafe class SKDocument : SKObject, ISKReferenceCounted, ISKSkipObjectRegistration +{ + public const float DefaultRasterDpi = 72.0f; - protected override void Dispose (bool disposing) => - base.Dispose (disposing); + internal SKDocument (IntPtr handle, bool owns) + : base (handle, owns) + { + } - public void Abort () => - SkiaApi.sk_document_abort (Handle); + protected override void Dispose (bool disposing) => + base.Dispose (disposing); - public SKCanvas BeginPage (float width, float height) => - OwnedBy (SKCanvas.GetObject (SkiaApi.sk_document_begin_page (Handle, width, height, null), false), this); + public void Abort () => + SkiaApi.sk_document_abort (Handle); - public SKCanvas BeginPage (float width, float height, SKRect content) => - OwnedBy (SKCanvas.GetObject (SkiaApi.sk_document_begin_page (Handle, width, height, &content), false), this); + public SKCanvas BeginPage (float width, float height) => + OwnedBy (SKCanvas.GetObject (SkiaApi.sk_document_begin_page (Handle, width, height, null), false), this); - public void EndPage () => - SkiaApi.sk_document_end_page (Handle); + public SKCanvas BeginPage (float width, float height, SKRect content) => + OwnedBy (SKCanvas.GetObject (SkiaApi.sk_document_begin_page (Handle, width, height, &content), false), this); - public void Close () => - SkiaApi.sk_document_close (Handle); + public void EndPage () => + SkiaApi.sk_document_end_page (Handle); - // CreateXps + public void Close () => + SkiaApi.sk_document_close (Handle); - public static SKDocument? CreateXps (string path) => - CreateXps (path, DefaultRasterDpi); + // CreateXps - public static SKDocument? CreateXps (Stream stream) => - CreateXps (stream, DefaultRasterDpi); + public static SKDocument? CreateXps (string path) => + CreateXps (path, DefaultRasterDpi); - public static SKDocument? CreateXps (SKWStream stream) => - CreateXps (stream, DefaultRasterDpi); + public static SKDocument? CreateXps (Stream stream) => + CreateXps (stream, DefaultRasterDpi); - public static SKDocument? CreateXps (string path, float dpi) - { - if (path == null) { - throw new ArgumentNullException (nameof (path)); - } + public static SKDocument? CreateXps (SKWStream stream) => + CreateXps (stream, DefaultRasterDpi); - var stream = SKFileWStream.OpenStream (path); - return Owned (CreateXps (stream, dpi), stream); + public static SKDocument? CreateXps (string path, float dpi) + { + if (path == null) { + throw new ArgumentNullException (nameof (path)); } - public static SKDocument? CreateXps (Stream stream, float dpi) - { - if (stream == null) { - throw new ArgumentNullException (nameof (stream)); - } + var stream = SKFileWStream.OpenStream (path); + return Owned (CreateXps (stream, dpi), stream); + } - var managed = new SKManagedWStream (stream); - return Owned (CreateXps (managed, dpi), managed); + public static SKDocument? CreateXps (Stream stream, float dpi) + { + if (stream == null) { + throw new ArgumentNullException (nameof (stream)); } - public static SKDocument? CreateXps (SKWStream stream, float dpi) - { - if (stream == null) { - throw new ArgumentNullException (nameof (stream)); - } + var managed = new SKManagedWStream (stream); + return Owned (CreateXps (managed, dpi), managed); + } - return Referenced (GetObject (SkiaApi.sk_document_create_xps_from_stream (stream.Handle, dpi)), stream); + public static SKDocument? CreateXps (SKWStream stream, float dpi) + { + if (stream == null) { + throw new ArgumentNullException (nameof (stream)); } - // CreatePdf + return Referenced (GetObject (SkiaApi.sk_document_create_xps_from_stream (stream.Handle, dpi)), stream); + } - public static SKDocument CreatePdf (string path) - { - if (path == null) { - throw new ArgumentNullException (nameof (path)); - } + // CreatePdf - var stream = SKFileWStream.OpenStream (path); - return Owned (CreatePdf (stream), stream); + public static SKDocument CreatePdf (string path) + { + if (path == null) { + throw new ArgumentNullException (nameof (path)); } - public static SKDocument CreatePdf (Stream stream) - { - if (stream == null) { - throw new ArgumentNullException (nameof (stream)); - } + var stream = SKFileWStream.OpenStream (path); + return Owned (CreatePdf (stream), stream); + } - var managed = new SKManagedWStream (stream); - return Owned (CreatePdf (managed), managed); + public static SKDocument CreatePdf (Stream stream) + { + if (stream == null) { + throw new ArgumentNullException (nameof (stream)); } - public static SKDocument CreatePdf (SKWStream stream) - { - if (stream == null) { - throw new ArgumentNullException (nameof (stream)); - } + var managed = new SKManagedWStream (stream); + return Owned (CreatePdf (managed), managed); + } - return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream (stream.Handle)), stream); + public static SKDocument CreatePdf (SKWStream stream) + { + if (stream == null) { + throw new ArgumentNullException (nameof (stream)); } - public static SKDocument CreatePdf (string path, float dpi) => - CreatePdf (path, new SKDocumentPdfMetadata (dpi)); + return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream (stream.Handle)), stream); + } - public static SKDocument CreatePdf (Stream stream, float dpi) => - CreatePdf (stream, new SKDocumentPdfMetadata (dpi)); + public static SKDocument CreatePdf (string path, float dpi) => + CreatePdf (path, new SKPdfMetadata { RasterDpi = dpi }); - public static SKDocument CreatePdf (SKWStream stream, float dpi) => - CreatePdf (stream, new SKDocumentPdfMetadata (dpi)); + public static SKDocument CreatePdf (Stream stream, float dpi) => + CreatePdf (stream, new SKPdfMetadata { RasterDpi = dpi }); - public static SKDocument CreatePdf (string path, SKDocumentPdfMetadata metadata) => - CreatePdf (path, new SKPdfMetadata (metadata)); + public static SKDocument CreatePdf (SKWStream stream, float dpi) => + CreatePdf (stream, new SKPdfMetadata { RasterDpi = dpi }); - public static SKDocument CreatePdf (Stream stream, SKDocumentPdfMetadata metadata) => - CreatePdf (stream, new SKPdfMetadata (metadata)); + [Obsolete ("Use CreatePdf(string, SKPdfMetadata) instead.")] + public static SKDocument CreatePdf (string path, SKDocumentPdfMetadata metadata) => + CreatePdf (path, new SKPdfMetadata (metadata)); - public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata) => - CreatePdf (stream, new SKPdfMetadata (metadata)); + [Obsolete ("Use CreatePdf(Stream, SKPdfMetadata) instead.")] + public static SKDocument CreatePdf (Stream stream, SKDocumentPdfMetadata metadata) => + CreatePdf (stream, new SKPdfMetadata (metadata)); - public static SKDocument CreatePdf (string path, SKPdfMetadata metadata) - { - if (path == null) { - throw new ArgumentNullException (nameof (path)); - } + [Obsolete ("Use CreatePdf(SKWStream, SKPdfMetadata) instead.")] + public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata) => + CreatePdf (stream, new SKPdfMetadata (metadata)); - var stream = SKFileWStream.OpenStream (path); - return Owned (CreatePdf (stream, metadata), stream); + public static SKDocument CreatePdf (string path, SKPdfMetadata metadata) + { + if (path is null) { + throw new ArgumentNullException (nameof (path)); } - public static SKDocument CreatePdf (Stream stream, SKPdfMetadata metadata) - { - if (stream == null) { - throw new ArgumentNullException (nameof (stream)); - } + var stream = SKFileWStream.OpenStream (path); + return Owned (CreatePdf (stream, metadata), stream); + } - var managed = new SKManagedWStream (stream); - return Owned (CreatePdf (managed, metadata), managed); + public static SKDocument CreatePdf (Stream stream, SKPdfMetadata metadata) + { + if (stream == null) { + throw new ArgumentNullException (nameof (stream)); } - public static SKDocument CreatePdf (SKWStream stream, SKPdfMetadata metadata) - { - if (stream == null) { - throw new ArgumentNullException (nameof (stream)); - } + var managed = new SKManagedWStream (stream); + return Owned (CreatePdf (managed, metadata), managed); + } - var structureHandle = ToNative (metadata.Structure); + public static SKDocument CreatePdf (SKWStream stream, SKPdfMetadata metadata) + { + if (stream is null) { + throw new ArgumentNullException (nameof (stream)); + } - var metadataHandle = SkiaApi.sk_pdf_metadata_new (); + var (metadataHandle, structureHandle) = ToNative (metadata); - try { - if (metadata.Title is not null) { - using var title = SKString.CreateRaw (metadata.Title); - SkiaApi.sk_pdf_metadata_set_title (metadataHandle, title.Handle); - } - using var author = SKString.CreateRaw (metadata.Author); - SkiaApi.sk_pdf_metadata_set_author (metadataHandle, author.Handle); - using var subject = SKString.CreateRaw (metadata.Subject); - SkiaApi.sk_pdf_metadata_set_subject (metadataHandle, subject.Handle); - using var keywords = SKString.CreateRaw (metadata.Keywords); - SkiaApi.sk_pdf_metadata_set_keywords (metadataHandle, keywords.Handle); - using var creator = SKString.CreateRaw (metadata.Creator); - SkiaApi.sk_pdf_metadata_set_creator (metadataHandle, creator.Handle); - using var producer = SKString.CreateRaw (metadata.Producer); - SkiaApi.sk_pdf_metadata_set_producer (metadataHandle, producer.Handle); - - if (metadata.Creation is not null) { - var creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value); - SkiaApi.sk_pdf_metadata_set_creation (metadataHandle, &creation); - } + try { + return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, metadataHandle)), stream); + } finally { + if (metadataHandle != IntPtr.Zero) + SkiaApi.sk_pdf_metadata_delete (metadataHandle); - if (metadata.Modified is not null) { - var modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value); - SkiaApi.sk_pdf_metadata_set_modified (metadataHandle, &modified); - } + // dispose because the root node is not owned + if (structureHandle != IntPtr.Zero) + SkiaApi.sk_pdf_structure_element_delete (structureHandle); + } + } - SkiaApi.sk_pdf_metadata_set_raster_dpi (metadataHandle, metadata.RasterDpi); - SkiaApi.sk_pdf_metadata_set_pdfa (metadataHandle, metadata.PdfA); - SkiaApi.sk_pdf_metadata_set_encoding_quality (metadataHandle, metadata.EncodingQuality); - SkiaApi.sk_pdf_metadata_set_compression_level (metadataHandle, metadata.Compression); + internal static SKDocument? GetObject (IntPtr handle) => + handle == IntPtr.Zero ? null : new SKDocument (handle, true); - SkiaApi.sk_pdf_metadata_set_structure_element_tree_root (metadataHandle, structureHandle); + private static (IntPtr Metadata, IntPtr Structure) ToNative (SKPdfMetadata metadata) + { + var metadataHandle = SkiaApi.sk_pdf_metadata_new (); - return Referenced (GetObject (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, &cmetadata)), stream); - } finally { - if (metadataHandle != IntPtr.Zero) - SkiaApi.sk_pdf_metadata_delete (metadataHandle); + if (metadata.Title is not null) { + using var title = SKString.CreateRaw (metadata.Title); + SkiaApi.sk_pdf_metadata_set_title (metadataHandle, title.Handle); + } + if (metadata.Author is not null) { + using var author = SKString.CreateRaw (metadata.Author); + SkiaApi.sk_pdf_metadata_set_author (metadataHandle, author.Handle); + } + if (metadata.Subject is not null) { + using var subject = SKString.CreateRaw (metadata.Subject); + SkiaApi.sk_pdf_metadata_set_subject (metadataHandle, subject.Handle); + } + if (metadata.Keywords is not null) { + using var keywords = SKString.CreateRaw (metadata.Keywords); + SkiaApi.sk_pdf_metadata_set_keywords (metadataHandle, keywords.Handle); + } + if (metadata.Creator is not null) { + using var creator = SKString.CreateRaw (metadata.Creator); + SkiaApi.sk_pdf_metadata_set_creator (metadataHandle, creator.Handle); + } + if (metadata.Producer is not null) { + using var producer = SKString.CreateRaw (metadata.Producer); + SkiaApi.sk_pdf_metadata_set_producer (metadataHandle, producer.Handle); + } + if (metadata.Creation is not null) { + var creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value); + SkiaApi.sk_pdf_metadata_set_creation (metadataHandle, &creation); + } + if (metadata.Modified is not null) { + var modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value); + SkiaApi.sk_pdf_metadata_set_modified (metadataHandle, &modified); + } - // dispose because the root node is not owned - if (structureHandle != IntPtr.Zero) - SkiaApi.sk_pdf_structure_element_delete (structureHandle); - } + SkiaApi.sk_pdf_metadata_set_raster_dpi (metadataHandle, metadata.RasterDpi); + SkiaApi.sk_pdf_metadata_set_pdfa (metadataHandle, metadata.PdfA); + SkiaApi.sk_pdf_metadata_set_encoding_quality (metadataHandle, metadata.EncodingQuality); + SkiaApi.sk_pdf_metadata_set_compression_level (metadataHandle, metadata.Compression); + + IntPtr structureHandle; + if (metadata.Structure is not null) { + structureHandle = ToNative (metadata.Structure); + SkiaApi.sk_pdf_metadata_set_structure_element_tree_root (metadataHandle, structureHandle); + } else { + structureHandle = IntPtr.Zero; } - internal static SKDocument GetObject (IntPtr handle) => - handle == IntPtr.Zero ? null : new SKDocument (handle, true); + return (metadataHandle, structureHandle); + } - static IntPtr ToNative(SKPdfStructureElement? structure) - { - if (structure is null) - return IntPtr.Zero; + private static IntPtr ToNative (SKPdfStructureElement? structure) + { + if (structure is null) + return IntPtr.Zero; + + var handle = SkiaApi.sk_pdf_structure_element_new (); - var handle = SkiaApi.sk_pdf_structure_element_new (); + SkiaApi.sk_pdf_structure_element_set_node_id (handle, structure.Id); - SkiaApi.sk_pdf_structure_element_set_node_id (handle, structure.Id); - #if NET5_0_OR_GREATER - if (structure.AdditionalNodeIds is List nodes) { - var span = CollectionsMarshal.AsSpan (nodes); - SkiaApi.sk_pdf_structure_element_add_additional_node_ids (handle, &span, span.Length); - } else + if (structure.AdditionalNodeIds is List nodes) { + var span = CollectionsMarshal.AsSpan (nodes); + fixed (int* ptr = span) { + SkiaApi.sk_pdf_structure_element_add_additional_node_ids (handle, ptr, span.Length); + } + } else #endif - { - using var nodes = Utils.ToRentedArray (structure.AdditionalNodeIds); - SkiaApi.sk_pdf_structure_element_add_additional_node_ids (handle, &nodes, nodes.Length); + { + using var nodesArray = Utils.ToRentedArray (structure.AdditionalNodeIds); + fixed (int* ptr = nodesArray) { + SkiaApi.sk_pdf_structure_element_add_additional_node_ids (handle, ptr, (IntPtr)nodesArray.Length); } + } - using var type = SKString.CreateRaw (structure.Type); - SkiaApi.sk_pdf_structure_element_set_type_string (handle, type.Handle); - - using var alt = SKString.CreateRaw (structure.Alt); - SkiaApi.sk_pdf_structure_element_set_alt (handle, alt.Handle); - - using var lang = SKString.CreateRaw (structure.Language); - SkiaApi.sk_pdf_structure_element_set_lang (handle, lang.Handle); - - if (structure.Attributes.Inner.Count > 0) { - foreach (var attr in structure.Attributes.Inner) { - switch (attr.Value) { - case int intValue: - SkiaApi.sk_pdf_structure_element_add_int_attribute (handle, attr.Owner, attr.Name, intValue); - break; - case float floatValue: - SkiaApi.sk_pdf_structure_element_add_float_attribute (handle, attr.Owner, attr.Name, floatValue); - break; - case string stringValue: - SkiaApi.sk_pdf_structure_element_add_int_attribute (handle, attr.Owner, attr.Name, stringValue); - break; - case int[] intArray: - SkiaApi.sk_pdf_structure_element_add_node_id_array_attribute (handle, attr.Owner, attr.Name, &intArray, intArray.Length); - break; - case float[] floatArray: - SkiaApi.sk_pdf_structure_element_add_float_array_attribute (handle, attr.Owner, attr.Name, &floatArray, floatArray.Length); - break; - default: - // TODO - break; - } + using var type = SKString.CreateRaw (structure.Type); + SkiaApi.sk_pdf_structure_element_set_type_string (handle, type.Handle); + + using var alt = SKString.CreateRaw (structure.Alt); + SkiaApi.sk_pdf_structure_element_set_alt (handle, alt.Handle); + + using var lang = SKString.CreateRaw (structure.Language); + SkiaApi.sk_pdf_structure_element_set_lang (handle, lang.Handle); + + if (structure.Attributes.Inner.Count > 0) { + foreach (var (owner, name, val) in structure.Attributes.Inner) { + switch (val) { + case int intValue: + SkiaApi.sk_pdf_structure_element_add_int_attribute (handle, owner, name, intValue); + break; + case float floatValue: + SkiaApi.sk_pdf_structure_element_add_float_attribute (handle, owner, name, floatValue); + break; + case string stringValue: + SkiaApi.sk_pdf_structure_element_add_name_attribute (handle, owner, name, stringValue); + break; + case int[] intArray: + fixed (int* ptr = intArray) { + SkiaApi.sk_pdf_structure_element_add_node_id_array_attribute (handle, owner, name, ptr, (IntPtr)intArray.Length); + } + break; + case float[] floatArray: + fixed (float* ptr = floatArray) { + SkiaApi.sk_pdf_structure_element_add_float_array_attribute (handle, owner, name, ptr, (IntPtr)floatArray.Length); + } + break; + default: + // TODO + break; } } + } - if (structure.Children.Count > 0) { - foreach (var child in structure.Children) { - // do not dispose as the parent takes ownership - var childHandle = ToNative (child); - if (childHandle == IntPtr.Zero) - continue; - SkiaApi.sk_pdf_structure_element_add_child (handle, childHandle); - } + if (structure.Children.Count > 0) { + foreach (var child in structure.Children) { + // do not dispose as the parent takes ownership + var childHandle = ToNative (child); + if (childHandle == IntPtr.Zero) + continue; + SkiaApi.sk_pdf_structure_element_add_child (handle, childHandle); } } + + return handle; } } diff --git a/binding/SkiaSharp/SKString.cs b/binding/SkiaSharp/SKString.cs index b2a860a60f..cc96c020fe 100644 --- a/binding/SkiaSharp/SKString.cs +++ b/binding/SkiaSharp/SKString.cs @@ -1,5 +1,8 @@ using System; -using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; +#if NETSTANDARD1_3 || WINDOWS_UWP +using System.Reflection; +#endif namespace SkiaSharp { @@ -17,7 +20,7 @@ public SKString () throw new InvalidOperationException ("Unable to create a new SKString instance."); } } - + public SKString (ReadOnlySpan src, long length) : base (CreateCopy (src, length), true) { @@ -25,11 +28,9 @@ public SKString (ReadOnlySpan src, long length) throw new InvalidOperationException ("Unable to copy the SKString instance."); } } - + private static IntPtr CreateCopy (ReadOnlySpan src, long length) { - if (src is null) - throw new ArgumentNullException (nameof (src)); if (length > src.Length) throw new ArgumentOutOfRangeException (nameof (length)); @@ -42,12 +43,12 @@ public SKString (ReadOnlySpan src) : this (src, src.Length) { } - + public SKString (string str) : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf8)) { } - + public override string ToString () { var cstr = SkiaApi.sk_string_get_c_str (Handle); @@ -59,8 +60,8 @@ public static explicit operator string (SKString skString) { return skString.ToString (); } - - [return: NotNullIfNotNull(nameof(str))] + + [return: NotNullIfNotNull (nameof (str))] internal static SKString? Create (string? str) { if (str is null) { @@ -69,7 +70,7 @@ public static explicit operator string (SKString skString) return new SKString (str); } - internal SKStringRaw CreateRaw (string? str) => + internal static SKStringRaw CreateRaw (string? str) => new SKStringRaw (str); protected override void Dispose (bool disposing) => @@ -78,22 +79,22 @@ protected override void Dispose (bool disposing) => protected override void DisposeNative () => SkiaApi.sk_string_destructor (Handle); - internal static SKString GetObject (IntPtr handle) => + internal static SKString? GetObject (IntPtr handle) => handle == IntPtr.Zero ? null : new SKString (handle, true); internal readonly ref struct SKStringRaw { - internal SKStringRaw () + public SKStringRaw () { Handle = IntPtr.Zero; } - internal SKStringRaw (string? text) + public SKStringRaw (string? text) : this (text.AsSpan ()) { } - internal SKStringRaw (ReadOnlySpan text) + public SKStringRaw (ReadOnlySpan text) { if (text.Length == 0) { Handle = IntPtr.Zero; @@ -101,11 +102,12 @@ internal SKStringRaw (ReadOnlySpan text) } var bufferSize = StringUtilities.GetMaxByteCount (text, SKTextEncoding.Utf8); - var buffer = stackalloc byte [bufferSize]; + var buffer = stackalloc byte[bufferSize]; + var bufferSpan = new Span (buffer, bufferSize); - var bytesSize = StringUtilities.GetEncodedText (text, buffer, SKTextEncoding.Utf8); + var bytesSize = StringUtilities.GetEncodedText (text, bufferSpan, SKTextEncoding.Utf8); - Handle = SkiaApi.sk_string_new_with_copy (buffer, bytesSize); + Handle = SkiaApi.sk_string_new_with_copy (buffer, (IntPtr)bytesSize); } public readonly IntPtr Handle; diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index 093c2d4dad..92376e0558 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -4032,70 +4032,70 @@ internal static void sk_pdf_structure_element_add_child (sk_pdf_structure_elemen // void sk_pdf_structure_element_add_float_array_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, float* values, size_t count) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single* values, /* size_t */ IntPtr count); + internal static extern void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Single* values, /* size_t */ IntPtr count); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single* values, /* size_t */ IntPtr count); + internal delegate void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Single* values, /* size_t */ IntPtr count); } private static Delegates.sk_pdf_structure_element_add_float_array_attribute sk_pdf_structure_element_add_float_array_attribute_delegate; - internal static void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single* values, /* size_t */ IntPtr count) => + internal static void sk_pdf_structure_element_add_float_array_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Single* values, /* size_t */ IntPtr count) => (sk_pdf_structure_element_add_float_array_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_float_array_attribute")).Invoke (element, owner, name, values, count); #endif // void sk_pdf_structure_element_add_float_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, float value) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single value); + internal static extern void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Single value); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single value); + internal delegate void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Single value); } private static Delegates.sk_pdf_structure_element_add_float_attribute sk_pdf_structure_element_add_float_attribute_delegate; - internal static void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Single value) => + internal static void sk_pdf_structure_element_add_float_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Single value) => (sk_pdf_structure_element_add_float_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_float_attribute")).Invoke (element, owner, name, value); #endif // void sk_pdf_structure_element_add_int_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, int value) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32 value); + internal static extern void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Int32 value); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32 value); + internal delegate void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Int32 value); } private static Delegates.sk_pdf_structure_element_add_int_attribute sk_pdf_structure_element_add_int_attribute_delegate; - internal static void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32 value) => + internal static void sk_pdf_structure_element_add_int_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Int32 value) => (sk_pdf_structure_element_add_int_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_int_attribute")).Invoke (element, owner, name, value); #endif // void sk_pdf_structure_element_add_name_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, const char* value) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, /* char */ void* value); + internal static extern void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, [MarshalAs (UnmanagedType.LPStr)] String value); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, /* char */ void* value); + internal delegate void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, [MarshalAs (UnmanagedType.LPStr)] String value); } private static Delegates.sk_pdf_structure_element_add_name_attribute sk_pdf_structure_element_add_name_attribute_delegate; - internal static void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, /* char */ void* value) => + internal static void sk_pdf_structure_element_add_name_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, [MarshalAs (UnmanagedType.LPStr)] String value) => (sk_pdf_structure_element_add_name_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_name_attribute")).Invoke (element, owner, name, value); #endif // void sk_pdf_structure_element_add_node_id_array_attribute(sk_pdf_structure_element_t* element, const char* owner, const char* name, int* values, size_t count) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32* values, /* size_t */ IntPtr count); + internal static extern void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Int32* values, /* size_t */ IntPtr count); #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32* values, /* size_t */ IntPtr count); + internal delegate void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Int32* values, /* size_t */ IntPtr count); } private static Delegates.sk_pdf_structure_element_add_node_id_array_attribute sk_pdf_structure_element_add_node_id_array_attribute_delegate; - internal static void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, /* char */ void* owner, /* char */ void* name, Int32* values, /* size_t */ IntPtr count) => + internal static void sk_pdf_structure_element_add_node_id_array_attribute (sk_pdf_structure_element_t element, [MarshalAs (UnmanagedType.LPStr)] String owner, [MarshalAs (UnmanagedType.LPStr)] String name, Int32* values, /* size_t */ IntPtr count) => (sk_pdf_structure_element_add_node_id_array_attribute_delegate ??= GetSymbol ("sk_pdf_structure_element_add_node_id_array_attribute")).Invoke (element, owner, name, values, count); #endif diff --git a/binding/SkiaSharp/Util.cs b/binding/SkiaSharp/Util.cs index 2c00cc71f2..f5ed1a7fa4 100644 --- a/binding/SkiaSharp/Util.cs +++ b/binding/SkiaSharp/Util.cs @@ -2,6 +2,7 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.ComponentModel; using System.Text; #if NETSTANDARD1_3 || WINDOWS_UWP @@ -60,6 +61,16 @@ internal static byte[] GetBytes (this Encoding encoding, ReadOnlySpan text } } +#if NET45_OR_GREATER || NETSTANDARD2_0 + public static int GetBytes (this Encoding encoding, ReadOnlySpan text, Span bytes) + { + fixed (char* t = text) + fixed (byte* b = bytes) { + return encoding.GetBytes (t, text.Length, b, bytes.Length); + } + } +#endif + public static RentedArray RentArray (int length, bool nullIfEmpty = false) => nullIfEmpty && length <= 0 ? default @@ -77,8 +88,8 @@ public static RentedArray RentHandlesArray (SKObject[] objects, bool nul public static RentedArray ToRentedArray (IList list) { var rented = new RentedArray (list.Count); - for (var idx = 0; idx < list.Count; idx++) { - rented [idx] = list [idx]; + for (var idx = 0; idx < list.Count; idx++) { + rented[idx] = list[idx]; } return rented; } diff --git a/binding/libSkiaSharp.json b/binding/libSkiaSharp.json index 9222044183..e698a8d602 100644 --- a/binding/libSkiaSharp.json +++ b/binding/libSkiaSharp.json @@ -491,6 +491,37 @@ "parameters": { "1": "[MarshalAs (UnmanagedType.LPStr)] String" } + }, + "sk_pdf_structure_element_add_int_attribute": { + "parameters": { + "1": "[MarshalAs (UnmanagedType.LPStr)] String", + "2": "[MarshalAs (UnmanagedType.LPStr)] String" + } + }, + "sk_pdf_structure_element_add_float_attribute": { + "parameters": { + "1": "[MarshalAs (UnmanagedType.LPStr)] String", + "2": "[MarshalAs (UnmanagedType.LPStr)] String" + } + }, + "sk_pdf_structure_element_add_name_attribute": { + "parameters": { + "1": "[MarshalAs (UnmanagedType.LPStr)] String", + "2": "[MarshalAs (UnmanagedType.LPStr)] String", + "3": "[MarshalAs (UnmanagedType.LPStr)] String" + } + }, + "sk_pdf_structure_element_add_node_id_array_attribute": { + "parameters": { + "1": "[MarshalAs (UnmanagedType.LPStr)] String", + "2": "[MarshalAs (UnmanagedType.LPStr)] String" + } + }, + "sk_pdf_structure_element_add_float_array_attribute": { + "parameters": { + "1": "[MarshalAs (UnmanagedType.LPStr)] String", + "2": "[MarshalAs (UnmanagedType.LPStr)] String" + } } } } From 8253096140bf4e673e0e8a9ff01943fe0aab0baa Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Tue, 21 May 2024 01:08:59 +0800 Subject: [PATCH 6/8] it builds --- binding/SkiaSharp/SKDocument.cs | 13 ++- binding/SkiaSharp/SKPdfMetadata.cs | 10 +- binding/SkiaSharp/SkiaApi.generated.cs | 14 +++ externals/skia | 2 +- tests/Tests/SkiaSharp/SKDocumentTest.cs | 125 +++++++++++++++--------- tests/Tests/SkiaSharp/SKTest.cs | 6 ++ 6 files changed, 116 insertions(+), 54 deletions(-) diff --git a/binding/SkiaSharp/SKDocument.cs b/binding/SkiaSharp/SKDocument.cs index 75f95a86a3..5f058edf56 100644 --- a/binding/SkiaSharp/SKDocument.cs +++ b/binding/SkiaSharp/SKDocument.cs @@ -220,7 +220,7 @@ private static (IntPtr Metadata, IntPtr Structure) ToNative (SKPdfMetadata metad return (metadataHandle, structureHandle); } - private static IntPtr ToNative (SKPdfStructureElement? structure) + private static IntPtr ToNative (SKPdfStructureElementNode? structure) { if (structure is null) return IntPtr.Zero; @@ -295,3 +295,14 @@ private static IntPtr ToNative (SKPdfStructureElement? structure) return handle; } } + +public static class SkPdfDocumentExtensions +{ + public static void DrawPdfNodeAnnotation (this SKCanvas canvas, int nodeId) + { + if (canvas is null) + throw new ArgumentNullException (nameof (canvas)); + + SkiaApi.sk_canvas_draw_pdf_node_id_annotation (canvas.Handle, nodeId); + } +} diff --git a/binding/SkiaSharp/SKPdfMetadata.cs b/binding/SkiaSharp/SKPdfMetadata.cs index dd9f358690..ca8655b577 100644 --- a/binding/SkiaSharp/SKPdfMetadata.cs +++ b/binding/SkiaSharp/SKPdfMetadata.cs @@ -53,16 +53,16 @@ internal SKPdfMetadata (SKDocumentPdfMetadata metadata) public SKPdfCompression Compression { get; set; } = SKPdfCompression.Default; - public SKPdfStructureElement? Structure { get; set; } + public SKPdfStructureElementNode? Structure { get; set; } } -public class SKPdfStructureElement +public class SKPdfStructureElementNode { - private List? children; + private List? children; private List? additionalNodeIds; private SKPdfAttributeList? attributes; - public SKPdfStructureElement (int id, string type) + public SKPdfStructureElementNode (int id, string type) { Id = id; Type = type; @@ -76,7 +76,7 @@ public SKPdfStructureElement (int id, string type) public string? Language { get; set; } - public IList Children => children ??= new (); + public IList Children => children ??= new (); public IList AdditionalNodeIds => additionalNodeIds ??= new (); diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index 92376e0558..1d98481a13 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -3665,6 +3665,20 @@ internal static void sk_data_unref (sk_data_t param0) => #region sk_document.h + // void sk_canvas_draw_pdf_node_id_annotation(sk_canvas_t* t, int nodeId) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_canvas_draw_pdf_node_id_annotation (sk_canvas_t t, Int32 nodeId); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_canvas_draw_pdf_node_id_annotation (sk_canvas_t t, Int32 nodeId); + } + private static Delegates.sk_canvas_draw_pdf_node_id_annotation sk_canvas_draw_pdf_node_id_annotation_delegate; + internal static void sk_canvas_draw_pdf_node_id_annotation (sk_canvas_t t, Int32 nodeId) => + (sk_canvas_draw_pdf_node_id_annotation_delegate ??= GetSymbol ("sk_canvas_draw_pdf_node_id_annotation")).Invoke (t, nodeId); + #endif + // void sk_document_abort(sk_document_t* document) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] diff --git a/externals/skia b/externals/skia index 0f5ff1ce30..0dcfa8f89a 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit 0f5ff1ce3015a7e49b912513045c4d439ecb8e5b +Subproject commit 0dcfa8f89aedc853a7934bba1953b7eac82a8179 diff --git a/tests/Tests/SkiaSharp/SKDocumentTest.cs b/tests/Tests/SkiaSharp/SKDocumentTest.cs index 31bccd06e7..e3b88d27ac 100644 --- a/tests/Tests/SkiaSharp/SKDocumentTest.cs +++ b/tests/Tests/SkiaSharp/SKDocumentTest.cs @@ -81,8 +81,9 @@ public void CanCreatePdf() } } + [Obsolete] [SkippableFact] - public void CanCreatePdfWithMetadata() + public void CanCreatePdfWithObsoleteMetadata() { var metadata = SKDocumentPdfMetadata.Default; metadata.Author = "SkiaSharp Team"; @@ -110,6 +111,37 @@ public void CanCreatePdfWithMetadata() } } + [SkippableFact] + public void CanCreatePdfWithMetadata() + { + var metadata = new SKPdfMetadata + { + Author = "SkiaSharp Team" + }; + + using (var stream = new MemoryStream()) + { + using (var doc = SKDocument.CreatePdf(stream, metadata)) + { + Assert.NotNull(doc); + Assert.NotNull(doc.BeginPage(100, 100)); + + doc.EndPage(); + doc.Close(); + } + + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); + + stream.Position = 0; + using (var reader = new StreamReader(stream)) + { + var contents = reader.ReadToEnd(); + Assert.Contains("/Author (SkiaSharp Team)", contents); + } + } + } + [SkippableFact] public void ManagedStreamDisposeOrder() { @@ -200,12 +232,12 @@ static SKDocument CreateDocument(out IntPtr streamHandle) var stream = new SKDynamicMemoryWStream(); streamHandle = stream.Handle; - return SKDocument.CreatePdf(stream, new SKDocumentPdfMetadata()); + return SKDocument.CreatePdf(stream, new SKPdfMetadata()); } } - + [SkippableFact] - public void CanCreateTaggedPdf() + public void CanCreateTaggedLinkPdf() { var metadata = new SKPdfMetadata(); metadata.Title = "Example Tagged PDF With Links"; @@ -214,26 +246,22 @@ public void CanCreateTaggedPdf() metadata.Modified = DateTime.Now; // The document tag. - var root = new SKPdfStructureElement(); - root.Id = 1; - root.Type = "Document"; + var root = new SKPdfStructureElementNode(1, "Document"); root.Language = "en-US"; // A link. - var l1 = new SKPdfStructureElement(); - l1.Id = 2; - l1.Type = "Link"; + var l1 = new SKPdfStructureElementNode(2, "Link"); root.Children.Add(l1); metadata.Structure = root; - using var stream = new MemoryStream(); - using (var doc = SKDocument.CreatePdf(stream, metadata)) - { - Assert.NotNull(doc); + using var stream = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(stream, metadata)) + { + Assert.NotNull(doc); var canvas = doc.BeginPage(612, 792); // U.S. Letter - Assert.NotNull(canvas); + Assert.NotNull(canvas); var paint = new SKPaint(); paint.Color = SKColors.Blue; @@ -242,47 +270,50 @@ public void CanCreateTaggedPdf() font.Size = 20; // The node ID should cover both the text and the annotation. - canvas.SetPdfNodeId(2); - canvasDrawText("Click to visit Google.com", 72, 72, font, paint); + canvas.DrawPdfNodeAnnotation(2); + canvas.DrawText("Click to visit Google.com", 72, 72, font, paint); var linkRect = SKRect.Create(72, 54, 218, 24); canvas.DrawUrlAnnotation(linkRect, "http://www.google.com"); - document.EndPage(); - document.Close(); - } + doc.EndPage(); + doc.Close(); + } + + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); - Assert.True(stream.Length > 0); - Assert.True(stream.Position > 0); - } + stream.Position = 0; + SaveStream(stream, "test.pdf"); + } [SkippableFact] - public void CanCreateTaggedPdfWithConstructorStructures() + public void CanCreateTaggedLinkPdfWithConstructorStructures() { - var metadata = new SKPdfMetadata - { - Title = "Example Tagged PDF With Links", - Creator = "Skia", - Creation = DateTime.Now, - Modified = DateTime.Now, - // The document tag. - Structure = new SKPdfStructureElement(1, "Document") + var metadata = new SKPdfMetadata + { + Title = "Example Tagged PDF With Links", + Creator = "Skia", + Creation = DateTime.Now, + Modified = DateTime.Now, + // The document tag. + Structure = new SKPdfStructureElementNode(1, "Document") { Language = "en-US", Children = { // A link. - new SKPdfStructureElement(2, "Link") + new SKPdfStructureElementNode(2, "Link") } } - }; + }; - using var stream = new MemoryStream(); - using (var doc = SKDocument.CreatePdf(stream, metadata)) - { - Assert.NotNull(doc); + using var stream = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(stream, metadata)) + { + Assert.NotNull(doc); var canvas = doc.BeginPage(612, 792); // U.S. Letter - Assert.NotNull(canvas); + Assert.NotNull(canvas); var paint = new SKPaint(); paint.Color = SKColors.Blue; @@ -291,17 +322,17 @@ public void CanCreateTaggedPdfWithConstructorStructures() font.Size = 20; // The node ID should cover both the text and the annotation. - canvas.SetPdfNodeId(2); - canvasDrawText("Click to visit Google.com", 72, 72, font, paint); + canvas.DrawPdfNodeAnnotation(2); + canvas.DrawText("Click to visit Google.com", 72, 72, font, paint); var linkRect = SKRect.Create(72, 54, 218, 24); canvas.DrawUrlAnnotation(linkRect, "http://www.google.com"); - document.EndPage(); - document.Close(); - } + doc.EndPage(); + doc.Close(); + } - Assert.True(stream.Length > 0); - Assert.True(stream.Position > 0); - } + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); + } } } diff --git a/tests/Tests/SkiaSharp/SKTest.cs b/tests/Tests/SkiaSharp/SKTest.cs index 05dbcafae5..3a0fe6c04d 100644 --- a/tests/Tests/SkiaSharp/SKTest.cs +++ b/tests/Tests/SkiaSharp/SKTest.cs @@ -53,6 +53,12 @@ protected static SKStreamAsset CreateTestSKStream(int length = 1024) return new SKMemoryStream(bytes); } + protected static void SaveStream(Stream stream, string filename) + { + using var file = File.Create(Path.Combine(PathToImages, filename)); + stream.CopyTo(file); + } + protected static void SaveSurface(SKSurface surface, string filename = "output.png") { using var image = surface.Snapshot(); From 47ea33171138dbd2896173a537706874bb1a6d83 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Tue, 21 May 2024 01:37:11 +0800 Subject: [PATCH 7/8] this --- tests/Tests/SkiaSharp/SKDocumentTest.cs | 45 +++++++++++++++++++++++-- tests/Tests/SkiaSharp/SKTest.cs | 2 ++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/tests/Tests/SkiaSharp/SKDocumentTest.cs b/tests/Tests/SkiaSharp/SKDocumentTest.cs index e3b88d27ac..866683da39 100644 --- a/tests/Tests/SkiaSharp/SKDocumentTest.cs +++ b/tests/Tests/SkiaSharp/SKDocumentTest.cs @@ -236,6 +236,48 @@ static SKDocument CreateDocument(out IntPtr streamHandle) } } + [SkippableFact] + public void MetadataIsAddedToFile() + { + var now = DateTime.Now; + var metadata = new SKPdfMetadata + { + Title = "A1", + Author = "A2", + Subject = "A3", + Keywords = "A4", + Creator = "A5", + Creation = now, + Modified = now, + }; + + using var pdf = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(pdf, metadata)) + { + doc.BeginPage(612.0f, 792.0f); + doc.Close(); + } + + pdf.Position = 0; + using var reader = new StreamReader(pdf); + var data = reader.ReadToEnd(); + + var expectations = new string[] { + "/Title (A1)", + "/Author (A2)", + "/Subject (A3)", + "/Keywords (A4)", + "/Creator (A5)", + "/Producer (Skia/PDF ", + "/CreationDate (D:", + "/ModDate (D:" + }; + foreach (var expectation in expectations) + { + Assert.Contains(expectation, data); + } + } + [SkippableFact] public void CanCreateTaggedLinkPdf() { @@ -281,9 +323,6 @@ public void CanCreateTaggedLinkPdf() Assert.True(stream.Length > 0); Assert.True(stream.Position > 0); - - stream.Position = 0; - SaveStream(stream, "test.pdf"); } [SkippableFact] diff --git a/tests/Tests/SkiaSharp/SKTest.cs b/tests/Tests/SkiaSharp/SKTest.cs index 3a0fe6c04d..a5243212ef 100644 --- a/tests/Tests/SkiaSharp/SKTest.cs +++ b/tests/Tests/SkiaSharp/SKTest.cs @@ -55,6 +55,8 @@ protected static SKStreamAsset CreateTestSKStream(int length = 1024) protected static void SaveStream(Stream stream, string filename) { + stream.Position = 0; + using var file = File.Create(Path.Combine(PathToImages, filename)); stream.CopyTo(file); } From b117ee8b079cde6eca5ee4afb5d25385be8631a7 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 22 May 2024 03:16:02 +0800 Subject: [PATCH 8/8] more tests --- binding/SkiaSharp/SKBitmap.cs | 5 + tests/Tests/SkiaSharp/SKDocumentTest.cs | 289 +++++++++++++++++++++--- 2 files changed, 263 insertions(+), 31 deletions(-) diff --git a/binding/SkiaSharp/SKBitmap.cs b/binding/SkiaSharp/SKBitmap.cs index 4c993d9699..b1b1e6b42a 100644 --- a/binding/SkiaSharp/SKBitmap.cs +++ b/binding/SkiaSharp/SKBitmap.cs @@ -774,5 +774,10 @@ public SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy, SKSampling private SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy, SKSamplingOptions sampling, SKMatrix* localMatrix) => SKShader.GetObject (SkiaApi.sk_bitmap_make_shader (Handle, tmx, tmy, &sampling, localMatrix)); + + // ToImage + + public SKImage ToImage () => + SKImage.FromBitmap (this); } } diff --git a/tests/Tests/SkiaSharp/SKDocumentTest.cs b/tests/Tests/SkiaSharp/SKDocumentTest.cs index 866683da39..3b5f3425fd 100644 --- a/tests/Tests/SkiaSharp/SKDocumentTest.cs +++ b/tests/Tests/SkiaSharp/SKDocumentTest.cs @@ -88,27 +88,23 @@ public void CanCreatePdfWithObsoleteMetadata() var metadata = SKDocumentPdfMetadata.Default; metadata.Author = "SkiaSharp Team"; - using (var stream = new MemoryStream()) + using var stream = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(stream, metadata)) { - using (var doc = SKDocument.CreatePdf(stream, metadata)) - { - Assert.NotNull(doc); - Assert.NotNull(doc.BeginPage(100, 100)); + Assert.NotNull(doc); + Assert.NotNull(doc.BeginPage(100, 100)); - doc.EndPage(); - doc.Close(); - } + doc.EndPage(); + doc.Close(); + } - Assert.True(stream.Length > 0); - Assert.True(stream.Position > 0); + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); - stream.Position = 0; - using (var reader = new StreamReader(stream)) - { - var contents = reader.ReadToEnd(); - Assert.Contains("/Author (SkiaSharp Team)", contents); - } - } + stream.Position = 0; + using var reader = new StreamReader(stream); + var contents = reader.ReadToEnd(); + Assert.Contains("/Author (SkiaSharp Team)", contents); } [SkippableFact] @@ -251,17 +247,6 @@ public void MetadataIsAddedToFile() Modified = now, }; - using var pdf = new MemoryStream(); - using (var doc = SKDocument.CreatePdf(pdf, metadata)) - { - doc.BeginPage(612.0f, 792.0f); - doc.Close(); - } - - pdf.Position = 0; - using var reader = new StreamReader(pdf); - var data = reader.ReadToEnd(); - var expectations = new string[] { "/Title (A1)", "/Author (A2)", @@ -272,10 +257,73 @@ public void MetadataIsAddedToFile() "/CreationDate (D:", "/ModDate (D:" }; - foreach (var expectation in expectations) + + AssertPdfMetadata(metadata, expectations); + } + + [SkippableFact] + public void PdfAMetadataIsAddedToFile() + { + var metadata = new SKPdfMetadata { - Assert.Contains(expectation, data); - } + Title = "test document", + Creation = new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Utc), + PdfA = true, + Producer = "phoney library" + }; + + var expectations = new string[] { + "sRGB IEC61966-2.1", + "test document", + "1999-12-31T23:59:59+00:00", + "/Subtype /XML", + "/CreationDate (D:19991231235959+00'00')>>", + "/Producer (phoney library)", + "phoney library", + }; + + AssertPdfMetadata(metadata, expectations); + } + + [SkippableFact] + public void UnicodeMetadataIsAddedToFile() + { + var metadata = new SKPdfMetadata + { + PdfA = true, + Title = "𝓐𝓑𝓒𝓓𝓔 𝓕𝓖𝓗𝓘𝓙", // Out of basic multilingual plane + Author = "ABCDE FGHIJ", // ASCII + Subject = "αβγδε ζηθικ", // inside basic multilingual plane + }; + + var expectations = new string[] { + "<", + "/Author (ABCDE FGHIJ)", + "Subject ", + }; + + AssertPdfMetadata(metadata, expectations); + } + + [SkippableTheory] + [InlineData(1)] + [InlineData(2)] + [InlineData(100)] + public void MultiplePagesWorkCorrectly(int pageCount) + { + var pdfData = GeneratePdf(new(), doc => + { + for (var i = 0; i < pageCount; ++i) + { + var cnv = doc.BeginPage(612, 792); + var color = new SKColor(0x00, (byte)(255.0f * i / (pageCount - 1)), 0x00); + cnv.DrawColor(color); + } + }); + + var pagesString = $"< 0); Assert.True(stream.Position > 0); } + + [SkippableFact] + public void CanCreateTaggedPdf() + { + var pageSize = new SKSize(612, 792); // U.S. Letter + + var metadata = new SKPdfMetadata(); + metadata.Title = "Example Tagged PDF"; + metadata.Creator = "Skia"; + var now = DateTime.Now; + metadata.Creation = now; + metadata.Modified = now; + + // The document tag. + var root = new SKPdfStructureElementNode(1, "Document"); + + // Heading. + var h1 = new SKPdfStructureElementNode(2, "H1"); + root.Children.Add(h1); + + // Initial paragraph. + var p = new SKPdfStructureElementNode(3, "P"); + root.Children.Add(p); + + // Hidden div. This is never referenced by marked content + // so it should not appear in the resulting PDF. + var div = new SKPdfStructureElementNode(4, "Div"); + root.Children.Add(div); + + // A bulleted list of two items. + var l = new SKPdfStructureElementNode(5, "L"); + + var lm1 = new SKPdfStructureElementNode(6, "Lbl"); + l.Children.Add(lm1); + + var li1 = new SKPdfStructureElementNode(7, "LI"); + l.Children.Add(li1); + + var lm2 = new SKPdfStructureElementNode(8, "Lbl"); + l.Children.Add(lm2); + + var li2 = new SKPdfStructureElementNode(9, "LI"); + l.Children.Add(li2); + + root.Children.Add(l); + + // Paragraph spanning two pages. + var p2 = new SKPdfStructureElementNode(10, "P"); + root.Children.Add(p2); + + // Image with alt text. + var img = new SKPdfStructureElementNode(11, "Figure"); + img.Alt = "Red box"; + root.Children.Add(img); + + metadata.Structure = root; + + var outputStream = new MemoryStream(); + var document = SKDocument.CreatePdf(outputStream, metadata); + + var paint = new SKPaint(); + paint.Color = SKColors.Black; + + // First page. + { + var canvas = document.BeginPage(pageSize.Width, pageSize.Height); + + canvas.DrawPdfNodeAnnotation(2); + var font = new SKFont(null, 36); + var message = "This is the title"; + canvas.Translate(72, 72); + canvas.DrawText(message, 0, 0, font, paint); + + canvas.DrawPdfNodeAnnotation(3); + font.Size = 14; + message = "This is a simple paragraph."; + canvas.Translate(0, 72); + canvas.DrawText(message, 0, 0, font, paint); + + canvas.DrawPdfNodeAnnotation(6); + message = "*"; + canvas.Translate(0, 72); + canvas.DrawText(message, 0, 0, font, paint); + + canvas.DrawPdfNodeAnnotation(7); + message = "List item 1"; + canvas.Translate(36, 0); + canvas.DrawText(message, 0, 0, font, paint); + + canvas.DrawPdfNodeAnnotation(8); + message = "*"; + canvas.Translate(-36, 36); + canvas.DrawText(message, 0, 0, font, paint); + + canvas.DrawPdfNodeAnnotation(9); + message = "List item 2"; + canvas.Translate(36, 0); + canvas.DrawText(message, 0, 0, font, paint); + + canvas.DrawPdfNodeAnnotation(10); + message = "This is a paragraph that starts on one page"; + canvas.Translate(-36, 6 * 72); + canvas.DrawText(message, 0, 0, font, paint); + + document.EndPage(); + } + + // Second page. + { + var canvas = document.BeginPage(pageSize.Width, pageSize.Height); + + canvas.DrawPdfNodeAnnotation(10); + var font = new SKFont(null, 14); + var message = "and finishes on the second page."; + canvas.Translate(72, 72); + canvas.DrawText(message, 0, 0, font, paint); + + // Test a tagged image with alt text. + canvas.DrawPdfNodeAnnotation(11); + var testBitmap = new SKBitmap(new SKImageInfo(72, 72)); + testBitmap.Erase(SKColors.Red); + canvas.Translate(72, 72); + canvas.DrawImage(testBitmap.ToImage(), 0, 0); + + // This has a node ID but never shows up in the tag tree so it + // won't be tagged. + canvas.DrawPdfNodeAnnotation(999); + message = "Page 2"; + canvas.Translate(468, -36); + canvas.DrawText(message, 0, 0, font, paint); + + document.EndPage(); + } + + document.Close(); + } + + private static void AssertPdfMetadata(SKPdfMetadata metadata, params string[] expectations) + { + var pdfData = GeneratePdf(metadata); + + foreach (var expectation in expectations) + { + Assert.Contains(expectation, pdfData); + } + } + + private static string GeneratePdf(SKPdfMetadata metadata, Action generate = null) + { + using var stream = new MemoryStream(); + using (var doc = SKDocument.CreatePdf(stream, metadata)) + { + Assert.NotNull(doc); + + if (generate is not null) + { + generate.Invoke(doc); + } + else + { + var canvas = doc.BeginPage(612.0f, 792.0f); + Assert.NotNull(canvas); + + canvas.DrawColor(SKColors.Red); + + doc.Close(); + } + } + + Assert.True(stream.Length > 0); + Assert.True(stream.Position > 0); + + stream.Position = 0; + + using var reader = new StreamReader(stream); + var data = reader.ReadToEnd(); + + return data; + } } }