mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-19 10:47:56 +08:00
enable re-use of jpeg images between or within pages
returns a reference to the added image object when calling addjpeg so that it can be shared between or within pages meaning the image is only written to the output file once but can appear multiple times. this image doesn't seem to be displaying correctly in adobe reader.
This commit is contained in:
@@ -436,6 +436,64 @@
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWrite2PagesSharingJpeg()
|
||||
{
|
||||
var builder = new PdfDocumentBuilder();
|
||||
var page = builder.AddPage(PageSize.A4);
|
||||
|
||||
var font = builder.AddStandard14Font(Standard14Font.Helvetica);
|
||||
|
||||
page.AddText("Smile", 12, new PdfPoint(25, page.PageSize.Height - 52), font);
|
||||
|
||||
var img = IntegrationHelpers.GetDocumentPath("smile-250-by-160.jpg", false);
|
||||
|
||||
var expectedBounds1 = new PdfRectangle(25, page.PageSize.Height - 300, 200, page.PageSize.Height - 200);
|
||||
|
||||
var imageBytes = File.ReadAllBytes(img);
|
||||
|
||||
var expectedBounds2 = new PdfRectangle(25, 600, 75, 650);
|
||||
|
||||
var jpeg = page.AddJpeg(imageBytes, expectedBounds1);
|
||||
page.AddJpeg(jpeg, expectedBounds2);
|
||||
|
||||
var expectedBounds3 = new PdfRectangle(30, 500, 130, 550);
|
||||
|
||||
var page2 = builder.AddPage(PageSize.A4);
|
||||
page2.AddJpeg(jpeg, expectedBounds3);
|
||||
|
||||
var bytes = builder.Build();
|
||||
WriteFile(nameof(CanWrite2PagesSharingJpeg), bytes);
|
||||
|
||||
using (var document = PdfDocument.Open(bytes))
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
|
||||
Assert.Equal("Smile", page1.Text);
|
||||
|
||||
var page1Images = page1.GetImages().ToList();
|
||||
Assert.Equal(2, page1Images.Count);
|
||||
|
||||
var image1 = page1Images[0];
|
||||
Assert.Equal(expectedBounds1, image1.Bounds);
|
||||
|
||||
var image2 = page1Images[1];
|
||||
Assert.Equal(expectedBounds2, image2.Bounds);
|
||||
|
||||
var page2Doc = document.GetPage(2);
|
||||
|
||||
var image3 = Assert.Single(page2Doc.GetImages());
|
||||
|
||||
Assert.NotNull(image3);
|
||||
|
||||
Assert.Equal(expectedBounds3, image3.Bounds);
|
||||
|
||||
Assert.Equal(imageBytes, image1.RawBytes);
|
||||
Assert.Equal(imageBytes, image2.RawBytes);
|
||||
Assert.Equal(imageBytes, image3.RawBytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteFile(string name, byte[] bytes)
|
||||
{
|
||||
try
|
||||
|
@@ -261,18 +261,18 @@
|
||||
/// <summary>
|
||||
/// Adds the JPEG image represented by the input bytes at the specified location.
|
||||
/// </summary>
|
||||
public void AddJpeg(byte[] fileBytes, PdfRectangle placementRectangle)
|
||||
public AddedImage AddJpeg(byte[] fileBytes, PdfRectangle placementRectangle)
|
||||
{
|
||||
using (var stream = new MemoryStream(fileBytes))
|
||||
{
|
||||
AddJpeg(stream, placementRectangle);
|
||||
return AddJpeg(stream, placementRectangle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the JPEG image represented by the input stream at the specified location.
|
||||
/// </summary>
|
||||
public void AddJpeg(Stream fileStream, PdfRectangle placementRectangle)
|
||||
public AddedImage AddJpeg(Stream fileStream, PdfRectangle placementRectangle)
|
||||
{
|
||||
var startFrom = fileStream.Position;
|
||||
var info = JpegHandler.GetInformation(fileStream);
|
||||
@@ -299,7 +299,8 @@
|
||||
|
||||
var reference = documentBuilder.AddImage(new DictionaryToken(imgDictionary), data);
|
||||
|
||||
if (!resourcesDictionary.TryGetValue(NameToken.Xobject, out var xobjectsDict) || !(xobjectsDict is DictionaryToken xobjects))
|
||||
if (!resourcesDictionary.TryGetValue(NameToken.Xobject, out var xobjectsDict)
|
||||
|| !(xobjectsDict is DictionaryToken xobjects))
|
||||
{
|
||||
xobjects = new DictionaryToken(new Dictionary<NameToken, IToken>());
|
||||
resourcesDictionary[NameToken.Xobject] = xobjects;
|
||||
@@ -319,6 +320,39 @@
|
||||
}));
|
||||
operations.Add(new InvokeNamedXObject(key));
|
||||
operations.Add(Pop.Value);
|
||||
|
||||
return new AddedImage(reference, info.Width, info.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the JPEG image previously added using <see cref="AddJpeg(byte[],PdfRectangle)"/>,
|
||||
/// this will share the same image data to prevent duplication.
|
||||
/// </summary>
|
||||
/// <param name="image">An image previously added to this page or another page.</param>
|
||||
/// <param name="placementRectangle">The size and location to draw the image on this page.</param>
|
||||
public void AddJpeg(AddedImage image, PdfRectangle placementRectangle)
|
||||
{
|
||||
if (!resourcesDictionary.TryGetValue(NameToken.Xobject, out var xobjectsDict)
|
||||
|| !(xobjectsDict is DictionaryToken xobjects))
|
||||
{
|
||||
xobjects = new DictionaryToken(new Dictionary<NameToken, IToken>());
|
||||
resourcesDictionary[NameToken.Xobject] = xobjects;
|
||||
}
|
||||
|
||||
var key = NameToken.Create($"I{imageKey++}");
|
||||
|
||||
resourcesDictionary[NameToken.Xobject] = xobjects.With(key, new IndirectReferenceToken(image.Reference));
|
||||
|
||||
operations.Add(Push.Value);
|
||||
// This needs to be the placement rectangle.
|
||||
operations.Add(new ModifyCurrentTransformationMatrix(new[]
|
||||
{
|
||||
(decimal)placementRectangle.Width, 0,
|
||||
0, (decimal)placementRectangle.Height,
|
||||
(decimal)placementRectangle.BottomLeft.X, (decimal)placementRectangle.BottomLeft.Y
|
||||
}));
|
||||
operations.Add(new InvokeNamedXObject(key));
|
||||
operations.Add(Pop.Value);
|
||||
}
|
||||
|
||||
private List<Letter> DrawLetters(string text, IWritingFont font, TransformationMatrix fontMatrix, decimal fontSize, TransformationMatrix textMatrix)
|
||||
@@ -414,5 +448,43 @@
|
||||
Operations = operations;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A key representing an image available to use for the current document builder.
|
||||
/// Create it by adding an image to a page using <see cref="AddJpeg(byte[],PdfRectangle)"/>.
|
||||
/// </summary>
|
||||
public class AddedImage
|
||||
{
|
||||
/// <summary>
|
||||
/// The Id uniquely identifying this image on the builder.
|
||||
/// </summary>
|
||||
internal Guid Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The reference to the stored image XObject.
|
||||
/// </summary>
|
||||
internal IndirectReference Reference { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The width of the raw image in pixels.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The height of the raw image in pixels.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="AddedImage"/>.
|
||||
/// </summary>
|
||||
internal AddedImage(IndirectReference reference, int width, int height)
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
Reference = reference;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user