summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/colo-icc.lua
blob: f7ed561c1bfea564b493d985f71f89d0ab2ebfb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
if not modules then modules = { } end modules ['colo-icc'] = {
    version   = 1.000,
    comment   = "companion to colo-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local char, byte, gsub, match, format, strip = string.char, string.byte, string.gsub, string.match, string.format, string.strip
local readstring, readnumber = io.readstring, io.readnumber
local formatters = string.formatters

local colors = attributes and attributes.colors or { } -- when used in mtxrun

local report_colors = logs.reporter("colors","icc")

local R, Cs, lpegmatch = lpeg.R, lpeg.Cs, lpeg.match

local invalid = R(char(0)..char(31))
local cleaned = invalid^0 * Cs((1-invalid)^0)

function colors.iccprofile(filename,verbose)
    local fullname = resolvers.findfile(filename,"icc") or ""
    if fullname == "" then
        local locate = resolvers.finders.byscheme -- not in mtxrun
        if locate then
            fullname = locate("loc",filename)
        end
    end
    if fullname == "" then
        report_colors("profile %a cannot be found",filename)
        return nil, false
    end
    local f = io.open(fullname,"rb")
    if not f then
        report_colors("profile %a cannot be loaded",fullname)
        return nil, false
    end
    local header =  {
        size               = readnumber(f,4),
        cmmtype            = readnumber(f,4),
        version            = readnumber(f,4),
        deviceclass        = strip(readstring(f,4)),
        colorspace         = strip(readstring(f,4)),
        connectionspace    = strip(readstring(f,4)),
        datetime           = {
            year    = readnumber(f,2),
            month   = readnumber(f,2),
            day     = readnumber(f,2),
            hour    = readnumber(f,2),
            minutes = readnumber(f,2),
            seconds = readnumber(f,2),
        },
        filesignature      = strip(readstring(f,4)),
        platformsignature  = strip(readstring(f,4)),
        options            = readnumber(f,4),
        devicemanufacturer = strip(readstring(f,4)),
        devicemodel        = strip(readstring(f,4)),
        deviceattributes   = readnumber(f,4),
        renderingintent    = readnumber(f,4),
        illuminantxyz      = {
            x = readnumber(f,4),
            y = readnumber(f,4),
            z = readnumber(f,4),
        },
        profilecreator     = readnumber(f,4),
        id                 = strip(readstring(f,16)),
    }
    local tags = { }
    for i=1,readnumber(f,128,4) do
        tags[readstring(f,4)] = {
            offset = readnumber(f,4),
            length = readnumber(f,4),
        }
    end
    local o = header.options
    header.options =
        o == 0 and "embedded"  or
        o == 1 and "dependent" or "unknown"
    local d = header.deviceattributes
    header.deviceattributes = {
        [number.hasbit(d,1) and "transparency" or "reflective"] = true,
        [number.hasbit(d,2) and "mate"         or "glossy"    ] = true,
        [number.hasbit(d,3) and "negative"     or "positive"  ] = true,
        [number.hasbit(d,4) and "bw"           or "color"     ] = true,
    }
    local r = header.renderingintent
    header.renderingintent =
        r == 0 and "perceptual" or
        r == 1 and "relative"   or
        r == 2 and "saturation" or
        r == 3 and "absolute"   or "unknown"
    for tag, spec in next, tags do
        if tag then
            local offset, length = spec.offset, spec.length
            local variant = readstring(f,offset,4)
            if variant == "text" or variant == "desc" then
                local str = readstring(f,length-4)
                tags[tag] = {
                    data    = str,
                    cleaned = lpegmatch(cleaned,str),
                }
            else
                if verbose then
                    report_colors("ignoring tag %a or type %a in profile %a",tag,variant,fullname)
                end
                tags[tag] = nil
            end
        end
    end
    f:close()
    local profile = {
        filename = filename,
        fullname = fullname,
        header   = header,
        tags     = tags,
    }
    report_colors("profile %a loaded",fullname)
    return profile, true
end