Working with CorelDRAW Palettes part 2, writing .pal files
In my previous article, I described how to read an archaic CorelDRAW! 3.0 palette file. This continuation covers how to write files in this format.
Writing the palette
Just like reading the file, writing is also a simple enough process.
The first task is to work out the longest swatch name, with a minimum length of 32. This will allow us to write the text aligned so it's still easily readable or writable with a standard text editor.
private int GetLongestName()
{
int result;
result = 32;
for (int i = 0; i < _palette.Count; i++)
{
string name;
name = _palette[i].Name;
if (!string.IsNullOrEmpty(name) && name.Length > result)
{
result = name.Length;
}
}
return result;
}
After that, it is a matter of looping our colours and for each
colour write a line describing it to a TextWriter
.
public void Save(Stream stream)
{
int longestSwatchName;
StringBuilder sb;
longestSwatchName = this.GetLongestName();
sb = new StringBuilder(longestSwatchName + 20);
using (TextWriter writer = new StreamWriter(stream, Encoding.ASCII, 1024, true))
{
for (int i = 0; i < _palette.Count; i++)
{
Color color;
string name;
color = _palette[i];
name = color.Name;
this.ConvertRgbToCmyk(color, out byte c, out byte m, out byte y, out byte k);
this.WriteName(sb, name, longestSwatchName);
this.WriteNumber(sb, c);
this.WriteNumber(sb, m);
this.WriteNumber(sb, y);
this.WriteNumber(sb, k);
writer.WriteLine(sb.ToString());
sb.Length = 0;
}
}
}
After writing each line, I reset the Length
property of the
StringBuilder
instance to zero so I can reuse the same
instance, avoiding new object allocations.
Newer versions of .NET include a
Clear
method, but it does exactly the same thing under the hood.
Although I encountered the SUB
character in my previous
digging into these palettes, given it was the exception rather
than the norm I've chosen not to write an end of file character
in this sample application.
Writing the swatch name
Remembering that the names need to be quoted, we write "
characters before and after the actual swatch name. Then, to
ensure that subsequent values are aligned, we write some padding
spaces. We could do this by creating a new string with a space
character repeated the requisite count, but as usual I'm going
try and sidestep the allocation.
private void WriteName(StringBuilder sb, string name, int longestSwatchName)
{
sb.Append('"');
sb.Append(name);
sb.Append('"');
//sb.Append(new string(' ', longestSwatchName - name.Length));
for (int j = (name ?? string.Empty).Length; j < longestSwatchName; j++)
{
sb.Append(' ');
}
}
Once again, this article is overdue and so I haven't done any profiling. When I get back on track I will probably revisit these potentially micro-optimisation routines I keep creating and see if there's a concrete reason for doing them or if I'm just writing code for no reason.
Writing colour values
As with writing the swatch name, I don't want to create a string version of the source value, and then another string for the padding as these are allocations that aren't required.
Instead, with a range of 0-100
that means I'm only dealing
values that will be 1, 2 or 3 characters long so I decided to
create a helper to write the padded value without creating
interim strings (the 100
below doesn't count as it is
automatically interned).
private void WriteNumber(StringBuilder sb, byte value)
{
//sb.Append(value.ToString().PadRight(4));
if (value == 100)
{
sb.Append("100 ");
}
else
{
sb.Append(' ');
if (value < 10)
{
sb.Append(' ');
}
sb.Append(value);
sb.Append(' ');
}
}
In the first version of this program, I was left aligning the strings, when they should actually be right aligned. The alignment doesn't actually seem to matter - CorelDRAW! 3 opened a left aligned palette quite happily, but better to be consistent.
Converting RGB to CMYK
Although I covered RGB to CMYK conversion in a previous article, the version used in this sample is slightly different to account for the fact that CorelDRAW 3 palettes use the range 0-100.
As previously noted, converting from RGB to CMYK and then back to RGB can introduce subtle errors which means the original RGB colour may not match the converted version.
private void ConvertRgbToCmyk(Color color, out byte c, out byte m, out byte y, out byte k)
{
float r;
float g;
float b;
float divisor;
r = color.R / 255F;
g = color.G / 255F;
b = color.B / 255F;
divisor = 1 - Math.Max(Math.Max(r, g), b);
c = ClampCmyk((1 - r - divisor) / (1 - divisor));
m = ClampCmyk((1 - g - divisor) / (1 - divisor));
y = ClampCmyk((1 - b - divisor) / (1 - divisor));
k = ClampCmyk(divisor);
}
private static byte ClampCmyk(float value)
{
if (value < 0 || float.IsNaN(value))
{
value = 0;
}
return Convert.ToByte(value * 100);
}
Sample application
The sample application from part 1 has been updated to include the ability to write CorelDRAW! 3.0 palettes in addition to reading them.
Update History
- 2018-08-05 - First published
- 2020-11-22 - Updated formatting
Related articles you may be interested in
Downloads
Filename | Description | Version | Release Date | |
---|---|---|---|---|
CorelDrawPalPaletteWriter.zip
|
Sample project for the working with CorelDRAW Palettes part 2 blog post. |
05/08/2018 | Download |
Leave a Comment
While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?