Skip to main content
Skip table of contents

Determine if a color channel is used

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.“

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.

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;
}
JavaScript errors detected

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

If this problem persists, please contact our support.