summaryrefslogtreecommitdiff
path: root/macros/optex/base/colors.opm
blob: bbda496761ee684057f943a64351f08151c3df0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
%% This is part of the OpTeX project, see http://petr.olsak.net/optex

\_codedecl \colordef {Colors <2020-03-18>} % loaded in format

   \_doc -----------------------------
   We declare internal boolean value \`\_iflocalcolor` ad do
   \`\localcolor` as default.
   \_cod -----------------------------

\_newifi \_iflocalcolor \_localcolortrue
\_protected\_def \_localcolor   {\_localcolortrue}
\_protected\_def \_nolocalcolor {\_localcolorfalse}
\_public \localcolor \nolocalcolor ;

   \_doc -----------------------------
   The basic colors in CMYK
   \`\Blue` \`\Red` \`\Brown` \`\Green` \`\Yellow` \`\Cyan` \`\Magenta`
   \`\Grey` \`\LightGrey` \`\White` and \`\Black`
   are declared here.
   \_cod -----------------------------

\_def\Blue      {\_setcmykcolor{1 1 0 0}}
\_def\Red       {\_setcmykcolor{0 1 1 0}}
\_def\Brown     {\_setcmykcolor{0 0.67 0.67 0.5}}
\_def\Green     {\_setcmykcolor{1 0 1 0}}
\_def\Yellow    {\_setcmykcolor{0 0 1 0}}
\_def\Cyan      {\_setcmykcolor{1 0 0 0}}
\_def\Magenta   {\_setcmykcolor{0 1 0 0}}
\_def\Grey      {\_setcmykcolor{0 0 0 0.5}}
\_def\LightGrey {\_setcmykcolor{0 0 0 0.2}}
\_def\White     {\_setgreycolor{1}}
\_def\Black     {\_setgreycolor{0}}

   \_doc -----------------------------
   By default, the \`\setcmykcolor` \`\setrgbcolor` and \`\setgreycolor`
   macros with `{<componetns>}` parameter
   expand to `\_setcolor{<pdf-primitive>}` using \`\_formatcmyk`
   or \`\_formatrgb` or \`\_formatgrey` expandable macros.
   For example `\setrgbcolor{1 0 0}` expands to `\_setcolor{1 0 0 rg 1 0 0 RG}`.\nl
   We set both types of colors (for lines (`K` or `RG` or `G`) and for fills
   (`r` or `rg` or `g`) together in the <pdf-primitive> command.
   This is the reason why the \`\_fillstroke` uses both its parameters.
   If only fills are needed you can do `\def\_fillstroke#1#2{#1}`.
   If only strokes are needed you can do `\def\_fillstroke#1#2{#2}`.
   \_cod -----------------------------

\_def\_setcmykcolor#1{\_setcolor{\_formatcmyk{#1}}}
\_def\_setrgbcolor#1{\_setcolor{\_formatrgb{#1}}}
\_def\_setgreycolor#1{\_setcolor{\_formatgrey{#1}}}
\_def\_formatcmyk#1{\_fillstroke{#1 k}{#1 K}}
\_def\_formatrgb#1{\_fillstroke{#1 rg}{#1 RG}}
\_def\_formatgrey#1{\_fillstroke{#1 g}{#1 G}}
\_def\_fillstroke#1#2{#1 #2}
\_public \setcmykcolor \setrgbcolor \setgreycolor ;

   \_doc -----------------------------
   The \`\onlyrgb` declaration redefines \^`\_formatcmyk` in order it expands
   to its conversion to RGB <pdf-primitive>. This conversion is done by
   the \^`\_cmyktorgb` macro. Moreover, `\onlyrgb` re-defines three basic RGB
   colors for RGB color space and re-declares \^`\colordef` as \^`\rgbcolordef`.
   The \hbox{\`\onlycmyk`} macro does similar work, it re-defines \^`\_formatrgb`
   macro. The Grey color space is unchanged and works in both main
   settings (RGB or CMYK) without collisions.
   \_cod -----------------------------

\_def\_onlyrgb{\_def\Red{\_setrgbcolor{1 0 0}}%
   \_def\Green{\_setrgbcolor{0 1 0}}\_def\Blue{\_setrgbcolor{0 0 1}}%
   \_let\_colordef=\_rgbcolordef
   \_def\_formatrgb##1{\_fillstroke{##1 rg}{##1 RG}}%
   \_def\_formatcmyk##1{\_fillstroke{\_cmyktorgb ##1 ; rg}{\_cmyktorgb ##1 ; RG}}}
\_def\_onlycmyk{\_def\_formatcmyk##1{\_fillstroke{##1 k}{##1 K}}%
   \_def\_formatrgb##1{\_fillstroke{\_rgbtocmyk ##1 ; k}{\_rgbtocmyk ##1 ; K}}}
\_public \onlyrgb \onlycmyk ;

   \_doc -----------------------------
   The \`\_setcolor` macro redefines empty `\_ensureblack` macro (used in
   output routine for headers and footers) to `\_ensureblackA` which sets
   Black at the start of its parameter and returns to the current color at the
   end of its parameter.

   The current color is saved into `\_currentcolor` macro and colorstack is pushed.
   Finally, the `\_colorstackpop` is initialized by `\aftergroup` if
   `\localcolor` is declared.

   You can save the current color to your macro  by `\let\yourmacro=\_currentcolor`
   and you can return to this color by the command `\_setcolor\yourmacro`.
   \_cod -----------------------------

\_protected\_def \_setcolor #1{\_global\_let\_ensureblack=\_ensureblackA
   \_iflocalcolor \_edef\_currentcolor{#1}\_colorstackpush\_currentcolor
                  \_aftergroup\_colorstackpop
   \_else         \_xdef\_currentcolor{#1}\_colorstackset\_currentcolor \_fi
}
\_def\_pdfblackcolor{0 g 0 G}
\_edef\_currentcolor{\_pdfblackcolor}
\_def\_ensureblackA#1{\_global\_let\_openfnotestack=\_openfnotestackA
   \_colorstackpush\_pdfblackcolor #1\_colorstackpop}

   \_doc -----------------------------
   The colorstack is initialized here and the basic macros
   \`\_colorstackpush`, \`\_colorstackpop` and \`\_colorstackset`
   are defined here.
   \_cod -----------------------------

\_mathchardef\_colorstackcnt=0 % Implicit stack usage
\_def\_colorstackpush#1{\_pdfcolorstack\_colorstackcnt push{#1}}
\_def\_colorstackpop{\_pdfcolorstack\_colorstackcnt pop}
\_def\_colorstackset#1{\_pdfcolorstack\_colorstackcnt set{#1}}

   \_doc -----------------------------
   We need to open a special color stack for footnotes because footnotes
   can follow on the next pages and their colors are independent of colors
   used in the main page-body. The \`\_openfnotestack` is defined as
   \`\_openfnotestackA` when the \^`\_setcolor` is used first.
   The \`\_fnotestack` is initialized in in `\everyjob` because the
   initialization is not saved to the format.
   \_cod -----------------------------

%\_mathchardef\_fnotestack=\_pdfcolorstackinit page {0 g 0 G} % must be in \everyjob
\_def \_openfnotestackA {\_pdfcolorstack\_fnotestack current}

   \_doc -----------------------------
   We use Lua codes for RGB to CMYK or CMYK to RGB conversions and for
   addition color components in the \^`\colordef` macro.
   The \`\_rgbtocmyk` `<R> <G> <B> ;` expands to `<C> <M> <Y> <K>` and
   the \`\_cmyktorgb` `<C> <M> <Y> <K> ;` expands to `<R> <G> <B>`.
   The \`\_colorcrop`, \`\_colordefFin` and \`\_douseK` are auxiliary macros
   used in the \^`\colordef`. The `\_colorcrop` rescales color components in order
   to they are in $[0,1]$ interval. The `\colordefFin` expands to the values
   accumulated in Lua code `color_C`, `color_M`, `color_Y` and `color_K`.
   The `\_douseK` applies \^`\useK` to CMYK components.
   \_cod -----------------------------

\_def\_rgbtocmyk #1 #2 #3 ;{%
   \_ea \_stripzeros \_detokenize \_ea{\_directlua{
      local kr = math.max(#1,#2,#3)
      if (kr==0) then
         tex.print('0. 0. 0. 1 ;')
      else
         tex.print(string.format('\_pcent.3f \_pcent.3f \_pcent.3f \_pcent.3f ;',
            (kr-#1)/kr, (kr-#2)/kr, (kr-#3)/kr, 1-kr))
      end
}}}
\_def\_cmyktorgb #1 #2 #3 #4 ;{%
   \_ea \_stripzeros \_detokenize \_ea{\_directlua{
      local kr = 1-#4
      tex.print(string.format('\_pcent.3f \_pcent.3f \_pcent.3f ;',
         (1-#1)*kr, (1-#2)*kr, (1-#3)*kr))
}}}
\_def\_colorcrop{\_directlua{
   local m=math.max(color_C, color_M, color_Y, color_K)
   if (m>1) then
      color_C=color_C/m  color_M=color_M/m  color_Y=color_Y/m color_K=color_K/m
   end
}}
\_def\_colordefFin{\_colorcrop \_ea \_stripzeros \_detokenize \_ea{\_directlua{
   tex.print(string.format('\_pcent.3f \_pcent.3f \_pcent.3f \_pcent.3f ;',
      color_C, color_M, color_Y, color_K))
}}}
\_def\_douseK{\_colorcrop \_directlua{
   kr=math.min(color_C, color_M, color_Y)
   if (kr>=1) then
      color_C=0  color_M=0  color_Y=0  color_K=1
   else
      color_C=(color_C-kr)/(1-kr)  color_M=(color_M-kr)/(1-kr)
      color_Y=(color_Y-kr)/(1-kr)  color_K=math.min(color_K+kr,1)
   end
}}

   \_doc -----------------------------
   We have a problem with the `%.3f` directive in Lua code. It prints trailed
   zeros: (0.300 instead desired 0.3) but we want to save PDF file space. The macro
   \`\_stripzeros` removes these trailing zeros at the expand processor
   level. So `\_stripzeros 0.300 0.400 0.560 ;` expands to `.3 .4 .56`.
   \_cod -----------------------------

\_def\_stripzeros #1.#2 #3{\_ifx0#1\_else#1\_fi.\_stripzeroA #2 0 :%
    \_ifx;#3\_else \_space \_ea\_stripzeros\_ea#3\_fi}
\_def\_stripzeroA #10 #2:{\_ifx^#2^\_stripzeroC#1:\_else \_stripzeroB#1 0 :\_fi}
\_def\_stripzeroB #10 #2:{\_ifx^#2^\_stripzeroC#1:\_else #1\_fi}
\_def\_stripzeroC #1 #2:{#1}

   \_doc -----------------------------
   The \`\rgbcolordef` and \`\cmykcolordef` use common macro
   \`\_commoncolordef` with different first four parameters.
   The `\_commoncolordef <selector><K><R><G><what-define>{<data>}` does the
   real work. It initializes the Lua variables for summation.
   It expands <data> in the group where color selectors have
   special meaning, then it adjusts the resulting string by \^`\replstring`
   and runs it. Example shows how the <data> are processed:
   \begtt \catcode`\<=13
   input <data>:  ".3\Blue + .6^\KhakiC \useK -\Black"
   expanded to:  ".3 !=K 1 1 0 0 +.6^!=R .804 .776 .45 \_useK -!=G 0"
   adjusted to:  "\_addcolor .3!=K 1 1 0 0 \_addcolor .6!^R .804 .776 .45
                  \_useK \_addcolor -1!=G 0"
   and this is processed.
   \endtt
   \`\_addcolor` `<coef.>!<mod><type>` expands to `\_addcolor:<mod><type> <coef>`
   for example it expands to `\_addcolor:=K <coef>` followed by one or three or four
   numbers (depending on <type>). <mod> is `=` (use as is) or `^` (use
   complementary color). <type> is `K` for CMYK, `R` for RGB and `G` for
   GREY color space. Uppercase <type> informs that `\cmykcolordef` is
   processed and lowercase <type> informs that `\rgbcolordef` is processed.
   All variants of commands `\_addcolor:<mod><type>` are defined. All of
   them expand to `\_addcolorA <v1> <v2> <v3> <v4>` which adds the values of Lua
   variables. The `\rgbcolordef` uses `\_addcolorA <R> <G> <B> 0` and
   `\cmkykcolordef` uses `\_addcolorA <C> <M> <Y> <K>`. So the Lua variable
   names are a little confusing when `\rgbcolordef` is processed.

   Next, `\_commoncolordef` saves resulting values from Lua to `\_tmpb`
   using `\_colordefFin`. If `\rgbcolordef` is processed, then we must to
   remove the last <K> component which is in the format `.0` in such case.
   The `\_stripK` macro does it.
   Finally, the `<what-define>` is defined as `<selector>{<expanded _tmpb>}`,
   for example `\_setcmykclor{1 0 .5 .3}`.
   \_cod -----------------------------

\_def\_rgbcolordef  {\_commoncolordef \_setrgbcolor  krg}
\_def\_cmykcolordef {\_commoncolordef \_setcmykcolor KRG}
\_def\_commoncolordef#1#2#3#4#5#6{%
  \_begingroup
     \_directlua{color_C=0 color_M=0 color_Y=0 color_K=0}%
     \_def\_setcmykcolor##1{!=#2 ##1 }%
     \_def\_setrgbcolor ##1{!=#3 ##1 }%
     \_def\_setgreycolor##1{!=#4 ##1 }%
     \_let\_useK=\_relax
     \_edef\_tmpb{+#6}%
     \_replstring\_tmpb{+ }{+}\_replstring\_tmpb{- }{-}%
     \_replstring\_tmpb{+}{\_addcolor}\_replstring\_tmpb{-}{\_addcolor-}%
     \_replstring\_tmpb{^!=}{!^}\_replstring\_tmpb{-!}{-1!}%
     \_ifx K#2\_let\_useK=\_douseK \_fi
     \_tmpb
     \_edef\_tmpb{\_colordefFin}%
     \_ifx k#2\_edef\_tmpb{\_ea\_stripK \_tmpb;}\_fi
  \_ea\_endgroup
  \_ea\_def\_ea#5\_ea{\_ea#1\_ea{\_tmpb}}%
}
\_def\_addcolor#1!#2#3{\_cs{addcolor:#2#3}#1}
\_def\_addcolorA #1 #2 #3 #4 #5 {%
   \_def\_tmpa{#1}\_ifx\_tmpa\_empty \_else \_edef\_tmpa{\_tmpa*}\_fi
   \_directlua{color_C=math.max(color_C+\_tmpa#2,0)
               color_M=math.max(color_M+\_tmpa#3,0)
               color_Y=math.max(color_Y+\_tmpa#4,0)
               color_K=math.max(color_K+\_tmpa#5,0)
}}
\_sdef{addcolor:=K}#1 #2 #3 #4 #5 {\_addcolorA #1 #2 #3 #4 #5 }
\_sdef{addcolor:^K}#1 #2 #3 #4 #5 {\_addcolorA #1 (1-#2) (1-#3) (1-#4) #5 }
\_sdef{addcolor:^G}#1 #2 {\_addcolorA #1 0 0 0 #2 }
\_sdef{addcolor:=G}#1 #2 {\_addcolorA #1 0 0 0 (1-#2) }
\_sdef{addcolor:=R}#1 #2 #3 #4 {%
   \_edef\_tmpa{\_noexpand\_addcolorA #1 \_rgbtocmyk #2 #3 #4 ; }\_tmpa
}
\_sdef{addcolor:^R}#1 #2 #3 #4 {\_cs{addcolor:=R}#1 (1-#2) (1-#3) (1-#4) }

\_sdef{addcolor:=k}#1 #2 #3 #4 #5 {%
   \_edef\_tmpa{\_noexpand\_addcolorA #1 \_cmyktorgb #2 #3 #4 #5 ; 0 }\_tmpa
}
\_sdef{addcolor:^k}#1 #2 #3 #4 #5 {\_cs{addcolor:=k}#1 (1-#2) (1-#3) (1-#4) #5 }
\_sdef{addcolor:^g}#1 #2 {\_addcolorA #1 (1-#2) (1-#2) (1-#2) 0 }
\_sdef{addcolor:=g}#1 #2 {\_addcolorA #1 #2 #2 #2 0 }
\_sdef{addcolor:=r}#1 #2 #3 #4 {\_addcolorA #1 #2 #3 #4 0 }
\_sdef{addcolor:^r}#1 #2 #3 #4 {\_addcolorA #1 (1-#2) (1-#3) (1-#4) 0 }
\_def\_stripK#1 .0;{#1}
\_let\_colordef=\_cmykcolordef  % default \_colordef is \_cmykcolordef

   \_doc -----------------------------
   Public versions of \`\colordef` and \`\useK` macros are declared using
   `\_def`, because the internal versions `\_colordef` and `\_useK` are
   changed during processing.
   \_cod -----------------------------

\_def \useK{\_useK}
\_def \colordef {\_colordef}
\_public \cmykcolordef \rgbcolordef ;

   \_doc -----------------------------
   The \LaTeX/ file `x11nam.def` is read by \`\morecolors`. The numbers
   0,1,2,3,4 are transformed to letters O, <none>, B, C, D in the name of the
   color. Colors defined already are not re-defined. The empty \`\_showcolor`
   macro should be re-defined for color catalog printing. For example:
   \begtt
   \def\vr{\vrule height10pt depth2pt width20pt}
   \def\_showcolor{\hbox{\tt\_bslash\_tmpb: \csname\_tmpb\endcsname \vr}\space\space}
   \begmulti 4 \typosize[11/14]
   \morecolors
   \endmulti
   \endtt
   \_cod -----------------------------

\_def\_morecolors{%
   \_long\_def\_tmp##1\preparecolorset##2##3##4##5{\_tmpa ##5;,,,;}
   \_def\_tmpa##1,##2,##3,##4;{\_ifx,##1,\_else
      \_def\_tmpb{##1}\_replstring\_tmpb{1}{}\_replstring\_tmpb{2}{B}%
      \_replstring\_tmpb{3}{C}\_replstring\_tmpb{4}{D}\_replstring\_tmpb{0}{O}%
      \_ifcsname \_tmpb\_endcsname \_else
          \_sdef{\_tmpb}{\_setrgbcolor{##2 ##3 ##4}}\_showcolor\_fi
      \_ea\_tmpa\_fi
   }
   \_ea\_tmp\_input x11nam.def
}
\_let\_showcolor=\_relax % re-define it if you want to print a color catalog
\_public \morecolors ;

\_endcode % -------------------------------------

The colors have different behavior than fonts. Marks (whatsits) with color
information are stored into PDF output and \TeX/ doesn't interpret them.
The PDF viewer (or PDF interpreter in a printer) reads these marks
and switches colors according to them. This
is independent of \TeX/ group mechanism. You can declare
`\nolocalcolor` at the beginning of the document, if you want this behavior.
In this case, if you set a color then you must return to the black color
using `\Black` manually.

By default, \OpTeX/ sets `\localcolor`. It means that the typesetting
returns to a previous color at the end of the current group, so you cannot
write `\Black` explicitly. This is implemented using the `\aftergroup` feature.
There is a limitation of this feature: when a color selector is used in a
group of a box, which is saved by `\setbox`, then the activity or
reconstruction of the previous color is processed at `\setbox` time, no in the
box itself. You must correct it by double group:
\begtt
\setbox0=\hbox{\Red text}   % bad:  \Black is done after \setbox
\setbox0=\hbox{{\Red text}} % good: \Black is done after group inside the box
\endtt

The implementation of colors is based on colorstack, so the current color
can follow across more pages. It is not so obvious because PDF viewer (or PDF
interpreter) manipulates with colors locally at each PDF page and it
initializes each PDF page with black on white color.

Macros \^`\setcmykcolor``{<C> <M> <Y> <K>}` or \^`\setrgbcolor``{<R> <G> <B>}`
or\nl \^`\setgreycolor``{<Grey>}` should be used in color selectors or user can
specify these macros explicitly.

The color mixing processed by the \^`\colordef` is done in the subtractive color
model CMYK. If the result has a component greater than 1 then all
components are multiplied by a coefficient in order to the maximal component is
equal to 1.

You can move a shared amount of CMY components (i.e. their minimum) to the
$K$ component. This saves the color tonners and the result is more true.
This should be done by \^`\useK` command at the end of a linear combination
used in `\colordef`. For example
\begtt
\colordef \myColor {.3\Green + .4\Blue \useK}
\endtt
The `\useK` command exactly does:
$$
  \displaylines{
  k' =\min(C,M,Y), \cr \ C=(C-k')/(1-k'), \ M=(M-k')/(1-k'), \ Y=(Y-k')/(1-k'), \cr
  K = \min(1,K+k').
}
$$

You can use minus instead of plus in the linear combination in `\colordef`. The
given color is substracted in such case and the negative components are
rounded to zero immediately. For example
\begtt
\colordef \Color {\Brown-\Black}
\endtt
can be used for removing the black component from the color. You can use
the `-\Black` trick after `\useK` command to remove grey components
occurred during color mixing.

Finally, you can use `^` immediately preceded before the macro name of the
color. Then the complementary color is used here.
\begtt
\colordef\mycolor{\Grey+.6^\Blue} % the same as \colordef\mycolor{\Grey+.6\Yellow}
\endtt

The \^`\rgbcolordef` can be used to mix colors in additive color model RGB.
If \^`\onlyrgb` is declared, then \^`\colordef` works as \^`\rgbcolordef`.

If a CMYK to RGB or RGB to CMYK conversion is needed then
the following simple formulae are used
(ICC profiles are not supported):
$$
\displaylines{
   \hbox{CMYK to RGB:}\cr
   R = (1-C)(1-K), \ G = (1-M)(1-K), \ B = (1-Y)(1-K). \cr
   \hbox{RGB to CMYK:}\cr
   K'=\max(R,G,B), \ C=(K'-R)/K', \ M=(K'-G)/K', \ Y=(K'-B)/K', \ K=1-K'.
}
$$
The RGB to CMYK conversion is invoked when a color is declared using `\setrgbcolor`
and it is used in \^`\colordef` or if it is printed when \^`\onlycmyk` is declared.
The CMYK to RGB conversion is invoked when a color is declared using \^`\setcmykcolor`
and it is used in \^`\rgbcolordef` or if it is printed when \^`\onlyrgb` is declared.

\_endinput

2020-04-22 \replstring\tmpb{+ }{+}, {- }{-} added in \colordef, bug fixed
2020-03-18 introduced