From 63b94c57bb6b6fdb161929da1cb226d234170ab8 Mon Sep 17 00:00:00 2001 From: PeeKay Date: Wed, 18 Feb 2026 09:26:25 +0100 Subject: [PATCH 1/6] Added PrinterUUID to PrinterDescriptionAttributes Added defines for more printer attributes (compression-default, media-col-supported, media-col-ready, printer-uuid, pdf-versions-supported, ipp-features-supported, media-type-supported) --- .../Mapping/Profiles/GetCUPSPrintersProfile.cs | 3 +++ SharpIpp/Protocol/Models/PrinterAttribute.cs | 14 ++++++++++++++ .../Models/PrinterDescriptionAttributes.cs | 2 ++ SharpIppTests/SharpIppIntegrationTests.cs | 6 ++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/SharpIpp/Mapping/Profiles/GetCUPSPrintersProfile.cs b/SharpIpp/Mapping/Profiles/GetCUPSPrintersProfile.cs index 01c7256..fbc55b9 100644 --- a/SharpIpp/Mapping/Profiles/GetCUPSPrintersProfile.cs +++ b/SharpIpp/Mapping/Profiles/GetCUPSPrintersProfile.cs @@ -130,6 +130,7 @@ public void CreateMaps(IMapperConstructor mapper) PrintColorModeDefault = map.MapFromDic(src, PrinterAttribute.PrintColorModeDefault), PrintColorModeSupported = map.MapFromDicSetNull(src, PrinterAttribute.PrintColorModeSupported), WhichJobsSupported = map.MapFromDicSetNull(src, PrinterAttribute.WhichJobsSupported), + PrinterUUID = map.MapFromDic(src, PrinterAttribute.PrinterUUID), }); mapper.CreateMap>( ( src, map ) => @@ -267,6 +268,8 @@ public void CreateMaps(IMapperConstructor mapper) dic.Add(PrinterAttribute.PrintColorModeSupported, src.PrintColorModeSupported.Select(x => new IppAttribute(Tag.Keyword, PrinterAttribute.PrintColorModeSupported, map.Map(x))).ToArray()); if (src.WhichJobsSupported?.Any() ?? false) dic.Add(PrinterAttribute.WhichJobsSupported, src.WhichJobsSupported.Select(x => new IppAttribute(Tag.Keyword, PrinterAttribute.WhichJobsSupported, map.Map(x))).ToArray()); + if (src.PrinterUUID?.Any() ?? false) + dic.Add(PrinterAttribute.PrinterUUID, new IppAttribute[] { new IppAttribute(Tag.Keyword, PrinterAttribute.PrinterUUID, src.PrinterUUID) }); return dic; } ); } diff --git a/SharpIpp/Protocol/Models/PrinterAttribute.cs b/SharpIpp/Protocol/Models/PrinterAttribute.cs index e27f92e..a537231 100644 --- a/SharpIpp/Protocol/Models/PrinterAttribute.cs +++ b/SharpIpp/Protocol/Models/PrinterAttribute.cs @@ -35,6 +35,7 @@ public static class PrinterAttribute public const string PrinterUpTime = "printer-up-time"; public const string PrinterCurrentTime = "printer-current-time"; public const string MultipleOperationTimeOut = "multiple-operation-time-out"; + public const string CompressionDefault = "compression-default"; public const string CompressionSupported = "compression-supported"; public const string JobKOctetsSupported = "job-k-octets-supported"; public const string JpegKOctetsSupported = "jpeg-k-octets-supported"; @@ -67,9 +68,15 @@ public static class PrinterAttribute public const string OutputBinDefault = "output-bin-default"; public const string OutputBinSupported = "output-bin-supported"; public const string MediaColDefault = "media-col-default"; + public const string MediaColSupported = "media-col-supported"; + public const string MediaColReady = "media-col-ready"; public const string PrintColorModeDefault = "print-color-mode-default"; public const string PrintColorModeSupported = "print-color-mode-supported"; public const string WhichJobsSupported = "which-jobs-supported"; + public const string PrinterUUID = "printer-uuid"; + public const string PdfVersionsSupported = "pdf-versions-supported"; + public const string IppFeaturesSupported = "ipp-features-supported"; + public const string MediaTypeSupported = "media-type-supported"; public static IEnumerable GetAttributes(IppVersion version) { @@ -104,6 +111,7 @@ public static IEnumerable GetAttributes(IppVersion version) yield return PrinterUpTime; yield return PrinterCurrentTime; yield return MultipleOperationTimeOut; + yield return CompressionDefault; yield return CompressionSupported; yield return JobKOctetsSupported; yield return JpegKOctetsSupported; @@ -136,9 +144,15 @@ public static IEnumerable GetAttributes(IppVersion version) yield return OutputBinDefault; yield return OutputBinSupported; yield return MediaColDefault; + yield return MediaColSupported; + yield return MediaColReady; yield return PrintColorModeDefault; yield return PrintColorModeSupported; yield return WhichJobsSupported; + yield return PrinterUUID; + yield return PdfVersionsSupported; + yield return IppFeaturesSupported; + yield return MediaTypeSupported; } } } diff --git a/SharpIpp/Protocol/Models/PrinterDescriptionAttributes.cs b/SharpIpp/Protocol/Models/PrinterDescriptionAttributes.cs index 43b3c37..861c5d5 100644 --- a/SharpIpp/Protocol/Models/PrinterDescriptionAttributes.cs +++ b/SharpIpp/Protocol/Models/PrinterDescriptionAttributes.cs @@ -248,5 +248,7 @@ public class PrinterDescriptionAttributes public PrintColorMode[]? PrintColorModeSupported { get; set; } public WhichJobs[]? WhichJobsSupported { get; set; } + + public string? PrinterUUID { get; set; } } } diff --git a/SharpIppTests/SharpIppIntegrationTests.cs b/SharpIppTests/SharpIppIntegrationTests.cs index 62ae9a4..4775a39 100644 --- a/SharpIppTests/SharpIppIntegrationTests.cs +++ b/SharpIppTests/SharpIppIntegrationTests.cs @@ -799,7 +799,8 @@ async Task func(Stream s, CancellationToken c) }, PrintColorModeDefault = PrintColorMode.Color, PrintColorModeSupported = [PrintColorMode.Color], - WhichJobsSupported = [WhichJobs.Completed] + WhichJobsSupported = [WhichJobs.Completed], + PrinterUUID = "{6541A875-C511-4273-909F-18CFBB38D9D0}" }, OperationAttributes = new() { @@ -1452,7 +1453,8 @@ async Task func(Stream s, CancellationToken c) }, PrintColorModeDefault = PrintColorMode.Color, PrintColorModeSupported = [PrintColorMode.Color], - WhichJobsSupported = [WhichJobs.Completed] + WhichJobsSupported = [WhichJobs.Completed], + PrinterUUID = "{6541A875-C511-4273-909F-18CFBB38D9D0}" } ] }; From 8b063a585a0ba8d5b7415d1808062ee86ba3d15e Mon Sep 17 00:00:00 2001 From: PeeKay Date: Wed, 18 Feb 2026 09:31:28 +0100 Subject: [PATCH 2/6] Fix calculation of length of type StringWithLanguage --- SharpIpp/Protocol/IppProtocol.Attributes.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SharpIpp/Protocol/IppProtocol.Attributes.cs b/SharpIpp/Protocol/IppProtocol.Attributes.cs index 9485ab7..8755c92 100644 --- a/SharpIpp/Protocol/IppProtocol.Attributes.cs +++ b/SharpIpp/Protocol/IppProtocol.Attributes.cs @@ -187,7 +187,8 @@ private static string ReadString(BinaryReader stream, Encoding? encoding = null) private static void Write(StringWithLanguage value, BinaryWriter stream, Encoding? encoding) { - stream.WriteBigEndian((short)(value.Language.Length + value.Value.Length)); + short len = (short)(2 + value.Language.Length + 2 + (encoding ?? Encoding.ASCII).GetBytes(value.Value).Length); + stream.WriteBigEndian(len); Write(value.Language, stream, null); Write(value.Value, stream, encoding); } From 35417b4b527805c4fa8a490421a69b362b0a1291 Mon Sep 17 00:00:00 2001 From: PeeKay Date: Wed, 18 Feb 2026 09:51:27 +0100 Subject: [PATCH 3/6] Fix test case of Write_StringWithLanguage_ShouldBeWritten to match the fixed behavior --- SharpIppTests/Protocol/IppProtocolTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharpIppTests/Protocol/IppProtocolTests.cs b/SharpIppTests/Protocol/IppProtocolTests.cs index aee3fde..7d177b8 100644 --- a/SharpIppTests/Protocol/IppProtocolTests.cs +++ b/SharpIppTests/Protocol/IppProtocolTests.cs @@ -108,7 +108,7 @@ public void WriteValue_Resolution_ShouldBeWritten( int width, int height, Resolu } [DataTestMethod] - [DataRow( "en-us", "Lorem", new byte[] { 0x00, 0x0A, 0x00, 0x05, 0x65, 0x6E, 0x2D, 0x75, 0x73, 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } )] + [DataRow( "en-us", "Lorem", new byte[] { 0x00, 0x0E, 0x00, 0x05, 0x65, 0x6E, 0x2D, 0x75, 0x73, 0x00, 0x05, 0x4C, 0x6F, 0x72, 0x65, 0x6D } )] public void Write_StringWithLanguage_ShouldBeWritten( string language, string text, byte[] expected ) { // Arrange From f5beef8de34918a9236c50eb2245d705fa4a2a4c Mon Sep 17 00:00:00 2001 From: PeeKay Date: Wed, 18 Feb 2026 09:52:41 +0100 Subject: [PATCH 4/6] Added new property 'ReadDocumentStream' to IppProtocol class, to be able to control if the library should read the incoming document into the memory or not. --- SharpIpp/Protocol/IppProtocol.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/SharpIpp/Protocol/IppProtocol.cs b/SharpIpp/Protocol/IppProtocol.cs index cac2a6e..3ab7c88 100644 --- a/SharpIpp/Protocol/IppProtocol.cs +++ b/SharpIpp/Protocol/IppProtocol.cs @@ -28,6 +28,16 @@ namespace SharpIpp.Protocol /// public partial class IppProtocol : IIppProtocol { + /// + /// Controls the behavior of ReadIppRequestAsync() method. + /// If true, the whole incoming document is read into a memory stream, + /// and can be accessed via message.Document. + /// If false, the document is not read into a memory stream, and it should + /// be consumed from the input stream by the caller. + /// Defaults to true. + /// + public bool ReadDocumentStream { get; set; } = true; + public async Task WriteIppRequestAsync(IIppRequestMessage ippRequestMessage, Stream stream, CancellationToken cancellationToken = default) { if (ippRequestMessage is null) @@ -449,8 +459,11 @@ private static string GetNormalizedName(Tag tag, string name, IppAttribute? prev }; ReadSections( reader, message ); message.Document = new MemoryStream(); - await reader.BaseStream.CopyToAsync( message.Document ); - message.Document.Seek( 0, SeekOrigin.Begin ); + if (ReadDocumentStream) + { + await reader.BaseStream.CopyToAsync(message.Document); + message.Document.Seek(0, SeekOrigin.Begin); + } return message; } From e08e65f2cf9aa1a0e3161588d28e3dbc5871a565 Mon Sep 17 00:00:00 2001 From: PeeKay Date: Wed, 18 Feb 2026 11:24:30 +0100 Subject: [PATCH 5/6] Calculate length of language bytes in a more precise way. --- SharpIpp/Protocol/IppProtocol.Attributes.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SharpIpp/Protocol/IppProtocol.Attributes.cs b/SharpIpp/Protocol/IppProtocol.Attributes.cs index 8755c92..5878042 100644 --- a/SharpIpp/Protocol/IppProtocol.Attributes.cs +++ b/SharpIpp/Protocol/IppProtocol.Attributes.cs @@ -187,8 +187,9 @@ private static string ReadString(BinaryReader stream, Encoding? encoding = null) private static void Write(StringWithLanguage value, BinaryWriter stream, Encoding? encoding) { - short len = (short)(2 + value.Language.Length + 2 + (encoding ?? Encoding.ASCII).GetBytes(value.Value).Length); - stream.WriteBigEndian(len); + var languageBytes = Encoding.ASCII.GetBytes(value.Language); + var valueBytes = (encoding ?? Encoding.ASCII).GetBytes(value.Value); + stream.WriteBigEndian((short)(languageBytes.Length + valueBytes.Length + 4)); Write(value.Language, stream, null); Write(value.Value, stream, encoding); } From 22cc33e891c373081b81f3c091b2b9ef30251a23 Mon Sep 17 00:00:00 2001 From: PeeKay Date: Wed, 18 Feb 2026 11:28:20 +0100 Subject: [PATCH 6/6] If the document stream should not be read by IppProtocol class, use Null stream instead of a newly created (but later unused) MemoryStream instance. --- SharpIpp/Protocol/IppProtocol.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SharpIpp/Protocol/IppProtocol.cs b/SharpIpp/Protocol/IppProtocol.cs index 3ab7c88..4655c69 100644 --- a/SharpIpp/Protocol/IppProtocol.cs +++ b/SharpIpp/Protocol/IppProtocol.cs @@ -458,12 +458,16 @@ private static string GetNormalizedName(Tag tag, string name, IppAttribute? prev RequestId = reader.ReadInt32BigEndian() }; ReadSections( reader, message ); - message.Document = new MemoryStream(); if (ReadDocumentStream) { + message.Document = new MemoryStream(); await reader.BaseStream.CopyToAsync(message.Document); message.Document.Seek(0, SeekOrigin.Begin); } + else + { + message.Document = Stream.Null; + } return message; }