#region MigraDoc - Creating Documents on the Fly
//
// Authors:
// Stefan Lange
// Klaus Potzesny
// David Stephensen
//
// Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://www.migradoc.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using MigraDoc.DocumentObjectModel.publics;
namespace MigraDoc.DocumentObjectModel
{
///
/// The Color class represents an ARGB color value.
///
[DebuggerDisplay("(A={A}, R={R}, G={G}, B={B} C={C}, M={M}, Y={Y}, K={K})")]
public struct Color : INullableValue
{
///
/// Initializes a new instance of the Color class.
///
public Color(uint argb)
{
_isCmyk = false;
_argb = argb;
_a = _c = _m = _y = _k = 0f; // Compiler enforces this line of code
InitCmykFromRgb();
}
///
/// Initializes a new instance of the Color class.
///
public Color(byte r, byte g, byte b)
{
_isCmyk = false;
_argb = 0xFF000000 | ((uint)r << 16) | ((uint)g << 8) | b;
_a = _c = _m = _y = _k = 0f; // Compiler enforces this line of code
InitCmykFromRgb();
}
///
/// Initializes a new instance of the Color class.
///
public Color(byte a, byte r, byte g, byte b)
{
_isCmyk = false;
_argb = ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | b;
_a = _c = _m = _y = _k = 0f; // Compiler enforces this line of code
InitCmykFromRgb();
}
///
/// Initializes a new instance of the Color class with a CMYK color.
/// All values must be in a range between 0 to 100 percent.
///
public Color(double alpha, double cyan, double magenta, double yellow, double black)
{
_isCmyk = true;
_a = (float)(alpha > 100 ? 100 : (alpha < 0 ? 0 : alpha));
_c = (float)(cyan > 100 ? 100 : (cyan < 0 ? 0 : cyan));
_m = (float)(magenta > 100 ? 100 : (magenta < 0 ? 0 : magenta));
_y = (float)(yellow > 100 ? 100 : (yellow < 0 ? 0 : yellow));
_k = (float)(black > 100 ? 100 : (black < 0 ? 0 : black));
_argb = 0; // Compiler enforces this line of code
InitRgbFromCmyk();
}
///
/// Initializes a new instance of the Color class with a CMYK color.
/// All values must be in a range between 0 to 100 percent.
///
public Color(double cyan, double magenta, double yellow, double black)
: this(100, cyan, magenta, yellow, black)
{ }
void InitCmykFromRgb()
{
// Similar formula as in PDFsharp
_isCmyk = false;
int c = 255 - (int)R;
int m = 255 - (int)G;
int y = 255 - (int)B;
int k = Math.Min(c, Math.Min(m, y));
if (k == 255)
_c = _m = _y = 0;
else
{
float black = 255f - k;
_c = 100f * (c - k) / black;
_m = 100f * (m - k) / black;
_y = 100f * (y - k) / black;
}
_k = 100f * k / 255f;
_a = A / 2.55f;
}
void InitRgbFromCmyk()
{
// Similar formula as in PDFsharp
_isCmyk = true;
float black = _k * 2.55f + 0.5f;
float factor = (255f - black) / 100f;
byte a = (byte)(_a * 2.55 + 0.5);
byte r = (byte)(255 - Math.Min(255f, _c * factor + black));
byte g = (byte)(255 - Math.Min(255f, _m * factor + black));
byte b = (byte)(255 - Math.Min(255f, _y * factor + black));
_argb = ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | b;
}
///
/// Gets a value indicating whether this instance is a CMYK color.
///
public bool IsCmyk
{
get { return _isCmyk; }
}
///
/// Determines whether this color is empty.
///
public bool IsEmpty
{
get { return this == Empty; }
}
///
/// Returns the value.
///
object INullableValue.GetValue()
{
return this;
}
///
/// Sets the given value.
///
void INullableValue.SetValue(object value)
{
if (value is uint)
_argb = (uint)value;
else
this = Parse(value.ToString());
}
///
/// Resets this instance, i.e. IsNull() will return true afterwards.
///
void INullableValue.SetNull()
{
this = Empty;
}
///
/// Determines whether this instance is null (not set).
///
bool INullableValue.IsNull
{
get { return this == Empty; }
}
///
/// Determines whether this instance is null (not set).
///
public bool IsNull
{
get { return this == Empty; }
}
///
/// Gets or sets the ARGB value.
///
public uint Argb
{
get { return _argb; }
set
{
if (_isCmyk)
throw new InvalidOperationException("Cannot change a CMYK color.");
_argb = value;
InitCmykFromRgb();
}
}
///
/// Gets or sets the RGB value.
///
public uint RGB
{
get { return _argb; }
set
{
if (_isCmyk)
throw new InvalidOperationException("Cannot change a CMYK color.");
_argb = value;
InitCmykFromRgb();
}
}
///
/// Calls base class Equals.
///
public override bool Equals(Object obj)
{
if (obj is Color)
{
Color color = (Color)obj;
if (_isCmyk ^ color._isCmyk)
return false;
if (_isCmyk)
return _a == color._a && _c == color._c && _m == color._m && _y == color._y && _k == color._k;
return _argb == color._argb;
}
return false;
}
///
/// Gets the ARGB value that this Color instance represents.
///
public override int GetHashCode()
{
return (int)_argb ^ _a.GetHashCode() ^ _c.GetHashCode() ^ _m.GetHashCode() ^ _y.GetHashCode() ^ _k.GetHashCode();
}
///
/// Compares two color objects. True if both argb values are equal, false otherwise.
///
public static bool operator ==(Color color1, Color color2)
{
if (color1._isCmyk ^ color2._isCmyk)
return false;
if (color1._isCmyk)
return color1._a == color2._a && color1._c == color2._c && color1._m == color2._m && color1._y == color2._y && color1._k == color2._k;
return color1._argb == color2._argb;
}
///
/// Compares two color objects. True if both argb values are not equal, false otherwise.
///
public static bool operator !=(Color color1, Color color2)
{
return !(color1 == color2);
}
///
/// Parses the string and returns a color object.
/// Throws ArgumentException if color is invalid.
/// Supports four different formats for hex colors.
/// Format 1: uses prefix "0x", followed by as many hex digits as needed. Important: do not forget the opacity, so use 7 or 8 digits.
/// Format 2: uses prefix "#", followed by exactly 8 digits including opacity.
/// Format 3: uses prefix "#", followed by exactly 6 digits; opacity will be 0xff.
/// Format 4: uses prefix "#", followed by exactly 3 digits; opacity will be 0xff; "#ccc" will be treated as "#ffcccccc", "#d24" will be treated as "#ffdd2244".
///
/// integer, hex or color name.
public static Color Parse(string color)
{
if (color == null)
throw new ArgumentNullException("color");
if (color == "")
throw new ArgumentException("color");
try
{
uint clr;
// Must use Enum.Parse because Enum.IsDefined is case sensitive
try
{
object obj = Enum.Parse(typeof(ColorName), color, true);
clr = (uint)obj;
return new Color(clr);
}
catch
{
// Ignore exception because it's not a ColorName.
}
NumberStyles numberStyle = NumberStyles.Integer;
string number = color.ToLower();
if (number.StartsWith("0x"))
{
numberStyle = NumberStyles.HexNumber;
number = color.Substring(2);
}
else if (number.StartsWith("#"))
{
numberStyle = NumberStyles.HexNumber;
switch (color.Length)
{
case 9:
number = color.Substring(1);
break;
case 7:
number = "ff" + color.Substring(1);
break;
case 4:
number = "ff" + color.Substring(1,1) + color.Substring(1, 1) +
color.Substring(2, 1) + color.Substring(2, 1) +
color.Substring(3, 1) + color.Substring(3, 1);
break;
default:
throw new ArgumentException(DomSR.InvalidColorString(color), "color");
}
}
clr = uint.Parse(number, numberStyle);
return new Color(clr);
}
catch (FormatException ex)
{
throw new ArgumentException(DomSR.InvalidColorString(color), ex);
}
}
///
/// Gets the alpha (transparency) part of the RGB Color.
/// The values is in the range between 0 to 255.
///
public uint A
{
get { return (_argb & 0xFF000000) >> 24; }
}
///
/// Gets the red part of the Color.
/// The values is in the range between 0 to 255.
///
public uint R
{
get { return (_argb & 0xFF0000) >> 16; }
}
///
/// Gets the green part of the Color.
/// The values is in the range between 0 to 255.
///
public uint G
{
get { return (_argb & 0x00FF00) >> 8; }
}
///
/// Gets the blue part of the Color.
/// The values is in the range between 0 to 255.
///
public uint B
{
get { return _argb & 0x0000FF; }
}
///
/// Gets the alpha (transparency) part of the CMYK Color.
/// The values is in the range between 0 (transparent) to 100 (opaque) percent.
///
public double Alpha
{
get { return _a; }
}
///
/// Gets the cyan part of the Color.
/// The values is in the range between 0 to 100 percent.
///
public double C
{
get { return _c; }
}
///
/// Gets the magenta part of the Color.
/// The values is in the range between 0 to 100 percent.
///
public double M
{
get { return _m; }
}
///
/// Gets the yellow part of the Color.
/// The values is in the range between 0 to 100 percent.
///
public double Y
{
get { return _y; }
}
///
/// Gets the key (black) part of the Color.
/// The values is in the range between 0 to 100 percent.
///
public double K
{
get { return _k; }
}
///
/// Gets a non transparent color brightened in terms of transparency if any is given(A < 255),
/// otherwise this instance itself.
///
public Color GetMixedTransparencyColor()
{
int alpha = (int)A;
if (alpha == 0xFF)
return this;
int red = (int)R;
int green = (int)G;
int blue = (int)B;
double whiteFactor = 1 - alpha / 255.0;
red = (int)(red + (255 - red) * whiteFactor);
green = (int)(green + (255 - green) * whiteFactor);
blue = (int)(blue + (255 - blue) * whiteFactor);
return new Color((uint)(0xFF << 24 | (red << 16) | (green << 8) | blue));
}
///
/// Writes the Color object in its hexadecimal value.
///
public override string ToString()
{
if (_stdColors == null)
{
#if !SILVERLIGHT
Array colorNames = Enum.GetNames(typeof(ColorName));
Array colorValues = Enum.GetValues(typeof(ColorName));
int count = colorNames.GetLength(0);
_stdColors = new Dictionary(count);
for (int index = 0; index < count; index++)
{
string c = (string)colorNames.GetValue(index);
uint d = (uint)colorValues.GetValue(index);
// Some color are double named...
// Aqua == Cyan
// Fuchsia == Magenta
Style key = new Style();
if (!_stdColors.ContainsKey(d))
_stdColors.Add(d, c);
}
#else
_stdColors = new Dictionary();
_stdColors.Add(0xFFF0F8FF, "AliceBlue");
_stdColors.Add(0xFFFAEBD7, "AntiqueWhite");
_stdColors.Add(0xFF00FFFF, "Aqua");
_stdColors.Add(0xFF7FFFD4, "Aquamarine");
_stdColors.Add(0xFFF0FFFF, "Azure");
_stdColors.Add(0xFFF5F5DC, "Beige");
_stdColors.Add(0xFFFFE4C4, "Bisque");
_stdColors.Add(0xFF000000, "Black");
_stdColors.Add(0xFFFFEBCD, "BlanchedAlmond");
_stdColors.Add(0xFF0000FF, "Blue");
_stdColors.Add(0xFF8A2BE2, "BlueViolet");
_stdColors.Add(0xFFA52A2A, "Brown");
_stdColors.Add(0xFFDEB887, "BurlyWood");
_stdColors.Add(0xFF5F9EA0, "CadetBlue");
_stdColors.Add(0xFF7FFF00, "Chartreuse");
_stdColors.Add(0xFFD2691E, "Chocolate");
_stdColors.Add(0xFFFF7F50, "Coral");
_stdColors.Add(0xFF6495ED, "CornflowerBlue");
_stdColors.Add(0xFFFFF8DC, "Cornsilk");
_stdColors.Add(0xFFDC143C, "Crimson");
//_stdColors.Add(0xFF00FFFF, "Cyan"); already added as "Aqua"
_stdColors.Add(0xFF00008B, "DarkBlue");
_stdColors.Add(0xFF008B8B, "DarkCyan");
_stdColors.Add(0xFFB8860B, "DarkGoldenrod");
_stdColors.Add(0xFFA9A9A9, "DarkGray");
_stdColors.Add(0xFF006400, "DarkGreen");
_stdColors.Add(0xFFBDB76B, "DarkKhaki");
_stdColors.Add(0xFF8B008B, "DarkMagenta");
_stdColors.Add(0xFF556B2F, "DarkOliveGreen");
_stdColors.Add(0xFFFF8C00, "DarkOrange");
_stdColors.Add(0xFF9932CC, "DarkOrchid");
_stdColors.Add(0xFF8B0000, "DarkRed");
_stdColors.Add(0xFFE9967A, "DarkSalmon");
_stdColors.Add(0xFF8FBC8B, "DarkSeaGreen");
_stdColors.Add(0xFF483D8B, "DarkSlateBlue");
_stdColors.Add(0xFF2F4F4F, "DarkSlateGray");
_stdColors.Add(0xFF00CED1, "DarkTurquoise");
_stdColors.Add(0xFF9400D3, "DarkViolet");
_stdColors.Add(0xFFFF1493, "DeepPink");
_stdColors.Add(0xFF00BFFF, "DeepSkyBlue");
_stdColors.Add(0xFF696969, "DimGray");
_stdColors.Add(0xFF1E90FF, "DodgerBlue");
_stdColors.Add(0xFFB22222, "Firebrick");
_stdColors.Add(0xFFFFFAF0, "FloralWhite");
_stdColors.Add(0xFF228B22, "ForestGreen");
_stdColors.Add(0xFFFF00FF, "Fuchsia");
_stdColors.Add(0xFFDCDCDC, "Gainsboro");
_stdColors.Add(0xFFF8F8FF, "GhostWhite");
_stdColors.Add(0xFFFFD700, "Gold");
_stdColors.Add(0xFFDAA520, "Goldenrod");
_stdColors.Add(0xFF808080, "Gray");
_stdColors.Add(0xFF008000, "Green");
_stdColors.Add(0xFFADFF2F, "GreenYellow");
_stdColors.Add(0xFFF0FFF0, "Honeydew");
_stdColors.Add(0xFFFF69B4, "HotPink");
_stdColors.Add(0xFFCD5C5C, "IndianRed");
_stdColors.Add(0xFF4B0082, "Indigo");
_stdColors.Add(0xFFFFFFF0, "Ivory");
_stdColors.Add(0xFFF0E68C, "Khaki");
_stdColors.Add(0xFFE6E6FA, "Lavender");
_stdColors.Add(0xFFFFF0F5, "LavenderBlush");
_stdColors.Add(0xFF7CFC00, "LawnGreen");
_stdColors.Add(0xFFFFFACD, "LemonChiffon");
_stdColors.Add(0xFFADD8E6, "LightBlue");
_stdColors.Add(0xFFF08080, "LightCoral");
_stdColors.Add(0xFFE0FFFF, "LightCyan");
_stdColors.Add(0xFFFAFAD2, "LightGoldenrodYellow");
_stdColors.Add(0xFFD3D3D3, "LightGray");
_stdColors.Add(0xFF90EE90, "LightGreen");
_stdColors.Add(0xFFFFB6C1, "LightPink");
_stdColors.Add(0xFFFFA07A, "LightSalmon");
_stdColors.Add(0xFF20B2AA, "LightSeaGreen");
_stdColors.Add(0xFF87CEFA, "LightSkyBlue");
_stdColors.Add(0xFF778899, "LightSlateGray");
_stdColors.Add(0xFFB0C4DE, "LightSteelBlue");
_stdColors.Add(0xFFFFFFE0, "LightYellow");
_stdColors.Add(0xFF00FF00, "Lime");
_stdColors.Add(0xFF32CD32, "LimeGreen");
_stdColors.Add(0xFFFAF0E6, "Linen");
// _stdColors.Add(0xFFFF00FF, "Magenta"); already added as "Fuchsia"
_stdColors.Add(0xFF800000, "Maroon");
_stdColors.Add(0xFF66CDAA, "MediumAquamarine");
_stdColors.Add(0xFF0000CD, "MediumBlue");
_stdColors.Add(0xFFBA55D3, "MediumOrchid");
_stdColors.Add(0xFF9370DB, "MediumPurple");
_stdColors.Add(0xFF3CB371, "MediumSeaGreen");
_stdColors.Add(0xFF7B68EE, "MediumSlateBlue");
_stdColors.Add(0xFF00FA9A, "MediumSpringGreen");
_stdColors.Add(0xFF48D1CC, "MediumTurquoise");
_stdColors.Add(0xFFC71585, "MediumVioletRed");
_stdColors.Add(0xFF191970, "MidnightBlue");
_stdColors.Add(0xFFF5FFFA, "MintCream");
_stdColors.Add(0xFFFFE4E1, "MistyRose");
_stdColors.Add(0xFFFFE4B5, "Moccasin");
_stdColors.Add(0xFFFFDEAD, "NavajoWhite");
_stdColors.Add(0xFF000080, "Navy");
_stdColors.Add(0xFFFDF5E6, "OldLace");
_stdColors.Add(0xFF808000, "Olive");
_stdColors.Add(0xFF6B8E23, "OliveDrab");
_stdColors.Add(0xFFFFA500, "Orange");
_stdColors.Add(0xFFFF4500, "OrangeRed");
_stdColors.Add(0xFFDA70D6, "Orchid");
_stdColors.Add(0xFFEEE8AA, "PaleGoldenrod");
_stdColors.Add(0xFF98FB98, "PaleGreen");
_stdColors.Add(0xFFAFEEEE, "PaleTurquoise");
_stdColors.Add(0xFFDB7093, "PaleVioletRed");
_stdColors.Add(0xFFFFEFD5, "PapayaWhip");
_stdColors.Add(0xFFFFDAB9, "PeachPuff");
_stdColors.Add(0xFFCD853F, "Peru");
_stdColors.Add(0xFFFFC0CB, "Pink");
_stdColors.Add(0xFFDDA0DD, "Plum");
_stdColors.Add(0xFFB0E0E6, "PowderBlue");
_stdColors.Add(0xFF800080, "Purple");
_stdColors.Add(0xFFFF0000, "Red");
_stdColors.Add(0xFFBC8F8F, "RosyBrown");
_stdColors.Add(0xFF4169E1, "RoyalBlue");
_stdColors.Add(0xFF8B4513, "SaddleBrown");
_stdColors.Add(0xFFFA8072, "Salmon");
_stdColors.Add(0xFFF4A460, "SandyBrown");
_stdColors.Add(0xFF2E8B57, "SeaGreen");
_stdColors.Add(0xFFFFF5EE, "SeaShell");
_stdColors.Add(0xFFA0522D, "Sienna");
_stdColors.Add(0xFFC0C0C0, "Silver");
_stdColors.Add(0xFF87CEEB, "SkyBlue");
_stdColors.Add(0xFF6A5ACD, "SlateBlue");
_stdColors.Add(0xFF708090, "SlateGray");
_stdColors.Add(0xFFFFFAFA, "Snow");
_stdColors.Add(0xFF00FF7F, "SpringGreen");
_stdColors.Add(0xFF4682B4, "SteelBlue");
_stdColors.Add(0xFFD2B48C, "Tan");
_stdColors.Add(0xFF008080, "Teal");
_stdColors.Add(0xFFD8BFD8, "Thistle");
_stdColors.Add(0xFFFF6347, "Tomato");
_stdColors.Add(0x00FFFFFF, "Transparent");
_stdColors.Add(0xFF40E0D0, "Turquoise");
_stdColors.Add(0xFFEE82EE, "Violet");
_stdColors.Add(0xFFF5DEB3, "Wheat");
_stdColors.Add(0xFFFFFFFF, "White");
_stdColors.Add(0xFFF5F5F5, "WhiteSmoke");
_stdColors.Add(0xFFFFFF00, "Yellow");
_stdColors.Add(0xFF9ACD32, "YellowGreen");
#endif
}
if (_isCmyk)
{
string s;
if (Alpha == 100.0)
s = String.Format(CultureInfo.InvariantCulture, "CMYK({0:0.##},{1:0.##},{2:0.##},{3:0.##})", C, M, Y, K);
else
s = String.Format(CultureInfo.InvariantCulture, "CMYK({0:0.##},{1:0.##},{2:0.##},{3:0.##},{4:0.##})", Alpha, C, M, Y, K);
return s;
}
else
{
if (_stdColors.ContainsKey(_argb))
return _stdColors[_argb];
if ((_argb & 0xFF000000) == 0xFF000000)
return "RGB(" +
((_argb & 0xFF0000) >> 16) + "," +
((_argb & 0x00FF00) >> 8) + "," +
(_argb & 0x0000FF) + ")";
return "0x" + _argb.ToString("X");
}
}
static Dictionary _stdColors;
///
/// Creates an RGB color from an existing color with a new alpha (transparency) value.
///
public static Color FromRgbColor(byte a, Color color)
{
return new Color(a, (byte)color.R, (byte)color.G, (byte)color.B);
}
///
/// Creates an RGB color from red, green, and blue.
///
public static Color FromRgb(byte r, byte g, byte b)
{
return new Color(255, r, g, b);
}
///
/// Creates an RGB color from alpha, red, green, and blue.
///
public static Color FromArgb(byte alpha, byte r, byte g, byte b)
{
return new Color(alpha, r, g, b);
}
///
/// Creates a Color structure from the specified CMYK values.
/// All values must be in a range between 0 to 100 percent.
///
public static Color FromCmyk(double cyan, double magenta, double yellow, double black)
{
return new Color(cyan, magenta, yellow, black);
}
///
/// Creates a Color structure from the specified CMYK values.
/// All values must be in a range between 0 to 100 percent.
///
public static Color FromCmyk(double alpha, double cyan, double magenta, double yellow, double black)
{
return new Color(alpha, cyan, magenta, yellow, black);
}
///
/// Creates a CMYK color from an existing color with a new alpha (transparency) value.
///
public static Color FromCmykColor(double alpha, Color color)
{
return new Color(alpha, color.C, color.M, color.Y, color.K);
}
uint _argb; // ARGB
bool _isCmyk;
private float _a; // \
private float _c; // |
private float _m; // |--- alpha + CMYK
private float _y; // |
private float _k; // /
///
/// Represents a null color.
///
public static readonly Color Empty = new Color(0);
}
}