1if not modules then modules = { } end modules ['lpdf-col'] = {
2 version = 1.001,
3 comment = "companion to lpdf-ini.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11local type, next, tostring, tonumber = type, next, tostring, tonumber
12local char, byte, format, gsub, rep, gmatch = string.char, string.byte, string.format, string.gsub, string.rep, string.gmatch
13local settings_to_array, settings_to_numbers = utilities.parsers.settings_to_array, utilities.parsers.settings_to_numbers
14local concat = table.concat
15local round = math.round
16local formatters = string.formatters
17
18local backends, lpdf, nodes = backends, lpdf, nodes
19
20local allocate = utilities.storage.allocate
21
22local nodeinjections = backends.pdf.nodeinjections
23local codeinjections = backends.pdf.codeinjections
24local registrations = backends.pdf.registrations
25
26local nodepool = nodes.nuts.pool
27local register = nodepool.register
28local pageliteral = nodepool.pageliteral
29
30local pdfconstant = lpdf.constant
31local pdfdictionary = lpdf.dictionary
32local pdfarray = lpdf.array
33local pdfreference = lpdf.reference
34local pdfverbose = lpdf.verbose
35local pdfflushobject = lpdf.flushobject
36local pdfdelayedobject = lpdf.delayedobject
37local pdfflushstreamobject = lpdf.flushstreamobject
38
39local pdfshareobjectreference = lpdf.shareobjectreference
40
41local addtopageattributes = lpdf.addtopageattributes
42local adddocumentcolorspace = lpdf.adddocumentcolorspace
43local adddocumentextgstate = lpdf.adddocumentextgstate
44
45local colors = attributes.colors
46local registercolor = colors.register
47local colorsvalue = colors.value
48local forcedmodel = colors.forcedmodel
49local getpagecolormodel = colors.getpagecolormodel
50local colortoattributes = colors.toattributes
51
52local transparencies = attributes.transparencies
53local registertransparancy = transparencies.register
54local transparenciesvalue = transparencies.value
55local transparencytoattribute = transparencies.toattribute
56
57local unsetvalue = attributes.unsetvalue
58
59local setmetatableindex = table.setmetatableindex
60
61local c_transparency = pdfconstant("Transparency")
62
63local f_gray = formatters["%.3N g %.3N G"]
64local f_rgb = formatters["%.3N %.3N %.3N rg %.3N %.3N %.3N RG"]
65local f_cmyk = formatters["%.3N %.3N %.3N %.3N k %.3N %.3N %.3N %.3N K"]
66local f_spot = formatters["/%s cs /%s CS %s SCN %s scn"]
67local f_tr = formatters["Tr%s"]
68local f_cm = formatters["q %.6N %.6N %.6N %.6N %.6N %.6N cm"]
69local f_effect = formatters["%s Tc %s w %s Tr"]
70local f_tr_gs = formatters["/Tr%s gs"]
71local f_num_1 = formatters["%.3N %.3N"]
72local f_num_2 = formatters["%.3N %.3N"]
73local f_num_3 = formatters["%.3N %.3N %.3N"]
74local f_num_4 = formatters["%.3N %.3N %.3N %.3N"]
75
76local report_color = logs.reporter("colors","backend")
77
78
79
80local colorspaceconstants = allocate {
81 gray = pdfconstant("DeviceGray"),
82 rgb = pdfconstant("DeviceRGB"),
83 cmyk = pdfconstant("DeviceCMYK"),
84 all = pdfconstant("DeviceRGB"),
85}
86
87local transparencygroups = { }
88
89lpdf.colorspaceconstants = colorspaceconstants
90lpdf.transparencygroups = transparencygroups
91
92setmetatableindex(transparencygroups, function(transparencygroups,colormodel)
93 local cs = colorspaceconstants[colormodel]
94 if cs then
95 local d = pdfdictionary {
96 S = c_transparency,
97 CS = cs,
98 I = true,
99 }
100
101 local g = pdfreference(pdfdelayedobject(tostring(d)))
102 transparencygroups[colormodel] = g
103 return g
104 else
105 transparencygroups[colormodel] = false
106 return false
107 end
108end)
109
110local function addpagegroup()
111 local model = getpagecolormodel()
112 if model then
113 local g = transparencygroups[model]
114 if g then
115 addtopageattributes("Group",g)
116 end
117 end
118end
119
120lpdf.registerpagefinalizer(addpagegroup,3,"pagegroup")
121
122
123
124
125
126function nodeinjections.rgbcolor(r,g,b)
127 return register(pageliteral(f_rgb(r,g,b,r,g,b)))
128end
129
130function nodeinjections.cmykcolor(c,m,y,k)
131 return register(pageliteral(f_cmyk(c,m,y,k,c,m,y,k)))
132end
133
134function nodeinjections.graycolor(s)
135 return register(pageliteral(f_gray(s,s)))
136end
137
138function nodeinjections.spotcolor(n,f,d,p)
139 if type(p) == "string" then
140 p = gsub(p,","," ")
141 end
142 return register(pageliteral(f_spot(n,n,p,p)))
143end
144
145function nodeinjections.transparency(n)
146 return register(pageliteral(f_tr_gs(n)))
147end
148
149
150
151local effects = {
152 normal = 0,
153 inner = 0,
154 outer = 1,
155 both = 2,
156 hidden = 3,
157}
158
159local bp = number.dimenfactors.bp
160
161function nodeinjections.effect(effect,stretch,rulethickness)
162
163 rulethickness = bp * rulethickness
164 effect = effects[effect] or effects['normal']
165 return register(pageliteral(f_effect(stretch,rulethickness,effect)))
166end
167
168
169
170local pdf_separation = pdfconstant("Separation")
171local pdf_indexed = pdfconstant("Indexed")
172local pdf_device_n = pdfconstant("DeviceN")
173local pdf_device_rgb = pdfconstant("DeviceRGB")
174local pdf_device_cmyk = pdfconstant("DeviceCMYK")
175local pdf_device_gray = pdfconstant("DeviceGray")
176local pdf_extgstate = pdfconstant("ExtGState")
177
178local pdf_rgb_range = pdfarray { 0, 1, 0, 1, 0, 1 }
179local pdf_cmyk_range = pdfarray { 0, 1, 0, 1, 0, 1, 0, 1 }
180local pdf_gray_range = pdfarray { 0, 1 }
181
182local f_rgb_function = formatters["dup %s mul exch dup %s mul exch %s mul"]
183local f_cmyk_function = formatters["dup %s mul exch dup %s mul exch dup %s mul exch %s mul"]
184local f_gray_function = formatters["%s mul"]
185
186local documentcolorspaces = pdfdictionary()
187
188local spotcolorhash = { }
189local spotcolornames = { }
190local indexcolorhash = { }
191local delayedindexcolors = { }
192
193function registrations.spotcolorname(name,e)
194 spotcolornames[name] = e or name
195end
196
197function registrations.getspotcolorreference(name)
198 return spotcolorhash[name]
199end
200
201
202
203
204
205
206
207
208local processcolors
209
210local function registersomespotcolor(name,noffractions,names,p,colorspace,range,funct)
211 noffractions = tonumber(noffractions) or 1
212 if noffractions == 0 then
213
214 elseif noffractions == 1 then
215 local dictionary = pdfdictionary {
216 FunctionType = 4,
217 Domain = { 0, 1 },
218 Range = range,
219 }
220 local calculations = pdfflushstreamobject(format("{ %s }",funct),dictionary)
221
222
223
224
225
226
227 local array = pdfarray {
228 pdf_separation,
229 pdfconstant(spotcolornames[name] or name),
230 colorspace,
231 pdfreference(calculations),
232 }
233 local m = pdfflushobject(array)
234 local mr = pdfreference(m)
235 spotcolorhash[name] = m
236 documentcolorspaces[name] = mr
237 adddocumentcolorspace(name,mr)
238 else
239 local cnames = pdfarray()
240 local domain = pdfarray()
241 local colorants = pdfdictionary()
242 for n in gmatch(names,"[^,]+") do
243 local name = spotcolornames[n] or n
244
245 if n == "cyan" then
246 name = "Cyan"
247 elseif n == "magenta" then
248 name = "Magenta"
249 elseif n == "yellow" then
250 name = "Yellow"
251 elseif n == "black" then
252 name = "Black"
253 else
254 local sn = spotcolorhash[name] or spotcolorhash[n]
255 if not sn then
256 report_color("defining %a as colorant",name)
257 colors.definespotcolor("",name,"p=1",true)
258 sn = spotcolorhash[name] or spotcolorhash[n]
259 end
260 if sn then
261 colorants[name] = pdfreference(sn)
262 else
263
264 report_color("unknown colorant %a, using black instead",name or n)
265 name = "Black"
266 end
267 end
268 cnames[#cnames+1] = pdfconstant(name)
269 domain[#domain+1] = 0
270 domain[#domain+1] = 1
271 end
272 if not processcolors then
273 local specification = pdfdictionary {
274 ColorSpace = pdfconstant("DeviceCMYK"),
275 Components = pdfarray {
276 pdfconstant("Cyan"),
277 pdfconstant("Magenta"),
278 pdfconstant("Yellow"),
279 pdfconstant("Black")
280 }
281 }
282 processcolors = pdfreference(pdfflushobject(specification))
283 end
284 local dictionary = pdfdictionary {
285 FunctionType = 4,
286 Domain = domain,
287 Range = range,
288 }
289 local calculation = pdfflushstreamobject(format("{ %s %s }",rep("pop ",noffractions),funct),dictionary)
290 local channels = pdfdictionary {
291 Subtype = pdfconstant("NChannel"),
292 Colorants = colorants,
293 Process = processcolors,
294 }
295 local array = pdfarray {
296 pdf_device_n,
297 cnames,
298 colorspace,
299 pdfreference(calculation),
300 pdfshareobjectreference(tostring(channels)),
301 }
302 local m = pdfflushobject(array)
303 local mr = pdfreference(m)
304 spotcolorhash[name] = m
305 documentcolorspaces[name] = mr
306 adddocumentcolorspace(name,mr)
307 end
308end
309
310
311
312local function registersomeindexcolor(name,noffractions,names,p,colorspace,range,funct)
313 noffractions = tonumber(noffractions) or 1
314 local cnames = pdfarray()
315 local domain = pdfarray()
316 local names = settings_to_array(#names == 0 and name or names)
317 local values = settings_to_numbers(p)
318 names [#names +1] = "None"
319 values[#values+1] = 1
320
321 for i=1,#names do
322 local name = names[i]
323 local spot = spotcolornames[name]
324 cnames[#cnames+1] = pdfconstant(spot ~= "" and spot or name)
325 domain[#domain+1] = 0
326 domain[#domain+1] = 1
327 end
328 local dictionary = pdfdictionary {
329 FunctionType = 4,
330 Domain = domain,
331 Range = range,
332 }
333 local n = pdfflushstreamobject(format("{ %s %s }",rep("exch pop ",noffractions),funct),dictionary)
334 local a = pdfarray {
335 pdf_device_n,
336 cnames,
337 colorspace,
338 pdfreference(n),
339 }
340 local vector = { }
341 local set = { }
342 local n = #values
343 for i=255,0,-1 do
344 for j=1,n do
345 set[j] = format("%02X",round(values[j]*i))
346 end
347 vector[#vector+1] = concat(set)
348 end
349 vector = pdfverbose { "<", concat(vector, " "), ">" }
350 local n = pdfflushobject(pdfarray{ pdf_indexed, a, 255, vector })
351 adddocumentcolorspace(format("%s_indexed",name),pdfreference(n))
352 return n
353end
354
355
356
357local function delayindexcolor(name,names,func)
358 local hash = (names ~= "" and names) or name
359 delayedindexcolors[hash] = func
360end
361
362local function indexcolorref(name)
363 local parent = colors.spotcolorparent(name)
364 local data = indexcolorhash[name]
365 if data == nil then
366 local delayedindexcolor = delayedindexcolors[parent]
367 if type(delayedindexcolor) == "function" then
368 data = delayedindexcolor()
369 delayedindexcolors[parent] = true
370 end
371 indexcolorhash[parent] = data or false
372 end
373 return data
374end
375
376function registrations.rgbspotcolor(name,noffractions,names,p,r,g,b)
377 if noffractions == 1 then
378 registersomespotcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_rgb_function(r,g,b))
379 else
380 registersomespotcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_num_3(r,g,b))
381 end
382 delayindexcolor(name,names,function()
383 return registersomeindexcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_rgb_function(r,g,b))
384 end)
385end
386
387function registrations.cmykspotcolor(name,noffractions,names,p,c,m,y,k)
388 if noffractions == 1 then
389 registersomespotcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_cmyk_function(c,m,y,k))
390 else
391 registersomespotcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_num_4(c,m,y,k))
392 end
393 delayindexcolor(name,names,function()
394 return registersomeindexcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_cmyk_function(c,m,y,k))
395 end)
396end
397
398function registrations.grayspotcolor(name,noffractions,names,p,s)
399 if noffractions == 1 then
400 registersomespotcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_gray_function(s))
401 else
402 registersomespotcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_num_1(s))
403 end
404 delayindexcolor(name,names,function()
405 return registersomeindexcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_gray_function(s))
406 end)
407end
408
409function registrations.rgbindexcolor(name,noffractions,names,p,r,g,b)
410 registersomeindexcolor(name,noffractions,names,p,pdf_device_rgb,pdf_rgb_range,f_rgb_function(r,g,b))
411end
412
413function registrations.cmykindexcolor(name,noffractions,names,p,c,m,y,k)
414 registersomeindexcolor(name,noffractions,names,p,pdf_device_cmyk,pdf_cmyk_range,f_cmyk_function(c,m,y,k))
415end
416
417function registrations.grayindexcolor(name,noffractions,names,p,s)
418 registersomeindexcolor(name,noffractions,names,p,pdf_device_gray,pdf_gray_range,f_gray_function(s))
419end
420
421function codeinjections.setfigurecolorspace(data,figure)
422 local color = data.request.color
423 if color then
424 local ref = indexcolorref(color)
425 if ref then
426 figure.colorspace = ref
427 data.used.color = color
428 data.used.colorref = ref
429 end
430 end
431end
432
433
434
435local pdftransparencies = { [0] =
436 pdfconstant("Normal"),
437 pdfconstant("Normal"),
438 pdfconstant("Multiply"),
439 pdfconstant("Screen"),
440 pdfconstant("Overlay"),
441 pdfconstant("SoftLight"),
442 pdfconstant("HardLight"),
443 pdfconstant("ColorDodge"),
444 pdfconstant("ColorBurn"),
445 pdfconstant("Darken"),
446 pdfconstant("Lighten"),
447 pdfconstant("Difference"),
448 pdfconstant("Exclusion"),
449 pdfconstant("Hue"),
450 pdfconstant("Saturation"),
451 pdfconstant("Color"),
452 pdfconstant("Luminosity"),
453 pdfconstant("Compatible"),
454}
455
456local documenttransparencies = { }
457local transparencyhash = { }
458
459local done, signaled = false, false
460
461function registrations.transparency(n,a,t)
462 if not done then
463 local d = pdfdictionary {
464 Type = pdf_extgstate,
465 ca = 1,
466 CA = 1,
467 BM = pdftransparencies[1],
468 AIS = false,
469 }
470 local m = pdfflushobject(d)
471 local mr = pdfreference(m)
472 transparencyhash[0] = m
473 documenttransparencies[0] = mr
474 adddocumentextgstate("Tr0",mr)
475 done = true
476 end
477 if n > 0 and not transparencyhash[n] then
478 local d = pdfdictionary {
479 Type = pdf_extgstate,
480 ca = tonumber(t),
481 CA = tonumber(t),
482 BM = pdftransparencies[tonumber(a)] or pdftransparencies[0],
483 AIS = false,
484 }
485 local m = pdfflushobject(d)
486 local mr = pdfreference(m)
487 transparencyhash[n] = m
488 documenttransparencies[n] = mr
489 adddocumentextgstate(f_tr(n),mr)
490 end
491end
492
493statistics.register("page group warning", function()
494 if done then
495 local model = getpagecolormodel()
496 if model and not transparencygroups[model] then
497 return "transparencies are used but no pagecolormodel is set"
498 end
499 end
500end)
501
502
503
504
505
506
507
508local function lpdfcolor(model,ca,default)
509 if colors.supported then
510 local cv = colorsvalue(ca)
511 if cv then
512 if model == 1 then
513 model = cv[1]
514 end
515 model = forcedmodel(model)
516 if model == 2 then
517 local s = cv[2]
518 return f_gray(s,s)
519 elseif model == 3 then
520 local r = cv[3]
521 local g = cv[4]
522 local b = cv[5]
523 return f_rgb(r,g,b,r,g,b)
524 elseif model == 4 then
525 local c = cv[6]
526 local m = cv[7]
527 local y = cv[8]
528 local k = cv[9]
529 return f_cmyk(c,m,y,k,c,m,y,k)
530 else
531 local n = cv[10]
532 local f = cv[11]
533 local d = cv[12]
534 local p = cv[13]
535 if type(p) == "string" then
536 p = gsub(p,","," ")
537 end
538 return f_spot(n,n,p,p)
539 end
540 else
541 return f_gray(default or 0,default or 0)
542 end
543 else
544 return ""
545 end
546end
547
548lpdf.color = lpdfcolor
549
550interfaces.implement {
551 name = "lpdf_color",
552 actions = { lpdfcolor, context },
553 arguments = "integer"
554}
555
556function lpdf.colorspec(model,ca,default)
557 if ca and ca > 0 then
558 local cv = colors.value(ca)
559 if cv then
560 if model == 1 then
561 model = cv[1]
562 end
563 if model == 2 then
564 return pdfarray { cv[2] }
565 elseif model == 3 then
566 return pdfarray { cv[3],cv[4],cv[5] }
567 elseif model == 4 then
568 return pdfarray { cv[6],cv[7],cv[8],cv[9] }
569 elseif model == 5 then
570 return pdfarray { cv[13] }
571 end
572 end
573 end
574 if default then
575 return default
576 end
577end
578
579function lpdf.pdfcolor(attribute)
580 return lpdfcolor(1,attribute)
581end
582
583function lpdf.transparency(ct,default)
584
585
586
587 if transparencies.supported then
588 local ct = transparenciesvalue(ct)
589 if ct then
590 return f_tr_gs(registertransparancy(nil,ct[1],ct[2],true))
591 else
592 return f_tr_gs(0)
593 end
594 else
595 return ""
596 end
597end
598
599function lpdf.colorvalue(model,ca,default)
600 local cv = colorsvalue(ca)
601 if cv then
602 if model == 1 then
603 model = cv[1]
604 end
605 model = forcedmodel(model)
606 if model == 2 then
607 return f_num_1(cv[2])
608 elseif model == 3 then
609 return f_num_3(cv[3],cv[4],cv[5])
610 elseif model == 4 then
611 return f_num_4(cv[6],cv[7],cv[8],cv[9])
612 else
613 return f_num_1(cv[13])
614 end
615 else
616 return f_num_1(default or 0)
617 end
618end
619
620function lpdf.colorvalues(model,ca,default)
621 local cv = colorsvalue(ca)
622 if cv then
623 if model == 1 then
624 model = cv[1]
625 end
626 model = forcedmodel(model)
627 if model == 2 then
628 return cv[2]
629 elseif model == 3 then
630 return cv[3], cv[4], cv[5]
631 elseif model == 4 then
632 return cv[6], cv[7], cv[8], cv[9]
633 elseif model == 5 then
634 return cv[13]
635 end
636 else
637 return default or 0
638 end
639end
640
641function lpdf.transparencyvalue(ta,default)
642 local tv = transparenciesvalue(ta)
643 if tv then
644 return tv[2]
645 else
646 return default or 1
647 end
648end
649
650function lpdf.colorspace(model,ca)
651 local cv = colorsvalue(ca)
652 if cv then
653 if model == 1 then
654 model = cv[1]
655 end
656 model = forcedmodel(model)
657 if model == 2 then
658 return "DeviceGray"
659 elseif model == 3 then
660 return "DeviceRGB"
661 elseif model == 4 then
662 return "DeviceCMYK"
663 end
664 end
665 return "DeviceGRAY"
666end
667
668
669
670local intransparency = false
671local pdfcolor = lpdf.color
672
673function lpdf.rgbcode(model,r,g,b)
674 if colors.supported then
675 return pdfcolor(model,registercolor(nil,'rgb',r,g,b))
676 else
677 return ""
678 end
679end
680
681function lpdf.cmykcode(model,c,m,y,k)
682 if colors.supported then
683 return pdfcolor(model,registercolor(nil,'cmyk',c,m,y,k))
684 else
685 return ""
686 end
687end
688
689function lpdf.graycode(model,s)
690 if colors.supported then
691 return pdfcolor(model,registercolor(nil,'gray',s))
692 else
693 return ""
694 end
695end
696
697function lpdf.spotcode(model,n,f,d,p)
698 if colors.supported then
699 return pdfcolor(model,registercolor(nil,'spot',n,f,d,p))
700 else
701 return ""
702 end
703end
704
705function lpdf.transparencycode(a,t)
706 if transparencies.supported then
707 intransparency = true
708 return f_tr_gs(registertransparancy(nil,a,t,true))
709 else
710 return ""
711 end
712end
713
714function lpdf.finishtransparencycode()
715 if transparencies.supported and intransparency then
716 intransparency = false
717 return f_tr_gs(0)
718 else
719 return ""
720 end
721end
722
723
724
725
726do
727
728 local pdfcolor = lpdf.color
729 local pdftransparency = lpdf.transparency
730
731 local f_slant = formatters["q 1 0 %N 1 0 0 cm"]
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774 local slants = setmetatableindex(function(t,k)
775 local v = { "pdf", "origin", f_slant(a) }
776 t[k] = v
777 return k
778 end)
779
780 local function startslant(a)
781 return slants[a]
782 end
783
784 local c_cache = setmetatableindex(function(t,m)
785 local v = setmetatableindex(function(t,c)
786 local p = { "pdf", "page", "q " .. pdfcolor(m,c) }
787 t[c] = p
788 return p
789 end)
790 t[m] = v
791 return v
792 end)
793
794
795
796 local t_cache = setmetatableindex(function(t,transparency)
797 local p = pdftransparency(transparency)
798 local v = setmetatableindex(function(t,colormodel)
799 local v = setmetatableindex(function(t,color)
800 local v = { "pdf", "page", "q " .. pdfcolor(colormodel,color) .. " " .. p }
801 t[color] = v
802 return v
803 end)
804 t[colormodel] = v
805 return v
806 end)
807 t[transparency] = v
808 return v
809 end)
810
811 local function startcolor(k)
812 local m, c = colortoattributes(k)
813 local t = transparencytoattribute(k)
814 if t then
815 return t_cache[t][m][c]
816 else
817 return c_cache[m][c]
818 end
819 end
820
821
822
823
824
825
826 backends.pdf.tables.vfspecials = allocate {
827
828 startcolor = startcolor,
829
830 stopcolor = { "pdf", "text", "Q" },
831
832 startslant = startslant,
833
834 stopslant = { "pdf", "text", "Q" },
835
836 }
837
838end
839 |