This content has moved - please find it at https://devblog.cyotek.com.

Although these pages remain accessible, some content may not display correctly in future as the new blog evolves.

Visit https://devblog.cyotek.com.

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.

The sample application adapted to support writing CorelDRAW palettes

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

An example of the neatly-aligned output produced by this article

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.

Converting from RGB to CYMK and back to RGB can result slightly mismatched results

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

Opening a palette created by this article in CorelDRAW! 3.0

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
  • sha256: 9cc4800ea9713c1d7f95660b1deba20f3c13e9f317171b2cbc0ff358ace10f7e

Sample project for the working with CorelDRAW Palettes part 2 blog post.

05/08/2018 Download

About The Author

Gravatar

The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game. Until that time, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.

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?

Styling with Markdown is supported