Last active
September 28, 2016 06:06
-
-
Save phi-gamma/6476351 to your computer and use it in GitHub Desktop.
bee gradients for TeX documents: http://tex.stackexchange.com/q/131883/14066
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
packagedata = packagedata or { } | |
packagedata.beegradients = { } | |
local beegradients = packagedata.beegradients | |
local processorid = "beegradients" --- name of callback | |
local err, warn, info | |
if luatexbase then | |
err, warn, info = luatexbase.provides_module { | |
name = "beegradients", | |
version = 42, | |
date = "2013-09-07 17:03:42+0200", | |
descriptions = "http://tex.stackexchange.com/q/131883/14066", | |
author = "Philipp Gesang", | |
copyright = "Philipp Gesang", | |
license = "BSD 2 clause", | |
} | |
end | |
local lpeg = require "lpeg" | |
local C, Cf, Cg, Ct = lpeg.C, lpeg.Cf, lpeg.Cg, lpeg.Ct | |
local P, R, S = lpeg.P, lpeg.R, lpeg.S | |
local lpegmatch = lpeg.match | |
local unpack = unpack or table.unpack | |
local stringformat = string.format | |
local stringis_empty = string.is_empty | |
local tableswapped = table.swapped | |
local nodes = nodes | |
local nodecodes = nodes.nodecodes or tableswapped (node.types ()) | |
local hlist_t = nodecodes.hlist | |
local vlist_t = nodecodes.vlist | |
local glyph_t = nodecodes.glyph | |
local disc_t = nodecodes.disc | |
local whatsit_t = nodecodes.whatsit | |
local pdf_literal_t = 8 | |
local traversenodes = node.traverse | |
local traversenodetype = node.traverse_id | |
local countnodes = node.count | |
local newnode = node.new | |
local copynode = node.copy | |
local insertnodebefore = node.insert_before | |
local insertnodeafter = node.insert_after | |
require "lualibs" --- requires extended set including util-prs.lua | |
local settingstoarray = utilities and utilities.parsers.settings_to_array | |
if not settingstoarray then --- old Lualibs | |
local comma = P"," | |
local spacechar = S" \f\n\r\t\v" | |
local separator = comma * spacechar^0 | |
local item = C((1 - comma - spacechar)^1) | |
local p_settings = spacechar^0 | |
* item | |
* (separator * item)^0 | |
* (separator + spacechar^0) | |
settingstoarray = function (settings) | |
return lpegmatch (Ct (p_settings), settings) | |
end | |
end | |
local practically_zero = 0.003921568627451 | |
local practically_one = 0.99607843137255 | |
local parse_gradient do | |
local tonumber16 = function (n) return tonumber (n, 16) end | |
local digit = R"09" | |
local hexdigit = R("09", "af", "AF") | |
local dash = P"-" | |
local colon = P":" | |
local asterisk = P"*" | |
--- values: | |
--- hexcolor -> 0xf00ba7 (triplet of hex octets, r->g->b) | |
--- deccolor -> 123*44*111 (triplet of decimal octets: r->g->b) | |
--- speccolor -> r:210*g:32*b:145 (prefixed octets, any order) | |
local hexcolor = Ct (P"0x" * Cg (hexdigit * hexdigit / tonumber16, "r") | |
* Cg (hexdigit * hexdigit / tonumber16, "g") | |
* Cg (hexdigit * hexdigit / tonumber16, "b")) | |
local decexp = digit * digit^-1 * digit^-1 | |
local deccolor = Ct (Cg (decexp / tonumber, "r") * asterisk | |
* Cg (decexp / tonumber, "g") * asterisk | |
* Cg (decexp / tonumber, "b")) | |
local specexp = C(S"rgb") * colon * (C(decexp) / tonumber) | |
local speccolor = Cf (Ct "" | |
* Cg (specexp) * asterisk | |
* Cg (specexp) * asterisk | |
* Cg (specexp), | |
rawset) | |
local colexp = hexcolor + deccolor + speccolor | |
local zero = { 0, 0, 0 } --- fallback | |
--- string -> float * float * float | |
parse_color = function (raw) | |
local color = lpegmatch (colexp, raw) | |
local r = color.r / 255 | |
local g = color.g / 255 | |
local b = color.b / 255 | |
if r < practically_zero then r = 0 end | |
if r > practically_one then r = 1 end | |
if g < practically_zero then g = 0 end | |
if g > practically_one then g = 1 end | |
if b < practically_zero then b = 0 end | |
if b > practically_one then b = 1 end | |
return { r, g, b } | |
end | |
end | |
local gradients = { } --- (float * float * float) list | |
--- string -> unit | |
local definegradients = function (groupid, raw) | |
local group = gradients [groupid] | |
if group then | |
warn (stringformat ("Gradient group %q already defined, redefining.", | |
groupid)) | |
else | |
group = { } | |
end | |
local definitions = settingstoarray (raw) | |
if #definitions < 1 then | |
warn (stringformat ("Need at least one definition in gradient group %q, skipping.", | |
groupid)) | |
return nil | |
end | |
for i = 1, #definitions do | |
local definition = definitions [i] | |
if definition and not stringis_empty (definition) then | |
group [#group + 1] = parse_color (definition) | |
end | |
end | |
gradients [groupid] = group | |
end | |
beegradients.define = definegradients | |
local pdf_literal = newnode(whatsit_t, pdf_literal_t) | |
local get_colornode = function (r, g, b) | |
local push, pop = copynode (pdf_literal), copynode (pdf_literal) | |
local pushcolor = stringformat ("%.3g %.3g %.3g rg", r, g, b) | |
local popcolor = "0 g" | |
push.mode, push.data = 1, pushcolor | |
pop.mode, pop.data = 1, popcolor | |
return push, pop | |
end | |
--- more accurate, recursive glyph counter than node.count; | |
--- this includes, for instance, the lowered -Y´E¡ in \TeX | |
--- node_t -> int? -> int | |
local countglyphs countglyphs = function (hd, cnt) | |
cnt = cnt or 0 | |
for n in traversenodes (hd) do | |
local nid = n.id | |
if nid == glyph_t or nid == disc_t then | |
cnt = cnt + 1 | |
elseif nid == hlist_t or nid == vlist_t then | |
cnt = countglyphs (n.list, cnt) | |
end | |
end | |
return cnt | |
end | |
--- node_t -> float -> float -> float -> | |
-- -> float -> float -> float -> node_t | |
local colorize_glyphs colorize_glyphs = function (hd, done, | |
r, g, b, | |
rstep, gstep, bstep) | |
local cur = hd | |
while cur do | |
local id = cur.id | |
if id == glyph_t or id == disc_t then | |
local before, after = get_colornode (r, g, b) | |
local curprev, curnext = cur.prev, cur.next | |
before.next, cur.prev = cur, before | |
after.prev, cur.next = cur, after | |
if not curprev then --- first | |
hd = before | |
else | |
before.prev, curprev.next = curprev, before | |
end | |
if curnext then | |
after.next, curnext.prev = curnext, after | |
end -- else last node | |
done = done + 1 | |
cur = curnext | |
if cur then | |
r = r + rstep | |
g = g + gstep | |
b = b + bstep | |
--- safeguard against rounding | |
if r < practically_zero then r = 0 end | |
if r > practically_one then r = 1 end | |
if g < practically_zero then g = 0 end | |
if g > practically_one then g = 1 end | |
if b < practically_zero then b = 0 end | |
if b > practically_one then b = 1 end | |
end | |
elseif id == hlist_t or id == vlist_t then | |
local list = cur.list | |
if list then | |
cur.list, done = colorize_glyphs (cur.list, done, | |
r, g, b, | |
rstep, gstep, bstep) | |
end | |
cur = cur.next | |
else | |
cur = cur.next | |
end | |
end | |
--print (stringformat ("final> %.3f %.3f %.3f -A× %d", r, g, b, done)) | |
return hd, done | |
end | |
local lineprocessor = function (hd, from, to) | |
local list = hd.list | |
local nglyphs = countglyphs (list) | |
local nsteps = nglyphs - 1 | |
local rstart, gstart, bstart = unpack (from) | |
local rstep = (to [1] - rstart) / nsteps | |
local gstep = (to [2] - gstart) / nsteps | |
local bstep = (to [3] - bstart) / nsteps | |
--print (stringformat ("from> %.3f %.3f %.3f", rstart, gstart, bstart)) | |
--print (stringformat ("to> %.3f %.3f %.3f", to [1], to [2], to [3])) | |
--print (stringformat ("step> %.3f %.3f %.3f × %d", rstep, gstep, bstep, nglyphs)) | |
--print (">>", nglyphs, countnodes (glyph_t, list), from, to) | |
local glyphs_done | |
hd.list, glyphs_done = colorize_glyphs (list, 0, | |
rstart, gstart, bstart, | |
rstep, gstep, bstep) | |
--print (">>", nglyphs, glyphs_done, from, to) | |
end | |
local currentgroup | |
local currentgradient = 1 | |
local processor = function (hd) | |
local group = gradients [currentgroup] | |
local ngradients = #group | |
if not group then | |
warn (stringformat ("No such gradient group: %q, bailing out.", | |
currentgroup)) | |
return hd | |
end | |
for line in traversenodetype (hlist_t, hd) do | |
local fromcolor = group [currentgradient] | |
currentgradient = currentgradient + 1 | |
if currentgradient > ngradients then | |
currentgradient = 1 | |
end | |
local tocolor = group [currentgradient] | |
lineprocessor (line, fromcolor, tocolor) | |
end | |
return hd | |
end | |
local active = false | |
local enable = function (groupid) | |
if not stringis_empty (groupid) then | |
if currentgroup ~= groupid then -- reset gradient pointer | |
currentgradient = 1 | |
end | |
currentgroup = groupid | |
end | |
if currentgroup == nil then | |
warn "Cannot inject node processor: no gradient group defined." | |
return | |
end | |
if active == false then | |
info (stringformat ("Injecting node processor, active group %q.", | |
currentgroup)) | |
luatexbase.add_to_callback ("post_linebreak_filter", | |
processor, | |
processorid) | |
active = true | |
end | |
end | |
local disable = function () | |
if active == true then | |
info "Removing node processor." | |
luatexbase.remove_from_callback ("post_linebreak_filter", | |
processorid) | |
active = false | |
end | |
end | |
beegradients.enable = enable | |
beegradients.disable = disable | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\RequireLuaModule {beegradients} | |
\def \pkgcmd #1{\directlua {packagedata.#1}} | |
%% defining a sequence of gradients; order matters | |
\def \definegradientgroup [#1][#2]{%% groupid, settings | |
\pkgcmd {beegradients.define ([[#1]], [[\detokenize {#2}]])}% | |
} | |
%% node processor handling | |
\chardef \oldatcatcode = \catcode `\@ | |
\catcode `\@ = 11 | |
\def \startbeegradients{%% groupid | |
\lltxb@ifnextchar[\startbeegradientsindeed | |
{\startbeegradientsindeed[]}% | |
} | |
\catcode `\@ = \oldatcatcode | |
\def \startbeegradientsindeed [#1]{%% groupid | |
\pkgcmd {beegradients.enable [[#1]]}% | |
} | |
\def \stopbeegradients {% | |
\endgraf %% post_linebreak_filter is paragraph-based | |
\pkgcmd {beegradients.disable ()}% | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\documentclass {scrartcl} | |
\usepackage {fontspec} %% this loads luaotfload as well | |
\setmainfont {Antykwa Poltawskiego} | |
\input beegradients.tex | |
\definegradientgroup [red-green-blue][255*0*0, 0*255*0, 0*0*255] | |
\begin {document} | |
\startbeegradients [red-green-blue] | |
\input knuth | |
\stopbeegradients | |
\end {document} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\input luaotfload.sty | |
\input beegradients.tex | |
\font \mainfont = file:Iwona-Regular.otf at 10pt | |
\mainfont | |
\definegradientgroup [mygradients][ | |
42*11*242, %% decimal notation, separated by “*” | |
83*242*55, | |
0xf00ba7, %% hex notation | |
0x1ec001, | |
g:23*b:42*r:133, %% rgb notation, also separated by “*” | |
b:53*g:184*r:10, | |
] | |
\definegradientgroup [blackwhite][0x000000, 0xFFFFFF] | |
\definegradientgroup [red-green-blue][255*0*0, 0*255*0, 0*0*255] | |
\definegradientgroup [red][255*0*0, 0*0*0] | |
\definegradientgroup [green][0*100*0, 0*255*0] | |
\definegradientgroup [blue][0*0*20, 0*0*210] | |
\input knuth | |
\startbeegradients [mygradients] \input knuth \stopbeegradients | |
\startbeegradients [blackwhite] \input knuth \stopbeegradients | |
\startbeegradients [red-green-blue] \input knuth \stopbeegradients | |
\input knuth | |
\startbeegradients \input knuth \stopbeegradients | |
\startbeegradients [red] \input knuth \stopbeegradients | |
\startbeegradients [green] \input knuth \stopbeegradients | |
\startbeegradients [blue] \input knuth \stopbeegradients | |
\bye |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This needs a few updates for current luatex, I added some minimal patches here http://tex.stackexchange.com/a/321962/1090