From c061603ad279ee863261aacc8027321a587a35c6 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sat, 13 Jan 2024 16:21:36 +0000 Subject: [PATCH] #710 set colorspace based on jpeg info --- src/UglyToad.PdfPig/Images/JpegHandler.cs | 4 +- src/UglyToad.PdfPig/Images/JpegInformation.cs | 8 +- src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs | 83 ++++++++++++------- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/UglyToad.PdfPig/Images/JpegHandler.cs b/src/UglyToad.PdfPig/Images/JpegHandler.cs index 74d7dd11..77b83333 100644 --- a/src/UglyToad.PdfPig/Images/JpegHandler.cs +++ b/src/UglyToad.PdfPig/Images/JpegHandler.cs @@ -29,7 +29,6 @@ switch (marker) { case JpegMarker.StartOfImage: - case JpegMarker.EndOfImage: case JpegMarker.Restart0: case JpegMarker.Restart1: case JpegMarker.Restart2: @@ -49,8 +48,9 @@ var bpp = stream.ReadByte(); var height = ReadShort(stream, shortBuffer); var width = ReadShort(stream, shortBuffer); + var numberOfComponents = stream.ReadByte(); - return new JpegInformation(width, height, bpp); + return new JpegInformation(width, height, bpp, numberOfComponents); } case JpegMarker.ApplicationSpecific0: case JpegMarker.ApplicationSpecific1: diff --git a/src/UglyToad.PdfPig/Images/JpegInformation.cs b/src/UglyToad.PdfPig/Images/JpegInformation.cs index 9c9496bf..0c560f93 100644 --- a/src/UglyToad.PdfPig/Images/JpegInformation.cs +++ b/src/UglyToad.PdfPig/Images/JpegInformation.cs @@ -20,14 +20,20 @@ /// public int BitsPerComponent { get; } + /// + /// 1 grayscale, 3 RGB, 4 CMYK. + /// + public int NumberOfComponents { get; } + /// /// Create a new . /// - public JpegInformation(int width, int height, int bitsPerComponent) + public JpegInformation(int width, int height, int bitsPerComponent, int numberOfComponents) { Width = width; Height = height; BitsPerComponent = bitsPerComponent; + NumberOfComponents = numberOfComponents; } } } \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs b/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs index 112fb096..e6f283c9 100644 --- a/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs +++ b/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs @@ -21,13 +21,13 @@ using Tokens; using Graphics.Operations.PathPainting; using Images.Png; - using UglyToad.PdfPig.Actions; + using Actions; internal class NameConflictSolver { - private string prefix; + private readonly string prefix; private int key = 0; - private HashSet xobjectNamesUsed = new HashSet(); + private readonly HashSet xobjectNamesUsed = new HashSet(); public NameConflictSolver(string prefix) { @@ -44,8 +44,8 @@ i++; } - - return i != 0 ? name.Substring(0,i) : prefix; + + return i != 0 ? name.Substring(0, i) : prefix; } public string NewName(string orginalName = null) @@ -53,11 +53,14 @@ var newPrefix = ExtractPrefix(orginalName); var name = $"{newPrefix}{key}"; + while (xobjectNamesUsed.Contains(name)) { name = $"{newPrefix}{++key}"; } + xobjectNamesUsed.Add(name); + return name; } @@ -67,11 +70,9 @@ { return NewName(name); } - else - { - xobjectNamesUsed.Add(name); - return name; - } + + xobjectNamesUsed.Add(name); + return name; } } @@ -86,14 +87,14 @@ // all page data other than content streams internal readonly Dictionary pageDictionary = new Dictionary(); - + // streams internal readonly List contentStreams; private IPageContentStream currentStream; // links to be resolved when all page references are available internal readonly List<(DictionaryToken token, PdfAction action)> links; - + // maps fonts added using PdfDocumentBuilder to page font names private readonly Dictionary documentFonts = new Dictionary(); internal int nextFontId = 1; @@ -135,7 +136,7 @@ PageNumber = number; currentStream = new DefaultContentStream(); - contentStreams = new List() {currentStream}; + contentStreams = new List() { currentStream }; } internal PdfPageBuilder(int number, PdfDocumentBuilder documentBuilder, IEnumerable copied, @@ -553,7 +554,7 @@ { value = NameToken.Create($"F{nextFontId++}"); var resources = pageDictionary.GetOrCreateDict(NameToken.Resources); - var fonts = resources.GetOrCreateDict(NameToken.Font); + var fonts = resources.GetOrCreateDict(NameToken.Font); while (fonts.ContainsKey(value)) { value = NameToken.Create($"F{nextFontId++}"); @@ -576,7 +577,7 @@ return AddJpeg(stream, placementRectangle); } } - + /// /// Adds the JPEG image represented by the input stream at the specified location. @@ -587,7 +588,13 @@ var info = JpegHandler.GetInformation(fileStream); if (placementRectangle.Equals(default(PdfRectangle))) - placementRectangle = new PdfRectangle(0, 0, info.Width, info.Height); + { + placementRectangle = new PdfRectangle( + 0, + 0, + info.Width, + info.Height); + } byte[] data; using (var memory = new MemoryStream()) @@ -597,6 +604,20 @@ data = memory.ToArray(); } + NameToken colorSpace; + if (info.NumberOfComponents == 1) + { + colorSpace = NameToken.Devicegray; + } + else if (info.NumberOfComponents == 4) + { + colorSpace = NameToken.Devicecmyk; + } + else + { + colorSpace = NameToken.Devicergb; + } + var imgDictionary = new Dictionary { {NameToken.Type, NameToken.Xobject }, @@ -604,7 +625,7 @@ {NameToken.Width, new NumericToken(info.Width) }, {NameToken.Height, new NumericToken(info.Height) }, {NameToken.BitsPerComponent, new NumericToken(info.BitsPerComponent)}, - {NameToken.ColorSpace, NameToken.Devicergb}, + {NameToken.ColorSpace, colorSpace}, {NameToken.Filter, NameToken.DctDecode}, {NameToken.Length, new NumericToken(data.Length)} }; @@ -613,12 +634,12 @@ var resources = pageDictionary.GetOrCreateDict(NameToken.Resources); var xObjects = resources.GetOrCreateDict(NameToken.Xobject); - var key = NameToken.Create( xobjectsNames.NewName()); + var key = NameToken.Create(xobjectsNames.NewName()); xObjects[key] = reference; currentStream.Add(Push.Value); // This needs to be the placement rectangle. - currentStream.Add(new ModifyCurrentTransformationMatrix(new [] + currentStream.Add(new ModifyCurrentTransformationMatrix(new[] { (decimal)placementRectangle.Width, 0, 0, (decimal)placementRectangle.Height, @@ -760,7 +781,7 @@ { imgDictionary.Add(NameToken.Smask, smaskReference); } - + var reference = documentBuilder.AddImage(new DictionaryToken(imgDictionary), compressed); var resources = pageDictionary.GetOrCreateDict(NameToken.Resources); @@ -839,7 +860,7 @@ // Special cases // Since we don't directly add font's to the pages resources, we have to go look at the document's font - if(srcResourceDictionary.TryGet(NameToken.Font, srcPage.pdfScanner, out DictionaryToken fontsDictionary)) + if (srcResourceDictionary.TryGet(NameToken.Font, srcPage.pdfScanner, out DictionaryToken fontsDictionary)) { var pageFontsDictionary = resources.GetOrCreateDict(NameToken.Font); @@ -1006,12 +1027,12 @@ var documentSpace = textMatrix.Transform(renderingMatrix.Transform(fontMatrix.Transform(rect))); var letter = new Letter( - c.ToString(), - documentSpace, - advanceRect.BottomLeft, - advanceRect.BottomRight, - width, - (double)fontSize, + c.ToString(), + documentSpace, + advanceRect.BottomLeft, + advanceRect.BottomRight, + width, + (double)fontSize, FontDetails.GetDefault(name), TextRenderingMode.Fill, GrayColor.Black, @@ -1083,7 +1104,7 @@ public DefaultContentStream() : this(new List()) { - + } public DefaultContentStream(List operations) { @@ -1124,7 +1145,7 @@ private readonly IndirectReferenceToken token; public bool ReadOnly => true; public bool HasContent => true; - + public CopiedContentStream(IndirectReferenceToken indirectReferenceToken) { token = indirectReferenceToken; @@ -1140,7 +1161,7 @@ throw new NotSupportedException("Writing to a copied content stream is not supported."); } - public List Operations => + public List Operations => throw new NotSupportedException("Reading raw operations is not supported from a copied content stream."); } @@ -1183,6 +1204,6 @@ } } - + } }