1
# -*- CPERL -*-
2
# /=====================================================================\ #
3
# | xcolor.sty | #
4
# | Implementation for LaTeXML | #
5
# |=====================================================================| #
6
# | Part of LaTeXML: | #
7
# | Public domain software, produced as part of work done by the | #
8
# | United States Government & not subject to copyright in the US. | #
9
# |---------------------------------------------------------------------| #
10
# | Thanks to Silviu Vlad Oprea <s.oprea@jacobs-university.de> | #
11
# | of the arXMLiv group for initial implementation | #
12
# | http://arxmliv.kwarc.info/ | #
13
# | Released under the Gnu Public License | #
14
# | Released to the Public Domain | #
15
# |---------------------------------------------------------------------| #
16
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
17
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
18
# \=========================================================ooo==U==ooo=/ #
19
package LaTeXML::Package::Pool;
20
use strict;
21
use warnings;
22
use LaTeXML::Package;
23
use List::Util qw(min max);
24
RequirePackage('color');
25
26
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27
# Options & Initializations.
28
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
29
30
# Setting target color model (ignored for now)
31
foreach my $option (qw(natural rgb cmy cmyk hsb gray RGB HTML HSB Gray monochrome)) { #mono???
32
DeclareOption($option, sub { }); }
33
34
# Ignorable options
35
foreach my $option (qw(showerrors hideerrors fixpdftex prologue
36
kernelfbox xcdraw noxcdraw fixinclude
37
dviwindo oztex xdvi
38
usenames)) { # which does... what?
39
DeclareOption($option, sub { }); }
40
41
# Loading sets of names
42
DeclareOption('dvipsnames', sub { InputDefinitions('dvipsnam', type => 'def'); return; });
43
DeclareOption('dvipsnames*', sub { InputDefinitions('dvipsnam', type => 'def'); return; });
44
DeclareOption('svgnames', sub { InputDefinitions('svgnam', type => 'def'); return; });
45
DeclareOption('svgnames*', sub { InputDefinitions('svgnam', type => 'def'); return; });
46
DeclareOption('x11names', sub { InputDefinitions('x11nam', type => 'def'); return; });
47
DeclareOption('x11names*', sub { InputDefinitions('x11nam', type => 'def'); return; });
48
49
# Load colortbl package;
50
DeclareOption('table', sub { RequirePackage('colortbl'); return; });
51
52
# Does this load hyperref, or modify it? - TODO??
53
DeclareOption('hyperref', sub { });
54
55
DefMacro('\GetGinDriver', '');
56
DefMacro('\GinDriver', 'LaTeXML');
57
58
DefRegister('\tracingcolors' => Number(0));
59
DefMacro('\XC@tracing', '0');
60
61
DefConditional('\ifglobalcolors', undef);
62
DefConditional('\ifdefinecolors', undef);
63
DefConditional('\ifconvertcolorsD', undef);
64
DefConditional('\ifconvertcolorsU', undef);
65
DefConditional('\ifblendcolors', undef);
66
DefConditional('\ifmaskcolors', undef);
67
DefConditional('\ifxglobal@', undef);
68
RawTeX('\globalcolorsfalse\definecolorstrue');
69
70
# Start with "current color" (using the shorthand ".") to black
71
AssignValue('color_.' => Black());
72
73
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74
# Extra Color Models
75
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76
sub delta {
77
my ($v, $n) = @_;
78
return ($v <= ($n + 1) / 2 ? $v / ($n + 1) : ($v + 1) / ($n + 1)); }
79
80
#======================================================================
81
# RGB: red,green,blue integers in 0..L, L = 255 by default
82
DefMacroI('\rangeRGB', undef, '255');
83
84
DefColorModel('RGB', 'rgb',
85
sub { # RGB ==> rgb
86
my $L = ToString(Expand(T_CS '\rangeRGB'));
87
Color('rgb', map { delta($_, $L) } $_[0]->components); },
88
sub { # rgb ==> RGB
89
my $L = ToString(Expand(T_CS '\rangeRGB'));
90
Color('RGB', map { int($_ * $L + 0.5) } $_[0]->components); });
91
92
#======================================================================
93
# HTML = RRGGBB where RR,GG,BB are red,green,blue components in hex
94
DefColorModel('HTML', 'rgb',
95
sub { # HTML ==> rgb
96
if ($_[0][1] =~ m/(..)(..)(..)/) {
97
Color('rgb', map { delta(hex($_), 255) } $1, $2, $3); } },
98
sub {
99
my $hex = $_[0]->toHex; $hex =~ s/^#//;
100
Color('HTML', $hex); });
101
#======================================================================
102
# Hsb: h in 0..H, s,b in 0..1, H = 360 by default
103
DefMacroI('\rangeHsb', undef, '360');
104
105
DefColorModel('Hsb', 'hsb',
106
sub { # Hsb ==> hsb
107
my $H = ToString(Expand(T_CS '\rangeHsb'));
108
Color('hsb', $_[0][1] / $H, $_[0][2], $_[0][3]); },
109
sub { # hsb ==> Hsb
110
my $H = ToString(Expand(T_CS '\rangeHsb'));
111
Color('Hsb', $H * $_[0][1], $_[0][2], $_[0][3]); });
112
113
#======================================================================
114
# HSB: h,s,b in 0..M, M = 240 by default
115
DefMacroI('\rangeHSB', undef, '240');
116
117
DefColorModel('HSB', 'hsb',
118
sub { # HSB ==> hsb
119
my $M = ToString(Expand(T_CS '\rangeHSB'));
120
Color('hsb', delta($_[0][1], $M), delta($_[0][2], $M), delta($_[0][3], $M)); },
121
sub { # hsb ==> HSB
122
my $M = ToString(Expand(T_CS '\rangeHSB'));
123
Color('HSB', map { int(0.5 + $M * $_) } $_[0][1], $_[0][2], $_[0][3]); });
124
125
#======================================================================
126
# "tuned" or Piecewise continuous Hsb
127
# \rangetHsb is sequence of pairs x,y (; x,y)*
128
DefMacroI('\rangetHsb', undef, '60,30;120,60;180,120;210,180;240,240');
129
130
DefColorModel('tHsb', 'hsb',
131
sub { # tHsb ==> hsb
132
my ($model, $h, $s, $b) = @{ $_[0] };
133
my $H = ToString(Expand(T_CS '\rangeHsb'));
134
# my $rangetHsb = '0,0;'.ToString(Expand T_CS '\rangetHsb').';'.$H.','.$H;
135
my $rangetHsb = ToString(Expand T_CS '\rangetHsb') . ';' . $H . ',' . $H;
136
my ($xn, $yn, $xn_1, $yn_1) = (0, 0, 0, 0);
137
foreach (split(';', $rangetHsb)) {
138
($xn_1, $yn_1) = ($xn, $yn);
139
($xn, $yn) = split(',', $_);
140
last if $h <= $xn; }
141
Color('hsb', ($yn_1 + (($yn - $yn_1) / ($xn - $xn_1)) * ($h - $xn_1)) / $H, $s, $b); },
142
sub { # hsb ==> tHsb
143
my ($model, $h, $s, $b) = @{ $_[0] };
144
# First scale Hue.
145
my $H = ToString(Expand(T_CS '\rangeHsb'));
146
$h *= $H;
147
my $rangetHsb = ToString(Expand T_CS '\rangetHsb') . ';' . $H . ',' . $H;
148
my ($xn, $yn, $xn_1, $yn_1) = (0, 0, 0, 0);
149
foreach (split(';', $rangetHsb)) {
150
($xn_1, $yn_1) = ($xn, $yn);
151
($xn, $yn) = split(',', $_);
152
# last if $h >= $yn_1 && $h <= $yn; }
153
last if $h <= $yn; }
154
Color('tHsb', $xn_1 + (($xn - $xn_1) / ($yn - $yn_1)) * ($h - $yn_1), $s, $b); });
155
#======================================================================
156
157
DefMacroI('\rangeGray', undef, '15');
158
DefColorModel('Gray', 'gray',
159
sub { # Gray ==> gray
160
my $N = ToString(Expand(T_CS '\rangeGray'));
161
Color('gray', delta($_[0][1], $N)); },
162
sub { # gray ==> Gray
163
my $N = ToString(Expand(T_CS '\rangeGray'));
164
Color('Gray', int(0.5 + $N * $_[0][1])); });
165
166
#======================================================================
167
DefColorModel('wave', 'hsb',
168
sub { # wave ==> hsb
169
my ($model, $lambda) = @{ $_[0] };
170
my $g = 1; # fixed correction number; xcolor uses 1; pstricks uses others (e.g. 0.8).
171
# anyway, no significant difference can be notified.
172
local *eta = sub {
173
my ($x) = @_;
174
min(1, max(0, $x))**$g; };
175
my ($h, $bb);
176
if ($lambda < 440) { $h = 4 + eta(($lambda - 440) / (-60)); }
177
elsif ($lambda < 490) { $h = 4 - eta(($lambda - 440) / 50); }
178
elsif ($lambda < 510) { $h = 2 + eta(($lambda - 510) / (-20)); }
179
elsif ($lambda < 580) { $h = 2 - eta(($lambda - 510) / 70); }
180
elsif ($lambda < 645) { $h = eta(($lambda - 645) / (-65)); }
181
else { $h = 0; }
182
if ($lambda < 420) { $bb = eta(0.3 + 0.7 * ($lambda - 380) / 40); }
183
elsif ($lambda < 700) { $bb = 1; }
184
else { $bb = eta(0.3 + 0.7 * ($lambda - 780) / (-80)); }
185
Color('hsb', $h / 6, 1, $bb); },
186
sub {
187
Error(); });
188
189
#======================================================================
190
191
DefMacro('\adjustUCRBG', '1,1,1,1'); # ??
192
DefMacro('\paperquality', '1');
193
194
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195
# Specifying colors
196
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197
198
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199
# Decoding colors etc.
200
# Several kinds of target forms of color.
201
# We COULD assume HTML model (the rest of latexml sorta does) "#RRGGBB"
202
# OR we could assume rgb, ('rgb',r,g,b)
203
# OR we could assume converted to any core model: (model,components,...)
204
###our $TARGET_COLOR_MODEL = 'rgb';
205
our $TARGET_COLOR_MODEL = '';
206
207
# ParseXColor(<model_list>,<spec_list_or_color_expr>, <tomodel>);
208
# If <model_list> is non-empty, then treat it as a list of models (separated by '/')
209
# choose the current target model, or the 1st model if none match the current one
210
# then find the corresponding spec in <spec_list> (also separated by '/')
211
# if<model_list> was prefixed with <model>:, convert result to that model.
212
# Otherwise,
213
# treat <spec_list_or_color_expr> as a color expression.
214
# evaluate it.
215
# if <tomodel> is non-null,
216
# convert the final result to that model
217
sub ParseXColor {
218
my ($models, $specs, $tomodel) = @_;
219
$models = ToString($models) if ref $models;
220
$specs = ToString($specs) if ref $specs;
221
$tomodel = ToString($tomodel) if ref $tomodel;
222
my $color;
223
if ($models) { # If models given, it's in form: (tomodel:)? model (,model)*
224
if ($models =~ s/^(.*?)://) {
225
$tomodel = $1 unless $tomodel; }
226
my @models = split(/\//, $models);
227
my @specs = split(/\//, $specs);
228
if (scalar(@models) != scalar(@specs)) {
229
Error('unexpected', $specs, $STATE->getStomach,
230
"Length of color model_list must be same as spec_list.",
231
"models is '$models'; specs is '$specs'");
232
return Black(); }
233
my ($model, $spec) = ($models[0], $specs[0]);
234
while (@models) {
235
if ($models[0] eq $TARGET_COLOR_MODEL) {
236
($model, $spec) = ($models[0], $specs[0]); last; }
237
shift(@models); shift(@specs); }
238
# Now, parse the spec relative to the chosen model
239
$spec =~ s/^\s+//; $spec =~ s/\s+$//;
240
if ($spec =~ /^\{\s*(.*?)\s*\}$/) { # Trim
241
$spec = $1; }
242
if ($model eq 'named') {
243
$color = LookupColor($spec); }
244
else {
245
$color = Color($model, ($spec =~ /,/ ? split(/\s*,\s*/, $spec) : split(/\s+/, $spec)))->toCore; } }
246
else {
247
$color = DecodeColor($specs); }
248
# And finally convert to the target model, if requested.
249
return ($tomodel ? $color->convert($tomodel) : $color); }
250
251
#======================================================================
252
# Given a <colorexpr>
253
# (<name>|<expression>|<extended_expression>) <functional_expression>*
254
# decode it into ($model,@spec) form
255
256
# NOTE: Clean up this code....
257
sub DecodeColor {
258
my ($expression) = @_;
259
$expression = ToString($expression);
260
my $prefix_re = qr/-/; #
261
# [ <name> = . ==> current color; <name> = '' ==> white]
262
my $name_re = qr/|[-]*\.|[-]*[a-zA-Z0-9@\*]+/;
263
my $ne_name_re = qr/[-]*\.|[-]*[a-zA-Z0-9@\*]+/;
264
my $pct_re = qr/(?:\d*\.?\d*|[+-]*\d+\.?\d*|[+-]*\d*\.?\d+)/;
265
my $pct_capture_re = qr/(\d*\.?\d*|[+-]*\d+\.?\d*|[+-]*\d*\.?\d+)/;
266
# <mix_expr> : !<pct1>!<name1>!...<pctn>!(<namen>)?
267
my $mix_expr_re = qr/!$pct_re(?:!$name_re!$pct_re)*(?:!$name_re)?/;
268
# <postfix> -> |!!<plus>|!![<num>]
269
my $postfix_re = qr/!!(?:\++|\[\d+\])/;
270
# <expr> : <prefix><ne_name><mix_expr><postfix>
271
my $expr_re = qr/($prefix_re*)($ne_name_re)
272
($mix_expr_re)?($postfix_re)?/x; # 4 inner groups
273
my $core_model_re = qr/rgb|cmy|cmyk|hsb|gray/;
274
# PGF flaw here; don't allow div to be empty
275
my $div_re = qr/[+-]*(?:\d*[1-9]+\d*(?:\.\d*)?|\d*\.\d*[1-9]+\d*)/;
276
my $div_capture_re = qr/[+-]*(\d*[1-9]+\d*(\.\d*)?|\d*\.\d*[1-9]+\d*)/;
277
my $dec_re = qr/[+-]*(?:\d*\.?\d*)/;
278
# <ext_expr> : <core_model>,<div>:<expr1><dec1>;...;<exprk><deck>
279
# | <core_model>:<expr1><dec1>;...;<exprk><deck>
280
my $ext_expr_re = qr/($core_model_re)(,($div_re))?:
281
(($expr_re|$name_re),$dec_re(?:;(?:$expr_re|$name_re),$dec_re)*)/x;
282
# <color_expr> : <name> | <expr> | <ext_expt>
283
my $color_expr_re = qr/$expr_re|$ext_expr_re/;
284
my $function_re = qr/wheel|twheel/;
285
my $arg_re = $div_re;
286
# <func_expr> : ><function>,<arg1>,...,<argj>
287
my $func_expr_re = qr/>$function_re,(?:$arg_re|$arg_re,$arg_re)/;
288
# <color> : <color_expr><func_expr1>...<func_expri>
289
my $color_re = qr/($color_expr_re)(($func_expr_re)*)/;
290
291
my $color;
292
if ($expression =~ /^$color_re$/) {
293
#DG: Dear reader, I present to you: maintenance hell:
294
my $prefix = $2 || $10;
295
my $name = $3 || $11;
296
my $mix_expr = $4 || $12;
297
my $postfix = $5 || $13;
298
my $core_model = $6;
299
my $div = $8;
300
my $exprs = $9;
301
my $func_expr = $19;
302
my @pallete = ();
303
304
if (defined $core_model) { # Extended color expression: combine colors as on a pallete
305
$color = Black->convert($core_model);
306
my $dectot = 0;
307
while ($exprs =~ s/($expr_re),($dec_re)//) {
308
my $dec = $6; $dec =~ s/--//g;
309
next if !$dec || $dec eq '.'; # the contribution is 0!
310
$dectot += $dec;
311
push(@pallete, [DecodeColor($1), $dec]); }
312
$div = $dectot unless $div;
313
foreach my $cp (@pallete) {
314
$color = $color->add($$cp[0]->scale($$cp[1] / $div)); } }
315
else { # Standard Color Expression: <prefix><name><mix_expr><postfix>
316
$color = ($postfix && ($postfix =~ /!!\[(\d+)\]/) # Note "out-of-order" effect!
317
? indexColorSeries($name, $1)
318
: LookupXColor($name));
319
if (my $blend = LookupValue('color_blend')) { # Combine any stored blend with the mix_expr.
320
$mix_expr .= $blend; }
321
if ($mix_expr) {
322
while ($mix_expr =~ s/^!([^!]*)(!([^!]*))?//x) {
323
my ($nm, $pct) = ($3 || 'white', $1);
324
$pct =~ s/--//g; $pct = ($pct eq '' ? 100 : ($pct eq '.' ? 0 : $pct));
325
$color = $color->mix(LookupXColor($nm), max(0, min(100, $pct)) / 100); } }
326
$color = $color->complement if $prefix && (length($prefix) % 2);
327
if ($postfix && ($postfix =~ /^!!(\++)$/)) {
328
stepColorSeries($name, length($1)); } } # Step the series, but no effect on color
329
# Now apply any function expressions to the result.
330
if ($func_expr) {
331
while ($func_expr =~ s/>(wheel|twheel),$pct_capture_re(,$div_capture_re)?//) {
332
my ($func, $angle, $full) = ($1, $2, $4);
333
my $model = ($func eq 'wheel' ? 'Hsb' : 'tHsb');
334
my ($h, $s, $b) = $color->convert($model)->components;
335
my $circle = ($full ? ToString(Expand(T_CS('\rangeHsb'))) / $full : 1);
336
$color = Color($model, $h + $angle * $circle, $s, $b); } }
337
}
338
else {
339
Error('misdefined', $expression, $STATE->getStomach,
340
"syntax error in <color> expression '$expression'"); }
341
return $color; }
342
343
sub LookupXColor {
344
my ($name) = @_;
345
if ($name =~ /^(-*)([^-].*)$/) {
346
return (length($1) % 2 ? LookupColor($2)->complement : LookupColor($2)); } }
347
348
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349
# Selecting a Color Model.
350
# But I THINK we're going to end up using pure HTML color model as the TARGET model?
351
# Is it worth thinking about the "natural" model to store the intermediate colors?
352
353
# \selectcolormodel{model}
354
# Sets the target model to model
355
DefMacro('\selectcolormodel{}', '');
356
357
# \substitutecolormodel{sourcemodel}{targetmodellist}
358
# makes xcolor use (one of) target model whenever source model was specified
359
DefMacro('\substitutecolormodel{}{}', '');
360
361
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362
# Defining colors
363
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
364
365
DefMacroI('\xglobal@list', undef,
366
'\definecolor\definecolors\definecolorset\colorlet\providecolor'
367
. '\providecolors\providecolorset\blendcolors\maskcolors'); # \substitutecolormodel}
368
369
DefMacro('\xglobal Token', sub {
370
my ($gullet, $token) = @_;
371
if (grep { $token->equals($_) } LookupDefinition(T_CS('\xglobal@list'))->getExpansion->unlist) {
372
AssignValue('Boolean:xglobal@' => 1);
373
$token; }
374
else {
375
(T_CS('\global'), $token); } });
376
377
# Internal storage of color definition 5 elements:
378
# \\color@<name> => { \xcolor@ {<type>} {<driver_rep>} {<model>} {<spec>}
379
# [if <type> is 'named', <driver_rep> might be the name?
380
# Silviu says that the \\color@<name> expanding to some encoding is crucial for pgf->svg.
381
# He's using the form {\relax \relax {rgb r g b} {rgb} {r,g,b} }
382
# Which parts of that are crucial? the internal form?, the spec? all of it?
383
384
sub checkNoPostscript {
385
my ($type, $macro) = @_;
386
$type = ToString($type->isaBox ? $type : Expand($type)) if ref $type;
387
if ($type && ($type eq 'ps')) { # Warn? Ignore postscript
388
Warn('ignored', $macro, $STATE->getStomach, "Ignoring definition of postscript color in $macro");
389
return; }
390
return 1; }
391
392
# \definecolor[<type>]{<name>}{<model_list>}{<spec_list>}
393
DefMacro('\definecolor[]{}{}{}', '\XC@definecolor[#1]{#2}[\colornameprefix]{#3}{#4}');
394
# prepare, same but defered.... but we don't bother defering!
395
Let('\preparecolor', '\definecolor');
396
Let('\xdefinecolor', '\definecolor');
397
398
# \providecolor[<type>]{<name>}{<model_list>}{<spec_list>}
399
DefMacro('\providecolor[]{}{}{}', '\XC@providecolor[#1]{#2}[\colornameprefix]{#3}{#4}');
400
401
# \DefineNamedColor{<type>}{<name>}{<model_list>}{<spec_list>}
402
DefMacro('\DefineNamedColor{}{}{}{}', '\definecolor[#1]{#2}{#3}{#4}');
403
404
# What is $prefix (\colornameprefix, defaults to XC@ ??? ) used for?
405
DefMacroI('\colornameprefix', undef, 'XC@');
406
407
DefPrimitive('\XC@definecolor[]{}[]{}{}', sub {
408
my ($stomach, $type, $name, $prefix, $models, $specs) = @_;
409
return unless checkNoPostscript($type, '\XC@definecolor');
410
($type, $name, $prefix, $models, $specs)
411
= map { $_ && Expand($_) } $type, $name, $prefix, $models, $specs;
412
DefColor(ToString($name), ParseXColor($models, $specs),
413
(LookupValue('Boolean:xglobal@' => 0) ? 'global' : undef));
414
AssignValue('Boolean:xglobal@' => 0);
415
# and return a box, so it can be recorded?
416
# but we don't want the \XC@ version, and we're not handling the prefix anyway...
417
Box(undef, undef, undef,
418
Invocation(T_CS('\definecolor'), ($type && $type->unlist ? $type : undef),
419
$name, $models, $specs)); });
420
421
DefPrimitive('\XC@providecolor[]{}[]{}{}', sub {
422
my ($stomach, $type, $name, $prefix, $models, $specs) = @_;
423
return unless checkNoPostscript($type, '\XC@providecolor');
424
($type, $name, $prefix, $models, $specs)
425
= map { $_ && Expand($_) } $type, $name, $prefix, $models, $specs;
426
my $sname = ToString($name);
427
return if LookupValue('color_' . $sname);
428
DefColor($sname, ParseXColor($models, $specs),
429
(LookupValue('Boolean:xglobal@' => 0) ? 'global' : undef));
430
AssignValue('Boolean:xglobal@' => 0);
431
Box(undef, undef, undef,
432
# Invocation(T_CS('\XC@providecolor'),$type,$name,$prefix,$models,$specs)); });
433
Invocation(T_CS('\providecolor'), ($type && $type->unlist ? $type : undef),
434
$name, $models, $specs)); });
435
436
# \colorlet[<type>]{<name>}[<num_model>]{<color>}
437
DefPrimitive('\colorlet[]{}[]{}', sub {
438
my ($stomach, $type, $name, $tomodel, $colordesc) = @_;
439
return unless checkNoPostscript($type, '\colorlet');
440
($type, $name, $tomodel, $colordesc)
441
= map { $_ && Expand($_) } $type, $name, $tomodel, $colordesc;
442
DefColor(ToString($name), ParseXColor(undef, $colordesc, $tomodel),
443
(LookupValue('Boolean:xglobal@' => 0) ? 'global' : undef));
444
AssignValue('Boolean:xglobal@' => 0);
445
Box(undef, undef, undef,
446
Invocation(T_CS('\colorlet'), $type, $name, $tomodel, $colordesc)); });
447
448
# \definecolorset[<type>]{<model_list>}{<head>}{<tail>}{<set_spec>}
449
DefPrimitive('\definecolorset[]{}{}{}{}', sub {
450
my ($stomach, $type, $models, $head, $tail, $specset) = @_;
451
return unless checkNoPostscript($type, '\definecolorset');
452
($type, $models, $head, $tail, $specset)
453
= map { $_ && Expand($_) } $type, $models, $head, $tail, $specset;
454
my $shead = ToString($head);
455
my $stail = ToString($tail);
456
my $scope = (LookupValue('Boolean:xglobal@' => 0) ? 'global' : undef);
457
foreach my $spec (split(/;/, ToString($specset))) {
458
459
if ($spec =~ /^([^,]*),(.*)$/) {
460
my ($name, $specs) = ($1, $2);
461
DefColor($shead . $name . $stail, ParseXColor($models, $specs), $scope); } }
462
AssignValue('Boolean:xglobal@' => 0);
463
Box(undef, undef, undef,
464
Invocation(T_CS('\definecolorset'), $type, $models, $head, $tail, $specset)); });
465
466
Let('\preparecolorset', '\definecolorset');
467
468
# \providecolorset[<type>]{<model_list>}{<head>}{<tail>}{<set_spec>}
469
DefPrimitive('\providecolorset[]{}{}{}{}', sub {
470
my ($stomach, $type, $models, $head, $tail, $specset) = @_;
471
return unless checkNoPostscript($type, '\providecolorset');
472
($type, $models, $head, $tail, $specset)
473
= map { $_ && Expand($_) } $type, $models, $head, $tail, $specset;
474
my $shead = ToString($head);
475
my $stail = ToString($tail);
476
my $scope = (LookupValue('Boolean:xglobal@' => 0) ? 'global' : undef);
477
foreach my $spec (split(/;/, ToString(Expand($specset)))) {
478
479
if ($spec =~ /^([^,]*),(.*)$/) {
480
my ($name, $specs) = ($1, $2);
481
my $defname = $shead . $name . $stail;
482
next if LookupValue('color_' . $defname);
483
DefColor($defname, ParseXColor($models, $specs), $scope); } }
484
AssignValue('Boolean:xglobal@' => 0);
485
Box(undef, undef, undef,
486
Invocation(T_CS('\providecolorset'), $type, $models, $head, $tail, $specset)); });
487
488
sub defineColors {
489
my ($stomach, $idpairs, $ifundef) = @_;
490
foreach my $pair (split(/,/, ToString($idpairs))) {
491
$pair =~ s/^\s*//; $pair =~ s/\s*$//;
492
my ($name, $from) = ($pair =~ /^([^=]*?)\s*=\s*(.*)$/ ? ($1, $2) : ($pair, $pair));
493
next if $ifundef && LookupValue('color_' . $name);
494
if (my $c = LookupValue('color_' . $from)) {
495
AssignValue('color_' . $name => $c);
496
DefMacroI('\\\\color@' . $name, undef, Expand(T_CS('\\\\color@' . $from))); } }
497
return; }
498
499
DefPrimitive('\definecolors{}', sub {
500
my $idpairs = Expand($_[1]);
501
defineColors($_[0], $idpairs, 0);
502
Box(undef, undef, undef, Invocation(T_CS('\definecolors'), $idpairs)); });
503
504
DefPrimitive('\providecolors{}', sub {
505
my $idpairs = Expand($_[1]);
506
defineColors($_[0], $idpairs, 1);
507
Box(undef, undef, undef, Invocation(T_CS('\providecolors'), $idpairs)); });
508
509
# Now, define the default colors.
510
RawTeX(<<'EOTeX');
511
\definecolorset{rgb/hsb/cmyk/gray}{}{}%
512
{red,1,0,0/0,1,1/0,1,1,0/.3;%
513
green,0,1,0/.33333,1,1/1,0,1,0/.59;%
514
blue,0,0,1/.66667,1,1/1,1,0,0/.11;%
515
brown,.75,.5,.25/.083333,.66667,.75/0,.25,.5,.25/.5475;%
516
lime,.75,1,0/.20833,1,1/.25,0,1,0/.815;%
517
orange,1,.5,0/.083333,1,1/0,.5,1,0/.595;%
518
pink,1,.75,.75/0,.25,1/0,.25,.25,0/.825;%
519
purple,.75,0,.25/.94444,1,.75/0,.75,.5,.25/.2525;%
520
teal,0,.5,.5/.5,1,.5/.5,0,0,.5/.35;%
521
violet,.5,0,.5/.83333,1,.5/0,.5,0,.5/.205}%
522
\definecolorset{cmyk/rgb/hsb/gray}{}{}%
523
{cyan,1,0,0,0/0,1,1/.5,1,1/.7;%
524
magenta,0,1,0,0/1,0,1/.83333,1,1/.41;%
525
yellow,0,0,1,0/1,1,0/.16667,1,1/.89;%
526
olive,0,0,1,.5/.5,.5,0/.16667,1,.5/.39}
527
\definecolorset{gray/rgb/hsb/cmyk}{}{}%
528
{black,0/0,0,0/0,0,0/0,0,0,1;%
529
darkgray,.25/.25,.25,.25/0,0,.25/0,0,0,.75;%
530
gray,.5/.5,.5,.5/0,0,.5/0,0,0,.5;%
531
lightgray,.75/.75,.75,.75/0,0,.75/0,0,0,.25;%
532
white,1/1,1,1/0,0,1/0,0,0,0}
533
EOTeX
534
535
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
536
# Using Colors
537
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
538
539
DefPrimitive('\color[]{}', sub {
540
my ($stomach, $models, $colororspecs) = @_;
541
($models, $colororspecs) = map { $_ && Expand($_) } $models, $colororspecs;
542
my $color = ParseXColor($models, $colororspecs);
543
DefColor('.', $color);
544
AssignValue('preambleTextcolor', $color) if LookupValue('inPreamble');
545
MergeFont(color => $color);
546
(Box(undef, undef, undef, Invocation(T_CS('\color'), $models, $colororspecs)),
547
Digest(T_CS('\XC@mcolor'))); });
548
549
DefPrimitive('\set@color', sub {
550
my $color = LookupValue('color_.');
551
AssignValue('preambleTextcolor', $color) if LookupValue('inPreamble');
552
MergeFont(color => $color);
553
Box(undef, undef, undef, T_CS('\set@color')); });
554
555
DefPrimitive('\pagecolor[]{}', sub {
556
my ($stomach, $models, $colororspecs) = @_;
557
($models, $colororspecs) = map { $_ && Expand($_) } $models, $colororspecs;
558
my $color = ParseXColor($models, $colororspecs);
559
AssignValue('preambleBackgroundcolor', $color) if LookupValue('inPreamble');
560
MergeFont(background => $color);
561
(LookupValue('inPreamble') ? ()
562
: Box(undef, undef, undef, Invocation(T_CS('\pagecolor'), $models, $colororspecs))); });
563
564
#======================================================================
565
# Color Boxes
566
#======================================================================
567
# These probably work from the previous & color definitions?
568
569
# \colorbox{<color>}{<text>}
570
# \fcolorbox{<color/frame>}{<color/background>}{<text>}
571
# \fcolorbox[<model_list>]{<spec_list/frame>}{<spec_list/background>}{<text>}
572
# \fcolorbox[<model_list/frame>]{<spec_list/frame>}[<model_list/background>{<spec_list/background>}{<text>}
573
# \fcolorbox{<color/frame>}[<model_list/background>{<spec_list/background>}{<text>}
574
575
# \boxframe{<width>}{<height>}{<depth}>
576
# this should probably derive from the code for \hrule,
577
# but arrange for the color to determine the border's color, not the background!
578
579
DefConstructor('\boxframe{Dimension}{Dimension}{Dimension}',
580
"<ltx:rule width='#1' height='#2' depth='#3'"
581
. " color='#color' framed='rectangle' framecolor='#framecolor'/>",
582
afterDigest => sub {
583
my ($stomach, $whatsit) = @_;
584
my $font = LookupValue('font');
585
$whatsit->setProperties(color => $font->getBackground || White,
586
framecolor => $font->getColor || Black);
587
return; });
588
589
#======================================================================
590
# Blending Colors
591
#======================================================================
592
# \blendcolors{<mix_expr>}
593
# \blendcolors*{<mix_expr>}
594
DefPrimitive('\blendcolors OptionalMatch:* {}', sub {
595
my ($stomach, $star, $mix) = @_;
596
# global if \global
597
AssignValue(color_blend => (($star && LookupValue('color_blend')) || '') . ToString(Expand($mix)),
598
(LookupValue('Boolean:xglobal@' => 0) ? 'global' : undef));
599
AssignValue('Boolean:xglobal@' => 0); });
600
601
DefMacro('\colorblend', sub { Explode(LookupValue('color_blend')); });
602
603
# \maskcolors[<num_model>]{<color>}
604
# Interestingly, this COULD work, but something like this
605
# $color = $color->convert($mask->model)->multiply($mask->components);
606
# needs to be applied just before merging the color into the font.
607
# And anyway, it seems absurd that someone wants to generate color separated XML?!?!
608
DefPrimitive('\maskcolors[]{}', sub {
609
my ($stomach, $model, $color) = @_;
610
Info('ignored', '\maskcolors', $stomach,
611
"Ignoring \\maskcolors declaration.");
612
return;
613
# DefMacroI('\colormask',undef,$color);
614
# AssignValue('Boolean:maskcolors'=>1);
615
# $color = ToString(Expand($color));
616
# if($color){
617
# $color = ParseXColor(undef,$color,$model); }
618
# AssignValue(color_mask=>$color);
619
});
620
621
# \colormask
622
DefMacroI('\colormask', undef, '');
623
624
#======================================================================
625
# Color Series
626
#======================================================================
627
# \definecolorseries{<name>}{<core_model>}{<method>}[<b_model>]{<b_spec>}[<s_model>]{<s_spec>}
628
# <name> becomes a named color, but with provisions to step it through a sequence
629
DefPrimitive('\definecolorseries{}{}{}[]{}[]{}', sub {
630
my ($stomach, $name, $model, $method, $bmodel, $bspec, $smodel, $sspec) = @_;
631
($name, $model, $method, $bmodel, $bspec, $smodel, $sspec) =
632
map { $_ && Expand($_) } $name, $model, $method, $bmodel, $bspec, $smodel, $sspec;
633
$name = ToString($name);
634
$model = ToString($model);
635
my $base = ParseXColor($bmodel, $bspec, $model);
636
$method = ToString($method);
637
my $grad = (($method eq 'step') || ($method eq 'grad')
638
? Color($model, split(/,/, ToString($sspec)))
639
: ParseXColor($smodel, $sspec, $model));
640
AssignValue('color_series_' . $name . '_base' => $base, 'global');
641
AssignValue('color_series_' . $name . '_method' => $method, 'global');
642
AssignValue('color_series_' . $name . '_delta' => $grad, 'global'); # gradient or last
643
});
644
645
# \resetcolorseries[<div>]{<name>}
646
# reset/initialize the color series <name> for <div> steps.
647
DefPrimitive('\resetcolorseries Optional:\colorseriescycle {}', sub {
648
my ($stomach, $div, $name) = @_;
649
$name = ToString(Expand($name));
650
$div = ToString(Expand($div));
651
my $base = LookupValue('color_series_' . $name . '_base');
652
my $method = LookupValue('color_series_' . $name . '_method');
653
my $grad = LookupValue('color_series_' . $name . '_delta'); # gradient or last
654
my $step;
655
if ($method eq 'step') { $step = $grad; }
656
elsif ($method eq 'grad') { $step = $grad->scale(1 / $div); }
657
elsif ($method eq 'last') {
658
my @f = $grad->components;
659
my @b = $base->components;
660
$step = Color($base->model, map { ($f[$_] - $b[$_]) / $div } 0 .. $#b); }
661
DefColor($name, $base, 'global'); # Reset <name> to it's base value
662
AssignValue('color_series_' . $name . '_step' => $step, 'global'); # and set the current step size
663
});
664
665
# \colorseriescycle Default number of steps in color series
666
DefMacro('\colorseriescycle', '16');
667
668
# perverse rotation of value back into [0..1], INCLUSIVE!
669
# accomodating rounding down to 1, up to 0, and fudging for rounding errors...
670
sub rangeReduction {
671
my ($value) = @_;
672
return ($value > 1 ? ($value > 1.00001 ? $value - int($value) : 1)
673
: ($value < 0 ? ($value < -0.0001 ? ($value - int($value) + 1) : 0)
674
: $value)); }
675
676
# Step the color series to the next position.
677
sub stepColorSeries {
678
my ($name, $n) = @_;
679
my $color = LookupValue('color_' . $name);
680
my $step = LookupValue('color_series_' . $name . '_step');
681
my @comp = $color->components;
682
my @step = $step->components;
683
DefColor($name, Color($color->model,
684
map { rangeReduction($comp[$_] + $n * $step[$_]) } 0 .. $#comp), 'global');
685
return; }
686
687
# return the $p-th color in the color series (but don't step it!)
688
sub indexColorSeries {
689
my ($name, $p) = @_;
690
my $base = LookupValue('color_series_' . $name . '_base');
691
my $step = LookupValue('color_series_' . $name . '_step');
692
my @comp = $base->components;
693
my @step = $step->components;
694
return Color($base->model, map { rangeReduction($comp[$_] + $p * $step[$_]) } 0 .. $#comp); }
695
696
#======================================================================
697
# Table support
698
#======================================================================
699
# \rowcolors[<commands>]{<row>}{<color/odd>}{<color/even>}
700
# \rowcolors*[<commands>]{<row>}{<color/odd>}{<color/even>}
701
AddToMacro('\@tabular@row@after', '\@xcolor@row@after');
702
DefMacroI('\@xcolor@row@after', undef, '');
703
DefPrimitive('\rowcolors OptionalMatch:* []{Number}{}{}', sub {
704
my ($stomach, $star, $commands, $first, $oddcolor, $evencolor) = @_;
705
($oddcolor, $evencolor) = map { $_ && Expand($_) } $oddcolor, $evencolor;
706
## Wishful thinking...?
707
DefMacroI('\@xcolor@row@after', undef, $commands);
708
AssignValue(tabular_row_color_first => $first->valueOf);
709
AssignValue(tabular_row_color_odd => ($oddcolor->unlist ? ParseXColor(undef, $oddcolor) : undef));
710
AssignValue(tabular_row_color_even => ($evencolor->unlist ? ParseXColor(undef, $evencolor) : undef)); });
711
712
DefConditional('\if@rowcolors', undef);
713
AssignValue('Boolean:@rowcolors' => 1);
714
DefMacroI('\showrowcolors', undef, '\global\@rowcolorstrue');
715
DefMacroI('\hiderowcolors', undef, '\global\@rowcolorsfalse');
716
717
DefMacro('\rownum', sub { Explode(LookupValue('Alignment')->currentRowNumber); });
718
719
AddToMacro('\@tabular@row@before', '\@tabular@row@before@xcolor');
720
#AddToMacro('\@tabular@row@after','\@tabular@row@after@xcolor');
721
722
# Note that this does NOT override columncolor!
723
# so we do NOT assign to tabular_row_color!!!
724
# only set the background color & font for the row.
725
DefConstructor('\@tabular@row@before@xcolor', sub {
726
my ($document, %props) = @_;
727
if (my $bg = $props{background}) { # only set if explicitly set a color
728
if (my $node = $document->findnode('ancestor-or-self::ltx:tr', $document->getNode)) {
729
$document->setAttribute($node, backgroundcolor => $bg); } }
730
return; },
731
afterDigest => sub {
732
my ($stomach, $whatsit) = @_;
733
if (LookupValue('Boolean:@rowcolors')) {
734
my $n = LookupValue('Alignment')->currentRowNumber;
735
my $first = LookupValue('tabular_row_color_first');
736
my $odd = LookupValue('tabular_row_color_odd');
737
my $even = LookupValue('tabular_row_color_even');
738
if ((defined $n) && (defined $first) && (defined $odd) && (defined $even)) {
739
if ($n >= $first) {
740
my $bg = ($n % 2 ? $odd : $even);
741
MergeFont(background => $bg);
742
$whatsit->setFont(LookupValue('font'));
743
$whatsit->setProperty(background => $bg); } } }
744
return; });
745
746
#======================================================================
747
# Color Specs
748
#======================================================================
749
sub fixedpt {
750
my ($value) = @_;
751
return int($value * 10000 + 0.5) / 10000; }
752
753
# \extractcolorspec{<color>}{<cmd>}
754
# Decodes <color> and defines
755
# \cmd => {{<model>}{<spec>}}
756
DefPrimitive('\extractcolorspec{}{}', sub {
757
my ($stomach, $colordesc, $cmd) = @_;
758
my $color = ParseXColor(undef, Expand($colordesc));
759
my $model = $color->model;
760
my @spec = ($model eq 'HTML' ? $color->components
761
: map { fixedpt($_) } $color->components);
762
DefMacroI(ToString($cmd), undef, '{' . $model . '}{' . join(',', @spec) . '}'); });
763
764
# \extractcolorspecs{<color>}{<modelcmd>}{<speccmd>}
765
DefPrimitive('\extractcolorspecs{}{}{}', sub {
766
my ($stomach, $colordesc, $modelcmd, $speccmd) = @_;
767
my $color = ParseXColor(undef, Expand($colordesc));
768
my $model = $color->model;
769
my @spec = ($model eq 'HTML' ? $color->components
770
: map { fixedpt($_) } $color->components);
771
DefMacroI(ToString($modelcmd), undef, $model);
772
DefMacroI(ToString($speccmd), undef, '{' . join(',', @spec) . '}'); });
773
774
# \convertcolorspec{<model>}{<spec>}{<model/target>}{<cmd>}
775
DefPrimitive('\convertcolorspec{}{}{}{}', sub {
776
my ($stomach, $fmodel, $spec, $tomodel, $cmd) = @_;
777
($fmodel, $spec, $tomodel) = map { $_ && Expand($_) } $fmodel, $spec, $tomodel;
778
# We expect only one model/spec here, but simplify API
779
my $color = ParseXColor($fmodel, $spec, $tomodel);
780
my $model = $color->model;
781
my @spec = ($model eq 'HTML' ? $color->components
782
: map { fixedpt($_) } $color->components);
783
DefMacroI(ToString($cmd), undef, join(',', @spec)); });
784
785
#======================================================================
786
# Arithmetic
787
#======================================================================
788
# \rdivide#1#2
789
# \rmultiply#1#2
790
# \rshift, \rrshift
791
# \lshift, \llshift
792
# (and a bunch more? \\llshift, \lshiftnum...
793
794
Let('\rmultiply', '\multiply');
795
Let('\rdivide', '\divide');
796
797
DefPrimitive('\lshift Variable', sub {
798
my ($stomach, $var) = @_;
799
return () unless $var;
800
my ($defn, @args) = @$var;
801
$defn->setValue($defn->valueOf(@args)->multiply(10), @args); });
802
803
DefPrimitive('\llshift Variable', sub {
804
my ($stomach, $var) = @_;
805
return () unless $var;
806
my ($defn, @args) = @$var;
807
$defn->setValue($defn->valueOf(@args)->multiply(100), @args); });
808
809
DefMacro('\lshiftnum {}', sub {
810
my ($gullet, $num) = @_;
811
Explode(10 * ToString(Expand($num))); });
812
813
DefMacro('\llshiftnum {}', sub {
814
my ($gullet, $num) = @_;
815
Explode(100 * ToString(Expand($num))); });
816
817
DefPrimitive('\lshiftset Variable {}', sub {
818
my ($stomach, $var, $num) = @_;
819
return () unless $var;
820
my ($defn, @args) = @$var;
821
$defn->setValue((10 * ToString(Expand($num)) . 'pt'), @args); });
822
823
DefPrimitive('\llshiftset Variable {}', sub {
824
my ($stomach, $var, $num) = @_;
825
return () unless $var;
826
my ($defn, @args) = @$var;
827
$defn->setValue((100 * ToString(Expand($num)) . 'pt'), @args); });
828
829
DefPrimitive('\rshift Variable', sub {
830
my ($stomach, $var) = @_;
831
return () unless $var;
832
my ($defn, @args) = @$var;
833
$defn->setValue($defn->valueOf(@args)->multiply(0.1), @args); });
834
835
DefPrimitive('\rrshift Variable', sub {
836
my ($stomach, $var) = @_;
837
return () unless $var;
838
my ($defn, @args) = @$var;
839
$defn->setValue($defn->valueOf(@args)->multiply(0.01), @args); });
840
841
#======================================================================
842
# General TeX internals
843
#======================================================================
844
RawTeX(<<'EOTeX');
845
\let\XC@bcolor\relax
846
\let\XC@mcolor\relax
847
\let\XC@ecolor\relax
848
849
\def\XC@append#1#2%
850
{\ifx#1\@undefined\def#1{#2}\else\ifx#1\relax\def#1{#2}\else
851
\toks@\expandafter{#1#2}\edef#1{\the\toks@}\fi\fi}
852
\def\XC@let@cc#1{\expandafter\XC@let@Nc\csname#1\endcsname}
853
\providecommand*\@namelet[1]{\expandafter\XC@let@Nc\csname#1\endcsname}
854
\def\XC@let@Nc#1#2{\expandafter\let\expandafter#1\csname#2\endcsname}
855
\def\XC@let@cN#1{\expandafter\let\csname#1\endcsname}
856
\def\@namexdef#1{\expandafter\xdef\csname #1\endcsname}
857
\def\aftergroupdef#1#2%
858
{\expandafter\endgroup\expandafter\def\expandafter#1\expandafter{#2}}
859
\def\aftergroupedef#1#2%
860
{\edef\@@tmp{\def\noexpand#1{#2}}\expandafter\endgroup\@@tmp}
861
\begingroup
862
\catcode`\!=13 \catcode`\:=13 \catcode`\-=13 \catcode`\+=13
863
\catcode`\;=13 \catcode`\/=13 \catcode`\"=13 \catcode`\>=13
864
\gdef\XC@edef#1#2%
865
{\begingroup
866
\ifnum\catcode`\!=13 \edef!{\string!}\fi
867
\ifnum\catcode`\:=13 \edef:{\string:}\fi
868
\ifnum\catcode`\-=13 \edef-{\string-}\fi
869
\ifnum\catcode`\+=13 \edef+{\string+}\fi
870
\ifnum\catcode`\;=13 \edef;{\string;}\fi
871
\ifnum\catcode`\"=13 \edef"{\string"}\fi
872
\ifnum\catcode`\>=13 \edef>{\string>}\fi
873
\edef#1{#2}\@onelevel@sanitize#1\aftergroupdef#1#1}
874
\gdef\XC@mdef#1#2%
875
{\begingroup
876
\ifnum\catcode`\/=13 \edef/{\string/}\fi
877
\ifnum\catcode`\:=13 \edef:{\string:}\fi
878
\edef#1{#2}\@onelevel@sanitize#1\aftergroupdef#1#1}
879
\endgroup
880
\def\XC@sdef#1#2{\edef#1{#2}\@onelevel@sanitize#1}
881
\def\@ifxempty#1{\@@ifxempty#1\@@ifxempty\XC@@}
882
\def\@@ifxempty#1#2\XC@@
883
{\ifx#1\@@ifxempty
884
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}
885
886
\def\XC@strip@comma#1,#2%
887
{\ifx,#2%
888
#1\expandafter\remove@to@nnil\else#1 \expandafter\XC@strip@comma\fi
889
#2}
890
{\catcode`Q=3
891
\gdef\XC@replace#1#2#3%
892
{\begingroup
893
\def\XC@repl@ce##1#2##2Q##3%
894
{\@ifxempty{##2}{\XC@r@pl@ce##1Q}{\XC@repl@ce##1##3##2Q{##3}}}%
895
\def\XC@r@pl@ce##1\@empty Q%
896
{\expandafter\endgroup\expandafter\def\expandafter#1\expandafter{##1}}%
897
\expandafter\XC@repl@ce\expandafter\@empty #1\@empty#2Q{#3}}
898
}
899
% ??
900
\def\XC@type#1%
901
{\expandafter\expandafter\expandafter\XC@typ@
902
\csname\string\color@#1\endcsname\@empty\@empty\@empty\XC@@}
903
\def\XC@typ@#1#2#3#4\XC@@
904
{\ifx#1\relax 0\else
905
\ifx#1\xcolor@
906
\ifx$#2$%
907
\ifx$#3$4\else3\fi\@gobbletwo
908
\else2\fi\@gobbletwo
909
\else1\fi
910
\fi}
911
912
EOTeX
913
914
#======================================================================
915
# Testing support (ugly)
916
#======================================================================
917
# Random TeX coding needed by xcolor
918
919
DefMacro('\testcolor', '\@testopt{\@testcolor}{}'); # define here, so texscan sees it!
920
921
RawTeX(<<'EOTeX');
922
\newenvironment*{testcolors}[1][rgb,cmyk,hsb,HTML]%
923
{\let\@@nam\@empty\count@\z@
924
\@for\@@tmp:=#1\do
925
{\advance\count@\@ne
926
\XC@sdef\@@tmp{\@@tmp}\edef\@@nam{\@@nam{\@@tmp}}}%
927
\edef\@@num{\the\count@}%
928
\def\XC@@gt{\textgreater}\def\@@tmp{OT1}%
929
\ifx\f@encoding\@@tmp
930
\@expandtwoargs\in@{,\f@family,}{,cmtt,pcr,}%
931
\ifin@\def\XC@@gt{>}\fi
932
\fi
933
\def\XC@@xcp@{-1}\ifnum\XC@tracing>1 \def\XC@tracing{1}\fi
934
\def\@testcolor[##1]##2%
935
{\XC@mdef\@@mod{##1}\XC@edef\@@clr{##2}%
936
\ifx\@@mod\@empty
937
\let\@@arg\@@clr\XC@replace\@@arg>\XC@@gt\else
938
\edef\@@arg{[\@@mod]{\@@clr}}\XC@definecolor[]{*}\@@mod\@@clr
939
\def\@@clr{*}\fi
940
\XC@append\@@arg{&}\extractcolorspecs\@@clr\@@mod\@@clr
941
\@testc@lor}%
942
\def\@testc@lor
943
{\count@\z@
944
\expandafter\@tfor\expandafter\@@tmp\expandafter:\expandafter=\@@nam\do
945
{\ifx\@@clr\@empty
946
\edef\@@cmd{\noexpand\textbf{\@@tmp}}%
947
\else
948
\convertcolorspec\@@mod\@@clr\@@tmp\@@cmd
949
\edef\@@cmd
950
{\noexpand\@testc@l@r{\@@tmp}{\@@cmd}%
951
\ifx\@@mod\@@tmp\noexpand\underline\fi
952
{\expandafter\XC@strip@comma\@@cmd,,\@nnil}}%
953
\fi
954
\expandafter\XC@append\expandafter\@@arg\expandafter{\@@cmd}%
955
\advance\count@\@ne
956
\ifnum\count@=\@@num\XC@append\@@arg{\\}\else\XC@append\@@arg{&}\fi}%
957
\@@arg}%
958
\def\@testc@l@r##1##2%
959
{\fboxsep\z@\fbox{\colorbox[##1]{##2}{\phantom{XX}}} }%
960
\tabular{@{}l*{\@@num}{l}@{}}%
961
\def\@@arg{\textbf{color}& }\let\@@clr\@empty\@testc@lor}%
962
{\endtabular\ignorespacesafterend}
963
EOTeX
964
965
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966
ProcessOptions();
967
968
1;
969