(Core API / C++) Create grayscale copy of entire document

A forum for questions or concerns related to the PDF-XChange Core API SDK

Moderators: TrackerSupp-Daniel, Tracker Support, Vasyl-Tracker Dev Team, Chris - Tracker Supp, Sean - Tracker, Tracker Supp-Stefan

Forum rules
DO NOT post your license/serial key, or your activation code - these forums, and all posts within, are public and we will be forced to immediately deactivate your license.

When experiencing some errors, use the IAUX_Inst::FormatHRESULT method to see their description and include it in your post along with the error code.
Post Reply
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

(Core API / C++) Create grayscale copy of entire document

Post by plexxis_paul »

Hello, we need to create a grayscale copy any document. The documents we are using contain vector data (lines, areas), images, and text. Rasterizing the pages is not an option. The vector data must remain vector data.

Using C++ and the Core API. So far, I am successfully able to open a document from file, insert the pages from the loaded document into a new document (in memory), and write out a new file.

What I seem unable to accomplish is changing the color of anything that is output. My strategy is this:

- From the new document, Iterate through all the pages.
- for each page, get the content, and from that get the items
- for each item get the color state

What I want is to be able to set the stroke color and fill color of the color state, for each item that has a color state.
Is this possible? Am I on the right track? Is there a simpler way to do what I'm trying to do? Can the Core API accomplish this? Here's what I have so far...

Code: Select all

// The following are members of the class...
// CComPtr<PXC::IPXC_Document> m_pDocument;
// CComPtr<PXC::IPXC_Pages>	m_pPages;

int PDFLoader_pdfxCoreSDK::SaveDocumentAsGrayscale(const std::string strPathToOutputFile)
{
	CComPtr<PXC::IPXC_Document> pNewDoc = 0;
	if ((g_Inst->NewDocument(&pNewDoc) != S_OK) || (pNewDoc == 0))
	{
		return (-4); // we'll assume "out of memory".
	}

	CComPtr<PXC::IPXC_Pages> pPages = 0;
	if ((pNewDoc->get_Pages(&pPages) != S_OK) || (pPages == 0))
	{
		return (-4);
	}


	PXC::ULONG_T numPages = 0;
	m_pPages->get_Count(&numPages);
	PXC::ULONG_T flags = 0; // PXC::PXC_InsertPagesFlags::IPF_Annots_Copy | PXC::PXC_InsertPagesFlags::IPF_Bookmarks_CopyAll;

	HRESULT hRes = pPages->InsertPagesFromDoc(m_pDocument, 0, 0, numPages, flags, 0);
	if (hRes != S_OK)
	{
		return (-4);
	}

	unsigned int uItemCounter = 0;

	for (unsigned int uPage = 0; uPage < (unsigned int)numPages; ++uPage)
	{
		CComPtr<PXC::IPXC_Page> pPage = 0;
		pPages->get_Item((PXC::ULONG_T)uPage, &pPage);

		CComPtr<PXC::IPXC_Content> pContent = 0;
		if (pPage->GetContent(PXC::PXC_ContentAccessMode::CAccessMode_FullClone, &pContent) == S_OK)
		{
			CComPtr<PXC::IPXC_ContentItems> pItems = 0;
			if (pContent->get_Items(&pItems) == S_OK)
			{
				PXC::ULONG_T numItems = 0;
				pItems->get_Count(&numItems);
				
				for (unsigned int uItem = 0; uItem < numItems; ++uItem)
				{
					CComPtr<PXC::IPXC_ContentItem> pItem = 0;

					if (pItems->get_Item(uItem, &pItem) == S_OK)
					{
						++uItemCounter;

						PXC::PXC_CIType type;
						pItem->get_Type(&type);

						DebugPrintf("Page %u,  Object %u of %u, Type: %s   Item:%u\n", uPage, uItem, (unsigned int)numItems, GetTypeString(type), uItemCounter);

						CComPtr<PXC::IPXC_CState > pCState = 0;
						if (pItem->GetCState(&pCState) == S_OK)
						{
							
							CComPtr<PXC::IPXC_Color> pFillColor = 0;
							if (pCState->get_FillColor(&pFillColor) == S_OK)
							{
								// So now I have the fill color.  What can I do to change it?  Is it a reference to the color being used, or is it a copy.
								// Do I have to call pCState->put_FillColor(pFillColor) after I change it?
								// Am I on the right track here?  What do I need to do?
								// MakeItGray(pFillColor);
								pCState->put_FillColor(pFillColor);
							}

							CComPtr<PXC::IPXC_Color> pStrokeColor = 0;
							if (pCState->get_StrokeColor(&pStrokeColor) == S_OK)
							{
								// So now I have the stroke color.  What can I do to change it?  Is it a reference to the color being used, or is it a copy.
								// Do I have to call pCState->put_FillColor(pFillColor) after I change it?
								// Am I on the right track here?  What do I need to do?
								// MakeItGray(pStrokeColor);
								pCState->put_StrokeColor(pStrokeColor);
							}

							// Do I need to do this?  Or will modifying the the object referenced by pCState change the  IPXC_CState associated with the item?
							pItem->SetCState(pCState);
						}
					}
				}
			}
		}
	}
	
	// This works fine, no problem writing the document.  All the pages appear, but the colors are identical
	// to the colors in the original document.
	std::wstring strOutputFilename = ToWString(strPathToOutputFile);
	if (pNewDoc->WriteToFile((LPWSTR)strOutputFilename.c_str(), 0, 0) != S_OK)
	{
		return 0;
	}

	return 1;
}
Thanks!
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Hello Paul,

You're on the right track :wink:

All you need is the PlaceContent method to replace the current pages content with your modified content.
Here's a piece of code on C# from one of the samples:

Code: Select all

//C#
page.PlaceContent(Content, (uint)PDFXEdit.PXC_PlaceContentFlags.PlaceContent_Replace);
You can also read the documentation on this method right here:
https://sdkhelp.pdf-xchange.com/vie ... aceContent

Cheers,
Alex
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

Thank you, that was it :-) I've got it working now for every type of item except images.

For images, I'm trying to access them like this (from in the inner items loop above)...

Code: Select all

PXC::PXC_CIType type;
pItem->get_Type(&type);

if ((type == PXC::CIT_Image) || (type == PXC::CIT_InlineImage))
{
	PXC::IPXC_ImagePtr pImage = 0;
	if (pItem->get_Image_Object(&pImage) == S_OK)
	{
		// Use pImage to modify image I assume?   How do I do that?  I want to convert to grayscale either by
		// accessing the pixel data directly, or by a single function call if it is a built-in feature.
		// Not sure what to do here, or if this is the right approach.

		// I guess I have to assign the modified image to the item?
		pItem->put_Image_Object(pImage);
	}
}
... is that the correct approach? How do I actually modify the image? Is there a simpler way to convert the images to grayscale?

Thanks!
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Hello Paul,

Look here - there was a forum topic in the Editor SDK thread about converting images to grayscale and the code used to do that was done on the Core SDK level so you won't have any problems using it:
https://forum.pdf-xchange.com/ ... 430#p98430

Cheers,
Alex
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

Great, thank you! I grabbed this code from the other post...

Code: Select all

ci.Image_CreateIXCPage(true);
...however it didn't work consistently (about 50% of the images were not converted to grayscale), so instead I'm doing this, for all items that are images, and it works consistently (error checking removed for brevity)...

Code: Select all

PXC::IIXC_PagePtr pNewImagePage = 0;
pItem->Image_CreateIXCPage(false, PXC::RI_Perceptual, &pNewImagePage);

PXC::IXC_PageFormat pf;
pNewImagePage->get_Format(&pf);

if (pf != PXC::PageFormat_8Gray)
{
	pNewImagePage->ConvertToFormat(PXC::PageFormat_8Gray);
}
It seems that annotations and some 'floating' or overlaid images (are they images in annotations?) are not converted to grayscale.
After some digging, I found that I can get the annotations from a page like this...

Code: Select all

PXC::ULONG_T numAnnots = 0;
pPage->GetAnnotsCount(&numAnnots);
for (unsigned int uAnnot = 0; uAnnot < numAnnots; ++uAnnot)
{
	PXC::IPXC_AnnotationPtr pAnnot = 0;
	if (pPage->GetAnnot((PXC::ULONG_T)uAnnot, &pAnnot) == S_OK)
	{
		PXC::IPXC_AnnotDataPtr pAnnotData = 0;
		if ((pAnnot->get_Data(&pAnnotData) == S_OK) && (pAnnotData != 0))
		{
			PXC::IColorPtr pColor = 0;
			if ((pAnnotData->get_Color(&pColor) == S_OK) && (pColor != 0))
			{
				float r = 0.0f;
				float g = 0.0f;
				float b = 0.0f;

				if (pColor->GetRGB(&r, &g, &b) == S_OK)
				{
					const double gray = (r * 0.212) + (g * 0.701) + (b * 0.087); // "luminance" grayscale conversion.

					pColor->SetRGB((float)gray, (float)gray, (float)gray);

					pAnnotData->put_Color(pColor);

					pAnnot->put_Data(pAnnotData);
				}
			}
		}
	}
}
...but I can't figure out how to set the stroke/fill/text colors separately (there is only one 'Color' property; get_Color in C++).
It also seems that the annotations are organized in some kind of tree hierarchy. Using this...

Code: Select all

PXC::IPXC_AnnotsListPtr pAnnotList = 0;
if ((pPage->GetAnnotGroupMembers(pAnnot, true, &pAnnotList) == S_OK) && (pAnnotList != 0))
{
	DebugPrintf("Processing child annotations ...\n");

	PXC::ULONG_T numAnnots = 0;
	pAnnotList->get_Count(&numAnnots);
	for (unsigned int uAnnot = 0; uAnnot < (unsigned int)numAnnots; ++uAnnot)
	{
		PXC::IPXC_AnnotationPtr pAnnotChild = 0;
		pAnnotList->get_Item((PXC::ULONG_T)uAnnot, &pAnnotChild);
		
		// ... process color of pAnnotChild here.  should this be recursive?  how many levels deep can it go?
	}
}
... I can get to more annotations, but still, I can not figure out how to properly set their color components.

Am I doing this right? How should I move forward?
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

Unfortunately I can not publicly share the document I am testing with, but I would love it if I could email it to someone at Tracker Software to see if you are able to figure it out. Is there someone I can send it to?
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17948
Joined: Mon Jan 12, 2009 8:07 am
Location: London
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Tracker Supp-Stefan »

Hello Paul,

You can send the file to support@pdf-xchange.com with a link back to this forum topic and we will pass it along to our devs for investigation.

Regards,
Stefan
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Hello Paul,

Here's the code, that will run through the annotations and based on their type, will change the Fill color and/or Stroke color if it was previously set.
https://gist.github.com/Polaringu/91796 ... 768dba9255
Though this code is not the final version and few things are missing. First one to do would be the stamp appearance change that would change the X-Form and make it grayscale (that I will update tomorrow). And also, I have a question:
The Text Box annotations contain text of some color. It is possible to make them grayscale by changing their appearance, though the real data would be still in that color. Meaning that if the annotations were modified then the color will "come back". If they won't be modified then the change of the appearance would be enough for this task and they will remain grayscale. Will that do for your case?

Cheers,
Alex
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

Yes, that will do fine for my case. Thank you very much for your help! :-)
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17948
Joined: Mon Jan 12, 2009 8:07 am
Location: London
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Tracker Supp-Stefan »

:)
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Hello Paul,

Since it was a very interesting and needed task, I devoted some time to it and wrote several methods that allow full page conversion to grayscale.
I've updated this sample with the correct working code that I tried on your document:
https://gist.github.com/Polaringu/20070f9d7ecb3810d5d6
You can try it for yourself - should be working now :wink:

Cheers,
Alex
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

Hi Alex, thank you so much, that is awesome =)

I tried it out and it seems that everything is converting correctly, with one exception.

On page 26 in the top left area of the page there is an image (looks like a table of numbers). It appears that it is being processed as a PXC::IPXC_AnnotData_SquareCircle. In the converted document it appears as a rectangle with a black border, instead of a grayscale image.

I've attached a before/after image in a zip file. (thanks for the hint Stefan!)


Thanks again, this is an enormous help.

Cheers,
Paul
Attachments
tracker_before_after.zip
(48.57 KiB) Downloaded 137 times
Last edited by plexxis_paul on Thu May 05, 2016 2:56 pm, edited 3 times in total.
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17948
Joined: Mon Jan 12, 2009 8:07 am
Location: London
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Tracker Supp-Stefan »

Hello Paul,

Please put the images inside an archive first and attach that to your post, as .jpg/.png files are not allowed for direct upload.

Regards,
Stefan
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

Thanks for the heads-up Stafan, I edited the post and uploaded an archive.

Cheers,
Paul
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Hello Paul,

I will look at that tomorrow at work and will write you back.

Cheers,
Alex
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Hello Paul,

It appears that this annotation is not entirely "correct" from the PDF specification perspective. As it appeared, that Square annotation had custom appearance on it thus the code that changed the color of the Square annotation nullified it. You can try it for yourself in the end-user version of the Editor by changing it somehow (for example change the Stroke Color). The code that changed the annotations' colors was valid for correct annotations but in this case only the appearance change code block should remain. Meaning that all of the annotations will appear as grayscale (due to changed appearance) though their properties will remain unchanged. Though for your case that would be enough - as you said yourself, because they won't be modified and the grayscale appearance should remain intact.
Here's the updated code with the commented code blocks:
https://gist.github.com/Polaringu/20070f9d7ecb3810d5d6

HTH,
Alex
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
plexxis_paul
User
Posts: 8
Joined: Thu Apr 28, 2016 3:16 pm

Re: (Core API / C++) Create grayscale copy of entire documen

Post by plexxis_paul »

That's perfect Alex, thank you very much! I'm all set now :D

I don't know how they generated that incorrect PDF, but it's nice that we can deal with it anyway.

Thanks for the excellent support!

Cheers,
Paul
Sasha - Tracker Dev Team
User
Posts: 5522
Joined: Fri Nov 21, 2014 8:27 am
Contact:

Re: (Core API / C++) Create grayscale copy of entire documen

Post by Sasha - Tracker Dev Team »

Glad that helped Paul :wink:
Subscribe at:
https://www.youtube.com/channel/UC-TwAMNi1haxJ1FX3LvB4CQ
Post Reply