001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Jean-Marc Orliaguet, Chalmers
018 *
019 * $Id$
020 */
021
022package org.nuxeo.theme.presets;
023
024import java.awt.Color;
025import java.io.ByteArrayInputStream;
026import java.io.DataInputStream;
027import java.io.IOException;
028import java.util.LinkedHashMap;
029import java.util.Map;
030
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033
034public class PhotoshopPaletteParser extends PaletteParser {
035
036    private static final Log log = LogFactory.getLog(PhotoshopPaletteParser.class);
037
038    private static final int RGB = 0;
039
040    private static final int HSB = 1;
041
042    private static final int CMYK = 2;
043
044    private static final int LAB = 7;
045
046    private static final int GRAYSCALE = 8;
047
048    private static final int WIDE_CMYK = 9;
049
050    public static boolean checkSanity(byte[] bytes) {
051        return true;
052    }
053
054    public static Map<String, String> parse(byte[] bytes) {
055        Map<String, String> entries = new LinkedHashMap<String, String>();
056        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
057        DataInputStream dis = new DataInputStream(is);
058
059        char[] words = new char[bytes.length];
060        int size = 0;
061        while (true) {
062            try {
063                words[size] = dis.readChar();
064                size++;
065            } catch (IOException e) {
066                break;
067            }
068        }
069
070        try {
071            is.close();
072            dis.close();
073        } catch (IOException e) {
074            log.error(e, e);
075        }
076
077        int offset = 1;
078        int version = words[0] & 0xffff;
079        int nc = words[1] & 0xffff;
080
081        // get version 2 if it exists
082        if (version == 1 && size > nc * 5 + 2) {
083            offset += nc * 5 + 2;
084            version = words[offset - 1] & 0xffff;
085            nc = words[offset] & 0xffff;
086        }
087
088        if (version == 1) {
089            log.debug("Found ACO v1 color file (Photoshop < 7.0)");
090        } else if (version == 2) {
091            log.debug("Found ACO v2 color file (Photoshop >= 7.0)");
092        } else {
093            log.error("Unknown ACO file version: " + version);
094            return entries;
095        }
096
097        log.debug("Found " + nc + " colors.");
098
099        int counter = 1;
100        for (int j = 0; j < nc; j++) {
101            String value = null;
102            int colorSpace = words[offset + 1] & 0xff;
103            int w = words[offset + 2] & 0xffff;
104            int x = words[offset + 3] & 0xffff;
105            int y = words[offset + 4] & 0xffff;
106            int z = words[offset + 5] & 0xffff;
107
108            if (colorSpace == RGB) {
109                value = rgbToHex(w / 256, x / 256, y / 256);
110
111            } else if (colorSpace == HSB) {
112                float hue = w / 65535F; // [0.0-1.0]
113                float saturation = x / 65535F; // [0.0-1.0]
114                float brightness = y / 65535F; // [0.0-1.0]
115                Color color = Color.getHSBColor(hue, saturation, brightness);
116                value = rgbToHex(color.getRed(), color.getGreen(), color.getBlue());
117
118            } else if (colorSpace == CMYK) {
119                float cyan = 1F - w / 65535F; // [0.0-1.0]
120                float magenta = 1F - x / 65535F; // [0.0-1.0]
121                float yellow = 1F - y / 65535F; // [0.0-1.0]
122                float black = 1F - z / 65535F; // [0.0-1.0]
123                // TODO: do the conversion to RGB. An ICC profile is required.
124                log.warn("Unsupported color space: CMYK");
125
126            } else if (colorSpace == GRAYSCALE) {
127                int gray = (int) (w * 256F / 10000F); // [0-256]
128                value = rgbToHex(gray, gray, gray);
129
130            } else if (colorSpace == LAB) {
131                float l = w / 100F;
132                float a = x / 100F;
133                float b = y / 100F;
134                // TODO: do the conversion to RGB. An ICC profile is required.
135                log.warn("Unsupported color space: CIE Lab");
136
137            } else if (colorSpace == WIDE_CMYK) {
138                float cyan = w / 10000F; // [0.0-1.0]
139                float magenta = x / 10000F; // [0.0-1.0]
140                float yellow = y / 10000F; // [0.0-1.0]
141                float black = z / 10000F; // [0.0-1.0]
142                // TODO: do the conversion to RGB. An ICC profile is required.
143                log.warn("Unsupported color space: Wide CMYK");
144
145            } else {
146                log.warn("Unknown color space: " + colorSpace);
147            }
148
149            String name = "";
150            if (version == 1) {
151                name = String.format("Color %s", counter);
152            }
153
154            else if (version == 2) {
155                int len = (words[offset + 7] & 0xffff) - 1;
156                name = String.copyValueOf(words, offset + 8, len);
157                offset += len + 3;
158
159                String n = name;
160                int c = 2;
161                while (entries.containsKey(n)) {
162                    n = String.format("%s %s", name, c);
163                    c++;
164                }
165                name = n;
166            }
167
168            if (value != null) {
169                entries.put(name, value);
170            }
171
172            offset += 5;
173            counter++;
174        }
175
176        return entries;
177    }
178
179}