diff mupdf-source/thirdparty/lcms2/utils/linkicc/linkicc.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/lcms2/utils/linkicc/linkicc.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,380 @@
+//---------------------------------------------------------------------------------
+//
+//  Little Color Management System
+//  Copyright (c) 1998-2023 Marti Maria Saguer
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//---------------------------------------------------------------------------------
+
+#include "utils.h"
+
+// ---------------------------------------------------------------------------------
+
+static char* Description = "Devicelink profile";
+static char* Copyright   = "No copyright, use freely";
+static int   Intent = INTENT_PERCEPTUAL;
+static char* cOutProf    = "devicelink.icc";
+static int   PrecalcMode  = 1;
+static int   NumOfGridPoints = 0;
+
+static cmsFloat64Number ObserverAdaptationState = 1.0;  // According ICC 4.2 this is the default
+
+static cmsBool BlackPointCompensation = FALSE;
+
+static cmsFloat64Number InkLimit   = 400;
+static cmsBool lUse8bits           = FALSE;
+static cmsBool TagResult           = FALSE;
+static cmsBool KeepLinearization   = FALSE;
+static cmsFloat64Number Version    = 4.3;
+
+
+// The manual
+static
+int Help(cmsContext ContextID, int level)
+{
+    UTILS_UNUSED_PARAMETER(level);
+
+    fprintf(stderr, "\nlinkicc: Links profiles into a single devicelink.\n");
+
+    fprintf(stderr, "\n");
+    fprintf(stderr, "usage: linkicc [flags] <profiles>\n\n");
+    fprintf(stderr, "flags:\n\n");
+    fprintf(stderr, "-o<profile> - Output devicelink profile. [defaults to 'devicelink.icc']\n");
+
+    PrintRenderingIntents(ContextID);
+
+    fprintf(stderr, "-c<0,1,2> - Precision (0=LowRes, 1=Normal, 2=Hi-res) [defaults to 1]\n");
+    fprintf(stderr, "-n<gridpoints> - Alternate way to set precision, number of CLUT points\n");
+    fprintf(stderr, "-d<description> - description text (quotes can be used)\n");
+    fprintf(stderr, "-y<copyright> - copyright notice (quotes can be used)\n");
+
+    fprintf(stderr, "\n-k<0..400> - Ink-limiting in %% (CMYK only)\n");
+    fprintf(stderr, "-8 - Creates 8-bit devicelink\n");
+    fprintf(stderr, "-x - Creatively, guess deviceclass of resulting profile.\n");
+    fprintf(stderr, "-b - Black point compensation\n");
+    fprintf(stderr, "-a<0..1> - Observer adaptation state (abs.col. only)\n\n");
+    fprintf(stderr, "-l - Use linearization curves (may affect accuracy)\n");
+    fprintf(stderr, "-r<v.r> - Profile version. (CAUTION: may change the profile implementation)\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "Colorspaces must be paired except Lab/XYZ, that can be interchanged.\n\n");
+
+    PrintBuiltins();
+
+    fprintf(stderr, "\nExamples:\n\n"
+        "To create 'devicelink.icm' from a.icc to b.icc:\n"
+        "\tlinkicc a.icc b.icc\n\n"
+        "To create 'out.icc' from sRGB to cmyk.icc:\n"
+        "\tlinkicc -o out.icc *sRGB cmyk.icc\n\n"
+        "To create a sRGB input profile working in Lab:\n"
+        "\tlinkicc -x -o sRGBLab.icc *sRGB *Lab\n\n"
+        "To create a XYZ -> sRGB output profile:\n"
+        "\tlinkicc -x -o sRGBLab.icc *XYZ *sRGB\n\n"
+        "To create a abstract profile doing softproof for cmyk.icc:\n"
+        "\tlinkicc -t1 -x -o softproof.icc *Lab cmyk.icc cmyk.icc *Lab\n\n"
+        "To create a 'grayer' sRGB input profile:\n"
+        "\tlinkicc -x -o grayer.icc *sRGB gray.icc gray.icc *Lab\n\n"
+        "To embed ink limiting into a cmyk output profile:\n"
+        "\tlinkicc -x -o cmyklimited.icc -k 250 cmyk.icc *Lab\n\n");
+
+    fprintf(stderr, "This program is intended to be a demo of the Little CMS\n"
+                    "color engine. Both lcms and this program are open source.\n"
+                    "You can obtain both in source code at https://www.littlecms.com\n"
+                    "For suggestions, comments, bug reports etc. send mail to\n"
+                    "info@littlecms.com\n\n");
+
+    exit(0);
+}
+
+// The toggles stuff
+static
+void HandleSwitches(cmsContext ContextID, int argc, char *argv[])
+{
+    int s;
+
+    while ((s = xgetopt(argc,argv,"a:A:BbC:c:D:d:h:H:k:K:lLn:N:O:o:r:R:T:t:V:v:xX8y:Y:-:")) != EOF) {
+
+    switch (s) {
+
+    case '-':
+        if (strcmp(xoptarg, "help") == 0)
+        {
+            Help(ContextID, 0);
+        }
+        else
+        {
+            FatalError("Unknown option - run without args to see valid ones.\n");
+        }
+        break;
+
+    case 'a':
+    case 'A':
+        ObserverAdaptationState = atof(xoptarg);
+        if (ObserverAdaptationState < 0 ||
+            ObserverAdaptationState > 1.0)
+            FatalError("Adaptation state should be 0..1");
+        break;
+
+    case 'b':
+    case 'B':
+        BlackPointCompensation = TRUE;
+        break;
+
+    case 'c':
+    case 'C':
+        PrecalcMode = atoi(xoptarg);
+        if (PrecalcMode < 0 || PrecalcMode > 2) {
+            FatalError("Unknown precalc mode '%d'", PrecalcMode);
+        }
+        break;
+
+    case 'd':
+    case 'D':
+        // Doing that is correct and safe: Description points to memory allocated in the command line.
+        // same for Copyright and output devicelink.
+        Description = xoptarg;
+        break;
+
+    case 'h':
+    case 'H':
+        Help(ContextID, atoi(xoptarg));
+        return;
+
+    case 'k':
+    case 'K':
+        InkLimit = atof(xoptarg);
+        if (InkLimit < 0.0 || InkLimit > 400.0) {
+            FatalError("Ink limit must be 0%%..400%%");
+        }
+        break;
+
+
+    case 'l':
+    case 'L': KeepLinearization = TRUE;
+        break;
+
+    case 'n':
+    case 'N':
+        if (PrecalcMode != 1) {
+            FatalError("Precalc mode already specified");
+        }
+        NumOfGridPoints = atoi(xoptarg);
+        break;
+
+    case 'o':
+    case 'O':
+        cOutProf = xoptarg;
+        break;
+
+
+    case 'r':
+    case 'R':
+        Version = atof(xoptarg);
+        if (Version < 2.0 || Version > 4.3) {
+            fprintf(stderr, "WARNING: lcms was not aware of this version, tag types may be wrong!\n");
+        }
+        break;
+
+    case 't':
+    case 'T':
+        Intent = atoi(xoptarg);  // Will be validated latter on
+        break;
+
+    case 'V':
+    case 'v':
+        Verbose = atoi(xoptarg);
+        if (Verbose < 0 || Verbose > 3) {
+            FatalError("Unknown verbosity level '%d'", Verbose);
+        }
+        break;
+
+    case '8':
+        lUse8bits = TRUE;
+        break;
+
+
+
+    case 'y':
+    case 'Y':
+        Copyright = xoptarg;
+        break;
+
+
+
+    case 'x':
+    case 'X': TagResult = TRUE;
+        break;
+
+
+    default:
+
+        FatalError("Unknown option - run without args to see valid ones.\n");
+    }
+    }
+}
+
+// Set the copyright and description
+static
+cmsBool SetTextTags(cmsContext ContextID, cmsHPROFILE hProfile)
+{
+    cmsMLU *DescriptionMLU, *CopyrightMLU;
+    cmsBool  rc = FALSE;
+
+    DescriptionMLU  = cmsMLUalloc(ContextID, 1);
+    CopyrightMLU    = cmsMLUalloc(ContextID, 1);
+
+    if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
+
+    if (!cmsMLUsetASCII(ContextID, DescriptionMLU,  "en", "US", Description)) goto Error;
+    if (!cmsMLUsetASCII(ContextID, CopyrightMLU,    "en", "US", Copyright)) goto Error;
+
+    if (!cmsWriteTag(ContextID, hProfile, cmsSigProfileDescriptionTag,  DescriptionMLU)) goto Error;
+    if (!cmsWriteTag(ContextID, hProfile, cmsSigCopyrightTag,           CopyrightMLU)) goto Error;
+
+    rc = TRUE;
+
+Error:
+
+    if (DescriptionMLU)
+        cmsMLUfree(ContextID, DescriptionMLU);
+    if (CopyrightMLU)
+        cmsMLUfree(ContextID, CopyrightMLU);
+    return rc;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+    int i, nargs, rc;
+    cmsHPROFILE Profiles[257];
+    cmsHPROFILE hProfile;
+    cmsUInt32Number dwFlags;
+    cmsHTRANSFORM hTransform = NULL;
+    cmsContext ContextID = NULL;
+
+    // Here we are
+    fprintf(stderr, "Little CMS ICC device link generator - v3.2 [LittleCMS %2.2f]\n", cmsGetEncodedCMMversion() / 1000.0);
+    fprintf(stderr, "Copyright (c) 1998-2023 Marti Maria Saguer. See COPYING file for details.\n");
+    fflush(stderr);
+
+    // Initialize
+    InitUtils(ContextID, "linkicc");
+    rc = 0;
+
+    // Get the options
+    HandleSwitches(ContextID, argc, argv);
+
+    // How many profiles to link?
+    nargs = (argc - xoptind);
+    if (nargs < 1)
+        return Help(ContextID, 0);
+
+    if (nargs > 255) {
+        FatalError("Holy profile! what are you trying to do with so many profiles!?");
+        goto Cleanup;
+    }
+
+    // Open all profiles
+    memset(Profiles, 0, sizeof(Profiles));
+    for (i=0; i < nargs; i++) {
+
+        Profiles[i] = OpenStockProfile(ContextID, argv[i + xoptind]);
+        if (Profiles[i] == NULL) goto Cleanup;
+
+        if (Verbose >= 1) {
+            PrintProfileInformation(ContextID, Profiles[i]);
+        }
+    }
+
+    // Ink limiting
+    if (InkLimit != 400.0) {
+        cmsColorSpaceSignature EndingColorSpace = cmsGetColorSpace(ContextID, Profiles[nargs-1]);
+        Profiles[nargs++] = cmsCreateInkLimitingDeviceLink(ContextID, EndingColorSpace, InkLimit);
+    }
+
+    // Set the flags
+    dwFlags = cmsFLAGS_KEEP_SEQUENCE;
+    switch (PrecalcMode) {
+
+        case 0: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
+        case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
+        case 1:
+            if (NumOfGridPoints > 0)
+                dwFlags |= cmsFLAGS_GRIDPOINTS(NumOfGridPoints);
+            break;
+
+        default:
+            {
+                FatalError("Unknown precalculation mode '%d'", PrecalcMode);
+                goto Cleanup;
+            }
+    }
+
+    if (BlackPointCompensation)
+        dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
+
+    if (TagResult)
+        dwFlags |= cmsFLAGS_GUESSDEVICECLASS;
+
+    if (KeepLinearization)
+        dwFlags |= cmsFLAGS_CLUT_PRE_LINEARIZATION|cmsFLAGS_CLUT_POST_LINEARIZATION;
+
+    if (lUse8bits) dwFlags |= cmsFLAGS_8BITS_DEVICELINK;
+
+     cmsSetAdaptationState(ContextID, ObserverAdaptationState);
+
+    // Create the color transform. Specify 0 for the format is safe as the transform
+    // is intended to be used only for the devicelink.
+    hTransform = cmsCreateMultiprofileTransform(ContextID, Profiles, nargs, 0, 0, Intent, dwFlags|cmsFLAGS_NOOPTIMIZE);
+    if (hTransform == NULL) {
+        FatalError("Transform creation failed");
+        goto Cleanup;
+    }
+
+    hProfile =  cmsTransform2DeviceLink(ContextID, hTransform, Version, dwFlags);
+    if (hProfile == NULL) {
+        FatalError("Devicelink creation failed");
+        goto Cleanup;
+    }
+
+    SetTextTags(ContextID, hProfile);
+    cmsSetHeaderRenderingIntent(ContextID, hProfile, Intent);
+
+    if (cmsSaveProfileToFile(ContextID, hProfile, cOutProf)) {
+
+        if (Verbose > 0)
+            fprintf(stderr, "Ok");
+    }
+    else
+        FatalError("Error saving file!");
+
+    cmsCloseProfile(ContextID, hProfile);
+
+
+Cleanup:
+
+    if (hTransform != NULL) cmsDeleteTransform(ContextID, hTransform);
+    for (i=0; i < nargs; i++) {
+
+        if (Profiles[i] != NULL) cmsCloseProfile(ContextID, Profiles[i]);
+    }
+
+    return rc;
+}