The convertor now converts to gimp, scribus and (enhanced) create draft palettes.

Converting from Lab requires LittleCMS (and the python bindings).


My additions to the draft are:

- metadata (should be corrected by someone who knows more about xml)

- display informations (columns,rows,line breaks). In inverted ACF/BCF values as it's the only one that scrolls horizontally.

- color groups (Adobe Swatch Exchange format feature).

- "preferredmodel" tag as you can find in ACF/BCF and Quark swatches

- "spot" attribute

I took the option of choosing '6CLR' for hexachrome values as it wasn't in the draft and it is the ICC and CGATS key. But it's maybe useless since I don't know any free software that could deal with these data...
I changed "g" to "k" in the "Gray" model.

I still advocate the optionality of the color space: in palettes like that one <http://html-color-codes.com/>, it's obvious that the RGB values are chosen for themselves and not for the color they represent. It is as useful with any rgb space. It helps you to have an idea of the entire field of possible colors with your computer's screen. If you "color manage" that palette, it looses any interest... Don't forget that many non-professional users won't ever publish their work and don't care about other user's screens...


PS: There were some mistakes in the Corel color models. 12 is Lab and not CMYK and RGB is actually coded BGR. And I'm not sure at all the first color model in the BCF format is XYZ... By the way, if someone knows how to decrypt AutoCAD's RGB8Encrypt values, I'm interested ;-)

#!/usr/bin/python
# coding: utf-8
#
#       swatch_convertor.py – version 2008-04-19
#       
#       Copyright 2008 Olivier Berten <[EMAIL PROTECTED]>
#       
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 3 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.
#
#
#       Output values
#       RGB,HSV,HLS,CMY,CMYK,6CLR,YIQ: 0 -> 1
#       Lab: L 0 -> 100 | ab -125 -> 125
#       XYZ: 0 -> ~100 (cfr. ref)
#
from __future__ import division
import sys
import struct
from collections import defaultdict
import os.path
from xml.etree import ElementTree as etree
from lcms import *

def Lab2RGB8(L,a,b):
	Lab = cmsCIELab(L,a,b)
	RGB = COLORB()
	
	hLab    = cmsCreateLabProfile(None)
	hsRGB   = cmsCreate_sRGBProfile()

	xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hsRGB, TYPE_RGB_8, INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC)

	cmsDoTransform(xform, Lab, RGB, 1)

	cmsDeleteTransform(xform)
	cmsCloseProfile(hsRGB)
	cmsCloseProfile(hLab)

	return (RGB[0],RGB[1],RGB[2])

#
# color model conversion formulas: http://www.easyrgb.com/math.php
#
def HSL2RGB(H,S,L):
	if ( S == 0 ): 
		R = L 
		G = L
		B = L
	else:
		if ( L < 0.5 ):
			var_2 = L * ( 1 + S )
		else:
			var_2 = ( L + S ) - ( S * L )

		var_1 = 2 * L - var_2

		R = Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) )
		G = Hue_2_RGB( var_1, var_2, H )
		B = Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) )
	
	return (R,G,B)

def Hue_2_RGB( v1, v2, vH ):
	if ( vH < 0 ):
		vH = vH+1
	if ( vH > 1 ):
		vH = vH-1
	if ( ( 6 * vH ) < 1 ):
		return ( v1 + ( v2 - v1 ) * 6 * vH )
	if ( ( 2 * vH ) < 1 ):
		return ( v2 )
	if ( ( 3 * vH ) < 2 ):
		return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6 )
	
	return ( v1 )

def HSV2RGB(H,S,V):
	if ( S == 0 ):
		R = V
		G = V
		B = V
	else:
		var_h = H * 6
		if ( var_h == 6 ):
			var_h = 0
		var_i = int( var_h )
		var_1 = V * ( 1 - S )
		var_2 = V * ( 1 - S * ( var_h - var_i ) )
		var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) )

		if ( var_i == 0 ):
			R = V
			G = var_3
			B = var_1
		elif ( var_i == 1 ):
			R = var_2
			G = V
			B = var_1
		elif ( var_i == 2 ):
			R = var_1
			G = V
			B = var_3
		elif ( var_i == 3 ):
			R = var_1
			G = var_2
			B = V
		elif ( var_i == 4 ):
			R = var_3
			G = var_1
			B = V
		else:
			R = V
			G = var_1
			B = var_2

	return (R,G,B)

def CMY2RGB(C,M,Y):
	R = ( 1 - C )
	G = ( 1 - M )
	B = ( 1 - Y )

	return (R,G,B)

def CMYK2CMY(C,M,Y,K):
	C = ( C * ( 1 - K ) + K )
	M = ( M * ( 1 - K ) + K )
	Y = ( Y * ( 1 - K ) + K )

	return (C,M,Y)

def CMYK2RGB(C,M,Y,K):
	C,M,Y = CMYK2CMY(C,M,Y,K)
	return CMY2RGB(C,M,Y)

def test_format(file):
	ext =  os.path.splitext(os.path.basename(file))[1].lower()
	# Adobe
	if ext == '.acb':
		data = open(file).read(4)
		if struct.unpack('4s', data)[0] == '8BCB':
			format = 'adobe_acb'
		elif struct.unpack('4s', data)[0] == '<?xm':
			if etree.parse(file).getroot().tag == 'colorBook':
				format = 'autocad_acb'
	elif ext == '.aco':
		data = open(file).read(2)
		if struct.unpack('>h', data)[0] in (1,2):
			format = 'adobe_aco'
	elif ext == '.act':
		if os.path.getsize(file) == 772 or os.path.getsize(file)%3 == 0:
			format = 'adobe_act'
	elif ext == '.ase':
		data = open(file).read(4)
		if struct.unpack('4s', data)[0] == 'ASEF':
			format = 'adobe_ase'
	elif ext == '.acf':
		data = open(file).read(7)
		if struct.unpack('7s', data)[0] in ('ACF 1.0','ACF 2.1'):
			format = 'adobe_acf'
	elif ext == '.bcf':
		data = open(file).read(7)
		if struct.unpack('7s', data)[0] in ('ACF 1.0','ACF 2.1','BCF 2.0'):
			format = 'adobe_bcf'
	elif ext == '.clr':
		data = open(file).read(4)
		if data == '\xff\xff\x00\x00':
			format = 'adobe_clr'
	# RAL
	elif ext == '.bcs':
		data = open(file).read(4)
		if struct.unpack('b3s', data)[1] in ('clf','rgb','atl'):
			format = 'ral_bcs'
	# Corel
	elif ext == '.cpl':
		data = open(file).read(2)
		if data in ('\xcc\xbc','\xcc\xdc','\xcd\xbc','\xcd\xdc','\xdd\xdc','\xdc\xdc','\xcd\xdd'):
			format = 'corel_cpl'
	# Quark
	elif ext == '.qcl':
		if etree.parse(file).getroot().tag == 'cgats17':
			format = 'quark_qcl'
	# ColorSchemer
	elif ext == '.cs':
		data = open(file).read(2)
		if struct.unpack('<H', data)[0] == 3:
			format = 'colorschemer'
	# RIFF
	elif ext == '.pal':
		data = open(file).read(12)
		RIFF, size, PAL = struct.unpack('<4s L 4s', data)
		if  RIFF == 'RIFF' and PAL == 'PAL ':
			format = 'riff_pal'
	if 'format' in vars():
		return format

def ords2str(ords):
	string = u''
	for ord in ords:
		string += unichr(ord)
	return string.split('\x00', 1)[0]

def acb_decode_str(str):
	if str[0:4] == '$$$/':
		str = str.partition('=')[2]
	return str.replace('^C',u'©').replace('^R',u'®')

adobe_model = {0: 'RGB', 1: 'HSV', 2: 'CMYK', 3: 'Pantone', 4: 'Focoltone', 5: 'Trumatch', 6: 'Toyo', 7: 'Lab', 8: 'Gray', 9: 'WideCMYK', 10: 'HKS', 11: 'DIC', 12: 'TotalInk', 13: 'MonitorRGB', 14: 'Duotone', 15: 'Opacity'}
bcf_model = {1: 'RGB', 2: 'CMYK',8: 'hifi', 16: 'Mixed'}
bcf_type = {1: 'Spot', 2: 'Process',8: 'Mixed', 16: 'hifi'}
corel_model = {1: 'Pantone', 2: 'CMYK100', 3: 'CMYK', 4: 'CMY', 5: 'RGB', 6: 'HSB', 7: 'HLS', 9: 'Grayscale', 10: 'B/W', 11: 'YIQ', 12: 'Lab', 15: 'Hexachrome', 17: 'CMYK', 18: 'Lab', 20: 'Registration', 21: 'Custom inks'}

def read(file):
	filesize = os.path.getsize(file)
	if test_format(file):
		format = test_format(file)
	else:
		sys.stderr.write('unknown swatch format\n')
		sys.exit(-1)
	swatch = defaultdict(dict)
	swatch['orig'] = format
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# Adobe Color Book                                              #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	if format == 'adobe_acb':
		file = open(file)
		file.seek(8, 1)
		length = struct.unpack('>L',file.read(4))[0]
		if length > 0:
			name = acb_decode_str(ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2))))
		if name > u'':
			swatch['swatch']['name'] = name
		length = struct.unpack('>L',file.read(4))[0]
		if length > 0:
			prefix = acb_decode_str(ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2))))
		else:
			prefix = u''
		length = struct.unpack('>L',file.read(4))[0]
		if length > 0:
			postfix = acb_decode_str(ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2))))
		else:
			postfix = u''
		length = struct.unpack('>L',file.read(4))[0]
		if length > 0:
			description = acb_decode_str(ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2))))
		if 'description' in vars() and description > u'':
			swatch['swatch']['description'] = description
		nbcolors = struct.unpack('>H',file.read(2))[0]
		swatch['display']['columns'] = struct.unpack('>H',file.read(2))[0]
		file.seek(2, 1)
		model = adobe_model[struct.unpack('>H',file.read(2))[0]]
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			length = struct.unpack('>L',file.read(4))[0]
			if length > 0:
				swatch['colors'][i]['name'] = prefix+acb_decode_str(ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2))))+postfix
			file.seek(6, 1)
			if model == 'RGB':
				R,G,B = struct.unpack('>3B',file.read(3))
				swatch['colors'][i]['RGB'] = (R/255,G/255,B/255)
			elif model == 'CMYK':
				C,M,Y,K = struct.unpack('>4B',file.read(4))
				swatch['colors'][i]['CMYK'] = (1-C/255,1-M/255,1-Y/255,1-K/255)
			elif model == 'Lab':
				L,a,b = struct.unpack('>3B',file.read(3))
				swatch['colors'][i]['Lab'] = (L/2.55,a-128,b-128)
			else:
				sys.stderr.write('unknown color model ['+model+']\n')
		if file.read(4):
			if struct.unpack('>4s',file.read(4))[0] == 'spot':
				swatch['swatch']['spot'] = True
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# Adobe Color Swatch                                            #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'adobe_aco':
		file = open(file)
		version, nbcolors = struct.unpack('>2H',file.read(4))
		if version == 1 and filesize > 4+nbcolors*10:
			file.seek(4+nbcolors*10)
			version, nbcolors = struct.unpack('>2H',file.read(4))
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			model = adobe_model[struct.unpack('>H',file.read(2))[0]]
			if model == 'CMYK':
				C,M,Y,K = struct.unpack('>4H',file.read(8))
				swatch['colors'][i]['CMYK'] = (1-C/65535,1-M/65535,1-Y/65535,1-K/65535)
			elif model == 'WideCMYK':
				C,M,Y,K = struct.unpack('>4H',file.read(8))
				swatch['colors'][i]['CMYK'] = (C/10000,M/10000,Y/10000,K/10000)
			elif model == 'RGB':
				R,G,B = struct.unpack('>3H',file.read(6))
				swatch['colors'][i]['RGB'] = (R/65535,G/65535,B/65535)
				file.seek(2, 1)
			elif model == 'HSV':
				H,S,V = struct.unpack('>3H',file.read(6))
				swatch['colors'][i]['HSV'] = (H/65535,S/65535,V/65535)
				file.seek(2, 1)
			elif model == 'Lab':
				R,G,B = struct.unpack('>H 2h',file.read(6))
				swatch['colors'][i]['Lab'] = (L/100,a/100,b/100)
				file.seek(2, 1)
			elif model == 'Gray':
				K = struct.unpack('>H',file.read(2))
				swatch['colors'][i]['Gray'] = (K/10000)
				file.seek(6, 1)
			else:
				sys.stderr.write('unknown color model ['+model+']\n')
			if version == 2:
				length = struct.unpack('>L',file.read(4))[0]
				if length > 0:
					swatch['colors'][i]['name'] = ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2)))
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# Adobe Swatch Exchange                                         #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'adobe_ase':
		file = open(file)
		file.seek(4)
		version = struct.unpack('>2H',file.read(4))
		nbblocks = struct.unpack('>L',file.read(4))[0]
		for i in range(nbblocks):
			swatch['colors'][i] = {}
			block_type,block_size = struct.unpack('>HL',file.read(6))
			if block_type == 0xc001:
				swatch['colors'][i]['group'] = 'start'
			elif block_type == 0xc002:
				swatch['colors'][i]['group'] = 'end'
			if block_size > 0:
				length = struct.unpack('>H',file.read(2))[0]
				if length > 0:
					name = ords2str(struct.unpack('>'+str(length)+'H',file.read(length*2)))
				if name > u'':
					swatch['colors'][i]['name'] = name
				if block_type == 0x0001:
					model = struct.unpack('4s',file.read(4))[0]
					if model == "CMYK":
						swatch['colors'][i]['CMYK'] = struct.unpack('>4f',file.read(16))
					elif model == "RGB ":
						swatch['colors'][i]['RGB'] = struct.unpack('>3f',file.read(12))
					elif model == "LAB ":
						L,a,b = struct.unpack('>3f',file.read(12))
						swatch['colors'][i]['Lab'] = (L*100,a,b)
					elif model == "Gray":
						swatch['colors'][i]['Gray'] = struct.unpack('>f',file.read(4))
					type = struct.unpack('>H',file.read(2))[0]
					if type == 0:
						swatch['colors'][i]['global'] = True
					elif type == 1:
						swatch['colors'][i]['spot'] = True
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# Adobe Color Table                                             #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'adobe_act':
		if os.path.getsize(file) == 772: # CS2
			file = open(file)
			file.seek(768, 0)
			nbcolors = struct.unpack('>H',file.read(2))[0]
			file.seek(0, 0)
		else:
			nbcolors = os.path.getsize(file)/3
			file = open(file)
		for i in range (nbcolors):
			swatch['colors'][i] = {}
			R,G,B = struct.unpack('3B',file.read(3))
			swatch['colors'][i]['RGB'] = (R/255,G/255,B/255)
			
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# ASCII Color Format                                            #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'adobe_acf':
		file = open(file, 'U').readlines()
		version = file[0].strip()
		swatch['swatch']['name'] = unicode(file[1].strip(),'macroman')
		swatch['swatch']['version'] = file[2].partition('LibraryVersion: ')[2].strip()
		copyright = file[3].partition('Copyright: ')[2].strip()
		if copyright > '':
			swatch['swatch']['copyright'] = unicode(copyright,'macroman')
		description = file[4].partition('AboutMessage: ')[2].strip()
		if description > '':
			swatch['swatch']['description'] = unicode(description,'macroman')
		name_format = file[5].partition('Names: ')[2].strip().lower() # Full Partial
		swatch['swatch']['columns'] = file[6].partition('Rows: ')[2].strip()
		swatch['swatch']['rows'] = file[7].partition('Columns: ')[2].strip()
		nbcolors = eval(file[8].partition('Entries: ')[2].strip())
		prefix = file[9].partition('Prefix: ')[2].strip()
		if prefix > '':
			prefix = prefix + ' '
		suffix = file[10].partition('Suffix: ')[2].strip()
		if suffix > '':
			suffix = ' ' + suffix
		type = file[11].partition('Type: ')[2].strip() # hifi Process Spot Mixed
		if type == 'Spot':
			swatch['swatch']['spot'] = True
		models = file[12].partition('Models: ')[2].strip().split() # hifi Lab RGB CMYK
		swatch['swatch']['preferredmodel'] = file[13].partition('PreferredModel: ')[2].strip()
		pos = 14
		if version == 'ACF 2.1':
			nbinks = int(file[pos].partition('Inks: ')[2].strip())
			pos = pos+1
			swatch['swatch']['inks'] = []
			for i in range(nbinks):
				swatch['swatch']['inks'].append(file[pos].strip())
				pos = pos+1
		pos = pos+1
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			for model in models:
				colors = file[pos].strip().split()
				for k in range(len(colors)):
					if model == 'RGB':
						colors[k] = round(eval(colors[k])/65535)
					else:
						colors[k] = eval(colors[k])
				swatch['colors'][i][model] = tuple(colors)
				pos = pos+1
			if type == 'Mixed':
				col_type = file[pos].strip()
				if col_type == 'Spot':
					swatch['colors'][i]['spot'] = True
				pos = pos+1
			swatch['colors'][i]['name'] = unicode(prefix+file[pos].strip()+suffix,'macroman')
			pos = pos+1
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# Binary Color Format                                           #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'adobe_bcf':
		file = open(file)
		version = struct.unpack('8s',file.read(8))[0].split('\x00', 1)[0]
		name = struct.unpack('32s',file.read(32))[0].split('\x00', 1)[0]
		if name > '':
			swatch['swatch']['name'] = unicode(name,'macroman')
		swatch['swatch']['version'] = struct.unpack('8s',file.read(8))[0].split('\x00', 1)[0]
		copyright = struct.unpack('32s',file.read(32))[0].split('\x00', 1)[0]
		if copyright > '':
			swatch['swatch']['copyright'] = unicode(copyright,'macroman')
		description = struct.unpack('512s',file.read(512))[0].split('\x00', 1)[0]
		if description > '':
			swatch['swatch']['description'] = unicode(description,'macroman')
		names, swatch['display']['columns'], swatch['display']['rows'], nbcolors =  struct.unpack('>4H',file.read(8))
		prefix =  struct.unpack('12s',file.read(12))[0].split('\x00', 1)[0]
		if prefix > '':
			prefix = prefix + ' '
		suffix = struct.unpack('4s',file.read(4))[0].split('\x00', 1)[0]
		if suffix > '':
			suffix = ' ' + suffix
		type, XYZ, CMYK, RGB, preferredmodel = struct.unpack('>5h',file.read(10))
		swatch['swatch']['preferredmodel'] = bcf_model[preferredmodel]
		if version in ('ACF 2.1','BCF 2.0'):
			extender = struct.unpack('>H',file.read(2))[0]
			if extender  == 1:
				description2 = struct.unpack('100s',file.read(100))[0].split('\x00', 1)[0]
				swatch['swatch']['description'] = swatch['swatch']['description']+unicode(description2,'macroman')
			inks,nbinks,Lab = struct.unpack('>3H',file.read(6))
			file.seek(24, 1)
			if inks  == 1:
				swatch['swatch']['inks'] = []
				for i in range(nbinks):
					swatch['swatch']['inks'] .append(struct.unpack('>10s 10s H 32s',file.read(54)))
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			if XYZ == 1:
				X,Y,Z  = struct.unpack('>3H',file.read(6))
				swatch['colors'][i]['XYZ'] = (X/65.535,Y/65.535,Z/65.535)
			elif 'Lab' in vars() and Lab == 1:
				swatch['colors'][i]['Lab'] = struct.unpack('>3h',file.read(6))
			else:
				file.seek(6, 1)
			if CMYK == 1:
				C,M,Y,K = struct.unpack('>4H',file.read(8))
				swatch['colors'][i]['CMYK'] = (C/65535,M/65535,Y/65535,K/65535)
			else:
				file.seek(8, 1)
			if RGB == 1:
				R,G,B = struct.unpack('>3H',file.read(6))
				swatch['colors'][i]['RGB'] = (round(R/65535),round(G/65535),round(B/65535))
			else:
				file.seek(6, 1)
			if version in ('ACF 2.1','BCF 2.0') and type in (8,16):
				col_nbinks = struct.unpack('>H',file.read(2))[0]
				hifi = []
				for j in range(col_nbinks):
					hifi.append( struct.unpack('>2H',file.read(4)))
				swatch['colors'][i]['hifi'] = tuple(hifi)
				file.seek((8-col_nbinks)*4, 1)
			col_type = struct.unpack('>H',file.read(2))[0]
			if col_type == 1:
				swatch['colors'][i]['spot'] = True
			if version in ('ACF 2.1','BCF 2.0') and type in (8,16):
				col_preferredmodel = struct.unpack('>H',file.read(2))[0]
				swatch['colors'][i]['preferredmodel'] = bcf_model[col_preferredmodel]
			swatch['colors'][i]['name'] = prefix+struct.unpack('32s',file.read(32))[0].split('\x00', 1)[0]+suffix
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# Flash Color Set                                               #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'adobe_clr':
		file = open(file)
		file.seek(16, 1)
		nbcolors = struct.unpack('<H',file.read(2))[0]
		file.seek(15, 1)
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			file.seek(1, 1)
			R,G,B,a = struct.unpack('4B',file.read(4))
			swatch['colors'][i]['RGBa'] = (R/255,G/255,B/255,a/255)
			file.seek(2, 1)
			H,S,L = struct.unpack('<3H',file.read(6))
			swatch['colors'][i]['HLS'] = (H/240,L/240,S/240)
			file.seek(2, 1)
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# AutoCAD Color Book                                            #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'autocad_acb':
		xml = etree.parse(file).getroot()
		swatch['swatch']['name'] = xml.getiterator('bookName')[0].text
		if len(xml.getiterator('majorVersion')) > 0:
			swatch['swatch']['version'] = xml.getiterator('majorVersion')[0].text+'.'+xml.getiterator('minorVersion')[0].text
		nbcolors = len(xml.getiterator('colorEntry'))
		swatch['display']['columns'] = 0
		i = 0
		for colorPage in xml.getiterator('colorPage'):
			swatch['display']['columns'] = max(swatch['display']['columns'],len(colorPage.getiterator('colorEntry')))
		breaks = []
		encrypted = 0
		for colorPage in xml.getiterator('colorPage'):
			for colorEntry in colorPage.getiterator('colorEntry'):
				swatch['colors'][i] = {}
				swatch['colors'][i]['name'] = colorEntry.find('colorName').text
				if colorEntry.find('RGB8Encrypt'):
					encrypted = 1
				elif colorEntry.find('RGB8'):
					swatch['colors'][i]['RGB'] = (eval(colorEntry.find('RGB8').find('red').text)/255,eval(colorEntry.find('RGB8').find('green').text)/255,eval(colorEntry.find('RGB8').find('blue').text)/255)
				i = i+1
			if len(colorPage.getiterator('colorEntry')) < swatch['display']['columns'] and i<nbcolors:
				breaks.append(i)
		if len(breaks)>0:
			swatch['display']['breaks'] = breaks
		if encrypted == 1:
			sys.stderr.write("this script can't decode encrypted RGB values\n")
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# QuarkXPress Color Library                                     #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'quark_qcl':
		xml = etree.parse(file).getroot()
		swatch['swatch']['name'] = xml.getiterator('file_descriptor')[0].text
		swatch['swatch']['copyright'] = xml.getiterator('originator')[0].text
		swatch['swatch']['preferredmodel'] = xml.getiterator('default_color_space')[0].text.strip()
		name_field_info = xml.getiterator('name_field_info')
		swatch['swatch']['name_format'] = {}
		prefix = {}
		suffix = {}
		for name in name_field_info:
			nid = eval(name.attrib['format_id'])-1
			prefix[nid], suffix[nid] =  name.attrib['long_form'].split('%n')
		ui_spec =  os.path.dirname(file)+'/'+xml.getiterator('ui_spec')[0].text
		if os.path.isfile(ui_spec):
			ui = etree.parse(ui_spec).getroot()
			swatch['display']['columns'] = eval(ui.getiterator('rows_per_page')[0].text)
			breaks = ui.getiterator('column_break')
			if len(breaks)>0:
				for i in range(len(breaks)):
					breaks[i] = eval(breaks[i].text)
				swatch['display']['breaks'] = breaks
		else:
			sys.stderr.write('ui file '+ui_spec+' doesn\'t exist\n')
		fields = xml.getiterator('field_info')
		data_format = {}
		for field in fields:
			data_format[field.attrib['name']] = field.attrib['pos']
		colors = xml.getiterator('tr')
		i = 0
		for color in colors:
			swatch['colors'][i] = {}
			name = color.getchildren()[eval(data_format['SAMPLE_ID'])-1].text
			if data_format.has_key('NAME_FORMAT_ID'):
				nid = eval(color.getchildren()[eval(data_format['NAME_FORMAT_ID'])-1].text)-1
				swatch['colors'][i]['name'] = prefix[nid]+name+suffix[nid]
			else:
				swatch['colors'][i]['name'] = prefix+name
			if data_format.has_key('LAB_L'):
				swatch['colors'][i]['Lab'] = (eval(color.getchildren()[eval(data_format['LAB_L'])-1].text),\
				                              eval(color.getchildren()[eval(data_format['LAB_A'])-1].text),\
				                              eval(color.getchildren()[eval(data_format['LAB_B'])-1].text))
			if data_format.has_key('RGB_R'):
				swatch['colors'][i]['RGB'] = (eval(color.getchildren()[eval(data_format['RGB_R'])-1].text),\
				                              eval(color.getchildren()[eval(data_format['RGB_G'])-1].text),\
				                              eval(color.getchildren()[eval(data_format['RGB_B'])-1].text))
			if data_format.has_key('CMYK_C'):
				swatch['colors'][i]['CMYK'] = (eval(color.getchildren()[eval(data_format['CMYK_C'])-1].text)/100,\
				                               eval(color.getchildren()[eval(data_format['CMYK_M'])-1].text)/100,\
				                               eval(color.getchildren()[eval(data_format['CMYK_Y'])-1].text)/100,\
				                               eval(color.getchildren()[eval(data_format['CMYK_K'])-1].text)/100)
			if data_format.has_key('PC6_1'):
				swatch['colors'][i]['6CLR'] = (eval(color.getchildren()[eval(data_format['PC6_1'])-1].text)/100,\
				                              eval(color.getchildren()[eval(data_format['PC6_2'])-1].text)/100,\
				                              eval(color.getchildren()[eval(data_format['PC6_3'])-1].text)/100,\
				                              eval(color.getchildren()[eval(data_format['PC6_4'])-1].text)/100,\
				                              eval(color.getchildren()[eval(data_format['PC6_5'])-1].text)/100,\
				                              eval(color.getchildren()[eval(data_format['PC6_6'])-1].text)/100)
			i = i+1
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# RAL                                                           #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'ral_bcs':
		file = open(file)
		offset, sig = struct.unpack('B 3s',file.read(4))
		if sig == 'clf':
			swatch['swatch']['spot'] = True
		file.seek(offset+1, 0)
		nbcolors = struct.unpack('<H',file.read(2))[0]
		length = struct.unpack('B',file.read(1))[0]
		name_tmp = struct.unpack(str(length)+'s',file.read(length))[0].split(':')
		swatch['swatch']['name'] = name_tmp[0].split('English_')[1]
		if name_tmp[1].split('German_')[1] != swatch['swatch']['name']:
			swatch['swatch']['name_de'] = name_tmp[1].split('German_')[1]
		file.seek(1, 1)
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			length = struct.unpack('B',file.read(1))[0]
			if length > 0:
				swatch['colors'][i]['name'] =  struct.unpack(str(length)+'s',file.read(length))[0]
			swatch['colors'][i]['Lab'] = struct.unpack('<3f',file.read(12))
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# RIFF Palette                                                  #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'riff_pal':
		file = open(file)
		file.seek(12, 0)
		chunk = struct.unpack('<4s L', file.read(8))
		while chunk[0] != 'data':
			file.seek(chunk[1], 1)
			chunk = struct.unpack('<4s L', file.read(8))
		version, nbcolors = struct.unpack('<2H',file.read(4))
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			R,G,B = struct.unpack('3B',file.read(3))
			swatch['colors'][i]['RGB'] = (R/255,G/255,B/255)
			file.seek(1, 1)
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# ColorSchemer                                                  #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'colorschemer':
		file = open(file)
		version, nbcolors = struct.unpack('<2H',file.read(4))
		file.seek(4, 1)
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			R,G,B = struct.unpack('3B',file.read(3))
			swatch['colors'][i]['RGB'] = (R/255,G/255,B/255)
			file.seek(1, 1)
			length = struct.unpack('<L',file.read(4))[0]
			if length > 0:
				swatch['colors'][i]['name'] =  struct.unpack(str(length)+'s',file.read(length))[0]
			file.seek(11, 1)
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	# CorelDRAW Palette                                             #
	# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
	elif format == 'corel_cpl':
		file = open(file)
		version = file.read(2)
		if version == '\xdc\xdc': #custom palettes
			length = struct.unpack('B',file.read(1))[0]
			if length > 0:
				swatch['swatch']['name'] =  struct.unpack(str(length)+'s',file.read(length))[0]
			nbcolors = struct.unpack('<H',file.read(2))[0]
		elif version in ('\xcc\xbc','\xcc\xdc'):
			nbcolors = struct.unpack('<H',file.read(2))[0]
		else:
			nbheaders = struct.unpack('<L',file.read(4))[0]
			headers = {}
			for i in range(nbheaders):
				id,offset = struct.unpack('<2L',file.read(8))
				headers[id] = offset
			# Header 0: Name
			file.seek(headers[0], 0)
			length = struct.unpack('B',file.read(1))[0]
			if length > 0:
				if version == '\xcd\xdc':
					swatch['swatch']['name'] =  struct.unpack(str(length)+'s',file.read(length))[0]
				else:
					swatch['swatch']['name'] =  ords2str(struct.unpack('<'+str(length)+'H',file.read(length*2)))
			# Header 1: Palette Type
			file.seek(headers[1], 0)
			type = struct.unpack('<H',file.read(2))[0]
			# Header 2: Number of colors
			file.seek(headers[2], 0)
			nbcolors = struct.unpack('<H',file.read(2))[0]
			# Header 3: Custom inks
			if 3 in headers:
				file.seek(headers[3], 0)
				nbinks = struct.unpack('<H',file.read(2))[0]
				swatch['swatch']['inks'] = struct.unpack('<'+str(nbinks)+'H',file.read(nbinks*2))
			# Header 5: UI informations
			if 5 in headers:
				file.seek(headers[5], 0)
				swatch['display']['columns'],swatch['display']['rows'] = struct.unpack('<2H',file.read(4))
			file.seek(headers[2]+2, 0)
		if version in ('\xcd\xbc','\xcd\xdc','\xcd\xdd') and type < 38 and type not in(5,16):
			long = True
		else:
			long = False
		row = {}
		col = {}
		breaks = []
		for i in range(nbcolors):
			swatch['colors'][i] = {}
			if long:
				file.seek(4, 1)
			model =  struct.unpack('<H',file.read(2))[0]
			file.seek(2, 1)
			if model == 1:
				file.seek(4, 1)
				swatch['colors'][i]['PMS'] =  struct.unpack('<2H',file.read(4))
			elif model == 2:
				file.seek(4, 1)
				C,M,Y,K =  struct.unpack('4B',file.read(4))
				swatch['colors'][i]['CMYK'] = (C/100,M/100,Y/100,K/100)
			elif model in (3,17):
				file.seek(4, 1)
				C,M,Y,K =  struct.unpack('4B',file.read(4))
				swatch['colors'][i]['CMYK'] = (C/255,M/255,Y/255,K/255)
			elif model == 4:
				file.seek(4, 1)
				C,M,Y =  struct.unpack('3B',file.read(3))
				swatch['colors'][i]['CMY'] = (C/255,M/255,Y/255)
				file.seek(1, 1)
			elif model in (5,21):
				file.seek(4, 1)
				B,G,R = struct.unpack('3B',file.read(3))
				swatch['colors'][i]['RGB'] = (R/255,G/255,B/255)
				file.seek(1, 1)
			elif model == 6:
				file.seek(4, 1)
				H,S,V = struct.unpack('<H2B',file.read(4))
				swatch['colors'][i]['HSV'] = (H/360,S/255,V/255)
			elif model == 7:
				file.seek(4, 1)
				H,L,S = struct.unpack('<H2B',file.read(4))
				swatch['colors'][i]['HLS'] = (H/360,L/255,S/255)
			elif model == 9:
				file.seek(4, 1)
				K =  struct.unpack('B',file.read(1))[0]
				swatch['colors'][i]['Gray'] = (1-K/255)
				file.seek(3, 1)
			elif model == 11:
				file.seek(4, 1)
				Y,I,Q =  struct.unpack('3B',file.read(3))
				swatch['colors'][i]['YIQ'] = (Y/255,I/255,Q/255)
				file.seek(1, 1)
			elif model == 12:
				file.seek(4, 1)
				L,a,b =  struct.unpack('B 2b',file.read(3))
				swatch['colors'][i]['Lab'] = (L/2.55,a,b)
				file.seek(1, 1)
			elif model == 15:
				file.seek(2, 1)
				Y,O,M,C,G,K =  struct.unpack('6B',file.read(3))
				swatch['colors'][i]['6CLR'] = (C/255,M/255,Y/255,K/255,O/255,G/255)
			elif model == 18:
				file.seek(4, 1)
				L,a,b =  struct.unpack('3B',file.read(3))
				swatch['colors'][i]['Lab'] = (L/2.55,a-128,b-128)
				file.seek(1, 1)
			else:
				file.seek(8, 1)
			if long:
				model2 =  struct.unpack('<H',file.read(2))[0]
				file.seek(2, 1)
				if model2 == model:
					file.seek(8, 1)
				else:
					if model2 == 1:
						file.seek(4, 1)
						swatch['colors'][i]['PMS'] =  struct.unpack('<2H',file.read(4))
					elif model2 == 2:
						file.seek(4, 1)
						C,M,Y,K =  struct.unpack('4B',file.read(4))
						swatch['colors'][i]['CMYK'] = (C/100,M/100,Y/100,K/100)
					elif model2 in (3,17):
						file.seek(4, 1)
						C,M,Y,K =  struct.unpack('4B',file.read(4))
						swatch['colors'][i]['CMYK'] = (C/255,M/255,Y/255,K/255)
					elif model2 == 4:
						file.seek(4, 1)
						C,M,Y =  struct.unpack('3B',file.read(3))
						swatch['colors'][i]['CMY'] = (C/255,M/255,Y/255)
						file.seek(1, 1)
					elif model2 in (5,21):
						file.seek(4, 1)
						B,G,R = struct.unpack('3B',file.read(3))
						swatch['colors'][i]['RGB'] = (R/255,G/255,B/255)
						file.seek(1, 1)
					elif model2 == 6:
						file.seek(4, 1)
						H,S,V = struct.unpack('<H2B',file.read(4))
						swatch['colors'][i]['HSV'] = (H/360,S/255,V/255)
					elif model2 == 7:
						file.seek(4, 1)
						H,L,S = struct.unpack('<H2B',file.read(4))
						swatch['colors'][i]['HLS'] = (H/360,L/255,S/255)
					elif model2 == 9:
						file.seek(4, 1)
						K =  struct.unpack('B',file.read(1))[0]
						swatch['colors'][i]['Gray'] = (1-K/255)
						file.seek(3, 1)
					elif model2 == 11:
						file.seek(4, 1)
						Y,I,Q =  struct.unpack('3B',file.read(3))
						swatch['colors'][i]['YIQ'] = (Y/255,I/255,Q/255)
						file.seek(1, 1)
					elif model == 12:
						file.seek(4, 1)
						L,a,b =  struct.unpack('B 2b',file.read(3))
						swatch['colors'][i]['Lab'] = (L/2.55,a,b)
						file.seek(1, 1)
					elif model2 == 15:
						file.seek(2, 1)
						Y,O,M,C,G,K =  struct.unpack('6B',file.read(3))
						swatch['colors'][i]['6CLR'] = (C/255,M/255,Y/255,K/255,O/255,G/255)
					elif model2 == 18:
						file.seek(4, 1)
						L,a,b =  struct.unpack('3B',file.read(3))
						swatch['colors'][i]['Lab'] = (L/2.55,a-128,b-128)
						file.seek(1, 1)
					else:
						file.seek(8, 1)
			length = struct.unpack('B',file.read(1))[0]
			if length > 0:
				if version in ('\xdc\xdc','\xcc\xdc') or (version == '\xcd\xdc' and type not in (16)):
					swatch['colors'][i]['name'] =  struct.unpack(str(length)+'s',file.read(length))[0]
				else:
					swatch['colors'][i]['name'] =  ords2str(struct.unpack('<'+str(length)+'H',file.read(length*2)))
			if version == '\xcd\xdd':
				row[i], col[i] = struct.unpack('<2L',file.read(8))
				if row[i] > 0 and col[i] == 0 and col[i-1] < swatch['display']['columns']-1:
					breaks.append(i)
				file.seek(4, 1)				
		if len(breaks)>0:
			swatch['display']['breaks'] = breaks
	return swatch

def toRGB8(color):
	if 'RGB' in color:
		R,G,B = color['RGB']
	elif 'HSV' in color:
		H,S,V = color['HSV']
		R,G,B = HSV2RGB(H,S,V)
	elif 'HLS' in color:
		H,L,S = color['HLS']
		R,G,B = HSL2RGB(H,S,L)
	elif 'Lab' in color:
		L,a,b = color['Lab']
		return Lab2RGB8(L,a,b)
	elif 'CMYK' in color:
		C,M,Y,K = color['CMYK']
		R,G,B = CMYK2RGB(C,M,Y,K)
	elif 'Gray' in color:
		R = G = B = 1-color['Gray'][0]
	else:
		return False
	return (int(round(R*255)),int(round(G*255)),int(round(B*255)))
		
def hex2(val):
	return hex(int(round(val)))[2:].rjust(2,'0')

def write(swatch, format):
	if format == 'gimp':
		gpl = 'GIMP Palette\n'
		if 'name' in swatch['swatch']:
			gpl += 'Name: '+swatch['swatch']['name']+'\n'
		if 'columns' in swatch['display']:
			gpl += 'Columns: '+str(swatch['display']['columns'])+'\n'
		gpl += '#'

		for key,color in swatch['colors'].items():
			if toRGB8(color):
				R,G,B = toRGB8(color)
				gpl += '\n'+str(R).rjust(3)+' '+str(G).rjust(3)+' '+str(B).rjust(3)
				if 'name' in color:
					gpl += '\t'+color['name']
		return gpl.encode('utf-8')
	elif format == 'scribus':
		scsw = '<?xml version="1.0" encoding="UTF-8"?>\n<swatch>\n'
		for key,color in swatch['colors'].items():
			if 'group' not in color:
				scsw += '\t<COLOR '
				if 'CMYK' in color:
					C,M,Y,K = color['CMYK']
					scsw += 'CMYK="#'+hex2(C*255)+hex2(M*255)+hex2(Y*255)+hex2(K*255)+'"'
				elif 'Gray' in color:
					K = color['Gray'][0]
					scsw += 'CMYK="#000000'+hex2(K*255)+'"'
				else:
					if toRGB8(color):
						R,G,B = toRGB8(color)
						scsw += 'RGB="#'+hex2(R)+hex2(G)+hex2(B)+'"'
				if 'name' in color:
					scsw += ' NAME="'+color['name']+'"'
				if 'spot' in swatch['swatch'] or 'spot' in color:
					scsw += ' Spot="1"'
				scsw += ' />\n'
		scsw += '</swatch>'
		return scsw.encode('utf-8')
	elif format == 'create':
		create = '<?xml version="1.0" encoding="UTF-8"?>\n'
		create += '<colors\n   xmlns:xlink="http://www.w3.org/1999/xlink"\n   xmlns:dc="http://purl.org/dc/elements/1.1/";>\n'
		if 'swatch' in swatch:
			create += '  <metadata>\n'
			if 'name' in swatch['swatch']:
				create += '    <dc:title>'+swatch['swatch']['name']+'</dc:title>\n'
			if 'name_de' in swatch['swatch']:
				create += '    <dc:title xml:lang="de-DE">'+swatch['swatch']['name_de']+'</dc:title>\n'
			if 'description' in swatch['swatch']:
				create += '    <dc:description>'+swatch['swatch']['description']+'</dc:description>\n'
			if 'copyright' in swatch['swatch']:
				create += '    <dc:rights>'+swatch['swatch']['copyright']+'</dc:rights>\n'
			if 'version' in swatch['swatch']:
				create += '    <version>'+swatch['swatch']['version']+'</version>\n'
			create += '  </metadata>\n'
		if 'display' in swatch:
			create += '  <display>\n'
			if 'columns' in swatch['display']:
				create += '    <columns>'+str(swatch['display']['columns'])+'</columns>\n'
			if 'rows' in swatch['display']:
				create += '    <rows>'+str(swatch['display']['rows'])+'</rows>\n'
			if 'breaks' in swatch['display']:
				create += '    <breaks>'+str(swatch['display']['breaks']).strip('[]')+'</breaks>\n'
			create += '  </display>\n'
		group = 0
		for key,color in swatch['colors'].items():
			if 'group' in color:
				if color['group'] == 'start':
					create += '  <colorgroup'
					if 'name' in color:
						create += ' name="'+color['name']+'"'
					create += '>\n'
					group = 1
				elif color['group'] == 'end':
					create += '  </colorgroup>\n'
					group = 0
			else:
				if group == 1:
					create += '  '
				create += '  <color'
				if 'name' in color:
					create += ' name="'+color['name']+'"'
				if 'spot' in swatch['swatch'] or 'spot' in color:
					create += ' spot="1"'
				create += '>\n'
				if 'RGB' in color:
					R,G,B = color['RGB']
					if group == 1:
						create += '  '
					create += '    <RGB r="'+str(round(R,4))+'" g="'+str(round(G,4))+'" b="'+str(round(B,4))+'" />\n'
				if 'HSV' in color:
					H,S,V = color['HSV']
					if group == 1:
						create += '  '
					create += '    <HSV h="'+str(int(round(H*360)))+'" s="'+str(round(S,4))+'" v="'+str(round(V,4))+'" />\n'
				if 'HLS' in color:
					H,L,S = color['HLS']
					if group == 1:
						create += '  '
					create += '    <HLS h="'+str(int(round(H*360)))+'" l="'+str(round(L,4))+'" s="'+str(round(S,4))+'" />\n'
				if 'CMYK' in color:
					C,M,Y,K = color['CMYK']
					if group == 1:
						create += '  '
					create += '    <CMYK c="'+str(round(C,4))+'" m="'+str(round(M,4))+'" y="'+str(round(Y,4))+'" k="'+str(round(K,4))+'" />\n'
				if 'CMY' in color:
					C,M,Y = color['CMY']
					if group == 1:
						create += '  '
					create += '    <CMY c="'+str(round(C,4))+'" m="'+str(round(M,4))+'" y="'+str(round(Y,4))+'" />\n'
				if 'Gray' in color:
					K = color['Gray'][0]
					if group == 1:
						create += '  '
					create += '    <Gray k="'+str(round(C,4))+'" />\n'
				if 'Lab' in color:
					L,a,b = color['Lab']
					if group == 1:
						create += '  '
					create += '    <Lab L="'+str(round(L,4))+'" a="'+str(round(a,4))+'" b="'+str(round(b,4))+'" />\n'
				if 'XYZ' in color:
					X,Y,Z = color['XYZ']
					if group == 1:
						create += '  '
					create += '    <XYZ x="'+str(round(L,4))+'" y="'+str(round(a,4))+'" z="'+str(round(b,4))+'" />\n'
				if '6CLR' in color:
					C,M,Y,K,O,G = color['6CLR']
					if group == 1:
						create += '  '
					create += '    <6CLR c="'+str(round(C,4))+'" m="'+str(round(M,4))+'" y="'+str(round(Y,4))+'" k="'+str(round(K,4))+'" o="'+str(round(O,4))+'" g="'+str(round(G,4))+'" />\n'
				if 'preferredmodel' in color or 'preferredmodel' in swatch['swatch']:
					if 'preferredmodel' in color:
						preferredmodel = color['preferredmodel']
					else:
						preferredmodel = swatch['swatch']['preferredmodel']
					if group == 1:
						create += '  '
					create += '    <preferredmodel>'+preferredmodel+'</preferredmodel>\n'
				if group == 1:
					create += '  '
				create += '  </color>\n'
		create += '</colors>'
		return create.encode('utf-8')
	elif format == 'dump':
		return swatch
	else:
		sys.stderr.write('unsupported output format\n')

def main():
	args = sys.argv[1:]
	if len(args) != 2:
		sys.stderr.write('usage: python swatch_convertor.py inputfile outputformat[gimp|scribus|create|dump]')
		sys.exit(-1)

	file = args[0]
	filename = os.path.basename(file)
	basename = os.path.splitext(filename)[0]

	if not os.path.isfile(file):
		sys.stderr.write('file '+file+' doesn\'t exist')
		sys.exit(-1)

	if os.path.getsize(file) == 0:
		sys.stderr.write('empty file')
		sys.exit(-1)

	print write(read(file),args[1])

if __name__ == '__main__':
    main()
_______________________________________________
CREATE mailing list
CREATE@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/create

Reply via email to