Module: HexaPDF::Content::ColorSpace

Defined in:
lib/hexapdf/content/color_space.rb

Overview

This module contains the color space implementations.

General Information

The PDF specification defines several color spaces. Probably the most used ones are the device color spaces DeviceRGB, DeviceCMYK and DeviceGray. However, there are several others. For example, patterns are also implemented via color spaces.

HexaPDF provides implementations for the most common color spaces. Additional ones can easily be added. After implementing one it just has to be registered on the global configuration object under the ‘color_space.map’ key.

Color space implementations are currently used so that different colors can be distinguished and to provide better error handling.

Color Space Implementations

A color space implementation consists of two classes: one for the color space and one for its colors.

The class for the color space needs to respond to the following methods:

#initialize(definition)

Creates the color space using the given array with the color space definition. The first item in the array is always the color space family, the other items are color space specific.

#family

Returns the PDF name of the color space family this color space belongs to.

#definition

Returns the color space definition as array or symbol.

#default_color

Returns the default color for this color space.

#color(*args)

Returns the color corresponding to the given arguments which may be normalized to conform to the PDF spec. The number and types of the arguments differ from one color space to another.

#prenormalized_color(*args)

Returns the color corresponding to the given arguments without applying value normalization. The number and types of the arguments differ from one color space to another.

This method should be used when the arguments are already normalized (e.g. when loaded from a content stream).

The class representing a color in the color space needs to respond to the following methods:

#color_space

Returns the associated color space object.

#components

Returns an array of components that uniquely identifies this color within the color space.

See: PDF2.0 s8.6

Defined Under Namespace

Modules: ColorUtils Classes: DeviceCMYK, DeviceGray, DeviceRGB, Universal

Constant Summary collapse

COLOR_NAMES =

Mapping of color names (CSS Color Module Level 3 names - see www.w3.org/TR/css-color-3/#svg-color - and HexaPDF design color names) to RGB and gray values.

Visual listing of all colors:

#>pdf-big
canvas.font("Helvetica", size: 7.5)
map = HexaPDF::Content::ColorSpace::COLOR_NAMES
map.each_slice(43).each_with_index do |slice, col|
  x = 10 + col * 100
  slice.each_with_index do |(name, rgb), row|
    canvas.fill_color(rgb).rectangle(x, 380 - row * 9, 9, 9).fill
    canvas.fill_color("black").text(name, at: [x + 15, 380 - row * 9 + 2])
  end
end
{
  "aliceblue" => [240, 248, 255],
  "antiquewhite" => [250, 235, 215],
  "aqua" => [0, 255, 255],
  "aquamarine" => [127, 255, 212],
  "azure" => [240, 255, 255],
  "beige" => [245, 245, 220],
  "bisque" => [255, 228, 196],
  "black" => [0, 0, 0],
  "blanchedalmond" => [255, 235, 205],
  "blue" => [0, 0, 255],
  "blueviolet" => [138, 43, 226],
  "brown" => [165, 42, 42],
  "burlywood" => [222, 184, 135],
  "cadetblue" => [95, 158, 160],
  "chartreuse" => [127, 255, 0],
  "chocolate" => [210, 105, 30],
  "coral" => [255, 127, 80],
  "cornflowerblue" => [100, 149, 237],
  "cornsilk" => [255, 248, 220],
  "crimson" => [220, 20, 60],
  "cyan" => [0, 255, 255],
  "darkblue" => [0, 0, 139],
  "darkcyan" => [0, 139, 139],
  "darkgoldenrod" => [184, 134, 11],
  "darkgray" => [169],
  "darkgreen" => [0, 100, 0],
  "darkgrey" => [169],
  "darkkhaki" => [189, 183, 107],
  "darkmagenta" => [139, 0, 139],
  "darkolivegreen" => [85, 107, 47],
  "darkorange" => [255, 140, 0],
  "darkorchid" => [153, 50, 204],
  "darkred" => [139, 0, 0],
  "darksalmon" => [233, 150, 122],
  "darkseagreen" => [143, 188, 143],
  "darkslateblue" => [72, 61, 139],
  "darkslategray" => [47, 79, 79],
  "darkslategrey" => [47, 79, 79],
  "darkturquoise" => [0, 206, 209],
  "darkviolet" => [148, 0, 211],
  "deeppink" => [255, 20, 147],
  "deepskyblue" => [0, 191, 255],
  "dimgray" => [105],
  "dimgrey" => [105],
  "dodgerblue" => [30, 144, 255],
  "firebrick" => [178, 34, 34],
  "floralwhite" => [255, 250, 240],
  "forestgreen" => [34, 139, 34],
  "fuchsia" => [255, 0, 255],
  "gainsboro" => [220, 220, 220],
  "ghostwhite" => [248, 248, 255],
  "gold" => [255, 215, 0],
  "goldenrod" => [218, 165, 32],
  "gray" => [128],
  "green" => [0, 128, 0],
  "greenyellow" => [173, 255, 47],
  "grey" => [128],
  "honeydew" => [240, 255, 240],
  "hotpink" => [255, 105, 180],
  "indianred" => [205, 92, 92],
  "indigo" => [75, 0, 130],
  "ivory" => [255, 255, 240],
  "khaki" => [240, 230, 140],
  "lavender" => [230, 230, 250],
  "lavenderblush" => [255, 240, 245],
  "lawngreen" => [124, 252, 0],
  "lemonchiffon" => [255, 250, 205],
  "lightblue" => [173, 216, 230],
  "lightcoral" => [240, 128, 128],
  "lightcyan" => [224, 255, 255],
  "lightgoldenrodyellow" => [250, 250, 210],
  "lightgray" => [211],
  "lightgreen" => [144, 238, 144],
  "lightgrey" => [211, 211, 211],
  "lightpink" => [255, 182, 193],
  "lightsalmon" => [255, 160, 122],
  "lightseagreen" => [32, 178, 170],
  "lightskyblue" => [135, 206, 250],
  "lightslategray" => [119, 136, 153],
  "lightslategrey" => [119, 136, 153],
  "lightsteelblue" => [176, 196, 222],
  "lightyellow" => [255, 255, 224],
  "lime" => [0, 255, 0],
  "limegreen" => [50, 205, 50],
  "linen" => [250, 240, 230],
  "magenta" => [255, 0, 255],
  "maroon" => [128, 0, 0],
  "mediumaquamarine" => [102, 205, 170],
  "mediumblue" => [0, 0, 205],
  "mediumorchid" => [186, 85, 211],
  "mediumpurple" => [147, 112, 219],
  "mediumseagreen" => [60, 179, 113],
  "mediumslateblue" => [123, 104, 238],
  "mediumspringgreen" => [0, 250, 154],
  "mediumturquoise" => [72, 209, 204],
  "mediumvioletred" => [199, 21, 133],
  "midnightblue" => [25, 25, 112],
  "mintcream" => [245, 255, 250],
  "mistyrose" => [255, 228, 225],
  "moccasin" => [255, 228, 181],
  "navajowhite" => [255, 222, 173],
  "navy" => [0, 0, 128],
  "oldlace" => [253, 245, 230],
  "olive" => [128, 128, 0],
  "olivedrab" => [107, 142, 35],
  "orange" => [255, 165, 0],
  "orangered" => [255, 69, 0],
  "orchid" => [218, 112, 214],
  "palegoldenrod" => [238, 232, 170],
  "palegreen" => [152, 251, 152],
  "paleturquoise" => [175, 238, 238],
  "palevioletred" => [219, 112, 147],
  "papayawhip" => [255, 239, 213],
  "peachpuff" => [255, 218, 185],
  "peru" => [205, 133, 63],
  "pink" => [255, 192, 203],
  "plum" => [221, 160, 221],
  "powderblue" => [176, 224, 230],
  "purple" => [128, 0, 128],
  "red" => [255, 0, 0],
  "rosybrown" => [188, 143, 143],
  "royalblue" => [65, 105, 225],
  "saddlebrown" => [139, 69, 19],
  "salmon" => [250, 128, 114],
  "sandybrown" => [244, 164, 96],
  "seagreen" => [46, 139, 87],
  "seashell" => [255, 245, 238],
  "sienna" => [160, 82, 45],
  "silver" => [192, 192, 192],
  "skyblue" => [135, 206, 235],
  "slateblue" => [106, 90, 205],
  "slategray" => [112, 128, 144],
  "slategrey" => [112, 128, 144],
  "snow" => [255, 250, 250],
  "springgreen" => [0, 255, 127],
  "steelblue" => [70, 130, 180],
  "tan" => [210, 180, 140],
  "teal" => [0, 128, 128],
  "thistle" => [216, 191, 216],
  "tomato" => [255, 99, 71],
  "turquoise" => [64, 224, 208],
  "violet" => [238, 130, 238],
  "wheat" => [245, 222, 179],
  "white" => [255, 255, 255],
  "whitesmoke" => [245, 245, 245],
  "yellow" => [255, 255, 0],
  "yellowgreen" => [154, 205, 50],
  "hp-blue" => [0, 128, 255],
  "hp-blue-dark" => [28, 91, 216],
  "hp-blue-dark2" => [34, 57, 184],
  "hp-blue-light" => [86, 176, 255],
  "hp-blue-light2" => [185, 220, 255],
  "hp-orange" => [255, 128, 0],
  "hp-orange-light" => [255, 195, 29],
  "hp-orange-light2" => [255, 246, 153],
  "hp-teal" => [0, 140, 130],
  "hp-teal-dark" => [5, 100, 94],
  "hp-teal-dark2" => [6, 70, 63],
  "hp-teal-light" => [75, 177, 176],
  "hp-teal-light2" => [177, 221, 221],
  "hp-gray" => [158],
  "hp-gray-dark" => [97],
  "hp-gray-dark2" => [33],
  "hp-gray-light" => [224],
  "hp-gray-light2" => [245],
}.freeze

Class Method Summary collapse

Class Method Details

.device_color_from_specification(*spec) ⇒ Object

:call-seq:

ColorSpace.device_color_from_specification(gray)           => color
ColorSpace.device_color_from_specification(r, g, b)        => color
ColorSpace.device_color_from_specification(c, m, y, k)     => color
ColorSpace.device_color_from_specification(string)         => color
ColorSpace.device_color_from_specification(array)          => color

Creates and returns a device color object from the given color specification.

There are several ways to define the color that should be used:

  • A single numeric argument specifies a gray color (see DeviceGray::Color).

  • Three numeric arguments specify an RGB color (see DeviceRGB::Color).

  • A string in the format “RRGGBB” where “RR” is the hexadecimal number for the red, “GG” for the green and “BB” for the blue color value also specifies an RGB color.

  • As does a string in the format “RGB” where “RR”, “GG” and “BB” would be used as the hexadecimal numbers for the red, green and blue color values of an RGB color.

  • Any other string is treated as a color name (CSS Color Module Level 3 and HexaPDF design color names are supported - see COLOR_NAMES).

  • Four numeric arguments specify a CMYK color (see DeviceCMYK::Color).

  • An array is treated as if its items were specified separately as arguments.

Note that it makes a difference whether integer or float values are used because the given values are first normalized (expected range by the PDF specification is 0.0 - 1.0) - see DeviceGray#color, DeviceRGB#color and DeviceCMYK#color for details.

Examples:

#>pdf
cs = HexaPDF::Content::ColorSpace
canvas.line_width(5)

# Note that Canvas#stroke_color implicitly uses this method, so
# explicitly using it like in this example is not needed
canvas.stroke_color(cs.device_color_from_specification(160))
canvas.line(10, 10, 10, 190).stroke
canvas.stroke_color(cs.device_color_from_specification(0, 128, 255))
canvas.line(35, 10, 35, 190).stroke
canvas.stroke_color(cs.device_color_from_specification("0088FF"))
canvas.line(60, 10, 60, 190).stroke
canvas.stroke_color(cs.device_color_from_specification("08F"))
canvas.line(85, 10, 85, 190).stroke
canvas.stroke_color(cs.device_color_from_specification("gold"))
canvas.line(110, 10, 110, 190).stroke
canvas.stroke_color(cs.device_color_from_specification("hp-blue"))
canvas.line(135, 10, 135, 190).stroke
canvas.stroke_color(cs.device_color_from_specification(10, 50, 0, 60))
canvas.line(160, 10, 160, 190).stroke
canvas.stroke_color(cs.device_color_from_specification([0, 128, 255]))
canvas.line(185, 10, 185, 190).stroke


338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/hexapdf/content/color_space.rb', line 338

def self.device_color_from_specification(*spec)
  spec.flatten!
  first_item = spec[0]
  if spec.length == 1 && first_item.kind_of?(String)
    spec = if first_item.match?(/\A\h{6}\z/)
             first_item.scan(/../).map!(&:hex)
           elsif first_item.match?(/\A\h{3}\z/)
             first_item.each_char.map {|x| (x * 2).hex }
           elsif COLOR_NAMES.key?(first_item)
             COLOR_NAMES[first_item]
           else
             raise ArgumentError, "Given string '#{first_item}' is neither a hex color " \
               "nor a color name"
           end
  end
  GlobalConfiguration.constantize('color_space.map', for_components(spec)).new.color(*spec)
end

.for_components(components) ⇒ Object

Returns the name of the device color space that should be used for creating a color object from the components array.



382
383
384
385
386
387
388
389
390
391
# File 'lib/hexapdf/content/color_space.rb', line 382

def self.for_components(components)
  case components.length
  when 1 then :DeviceGray
  when 3 then :DeviceRGB
  when 4 then :DeviceCMYK
  else
    raise ArgumentError, "Invalid number of color components, 1|3|4 expected, " \
      "#{components.length} given"
  end
end

.prenormalized_device_color(components) ⇒ Object

Returns a device color object for the given components array without applying value normalization.



375
376
377
378
# File 'lib/hexapdf/content/color_space.rb', line 375

def self.prenormalized_device_color(components)
  GlobalConfiguration.constantize('color_space.map', for_components(components)).new.
    prenormalized_color(*components)
end

.serialize_device_color(color, type: :fill) ⇒ Object

Serializes the given device color into the form expected by PDF content streams.

The type argument can either be :stroke to serialize as stroke color operator or :fill as fill color operator.



360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/hexapdf/content/color_space.rb', line 360

def self.serialize_device_color(color, type: :fill)
  operator = case color.color_space.family
             when :DeviceRGB then :rg
             when :DeviceGray then :g
             when :DeviceCMYK then :k
             else
               raise ArgumentError, "Device color object expected, got #{color.class}"
             end
  operator = operator.upcase if type == :stroke
  Content::Operator::DEFAULT_OPERATORS[operator].
    serialize(HexaPDF::Serializer.new, *color.components)
end