Last active
June 6, 2025 15:42
-
-
Save mako-team/108a86beb1c4b895232832b0f51a4628 to your computer and use it in GitHub Desktop.
A customer asked: “How can we determine if a process color channel is used on a page.“ To solve this, a custom transform can be used to scan the content to discover which color spaces are in use. This C+ example shows how it’s done.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* ----------------------------------------------------------------------- | |
| * <copyright file="ColorReporting.cpp" company="Global Graphics Software Ltd"> | |
| * Copyright (c) 2025 Global Graphics Software Ltd. All rights reserved. | |
| * </copyright> | |
| * <summary> | |
| * This example is provided on an "as is" basis and without warranty of any kind. | |
| * Global Graphics Software Ltd. does not warrant or make any representations | |
| * regarding the use or results of use of this example. | |
| * </summary> | |
| * ----------------------------------------------------------------------- | |
| */ | |
| #include <exception> | |
| #include <iostream> | |
| #include <jawsmako/jawsmako.h> | |
| #include <jawsmako/pdfinput.h> | |
| #include <jawsmako/customtransform.h> | |
| #include <edl/iabort.h> | |
| #include <fstream> | |
| #include <iostream> | |
| #include <filesystem> | |
| #include <map> | |
| using namespace JawsMako; | |
| using namespace EDL; | |
| namespace fs = std::filesystem; | |
| typedef std::map<IDOMColorSpace::eColorSpaceType, String> ColorSpaceTypeMap; | |
| // Print usage | |
| static void usage() | |
| { | |
| std::wcerr << L"Usage: <source folder> "; // required arguments | |
| } | |
| class CColorTransformImplementation : public ICustomTransform::IImplementation | |
| { | |
| public: | |
| CColorTransformImplementation() | |
| { | |
| } | |
| virtual IDOMBrushPtr transformShadingPatternBrush(IImplementation* genericImplementation, const IDOMShadingPatternBrushPtr& brush, const CTransformState& state) | |
| { | |
| return genericImplementation->transformShadingPatternBrush(NULL, brush, state); | |
| } | |
| virtual IDOMColorPtr transformColor(IImplementation* genericImplementation, const IDOMColorPtr& color, const CTransformState& state) | |
| { | |
| m_colorList.append(color); | |
| return genericImplementation->transformColor(NULL, color, state); | |
| } | |
| CEDLVector<IDOMColorPtr> getColorList() const | |
| { | |
| return m_colorList; | |
| } | |
| private: | |
| CEDLVector<IDOMColorPtr> m_colorList; | |
| }; | |
| ColorSpaceTypeMap getColorSpaceMap() | |
| { | |
| ColorSpaceTypeMap colorSpaceMap; | |
| colorSpaceMap.insert({ IDOMColorSpace::eDeviceRGB, L"DeviceRGB" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::eDeviceGray, L"DeviceGray" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::eDeviceCMYK, L"DeviceCMYK" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::esRGB, L"sRGB" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::esGray, L"sGray" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::escRGB, L"XPS scRGB" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::eICCBased, L"ICC-based" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::eIndexed, L"Indexed" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::eDeviceN, L"Spot" }); | |
| colorSpaceMap.insert({ IDOMColorSpace::eLAB, L"L*a*b*" }); | |
| return colorSpaceMap; | |
| } | |
| #ifdef _WIN32 | |
| int wmain(int argc, wchar_t* argv[]) | |
| #else | |
| int main(int argc, char* argv[]) | |
| #endif | |
| { | |
| ColorSpaceTypeMap colorSpaceMap = getColorSpaceMap(); | |
| try | |
| { | |
| // Create our JawsMako instance. | |
| // Currently only one instance is allowed at any one time. | |
| IJawsMakoPtr jawsMako = IJawsMako::create(); | |
| IJawsMako::enableAllFeatures(jawsMako); | |
| IAbortPtr abort = IAbort::create(); | |
| IProgressTickPtr progressTick; | |
| IProgressMonitorPtr progressMonitor = IProgressMonitor::create(progressTick, abort); | |
| // There should be at least one argument, a source file. | |
| if (argc < 2) | |
| { | |
| usage(); | |
| return 1; | |
| } | |
| const std::wstring inputFolderPath = argv[1]; | |
| for (auto& p : fs::directory_iterator(inputFolderPath)) | |
| { | |
| String inputFilePath = p.path().c_str(); | |
| std::wcout << L"File: " << inputFilePath << std::endl; | |
| // Declare our input and output pointers | |
| IInputPtr input = IPDFInput::create(jawsMako, progressMonitor); | |
| // Get the assembly from the input. | |
| IDocumentAssemblyPtr assembly = input->open(inputFilePath); | |
| IDocumentPtr document = assembly->getDocument(); | |
| uint32 pageCount = document->getNumPages(); | |
| // Create a transform to gather color info | |
| CColorTransformImplementation implementation; | |
| ITransformPtr getColors = ICustomTransform::create(jawsMako, &implementation, abort, true, true, true, true, true, true); | |
| std::map<String, IDOMColorPtr> inkMap; | |
| // Apply the transform to every page | |
| for (uint32 pageNum = 0; pageNum < pageCount; pageNum++) | |
| { | |
| IPagePtr page = document->getPage(pageNum); | |
| IDOMFixedPagePtr fixedPage = page->edit(); | |
| // Get inks | |
| inkMap.clear(); | |
| IRendererTransform::CInkInfoVect inks = IRendererTransform::findInks(jawsMako, fixedPage); | |
| for (auto ink : inks) | |
| { | |
| inkMap.insert({ U8StringToString(ink.inkName), ink.inkColor }); | |
| } | |
| // get colors | |
| bool result = false; | |
| getColors->transform(fixedPage, result); | |
| } | |
| CEDLVector<IDOMColorPtr> colors = implementation.getColorList(); | |
| std::wcout << L"Color count: " << colors.size() << std::endl; | |
| // Process colors | |
| int index = 0; | |
| for (IDOMColorPtr color : colors) | |
| { | |
| auto colorSpace = color->getColorSpace(); | |
| auto colorSpaceEnum = colorSpace->getColorSpaceType(); | |
| String colorSpaceType = colorSpaceMap.at(colorSpaceEnum); | |
| uint8 numComponents = colorSpace->getNumComponents(); | |
| wprintf_s(L"%3d ", ++index); | |
| std::wcout << colorSpaceType << L" ("; | |
| for (uint32 c = 0; c < numComponents; c++) | |
| { | |
| wprintf_s(L"%4.2f", color->getComponentValue(c)); | |
| if (c < numComponents - 1) | |
| std::wcout << ","; | |
| } | |
| std::wcout << L") "; | |
| if (colorSpaceEnum == IDOMColorSpace::eDeviceN) | |
| { | |
| IDOMColorSpaceDeviceNPtr deviceN = edlobj2IDOMColorSpaceDeviceN(colorSpace); | |
| numComponents = deviceN->getNumComponents(); | |
| for (uint8 componentIdx = 0; componentIdx < numComponents; componentIdx++) | |
| { | |
| auto altColorSpace = deviceN->getAlternateColorSpace(); | |
| auto altColorSpaceEnum = altColorSpace->getColorSpaceType(); | |
| IDOMDeviceNColorantPtr colorant = deviceN->getColorant(componentIdx); | |
| String colorantName = U8StringToString(U8String(colorant->getName())); | |
| // Only process if a not a special | |
| if (colorantName != L"All" && colorantName != L"None") | |
| { | |
| IDOMColorPtr colorantColor = inkMap.at(colorantName); | |
| std::wcout << L"('" << colorantName << L"'\t"; | |
| String altColorSpaceType = colorSpaceMap.at(altColorSpaceEnum); | |
| // Components of the alternate color space | |
| uint8 altNumComponents = altColorSpace->getNumComponents(); | |
| std::wcout << altColorSpaceType << L" ("; | |
| for (uint32 altComponentIdx = 0; altComponentIdx < altNumComponents; altComponentIdx++) | |
| { | |
| wprintf_s(L"%4.2f", colorantColor->getComponentValue(altComponentIdx)); | |
| if (altComponentIdx < (altNumComponents - 1)) | |
| std::wcout << ","; | |
| } | |
| std::wcout << L") "; | |
| } | |
| } | |
| std::wcout << std::endl; | |
| } | |
| std::wcout << std::endl; | |
| } | |
| } | |
| } | |
| catch (IError& e) | |
| { | |
| String errorFormatString = getEDLErrorString(e.getErrorCode()); | |
| std::wcerr << L"Exception thrown: " << e.getErrorDescription(errorFormatString) << std::endl; | |
| #ifdef _WIN32 | |
| // On windows, the return code allows larger numbers, and we can return the error code | |
| return e.getErrorCode(); | |
| #else | |
| // On other platforms, the exit code is masked to the low 8 bits. So here we just return | |
| // a fixed value. | |
| return 1; | |
| #endif | |
| } | |
| catch (std::exception& e) | |
| { | |
| std::wcerr << L"std::exception thrown: " << e.what() << std::endl; | |
| return 1; | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment