Skip to main content
Skip table of contents

Determine if a color channel is used

📌 Overview

This article explains the best practise for color reporting - using a custom transform.

(question) Issue Description

A customer asked: “How can we determine if a process color channel is used on a page. We want to know if a document has no pixel of cyan or magenta although these color channels always exist. We could count the separations contents, but this might be unreliable in low resolutions and I wonder if there is a simpler way.“

💡 Solution

To solve this, a custom transform can be used to scan the content to discover which color spaces are in use.

  1. Create a custom transform implementation: Override the transformColor() method and append each color to a new list property. See m_colorList in the sample code.

  2. Apply the transform to every page: Create an instance of the transform and apply it to a page with ICustomTransform::transform()

  3. Get the list of colors and process accordingly: Retrieve the list of colors using a “get method”. See CEDLVector<IDOMColorPtr> getColorList() const { return m_colorList; } in the sample code below. From here you can get the color space and color space type using IDOMColor::getColorSpace() and IDOMColorSpace::GetColorSpaceType() respectively.

⌨️ Sample Code

This C+ example shows how it’s done. Find it below or at ColorReporting.cpp.

ColorReporting.cpp
CPP
/*
 * Copyright (C) 2013-2025 Global Graphics Software Ltd. All rights reserved
 *
 *  Simple sample application for color reporting using the JawsMako APIs.
 */

#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;
}

☑️ Conclusion

By implementing a custom transform, you can effectively determine the usage of color channels within a document. This method provides a reliable way to identify color spaces and their components, ensuring accurate color reporting even in complex documents. Utilizing the provided sample code and resources, you can integrate this solution into your workflow to enhance color management capabilities.

📚 Additional Resources

If you need additional help, see our API documentation for detailed information on class/method usage, or raise a support ticket via our customer portal.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.