postsvg 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +105 -0
- data/CLAUDE.md +173 -0
- data/Gemfile +2 -3
- data/README.adoc +456 -179
- data/Rakefile +100 -0
- data/TODO.roadmap/00-architecture.md +139 -0
- data/TODO.roadmap/01-autoload-migration.md +39 -0
- data/TODO.roadmap/02-isolate-dormant-code.md +51 -0
- data/TODO.roadmap/03-domain-model.md +66 -0
- data/TODO.roadmap/04-lexer.md +40 -0
- data/TODO.roadmap/05-parser.md +43 -0
- data/TODO.roadmap/06-graphics-state.md +45 -0
- data/TODO.roadmap/07-matrix-and-color.md +37 -0
- data/TODO.roadmap/08-svg-builder.md +51 -0
- data/TODO.roadmap/09-renderer.md +52 -0
- data/TODO.roadmap/10-visitor.md +58 -0
- data/TODO.roadmap/11-operator-coverage.md +103 -0
- data/TODO.roadmap/12-svg-domain.md +62 -0
- data/TODO.roadmap/13-translation-handlers.md +69 -0
- data/TODO.roadmap/14-ps-serializer.md +49 -0
- data/TODO.roadmap/15-cli-and-public-api.md +47 -0
- data/TODO.roadmap/16-specs.md +84 -0
- data/TODO.roadmap/17-docs-sync.md +47 -0
- data/TODO.roadmap/18-performance-and-determinism.md +47 -0
- data/TODO.roadmap/19-error-model.md +45 -0
- data/TODO.roadmap/20-font-and-text.md +46 -0
- data/TODO.roadmap/21-images.md +45 -0
- data/TODO.roadmap/22-forms-and-resources.md +25 -0
- data/TODO.roadmap/23-level2-level3.md +52 -0
- data/TODO.roadmap/24-ci-and-release.md +42 -0
- data/TODO.roadmap/README.md +77 -0
- data/docs/.gitignore +29 -0
- data/docs/CHANGELOG.md +114 -0
- data/docs/COMPLETE_DOCUMENTATION_STATUS.md +376 -0
- data/docs/DEPLOYMENT.md +456 -0
- data/docs/DEPLOYMENT_INSTRUCTIONS.md +229 -0
- data/docs/DOCUMENTATION_PLAN.md +425 -0
- data/docs/FINAL_SUMMARY.md +657 -0
- data/docs/Gemfile +15 -0
- data/docs/README.md +327 -0
- data/docs/_config.yml +99 -0
- data/docs/advanced-topics.adoc +370 -0
- data/docs/api-reference/colors.adoc +705 -0
- data/docs/api-reference/converter.adoc +699 -0
- data/docs/api-reference/execution-context.adoc +1210 -0
- data/docs/api-reference/graphics-state.adoc +1070 -0
- data/docs/api-reference/interpreter.adoc +810 -0
- data/docs/api-reference/matrix.adoc +1179 -0
- data/docs/api-reference/path-builder.adoc +1284 -0
- data/docs/api-reference/postsvg-module.adoc +388 -0
- data/docs/api-reference/svg-generator.adoc +891 -0
- data/docs/api-reference/tokenizer.adoc +925 -0
- data/docs/api-reference.adoc +221 -0
- data/docs/architecture/command-registry.adoc +1191 -0
- data/docs/architecture/conversion-pipeline.adoc +746 -0
- data/docs/architecture/design-decisions.adoc +999 -0
- data/docs/architecture/generator-stage.adoc +1115 -0
- data/docs/architecture/graphics-state-model.adoc +1089 -0
- data/docs/architecture/interpreter-stage.adoc +1125 -0
- data/docs/architecture/parser-stage.adoc +1051 -0
- data/docs/architecture.adoc +354 -0
- data/docs/cli-reference/batch-command.adoc +616 -0
- data/docs/cli-reference/check-command.adoc +677 -0
- data/docs/cli-reference/cli-options.adoc +802 -0
- data/docs/cli-reference/convert-command.adoc +462 -0
- data/docs/cli-reference/version-command.adoc +296 -0
- data/docs/cli-reference.adoc +317 -0
- data/docs/concepts/conversion-pipeline.adoc +903 -0
- data/docs/concepts/coordinate-systems.adoc +836 -0
- data/docs/concepts/graphics-state.adoc +861 -0
- data/docs/concepts/path-operations.adoc +1076 -0
- data/docs/concepts/postscript-language.adoc +859 -0
- data/docs/concepts/svg-generation.adoc +937 -0
- data/docs/concepts.adoc +198 -0
- data/docs/contributing.adoc +443 -0
- data/docs/development.adoc +420 -0
- data/docs/faq.adoc +493 -0
- data/docs/getting-started/basic-usage.adoc +538 -0
- data/docs/getting-started/common-workflows.adoc +577 -0
- data/docs/getting-started/first-conversion.adoc +492 -0
- data/docs/getting-started/installation.adoc +534 -0
- data/docs/getting-started.adoc +94 -0
- data/docs/index.adoc +248 -0
- data/docs/optimization.adoc +196 -0
- data/docs/ps2svg_compatibility.adoc +149 -0
- data/docs/quick-reference.adoc +453 -0
- data/docs/sitemap.adoc +337 -0
- data/docs/troubleshooting.adoc +486 -0
- data/docs/validation.adoc +772 -0
- data/exe/postsvg +1 -0
- data/lib/postsvg/cli.rb +104 -57
- data/lib/postsvg/color.rb +132 -0
- data/lib/postsvg/errors.rb +68 -3
- data/lib/postsvg/format_number.rb +22 -0
- data/lib/postsvg/graphics_context.rb +80 -0
- data/lib/postsvg/graphics_stack.rb +43 -0
- data/lib/postsvg/model/literals/array.rb +41 -0
- data/lib/postsvg/model/literals/dictionary.rb +34 -0
- data/lib/postsvg/model/literals/hex.rb +37 -0
- data/lib/postsvg/model/literals/name.rb +40 -0
- data/lib/postsvg/model/literals/number.rb +36 -0
- data/lib/postsvg/model/literals/procedure.rb +41 -0
- data/lib/postsvg/model/literals/string.rb +30 -0
- data/lib/postsvg/model/literals.rb +19 -0
- data/lib/postsvg/model/operator.rb +58 -0
- data/lib/postsvg/model/operators/arithmetic.rb +264 -0
- data/lib/postsvg/model/operators/boolean.rb +182 -0
- data/lib/postsvg/model/operators/color.rb +74 -0
- data/lib/postsvg/model/operators/container.rb +186 -0
- data/lib/postsvg/model/operators/control_flow.rb +119 -0
- data/lib/postsvg/model/operators/device.rb +21 -0
- data/lib/postsvg/model/operators/dictionary.rb +118 -0
- data/lib/postsvg/model/operators/font.rb +121 -0
- data/lib/postsvg/model/operators/graphics_state.rb +84 -0
- data/lib/postsvg/model/operators/painting.rb +29 -0
- data/lib/postsvg/model/operators/path.rb +169 -0
- data/lib/postsvg/model/operators/stack.rb +72 -0
- data/lib/postsvg/model/operators/transformations.rb +103 -0
- data/lib/postsvg/model/operators.rb +89 -0
- data/lib/postsvg/model/program.rb +68 -0
- data/lib/postsvg/model/token.rb +43 -0
- data/lib/postsvg/model.rb +17 -0
- data/lib/postsvg/options.rb +29 -0
- data/lib/postsvg/renderer.rb +85 -0
- data/lib/postsvg/serializer.rb +325 -0
- data/lib/postsvg/source/ast_builder.rb +308 -0
- data/lib/postsvg/source/lexer.rb +322 -0
- data/lib/postsvg/source/operand_stack.rb +55 -0
- data/lib/postsvg/source.rb +21 -0
- data/lib/postsvg/svg/attribute_parser.rb +45 -0
- data/lib/postsvg/svg/clip_path_registry.rb +44 -0
- data/lib/postsvg/svg/document.rb +22 -0
- data/lib/postsvg/svg/element.rb +84 -0
- data/lib/postsvg/svg/elements/circle.rb +36 -0
- data/lib/postsvg/svg/elements/clip_path.rb +26 -0
- data/lib/postsvg/svg/elements/defs.rb +24 -0
- data/lib/postsvg/svg/elements/ellipse.rb +38 -0
- data/lib/postsvg/svg/elements/group.rb +37 -0
- data/lib/postsvg/svg/elements/image.rb +35 -0
- data/lib/postsvg/svg/elements/line.rb +36 -0
- data/lib/postsvg/svg/elements/path.rb +32 -0
- data/lib/postsvg/svg/elements/polygon.rb +12 -0
- data/lib/postsvg/svg/elements/polyline.rb +32 -0
- data/lib/postsvg/svg/elements/rect.rb +44 -0
- data/lib/postsvg/svg/elements/svg.rb +39 -0
- data/lib/postsvg/svg/elements/text.rb +42 -0
- data/lib/postsvg/svg/elements.rb +31 -0
- data/lib/postsvg/svg/paint.rb +34 -0
- data/lib/postsvg/svg/parser.rb +39 -0
- data/lib/postsvg/svg/path_data/command.rb +27 -0
- data/lib/postsvg/svg/path_data/parser.rb +82 -0
- data/lib/postsvg/svg/path_data.rb +17 -0
- data/lib/postsvg/svg/stroke.rb +31 -0
- data/lib/postsvg/svg/transform_list.rb +59 -0
- data/lib/postsvg/svg.rb +22 -0
- data/lib/postsvg/svg_builder.rb +249 -0
- data/lib/postsvg/translation/arc_converter.rb +86 -0
- data/lib/postsvg/translation/bounding_box.rb +59 -0
- data/lib/postsvg/translation/context.rb +34 -0
- data/lib/postsvg/translation/handler_registry.rb +38 -0
- data/lib/postsvg/translation/handlers/circle_handler.rb +28 -0
- data/lib/postsvg/translation/handlers/clip_path_handler.rb +13 -0
- data/lib/postsvg/translation/handlers/defs_handler.rb +15 -0
- data/lib/postsvg/translation/handlers/ellipse_handler.rb +31 -0
- data/lib/postsvg/translation/handlers/group_handler.rb +21 -0
- data/lib/postsvg/translation/handlers/image_handler.rb +20 -0
- data/lib/postsvg/translation/handlers/line_handler.rb +23 -0
- data/lib/postsvg/translation/handlers/open_handler.rb +18 -0
- data/lib/postsvg/translation/handlers/path_handler.rb +356 -0
- data/lib/postsvg/translation/handlers/polygon_handler.rb +33 -0
- data/lib/postsvg/translation/handlers/polyline_handler.rb +31 -0
- data/lib/postsvg/translation/handlers/rect_handler.rb +27 -0
- data/lib/postsvg/translation/handlers/shared.rb +110 -0
- data/lib/postsvg/translation/handlers/svg_handler.rb +25 -0
- data/lib/postsvg/translation/handlers/text_handler.rb +56 -0
- data/lib/postsvg/translation/handlers.rb +25 -0
- data/lib/postsvg/translation/ps_renderer.rb +105 -0
- data/lib/postsvg/translation/record_emitter.rb +35 -0
- data/lib/postsvg/translation.rb +17 -0
- data/lib/postsvg/version.rb +1 -1
- data/lib/postsvg/visitors/ps_visitor/arithmetic.rb +125 -0
- data/lib/postsvg/visitors/ps_visitor/boolean.rb +105 -0
- data/lib/postsvg/visitors/ps_visitor/color.rb +53 -0
- data/lib/postsvg/visitors/ps_visitor/common.rb +66 -0
- data/lib/postsvg/visitors/ps_visitor/container.rb +164 -0
- data/lib/postsvg/visitors/ps_visitor/control_flow.rb +110 -0
- data/lib/postsvg/visitors/ps_visitor/device.rb +20 -0
- data/lib/postsvg/visitors/ps_visitor/dictionary.rb +89 -0
- data/lib/postsvg/visitors/ps_visitor/font.rb +93 -0
- data/lib/postsvg/visitors/ps_visitor/graphics_state.rb +55 -0
- data/lib/postsvg/visitors/ps_visitor/painting.rb +90 -0
- data/lib/postsvg/visitors/ps_visitor/path.rb +112 -0
- data/lib/postsvg/visitors/ps_visitor/stack.rb +47 -0
- data/lib/postsvg/visitors/ps_visitor/transformations.rb +101 -0
- data/lib/postsvg/visitors/ps_visitor.rb +208 -0
- data/lib/postsvg/visitors.rb +9 -0
- data/lib/postsvg.rb +93 -59
- data/lychee.toml +86 -0
- metadata +216 -11
- data/postsvg.gemspec +0 -38
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
= Colors Module
|
|
2
|
+
:page-nav_order: 10
|
|
3
|
+
:page-parent: API Reference
|
|
4
|
+
|
|
5
|
+
== Purpose
|
|
6
|
+
|
|
7
|
+
The [`Colors`](../../lib/postsvg/colors.rb:4) module provides utility functions for converting PostScript color values to RGB color strings suitable for SVG output. It supports RGB, grayscale, and CMYK color models commonly used in PostScript graphics.
|
|
8
|
+
|
|
9
|
+
== References
|
|
10
|
+
|
|
11
|
+
* link:../index.adoc[Documentation Home]
|
|
12
|
+
* link:../api-reference.adoc[API Reference Overview]
|
|
13
|
+
* link:graphics-state.adoc[GraphicsState Class]
|
|
14
|
+
* link:svg-generator.adoc[SvgGenerator Class]
|
|
15
|
+
* link:interpreter.adoc[Interpreter Class]
|
|
16
|
+
* link:../architecture.adoc[Architecture Overview]
|
|
17
|
+
|
|
18
|
+
== Concepts
|
|
19
|
+
|
|
20
|
+
**Color Space**:: A mathematical model describing how colors are represented as tuples of numbers. PostScript supports multiple color spaces including RGB, CMYK, and grayscale.
|
|
21
|
+
|
|
22
|
+
**RGB Color Model**:: An additive color model where colors are represented as combinations of Red, Green, and Blue light values from 0 to 255 (or 0.0 to 1.0 in PostScript).
|
|
23
|
+
|
|
24
|
+
**CMYK Color Model**:: A subtractive color model used in printing where colors are represented as Cyan, Magenta, Yellow, and blacK values from 0.0 to 1.0.
|
|
25
|
+
|
|
26
|
+
**Grayscale**:: A color model representing shades of gray from black (0.0) to white (1.0).
|
|
27
|
+
|
|
28
|
+
**PostScript Color Values**:: Color components in PostScript are floating-point numbers in the range [0.0, 1.0], where 0.0 represents minimum intensity and 1.0 represents maximum intensity.
|
|
29
|
+
|
|
30
|
+
== Module Overview
|
|
31
|
+
|
|
32
|
+
The [`Colors`](../../lib/postsvg/colors.rb:4) module is defined in [`lib/postsvg/colors.rb`](../../lib/postsvg/colors.rb:1).
|
|
33
|
+
|
|
34
|
+
**Responsibilities:**
|
|
35
|
+
|
|
36
|
+
* Convert PostScript RGB values to SVG RGB strings
|
|
37
|
+
* Convert PostScript grayscale values to SVG RGB strings
|
|
38
|
+
* Convert PostScript CMYK values to SVG RGB strings
|
|
39
|
+
* Clamp color values to valid ranges
|
|
40
|
+
* Format color output for SVG compatibility
|
|
41
|
+
|
|
42
|
+
**Design Characteristics:**
|
|
43
|
+
|
|
44
|
+
* Pure utility module (no state)
|
|
45
|
+
* All methods are class methods
|
|
46
|
+
* Defensive clamping of color values
|
|
47
|
+
* Consistent output format
|
|
48
|
+
|
|
49
|
+
== Module Methods
|
|
50
|
+
|
|
51
|
+
=== color2rgb
|
|
52
|
+
|
|
53
|
+
Convert PostScript RGB values to SVG RGB color string.
|
|
54
|
+
|
|
55
|
+
**Syntax:**
|
|
56
|
+
|
|
57
|
+
[source,ruby]
|
|
58
|
+
----
|
|
59
|
+
rgb_string = Postsvg::Colors.color2rgb(rgb) <1>
|
|
60
|
+
----
|
|
61
|
+
<1> Convert PostScript RGB array to SVG color string
|
|
62
|
+
|
|
63
|
+
**Where:**
|
|
64
|
+
|
|
65
|
+
`rgb`:: Array of three floating-point values `[r, g, b]` where:
|
|
66
|
+
* `r` - Red component (0.0 to 1.0)
|
|
67
|
+
* `g` - Green component (0.0 to 1.0)
|
|
68
|
+
* `b` - Blue component (0.0 to 1.0)
|
|
69
|
+
|
|
70
|
+
**Returns:**
|
|
71
|
+
|
|
72
|
+
String in format `"rgb(R, G, B)"` where R, G, B are integers 0-255
|
|
73
|
+
|
|
74
|
+
**Clamping Behavior:**
|
|
75
|
+
|
|
76
|
+
* Values < 0.0 are clamped to 0
|
|
77
|
+
* Values > 1.0 are clamped to 255
|
|
78
|
+
* Values are multiplied by 255 and rounded to integers
|
|
79
|
+
|
|
80
|
+
**Source:**
|
|
81
|
+
|
|
82
|
+
[`lib/postsvg/colors.rb:7-13`](../../lib/postsvg/colors.rb:7)
|
|
83
|
+
|
|
84
|
+
.Convert pure colors
|
|
85
|
+
[example]
|
|
86
|
+
====
|
|
87
|
+
[source,ruby]
|
|
88
|
+
----
|
|
89
|
+
require 'postsvg'
|
|
90
|
+
|
|
91
|
+
# Pure red
|
|
92
|
+
red = Postsvg::Colors.color2rgb([1.0, 0.0, 0.0])
|
|
93
|
+
puts red # → "rgb(255, 0, 0)"
|
|
94
|
+
|
|
95
|
+
# Pure green
|
|
96
|
+
green = Postsvg::Colors.color2rgb([0.0, 1.0, 0.0])
|
|
97
|
+
puts green # → "rgb(0, 255, 0)"
|
|
98
|
+
|
|
99
|
+
# Pure blue
|
|
100
|
+
blue = Postsvg::Colors.color2rgb([0.0, 0.0, 1.0])
|
|
101
|
+
puts blue # → "rgb(0, 0, 255)"
|
|
102
|
+
|
|
103
|
+
# Black
|
|
104
|
+
black = Postsvg::Colors.color2rgb([0.0, 0.0, 0.0])
|
|
105
|
+
puts black # → "rgb(0, 0, 0)"
|
|
106
|
+
|
|
107
|
+
# White
|
|
108
|
+
white = Postsvg::Colors.color2rgb([1.0, 1.0, 1.0])
|
|
109
|
+
puts white # → "rgb(255, 255, 255)"
|
|
110
|
+
----
|
|
111
|
+
====
|
|
112
|
+
|
|
113
|
+
.Convert intermediate colors
|
|
114
|
+
[example]
|
|
115
|
+
====
|
|
116
|
+
[source,ruby]
|
|
117
|
+
----
|
|
118
|
+
# Orange (mix of red and green)
|
|
119
|
+
orange = Postsvg::Colors.color2rgb([1.0, 0.5, 0.0])
|
|
120
|
+
puts orange # → "rgb(255, 128, 0)"
|
|
121
|
+
|
|
122
|
+
# Purple (mix of red and blue)
|
|
123
|
+
purple = Postsvg::Colors.color2rgb([0.5, 0.0, 0.5])
|
|
124
|
+
puts purple # → "rgb(128, 0, 128)"
|
|
125
|
+
|
|
126
|
+
# Gray
|
|
127
|
+
gray = Postsvg::Colors.color2rgb([0.5, 0.5, 0.5])
|
|
128
|
+
puts gray # → "rgb(128, 128, 128)"
|
|
129
|
+
|
|
130
|
+
# Pastel pink
|
|
131
|
+
pink = Postsvg::Colors.color2rgb([1.0, 0.75, 0.8])
|
|
132
|
+
puts pink # → "rgb(255, 191, 204)"
|
|
133
|
+
----
|
|
134
|
+
====
|
|
135
|
+
|
|
136
|
+
.Value clamping
|
|
137
|
+
[example]
|
|
138
|
+
====
|
|
139
|
+
[source,ruby]
|
|
140
|
+
----
|
|
141
|
+
# Values > 1.0 are clamped
|
|
142
|
+
over = Postsvg::Colors.color2rgb([1.5, 1.2, 1.0])
|
|
143
|
+
puts over # → "rgb(255, 255, 255)"
|
|
144
|
+
|
|
145
|
+
# Values < 0.0 are clamped
|
|
146
|
+
under = Postsvg::Colors.color2rgb([-0.5, 0.5, 0.2])
|
|
147
|
+
puts under # → "rgb(0, 128, 51)"
|
|
148
|
+
|
|
149
|
+
# Mixed clamping
|
|
150
|
+
mixed = Postsvg::Colors.color2rgb([1.2, -0.1, 0.5])
|
|
151
|
+
puts mixed # → "rgb(255, 0, 128)"
|
|
152
|
+
----
|
|
153
|
+
====
|
|
154
|
+
|
|
155
|
+
.Use in graphics state
|
|
156
|
+
[example]
|
|
157
|
+
====
|
|
158
|
+
[source,ruby]
|
|
159
|
+
----
|
|
160
|
+
# Typical usage in graphics processing
|
|
161
|
+
postscript_color = [0.8, 0.2, 0.4] # PostScript RGB
|
|
162
|
+
|
|
163
|
+
# Convert for SVG
|
|
164
|
+
svg_color = Postsvg::Colors.color2rgb(postscript_color)
|
|
165
|
+
|
|
166
|
+
# Use in SVG path
|
|
167
|
+
svg = %Q{<path fill="#{svg_color}" d="M 10 10 L 50 50"/>}
|
|
168
|
+
# → <path fill="rgb(204, 51, 102)" d="M 10 10 L 50 50"/>
|
|
169
|
+
----
|
|
170
|
+
====
|
|
171
|
+
|
|
172
|
+
=== gray2rgb
|
|
173
|
+
|
|
174
|
+
Convert PostScript grayscale value to SVG RGB color string.
|
|
175
|
+
|
|
176
|
+
**Syntax:**
|
|
177
|
+
|
|
178
|
+
[source,ruby]
|
|
179
|
+
----
|
|
180
|
+
rgb_string = Postsvg::Colors.gray2rgb(gray) <1>
|
|
181
|
+
----
|
|
182
|
+
<1> Convert PostScript grayscale to SVG color string
|
|
183
|
+
|
|
184
|
+
**Where:**
|
|
185
|
+
|
|
186
|
+
`gray`:: Floating-point value (0.0 to 1.0) where:
|
|
187
|
+
* `0.0` - Black
|
|
188
|
+
* `0.5` - Middle gray
|
|
189
|
+
* `1.0` - White
|
|
190
|
+
|
|
191
|
+
**Returns:**
|
|
192
|
+
|
|
193
|
+
String in format `"rgb(V, V, V)"` where V is an integer 0-255 (same value for R, G, and B)
|
|
194
|
+
|
|
195
|
+
**Clamping Behavior:**
|
|
196
|
+
|
|
197
|
+
* Values < 0.0 are clamped to 0
|
|
198
|
+
* Values > 1.0 are clamped to 255
|
|
199
|
+
* Value is multiplied by 255 and rounded to integer
|
|
200
|
+
|
|
201
|
+
**Source:**
|
|
202
|
+
|
|
203
|
+
[`lib/postsvg/colors.rb:16-19`](../../lib/postsvg/colors.rb:16)
|
|
204
|
+
|
|
205
|
+
.Convert grayscale values
|
|
206
|
+
[example]
|
|
207
|
+
====
|
|
208
|
+
[source,ruby]
|
|
209
|
+
----
|
|
210
|
+
require 'postsvg'
|
|
211
|
+
|
|
212
|
+
# Black
|
|
213
|
+
black = Postsvg::Colors.gray2rgb(0.0)
|
|
214
|
+
puts black # → "rgb(0, 0, 0)"
|
|
215
|
+
|
|
216
|
+
# White
|
|
217
|
+
white = Postsvg::Colors.gray2rgb(1.0)
|
|
218
|
+
puts white # → "rgb(255, 255, 255)"
|
|
219
|
+
|
|
220
|
+
# 50% gray
|
|
221
|
+
gray50 = Postsvg::Colors.gray2rgb(0.5)
|
|
222
|
+
puts gray50 # → "rgb(128, 128, 128)"
|
|
223
|
+
|
|
224
|
+
# 25% gray (dark)
|
|
225
|
+
gray25 = Postsvg::Colors.gray2rgb(0.25)
|
|
226
|
+
puts gray25 # → "rgb(64, 64, 64)"
|
|
227
|
+
|
|
228
|
+
# 75% gray (light)
|
|
229
|
+
gray75 = Postsvg::Colors.gray2rgb(0.75)
|
|
230
|
+
puts gray75 # → "rgb(191, 191, 191)"
|
|
231
|
+
----
|
|
232
|
+
====
|
|
233
|
+
|
|
234
|
+
.Gradient examples
|
|
235
|
+
[example]
|
|
236
|
+
====
|
|
237
|
+
[source,ruby]
|
|
238
|
+
----
|
|
239
|
+
# Create grayscale gradient
|
|
240
|
+
gradient = (0..10).map do |i|
|
|
241
|
+
value = i / 10.0
|
|
242
|
+
Postsvg::Colors.gray2rgb(value)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
puts gradient.join("\n")
|
|
246
|
+
# → rgb(0, 0, 0)
|
|
247
|
+
# → rgb(26, 26, 26)
|
|
248
|
+
# → rgb(51, 51, 51)
|
|
249
|
+
# → ...
|
|
250
|
+
# → rgb(255, 255, 255)
|
|
251
|
+
----
|
|
252
|
+
====
|
|
253
|
+
|
|
254
|
+
.Clamping behavior
|
|
255
|
+
[example]
|
|
256
|
+
====
|
|
257
|
+
[source,ruby]
|
|
258
|
+
----
|
|
259
|
+
# Over maximum
|
|
260
|
+
over = Postsvg::Colors.gray2rgb(1.5)
|
|
261
|
+
puts over # → "rgb(255, 255, 255)"
|
|
262
|
+
|
|
263
|
+
# Under minimum
|
|
264
|
+
under = Postsvg::Colors.gray2rgb(-0.5)
|
|
265
|
+
puts under # → "rgb(0, 0, 0)"
|
|
266
|
+
----
|
|
267
|
+
====
|
|
268
|
+
|
|
269
|
+
=== cmyk2rgb
|
|
270
|
+
|
|
271
|
+
Convert PostScript CMYK values to SVG RGB color string.
|
|
272
|
+
|
|
273
|
+
**Syntax:**
|
|
274
|
+
|
|
275
|
+
[source,ruby]
|
|
276
|
+
----
|
|
277
|
+
rgb_string = Postsvg::Colors.cmyk2rgb(cmyk) <1>
|
|
278
|
+
----
|
|
279
|
+
<1> Convert PostScript CMYK array to SVG color string
|
|
280
|
+
|
|
281
|
+
**Where:**
|
|
282
|
+
|
|
283
|
+
`cmyk`:: Array of four floating-point values `[c, m, y, k]` where:
|
|
284
|
+
* `c` - Cyan component (0.0 to 1.0)
|
|
285
|
+
* `m` - Magenta component (0.0 to 1.0)
|
|
286
|
+
* `y` - Yellow component (0.0 to 1.0)
|
|
287
|
+
* `k` - Black (Key) component (0.0 to 1.0)
|
|
288
|
+
|
|
289
|
+
**Returns:**
|
|
290
|
+
|
|
291
|
+
String in format `"rgb(R, G, B)"` where R, G, B are integers 0-255
|
|
292
|
+
|
|
293
|
+
**Conversion Formula:**
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
R = 255 × (1 - C) × (1 - K)
|
|
297
|
+
G = 255 × (1 - M) × (1 - K)
|
|
298
|
+
B = 255 × (1 - Y) × (1 - K)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Clamping Behavior:**
|
|
302
|
+
|
|
303
|
+
* Each component is clamped to [0.0, 1.0] range
|
|
304
|
+
* Resulting RGB values are clamped to [0, 255] and rounded
|
|
305
|
+
|
|
306
|
+
**Source:**
|
|
307
|
+
|
|
308
|
+
[`lib/postsvg/colors.rb:22-31`](../../lib/postsvg/colors.rb:22)
|
|
309
|
+
|
|
310
|
+
.Convert pure CMYK colors
|
|
311
|
+
[example]
|
|
312
|
+
====
|
|
313
|
+
[source,ruby]
|
|
314
|
+
----
|
|
315
|
+
require 'postsvg'
|
|
316
|
+
|
|
317
|
+
# Cyan (C=1, M=0, Y=0, K=0)
|
|
318
|
+
cyan = Postsvg::Colors.cmyk2rgb([1.0, 0.0, 0.0, 0.0])
|
|
319
|
+
puts cyan # → "rgb(0, 255, 255)"
|
|
320
|
+
|
|
321
|
+
# Magenta (C=0, M=1, Y=0, K=0)
|
|
322
|
+
magenta = Postsvg::Colors.cmyk2rgb([0.0, 1.0, 0.0, 0.0])
|
|
323
|
+
puts magenta # → "rgb(255, 0, 255)"
|
|
324
|
+
|
|
325
|
+
# Yellow (C=0, M=0, Y=1, K=0)
|
|
326
|
+
yellow = Postsvg::Colors.cmyk2rgb([0.0, 0.0, 1.0, 0.0])
|
|
327
|
+
puts yellow # → "rgb(255, 255, 0)"
|
|
328
|
+
|
|
329
|
+
# Black (K=1)
|
|
330
|
+
black = Postsvg::Colors.cmyk2rgb([0.0, 0.0, 0.0, 1.0])
|
|
331
|
+
puts black # → "rgb(0, 0, 0)"
|
|
332
|
+
|
|
333
|
+
# White (all zeros)
|
|
334
|
+
white = Postsvg::Colors.cmyk2rgb([0.0, 0.0, 0.0, 0.0])
|
|
335
|
+
puts white # → "rgb(255, 255, 255)"
|
|
336
|
+
----
|
|
337
|
+
====
|
|
338
|
+
|
|
339
|
+
.Convert mixed CMYK colors
|
|
340
|
+
[example]
|
|
341
|
+
====
|
|
342
|
+
[source,ruby]
|
|
343
|
+
----
|
|
344
|
+
# Red (M=1, Y=1)
|
|
345
|
+
red = Postsvg::Colors.cmyk2rgb([0.0, 1.0, 1.0, 0.0])
|
|
346
|
+
puts red # → "rgb(255, 0, 0)"
|
|
347
|
+
|
|
348
|
+
# Green (C=1, Y=1)
|
|
349
|
+
green = Postsvg::Colors.cmyk2rgb([1.0, 0.0, 1.0, 0.0])
|
|
350
|
+
puts green # → "rgb(0, 255, 0)"
|
|
351
|
+
|
|
352
|
+
# Blue (C=1, M=1)
|
|
353
|
+
blue = Postsvg::Colors.cmyk2rgb([1.0, 1.0, 0.0, 0.0])
|
|
354
|
+
puts blue # → "rgb(0, 0, 255)"
|
|
355
|
+
|
|
356
|
+
# Orange (M=0.5, Y=1)
|
|
357
|
+
orange = Postsvg::Colors.cmyk2rgb([0.0, 0.5, 1.0, 0.0])
|
|
358
|
+
puts orange # → "rgb(255, 128, 0)"
|
|
359
|
+
----
|
|
360
|
+
====
|
|
361
|
+
|
|
362
|
+
.Effect of black component (K)
|
|
363
|
+
[example]
|
|
364
|
+
====
|
|
365
|
+
[source,ruby]
|
|
366
|
+
----
|
|
367
|
+
# Pure cyan with varying black
|
|
368
|
+
cyan_light = Postsvg::Colors.cmyk2rgb([1.0, 0.0, 0.0, 0.0])
|
|
369
|
+
puts cyan_light # → "rgb(0, 255, 255)" - Full brightness
|
|
370
|
+
|
|
371
|
+
cyan_dark = Postsvg::Colors.cmyk2rgb([1.0, 0.0, 0.0, 0.5])
|
|
372
|
+
puts cyan_dark # → "rgb(0, 128, 128)" - 50% darker
|
|
373
|
+
|
|
374
|
+
cyan_black = Postsvg::Colors.cmyk2rgb([1.0, 0.0, 0.0, 1.0])
|
|
375
|
+
puts cyan_black # → "rgb(0, 0, 0)" - Completely black
|
|
376
|
+
|
|
377
|
+
# The K component darkens all colors proportionally
|
|
378
|
+
----
|
|
379
|
+
====
|
|
380
|
+
|
|
381
|
+
.Typical print colors
|
|
382
|
+
[example]
|
|
383
|
+
====
|
|
384
|
+
[source,ruby]
|
|
385
|
+
----
|
|
386
|
+
# Common printing colors
|
|
387
|
+
process_blue = Postsvg::Colors.cmyk2rgb([1.0, 0.72, 0.0, 0.0])
|
|
388
|
+
puts process_blue # → "rgb(0, 71, 255)"
|
|
389
|
+
|
|
390
|
+
pantone_red = Postsvg::Colors.cmyk2rgb([0.0, 0.91, 0.76, 0.0])
|
|
391
|
+
puts pantone_red # → "rgb(255, 23, 61)"
|
|
392
|
+
|
|
393
|
+
rich_black = Postsvg::Colors.cmyk2rgb([0.6, 0.4, 0.4, 1.0])
|
|
394
|
+
puts rich_black # → "rgb(0, 0, 0)"
|
|
395
|
+
----
|
|
396
|
+
====
|
|
397
|
+
|
|
398
|
+
== Usage Patterns
|
|
399
|
+
|
|
400
|
+
=== Pattern 1: Color Space Detection
|
|
401
|
+
|
|
402
|
+
[source,ruby]
|
|
403
|
+
----
|
|
404
|
+
require 'postsvg'
|
|
405
|
+
|
|
406
|
+
def convert_postscript_color(color)
|
|
407
|
+
case color.length
|
|
408
|
+
when 1
|
|
409
|
+
# Grayscale
|
|
410
|
+
Postsvg::Colors.gray2rgb(color[0])
|
|
411
|
+
when 3
|
|
412
|
+
# RGB
|
|
413
|
+
Postsvg::Colors.color2rgb(color)
|
|
414
|
+
when 4
|
|
415
|
+
# CMYK
|
|
416
|
+
Postsvg::Colors.cmyk2rgb(color)
|
|
417
|
+
else
|
|
418
|
+
raise ArgumentError, "Unknown color format: #{color.inspect}"
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Usage
|
|
423
|
+
puts convert_postscript_color([0.5]) # Gray
|
|
424
|
+
puts convert_postscript_color([1.0, 0.0, 0.0]) # RGB
|
|
425
|
+
puts convert_postscript_color([0.0, 1.0, 1.0, 0.0]) # CMYK
|
|
426
|
+
----
|
|
427
|
+
|
|
428
|
+
=== Pattern 2: Color Palette Generator
|
|
429
|
+
|
|
430
|
+
[source,ruby]
|
|
431
|
+
----
|
|
432
|
+
require 'postsvg'
|
|
433
|
+
|
|
434
|
+
class ColorPalette
|
|
435
|
+
def self.grayscale_ramp(steps = 10)
|
|
436
|
+
(0..steps).map do |i|
|
|
437
|
+
value = i.to_f / steps
|
|
438
|
+
Postsvg::Colors.gray2rgb(value)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def self.rgb_primaries(intensity = 1.0)
|
|
443
|
+
{
|
|
444
|
+
red: Postsvg::Colors.color2rgb([intensity, 0, 0]),
|
|
445
|
+
green: Postsvg::Colors.color2rgb([0, intensity, 0]),
|
|
446
|
+
blue: Postsvg::Colors.color2rgb([0, 0, intensity])
|
|
447
|
+
}
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def self.cmyk_primaries
|
|
451
|
+
{
|
|
452
|
+
cyan: Postsvg::Colors.cmyk2rgb([1, 0, 0, 0]),
|
|
453
|
+
magenta: Postsvg::Colors.cmyk2rgb([0, 1, 0, 0]),
|
|
454
|
+
yellow: Postsvg::Colors.cmyk2rgb([0, 0, 1, 0]),
|
|
455
|
+
black: Postsvg::Colors.cmyk2rgb([0, 0, 0, 1])
|
|
456
|
+
}
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# Generate grayscale ramp
|
|
461
|
+
grays = ColorPalette.grayscale_ramp(5)
|
|
462
|
+
puts grays.join(", ")
|
|
463
|
+
|
|
464
|
+
# Get primary colors
|
|
465
|
+
primaries = ColorPalette.rgb_primaries(0.8)
|
|
466
|
+
puts primaries.inspect
|
|
467
|
+
----
|
|
468
|
+
|
|
469
|
+
=== Pattern 3: Color Validation and Normalization
|
|
470
|
+
|
|
471
|
+
[source,ruby]
|
|
472
|
+
----
|
|
473
|
+
require 'postsvg'
|
|
474
|
+
|
|
475
|
+
class ColorValidator
|
|
476
|
+
def self.normalize_rgb(values)
|
|
477
|
+
# Ensure values are in [0.0, 1.0] range
|
|
478
|
+
normalized = values.map { |v| [[v, 0.0].max, 1.0].min }
|
|
479
|
+
Postsvg::Colors.color2rgb(normalized)
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def self.normalize_cmyk(values)
|
|
483
|
+
# Ensure values are in [0.0, 1.0] range
|
|
484
|
+
normalized = values.map { |v| [[v, 0.0].max, 1.0].min }
|
|
485
|
+
Postsvg::Colors.cmyk2rgb(normalized)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def self.validate_and_convert(color_space, values)
|
|
489
|
+
case color_space
|
|
490
|
+
when :rgb
|
|
491
|
+
raise ArgumentError unless values.length == 3
|
|
492
|
+
normalize_rgb(values)
|
|
493
|
+
when :cmyk
|
|
494
|
+
raise ArgumentError unless values.length == 4
|
|
495
|
+
normalize_cmyk(values)
|
|
496
|
+
when :gray
|
|
497
|
+
raise ArgumentError unless values.length == 1
|
|
498
|
+
gray = [[values[0], 0.0].max, 1.0].min
|
|
499
|
+
Postsvg::Colors.gray2rgb(gray)
|
|
500
|
+
else
|
|
501
|
+
raise ArgumentError, "Unknown color space: #{color_space}"
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Usage with validation
|
|
507
|
+
safe_color = ColorValidator.validate_and_convert(:rgb, [0.8, 0.2, 0.5])
|
|
508
|
+
puts safe_color
|
|
509
|
+
----
|
|
510
|
+
|
|
511
|
+
=== Pattern 4: Color Conversion Pipeline
|
|
512
|
+
|
|
513
|
+
[source,ruby]
|
|
514
|
+
----
|
|
515
|
+
require 'postsvg'
|
|
516
|
+
|
|
517
|
+
class PostScriptColorProcessor
|
|
518
|
+
def initialize
|
|
519
|
+
@conversions = 0
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
def process_colors(graphics_operations)
|
|
523
|
+
graphics_operations.map do |op|
|
|
524
|
+
converted_op = op.dup
|
|
525
|
+
|
|
526
|
+
if op[:stroke_color]
|
|
527
|
+
converted_op[:stroke_svg] = convert_color(
|
|
528
|
+
op[:stroke_color],
|
|
529
|
+
op[:stroke_color_space]
|
|
530
|
+
)
|
|
531
|
+
@conversions += 1
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
if op[:fill_color]
|
|
535
|
+
converted_op[:fill_svg] = convert_color(
|
|
536
|
+
op[:fill_color],
|
|
537
|
+
op[:fill_color_space]
|
|
538
|
+
)
|
|
539
|
+
@conversions += 1
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
converted_op
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def convert_color(color, color_space)
|
|
547
|
+
case color_space
|
|
548
|
+
when :DeviceRGB
|
|
549
|
+
Postsvg::Colors.color2rgb(color)
|
|
550
|
+
when :DeviceGray
|
|
551
|
+
Postsvg::Colors.gray2rgb(color[0])
|
|
552
|
+
when :DeviceCMYK
|
|
553
|
+
Postsvg::Colors.cmyk2rgb(color)
|
|
554
|
+
else
|
|
555
|
+
# Default to RGB
|
|
556
|
+
Postsvg::Colors.color2rgb(color)
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def stats
|
|
561
|
+
{ conversions: @conversions }
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Process graphics operations
|
|
566
|
+
processor = PostScriptColorProcessor.new
|
|
567
|
+
operations = [
|
|
568
|
+
{ stroke_color: [1, 0, 0], stroke_color_space: :DeviceRGB },
|
|
569
|
+
{ fill_color: [0, 1, 0, 0], fill_color_space: :DeviceCMYK }
|
|
570
|
+
]
|
|
571
|
+
|
|
572
|
+
result = processor.process_colors(operations)
|
|
573
|
+
puts result.inspect
|
|
574
|
+
puts "Converted #{processor.stats[:conversions]} colors"
|
|
575
|
+
----
|
|
576
|
+
|
|
577
|
+
== Thread Safety
|
|
578
|
+
|
|
579
|
+
The `Colors` module is **completely thread-safe** because:
|
|
580
|
+
|
|
581
|
+
1. All methods are stateless class methods
|
|
582
|
+
2. No shared mutable state
|
|
583
|
+
3. No side effects
|
|
584
|
+
4. Pure functions (same input → same output)
|
|
585
|
+
|
|
586
|
+
.Thread-safe usage
|
|
587
|
+
[example]
|
|
588
|
+
====
|
|
589
|
+
[source,ruby]
|
|
590
|
+
----
|
|
591
|
+
# Safe: Can be called from multiple threads
|
|
592
|
+
threads = 1000.times.map do |i|
|
|
593
|
+
Thread.new do
|
|
594
|
+
r = (i % 256) / 255.0
|
|
595
|
+
g = ((i * 2) % 256) / 255.0
|
|
596
|
+
b = ((i * 3) % 256) / 255.0
|
|
597
|
+
|
|
598
|
+
Postsvg::Colors.color2rgb([r, g, b])
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
results = threads.map(&:value)
|
|
603
|
+
puts "Converted #{results.length} colors concurrently"
|
|
604
|
+
----
|
|
605
|
+
====
|
|
606
|
+
|
|
607
|
+
== Performance Considerations
|
|
608
|
+
|
|
609
|
+
**Time Complexity:**
|
|
610
|
+
|
|
611
|
+
* `color2rgb`: O(1) - 3 multiplications, 3 min/max operations
|
|
612
|
+
* `gray2rgb`: O(1) - 1 multiplication, min/max operations
|
|
613
|
+
* `cmyk2rgb`: O(1) - 12 multiplications, 3 min/max operations
|
|
614
|
+
|
|
615
|
+
**Space Complexity:**
|
|
616
|
+
|
|
617
|
+
* All methods: O(1) - constant space
|
|
618
|
+
* Returns single string
|
|
619
|
+
|
|
620
|
+
**Performance Characteristics:**
|
|
621
|
+
|
|
622
|
+
* Extremely fast operations
|
|
623
|
+
* No memory allocation except result string
|
|
624
|
+
* No expensive computations
|
|
625
|
+
* Safe for high-frequency calls
|
|
626
|
+
|
|
627
|
+
.Performance measurement
|
|
628
|
+
[example]
|
|
629
|
+
====
|
|
630
|
+
[source,ruby]
|
|
631
|
+
----
|
|
632
|
+
require 'postsvg'
|
|
633
|
+
require 'benchmark'
|
|
634
|
+
|
|
635
|
+
iterations = 100_000
|
|
636
|
+
|
|
637
|
+
rgb_time = Benchmark.measure do
|
|
638
|
+
iterations.times do |i|
|
|
639
|
+
Postsvg::Colors.color2rgb([0.5, 0.3, 0.8])
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
cmyk_time = Benchmark.measure do
|
|
644
|
+
iterations.times do |i|
|
|
645
|
+
Postsvg::Colors.cmyk2rgb([0.5, 0.3, 0.8, 0.1])
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
puts "RGB conversions: #{iterations} in #{'%.3f' % rgb_time.real}s"
|
|
650
|
+
puts "Rate: #{(iterations / rgb_time.real).to_i} conversions/sec"
|
|
651
|
+
|
|
652
|
+
puts "CMYK conversions: #{iterations} in #{'%.3f' % cmyk_time.real}s"
|
|
653
|
+
puts "Rate: #{(iterations / cmyk_time.real).to_i} conversions/sec"
|
|
654
|
+
----
|
|
655
|
+
====
|
|
656
|
+
|
|
657
|
+
**Optimization Tips:**
|
|
658
|
+
|
|
659
|
+
1. **Cache results**: If converting same color repeatedly, cache the result
|
|
660
|
+
2. **Batch processing**: Process multiple colors in sequence for better CPU cache utilization
|
|
661
|
+
3. **No premature optimization needed**: These methods are already very fast
|
|
662
|
+
|
|
663
|
+
== Color Accuracy Notes
|
|
664
|
+
|
|
665
|
+
**CMYK to RGB Conversion:**
|
|
666
|
+
|
|
667
|
+
The CMYK to RGB conversion uses a simplified formula that works well for display but may not match print output exactly:
|
|
668
|
+
|
|
669
|
+
* ✅ Good for: Display, web graphics, previews
|
|
670
|
+
* ⚠️ Limited for: Professional printing, color matching
|
|
671
|
+
|
|
672
|
+
For more accurate color conversion, consider:
|
|
673
|
+
* ICC color profiles
|
|
674
|
+
* Color management systems
|
|
675
|
+
* Professional color libraries
|
|
676
|
+
|
|
677
|
+
**Grayscale Conversion:**
|
|
678
|
+
|
|
679
|
+
The grayscale conversion creates neutral grays (R=G=B). For perceptual grayscale from RGB, consider using luminance formulas:
|
|
680
|
+
|
|
681
|
+
```
|
|
682
|
+
Luminance = 0.299*R + 0.587*G + 0.114*B
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Value Clamping:**
|
|
686
|
+
|
|
687
|
+
All methods clamp values to valid ranges. This is defensive but may hide issues in source data. Consider validating color values before conversion if accuracy is critical.
|
|
688
|
+
|
|
689
|
+
== Next Steps
|
|
690
|
+
|
|
691
|
+
* Learn about link:graphics-state.adoc[GraphicsState] which uses these color conversions
|
|
692
|
+
* Review link:svg-generator.adoc[SvgGenerator] for how colors are applied to SVG
|
|
693
|
+
* See link:interpreter.adoc[Interpreter] for PostScript color command handling
|
|
694
|
+
* Check link:../architecture.adoc[Architecture] for system design
|
|
695
|
+
|
|
696
|
+
== Bibliography
|
|
697
|
+
|
|
698
|
+
* link:graphics-state.adoc[GraphicsState Documentation]
|
|
699
|
+
* link:svg-generator.adoc[SvgGenerator Documentation]
|
|
700
|
+
* link:interpreter.adoc[Interpreter Documentation]
|
|
701
|
+
* link:../architecture.adoc[Architecture Overview]
|
|
702
|
+
* link:https://www.adobe.com/jp/print/postscript/pdfs/PLRM.pdf[PostScript Language Reference Manual - Color Spaces]
|
|
703
|
+
* link:https://www.w3.org/TR/css-color-3/[W3C CSS Color Module Level 3]
|
|
704
|
+
* link:https://en.wikipedia.org/wiki/CMYK_color_model[Wikipedia: CMYK Color Model]
|
|
705
|
+
* link:https://en.wikipedia.org/wiki/RGB_color_model[Wikipedia: RGB Color Model]
|