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