logo
Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

New Topic Post Reply
Options
Go to last post Go to first unread
TravisNorwood  
#1 Posted : Tuesday, August 29, 2017 1:16:44 PM(UTC)
Quote
TravisNorwood

Rank: Newbie

Groups: Registered
Joined: 7/14/2017(UTC)
Posts: 3
United States
Location: Montgomery, AL

Thanks: 3 times
I use the following code (that I got from this forum) to add an image to a PDF. That works well.
Now we have a situation where the user needs to remove the image from the PDF. How do I remove an image from a PDF?

Code:

        public static void AddImageToPage(PdfPage page, System.Drawing.Bitmap bmp, Point atPoint)
        {
            using(PdfImageObject signaturePDFImage = InsertImageToPage(page, bmp, atPoint))
            {
                InsertIntoDictionary(page, signaturePDFImage);
            }
        }

        private static PdfImageObject InsertImageToPage(PdfPage page, System.Drawing.Bitmap bmp, Point atPoint)
        {
            var bi = bmp.LockBits(
                new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            //Create PdfBitmap object from .Net bitmap
            var bitmap = new PdfBitmap(
                bmp.Width,
                bmp.Height,
                BitmapFormats.FXDIB_Argb,
                bi.Scan0,
                bi.Stride);

            //Create pdf image object and then set PdfBitmap object into it.
            var image = PdfImageObject.Create(page.Document);
            image.SetBitmap(bitmap);

            //Scale image object to it's actual width and heihgt
            image.SetMatrix(bmp.Width, 0, 0, bmp.Height, (float)atPoint.X, (float)atPoint.Y);

            page.PageObjects.InsertObject(image);
            return image;
        }

        private static void InsertIntoDictionary(PdfPage page, PdfImageObject image)
        {
            //Get page dictionary, list of indirect objects and original page content
            var pageDict = page.Dictionary;
            var list = PdfIndirectList.FromPdfDocument(page.Document);

            //Convert contents to array. 
            PdfTypeArray array = ConvertContentsToArray(pageDict["Contents"], list, pageDict);

            //Get stream of image.
            IntPtr streamHandle = Pdfium.FPDFImageObj_GenerateStream(image.Handle, page.Handle);
            var stream = PdfTypeStream.Create(streamHandle);

            //Add image's stream into list of indirect objects and then add it to array.
            int num = list.Add(stream);
            array.AddIndirect(list, num);
        }

        public static PdfTypeArray ConvertContentsToArray(PdfTypeBase contents, PdfIndirectList list, PdfTypeDictionary pageDict)
        {
            //check the original content whether it's an array
            if(contents is PdfTypeArray)
                return contents as PdfTypeArray;  //if contents is a array just return it
            else if(contents is PdfTypeIndirect)
            {
                if((contents as PdfTypeIndirect).Direct is PdfTypeArray)
                    return (contents as PdfTypeIndirect).Direct as PdfTypeArray; //if contents is a reference to array then return that array
                else if((contents as PdfTypeIndirect).Direct is PdfTypeStream)
                {
                    //if contents is a reference to a stream then create a new array and insert stream as a first element of array
                    var array = PdfTypeArray.Create();
                    array.AddIndirect(list, (contents as PdfTypeIndirect).Direct);
                    //Add array into list of indirect objects
                    list.Add(array);
                    //And set it as a contents of the page
                    pageDict.SetIndirectAt("Contents", list, array);
                    return array;
                }
                else
                    throw new Exception("Unexpected content type");
            }
            else
                throw new Exception("Unexpected content type");
        }
Paul Rayman  
#2 Posted : Saturday, September 2, 2017 9:42:07 PM(UTC)
Quote
Paul Rayman

Rank: Administration

Groups: Administrators
Joined: 1/5/2016(UTC)
Posts: 1,138

Thanks: 10 times
Was thanked: 133 time(s) in 130 post(s)
Currently we are working on an easy way to save the changes of the PdfPageObject through GenerateContent method.
But I can't give any forecast when exactly it will be released.

Currently you can try to use the following technique.

Please note, the code below assumes that the page's content is a PdfTypeStream.
But it also can be an array of such contents (PdfTypeArray), so you need to make some corrections in your production code.

However, that part of code is optional.

Note: PdfTypeStream.DecodedText is available since version 3.10.1.2704

Code:

static void Main(string[] args)
{
	using (var doc = PdfDocument.Load(@"d:\1\test.pdf"))
	{
		var page = doc.Pages[0];

		//All images are contains in the page resource dictionary, under XObject key.
		//So, get page's resource dictionary.
		var res = As<PdfTypeDictionary>(page.Dictionary["Resources"]);
		//Get XObject dictionary
		var xObjects = As<PdfTypeDictionary>(res["XObject"]);

		//Find first image in the xObject dictionary
		string nameForRemove = null;
		foreach(var xObject in xObjects)
		{
			var stream = As<PdfTypeStream>(xObject.Value);
			var streamType = As<PdfTypeName>(stream.Dictionary["Subtype"]).Value;
			if(streamType=="Image")
			{
				nameForRemove = xObject.Key;
				break;
			}
		}
		// And remove it.
		xObjects.Remove(nameForRemove);

		///////////// Optional part ////////////////////
		//Remove the reference to the image from the page's content stream.

		//Get content stream
		var contentStream = As<PdfTypeStream>(page.Dictionary["Contents"]);
		//Decode it to the ascii text
		string content = contentStream.DecodedText;
		//Remove reference from that string
		while (RemoveFromContentsStream(ref content, "/" + nameForRemove)) ;
		//Write changes back to the stream.
		//Please note the stream is not compressed now, so we remove all filters and filter params.
		contentStream.Init(System.Text.Encoding.ASCII.GetBytes(content));
		if(contentStream.Dictionary.ContainsKey("Filter"))
			contentStream.Dictionary.Remove("Filter");
		if (contentStream.Dictionary.ContainsKey("DecodeParms"))
			contentStream.Dictionary.Remove("DecodeParms");
		////////////////////////////////////////////////

		//Save changes to the document
		doc.Save(@"d:\1\test2.pdf", Patagames.Pdf.Enums.SaveFlags.NoIncremental);
	}
}

public static T As<T>(PdfTypeBase pdfType) where T : PdfTypeBase
{
	if (typeof(T) == pdfType.GetType())
		return (T)pdfType;
	else if (pdfType is PdfTypeIndirect)
	{
		PdfTypeBase obj = (pdfType as PdfTypeIndirect).Direct;
		return As<T>(obj);
	}
	return null;
}

private static bool RemoveFromContentsStream(ref string content, string nameForRemove)
{
	int startIdx = content.IndexOf(nameForRemove);
	if (startIdx < 0)
		return false;

	int endIdx = content.IndexOf("Do", startIdx);
	if (endIdx < 0)
		return false;
	endIdx += 2;

	content = content.Remove(startIdx, endIdx - startIdx);
	return true;
}

Edited by user Saturday, September 2, 2017 10:07:47 PM(UTC)  | Reason: Not specified

thanks 1 user thanked Paul Rayman for this useful post.
TravisNorwood on 9/6/2017(UTC)
TravisNorwood  
#3 Posted : Wednesday, September 6, 2017 8:06:29 AM(UTC)
Quote
TravisNorwood

Rank: Newbie

Groups: Registered
Joined: 7/14/2017(UTC)
Posts: 3
United States
Location: Montgomery, AL

Thanks: 3 times
That worked great. How do you handle the case where the Contents is PdfTypeArray?
Paul Rayman  
#4 Posted : Wednesday, September 6, 2017 8:19:10 AM(UTC)
Quote
Paul Rayman

Rank: Administration

Groups: Administrators
Joined: 1/5/2016(UTC)
Posts: 1,138

Thanks: 10 times
Was thanked: 133 time(s) in 130 post(s)
Just process each item of that array as a content stream.
Something like following
Code:

PdfTypeStream  contentStream = As<PdfTypeStream>(page.Dictionary["Contents"]);
if(contentStream == null)
{
    var array  = As<PdfTypeArray>(page.Dictionary["Contents"]);
    foreach(var item in  array)
    {
        contentStream = As<PdfTypeStream>(item);
    }
}

Edited by user Wednesday, September 6, 2017 8:20:25 AM(UTC)  | Reason: Not specified

thanks 1 user thanked Paul Rayman for this useful post.
TravisNorwood on 9/6/2017(UTC)
TravisNorwood  
#5 Posted : Wednesday, September 6, 2017 8:32:08 AM(UTC)
Quote
TravisNorwood

Rank: Newbie

Groups: Registered
Joined: 7/14/2017(UTC)
Posts: 3
United States
Location: Montgomery, AL

Thanks: 3 times
Perfect. (BTW, the support for this product is a excellent).
We want to insert images (which we have to code to do) and then remove images (which you just gave us the code for).
We want to somehow mark the images we insert so we can remove only those images. I'm investigating how to do this. Any suggestions would be appreciated.
Paul Rayman  
#6 Posted : Wednesday, September 6, 2017 9:18:35 AM(UTC)
Quote
Paul Rayman

Rank: Administration

Groups: Administrators
Joined: 1/5/2016(UTC)
Posts: 1,138

Thanks: 10 times
Was thanked: 133 time(s) in 130 post(s)
Thanks!

You can parse the decoded string of generated image stream and find the name of the image with which it was stored inside page's resource dictionary.
Image stream will be contains something like /FX08 Do where the FX08 it is a resource name.
Next you may store that name inside any pdf custom metadata or try to change it to more markable.
thanks 1 user thanked Paul Rayman for this useful post.
TravisNorwood on 9/6/2017(UTC)
Quick Reply Show Quick Reply
Users browsing this topic
Guest
New Topic Post Reply
Forum Jump  
You can post new topics in this forum.
You can reply to topics in this forum.
You can delete your posts in this forum.
You can edit your posts in this forum.
You cannot create polls in this forum.
You can vote in polls in this forum.