summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/mlib-svg.lua
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2020-07-14 00:25:53 +0200
committerContext Git Mirror Bot <phg@phi-gamma.net>2020-07-14 00:25:53 +0200
commitf1129626606384a7a55a21a83531f51f8b5dee25 (patch)
tree9bfaf9a8bf2c218007291023f771babc9ac2e1d9 /tex/context/base/mkiv/mlib-svg.lua
parentb821116421f0d942052ad225f4ea62aef2696817 (diff)
downloadcontext-f1129626606384a7a55a21a83531f51f8b5dee25.tar.gz
2020-07-13 23:52:00
Diffstat (limited to 'tex/context/base/mkiv/mlib-svg.lua')
-rw-r--r--tex/context/base/mkiv/mlib-svg.lua3277
1 files changed, 0 insertions, 3277 deletions
diff --git a/tex/context/base/mkiv/mlib-svg.lua b/tex/context/base/mkiv/mlib-svg.lua
deleted file mode 100644
index 4c4122476..000000000
--- a/tex/context/base/mkiv/mlib-svg.lua
+++ /dev/null
@@ -1,3277 +0,0 @@
-if not modules then modules = { } end modules ['mlib-svg'] = {
- version = 1.001,
- optimize = true,
- comment = "companion to mlib-ctx.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
-}
-
--- Just a few notes:
---
--- There is no real need to boost performance here .. we can always make a fast
--- variant when really needed. I will also do some of the todo's when I run into
--- proper fonts. I need to optimize this a bit but will do that once I'm satisfied
--- with the outcome and don't need more hooks and plugs. At some point I will
--- optimize the MetaPost part because now we probably have more image wrapping
--- than needed.
---
--- As usual with these standards, things like a path can be very compact while the
--- rest is very verbose which defeats the point. This is a first attempt. There will
--- be a converter to MP as well as directly to PDF. This module was made for one of
--- the dangerous curves talks at the 2019 CTX meeting. I will do the font when I
--- need it (not that hard).
---
--- The fact that in the more recent versions of SVG the older text related elements
--- are depricated and not even supposed to be supported, combined with the fact that
--- the text element assumes css styling, demonstrates that there is not so much as a
--- standard. It basically means that whatever technology dominates at some point
--- (probably combined with some libraries that at that point exist) determine what
--- is standard. Anyway, it probably also means that these formats are not that
--- suitable for long term archival purposes. So don't take the next implementation
--- too serious. So in the end we now have (1) attributes for properties (which is
--- nice and clean and what attributes are for, (2) a style attribute that needs to
--- be parsed, (3) classes that map to styles and (4) element related styles, plus a
--- kind of inheritance (given the limited number of elements sticking to only <g> as
--- wrapper would have made much sense. Anyway, we need to deal with it. With all
--- these style things going on, one can wonder where it will end. Basically svg
--- became just a html element that way and less clean too. The same is true for
--- tspan, which means that text itself is nested xml.
---
--- We can do a direct conversion to PDF but then we also loose the abstraction which
--- in the future will be used, and for fonts we need to spawn out to TeX anyway, so
--- the little overhead of calling MetaPost is okay I guess. Also, we want to
--- overload labels, share fonts with the main document, etc. and are not aiming at a
--- general purpose SVG converter. For going to PDF one can just use InkScape.
---
--- Written with Anne Clark on speakers as distraction.
---
--- Todo when I run into an example (but ony when needed and reasonable):
---
--- var(color,color)
--- --color<decimal>
--- currentColor : when i run into an example
--- a bit more shading
--- clip = [ auto | rect(llx,lly,urx,ury) ] (in svg)
--- xlink url ... whatever
--- masks
--- opacity per group (i need to add that to metafun first, inefficient pdf but
--- maybe filldraw can help here)
---
--- Maybe in metafun:
---
--- penciled n -> withpen pencircle scaled n
--- applied (...) -> transformed bymatrix (...)
--- withopacity n -> withtransparency (1,n)
-
--- When testing mbo files:
---
--- empty paths
--- missing control points
--- funny fontnames like abcdefverdana etc
--- paths representing glyphs but also with style specs
--- all kind of attributes
--- very weird and inefficient shading
-
--- One can run into pretty crazy images, like lines that are fills being clipped
--- to some width. That's the danger of hiding yourself behind an interface I guess.
-
-local rawget, rawset, type, tonumber, tostring, next, setmetatable = rawget, rawset, type, tonumber, tostring, next, setmetatable
-
-local P, S, R, C, Ct, Cs, Cc, Cp, Cg, Cf, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Cg, lpeg.Cf, lpeg.Carg
-
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-local sqrt, abs = math.sqrt, math.abs
-local concat, setmetatableindex, sortedhash = table.concat, table.setmetatableindex, table.sortedhash
-local gmatch, gsub, find, match, rep = string.gmatch, string.gsub, string.find, string.match, string.rep
-local formatters, fullstrip = string.formatters, string.fullstrip
-local utfsplit, utfbyte = utf.split, utf.byte
-
-local xmlconvert, xmlcollected, xmlcount, xmlfirst, xmlroot = xml.convert, xml.collected, xml.count, xml.first, xml.root
-local xmltext, xmltextonly = xml.text, xml.textonly
-local css = xml.css or { } -- testing
-
-local function xmlinheritattributes(c,pa)
- local at = c.at
- local dt = c.dt
- if at and dt then
- if pa then
- setmetatableindex(at,pa)
- end
- for i=1,#dt do
- local dti = dt[i]
- if type(dti) == "table" then
- xmlinheritattributes(dti,at)
- end
- end
- else
- -- comment of so
- end
-end
-
-xml.inheritattributes = xmlinheritattributes
-
--- Maybe some day helpers will move to the metapost.svg namespace!
-
-metapost = metapost or { }
-local metapost = metapost
-local context = context
-
-local report = logs.reporter("metapost","svg")
-
-local trace = false trackers.register("metapost.svg", function(v) trace = v end)
-local trace_text = false trackers.register("metapost.svg.text", function(v) trace_text = v end)
-local trace_path = false trackers.register("metapost.svg.path", function(v) trace_path = v end)
-local trace_result = false trackers.register("metapost.svg.result", function(v) trace_result = v end)
-local trace_colors = false trackers.register("metapost.svg.colors", function(v) trace_colors = v end)
-
-local pathtracer = {
- ["stroke"] = "darkred",
- ["stroke-opacity"] = ".5",
- ["stroke-width"] = ".5",
- ["fill"] = "darkgray",
- ["fill-opacity"] = ".75",
-}
-
--- This is just an experiment. Todo: reset hash etc. Also implement
--- an option handler.
-
-local svghash = false do
-
- local svglast = 0
- local svglist = false
-
- local function checkhash(t,k)
- local n = svglast + 1
- svglast = n
- svglist[n] = k
- t[k] = n
- return n
- end
-
- function metapost.startsvghashing()
- svglast = 0
- svglist = { }
- svghash = setmetatableindex(checkhash)
- end
-
- function metapost.stopsvghashing()
- svglast = 0
- svglist = false
- svghash = false
- end
-
- interfaces.implement {
- name = "svghashed",
- arguments = "integer",
- actions = function(n)
- local t = svglist and svglist[n]
- if t then
- context(t)
- end
- end
- }
-
-end
-
--- We have quite some closures because otherwise we run into the local variable
--- limitations. It doesn't always look pretty now, sorry. I'll clean up this mess
--- some day (the usual nth iteration of code).
---
--- Most of the conversion is rather trivial code till I ran into a file with arcs. A
--- bit of searching lead to the a2c javascript function but it has some puzzling
--- thingies (like sin and cos definitions that look like leftovers and possible
--- division by zero). Anyway, we can if needed optimize it a bit more. Here does it
--- come from:
-
--- http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
--- https://github.com/adobe-webplatform/Snap.svg/blob/b242f49e6798ac297a3dad0dfb03c0893e394464/src/path.js
-
-local a2c do
-
- local pi, sin, cos, tan, asin, abs = math.pi, math.sin, math.cos, math.tan, math.asin, math.abs
-
- local d120 = (pi * 120) / 180
- local pi2 = 2 * pi
-
- a2c = function(x1, y1, rx, ry, angle, large, sweep, x2, y2, f1, f2, cx, cy)
-
- if (rx == 0 or ry == 0 ) or (x1 == x2 and y1 == y2) then
- return { x1, y1, x2, y2, x2, y2 }
- end
-
- local recursive = f1
- local rad = pi / 180 * angle
- local res = nil
- local cosrad = cos(-rad) -- local cosrad = cosd(angle)
- local sinrad = sin(-rad) -- local sinrad = sind(angle)
-
- if not recursive then
-
- x1, y1 = x1 * cosrad - y1 * sinrad, x1 * sinrad + y1 * cosrad
- x2, y2 = x2 * cosrad - y2 * sinrad, x2 * sinrad + y2 * cosrad
-
- local x = (x1 - x2) / 2
- local y = (y1 - y2) / 2
- local xx = x * x
- local yy = y * y
- local h = xx / (rx * rx) + yy / (ry * ry)
-
- if h > 1 then
- h = sqrt(h)
- rx = h * rx
- ry = h * ry
- end
-
- local rx2 = rx * rx
- local ry2 = ry * ry
- local ry2xx = ry2 * xx
- local rx2yy = rx2 * yy
- local total = rx2yy + ry2xx -- otherwise overflow
-
- local k = total == 0 and 0 or sqrt(abs((rx2 * ry2 - rx2yy - ry2xx) / total))
-
- if large == sweep then
- k = -k
- end
-
- cx = k * rx * y / ry + (x1 + x2) / 2
- cy = k * -ry * x / rx + (y1 + y2) / 2
-
- f1 = (y1 - cy) / ry -- otherwise crash on a tiny eps
- f2 = (y2 - cy) / ry -- otherwise crash on a tiny eps
-
- f1 = asin((f1 < -1.0 and -1.0) or (f1 > 1.0 and 1.0) or f1)
- f2 = asin((f2 < -1.0 and -1.0) or (f2 > 1.0 and 1.0) or f2)
-
- if x1 < cx then f1 = pi - f1 end
- if x2 < cx then f2 = pi - f2 end
-
- if f1 < 0 then f1 = pi2 + f1 end
- if f2 < 0 then f2 = pi2 + f2 end
-
- if sweep ~= 0 and f1 > f2 then f1 = f1 - pi2 end
- if sweep == 0 and f2 > f1 then f2 = f2 - pi2 end
-
- end
-
- if abs(f2 - f1) > d120 then
- local f2old = f2
- local x2old = x2
- local y2old = y2
- f2 = f1 + d120 * ((sweep ~= 0 and f2 > f1) and 1 or -1)
- x2 = cx + rx * cos(f2)
- y2 = cy + ry * sin(f2)
- res = a2c(x2, y2, rx, ry, angle, 0, sweep, x2old, y2old, f2, f2old, cx, cy)
- end
-
- local c1 = cos(f1)
- local s1 = sin(f1)
- local c2 = cos(f2)
- local s2 = sin(f2)
-
- local t = tan((f2 - f1) / 4)
- local hx = 4 * rx * t / 3
- local hy = 4 * ry * t / 3
-
- local r = { x1 - hx * s1, y1 + hy * c1, x2 + hx * s2, y2 - hy * c2, x2, y2, unpack(res or { }) }
-
- if not recursive then -- we can also check for sin/cos being 0/1
- cosrad = cos(rad)
- sinrad = sin(rad)
- -- cosrad = cosd(angle)
- -- sinrad = sind(angle)
- for i0=1,#r,2 do
- local i1 = i0 + 1
- local x = r[i0]
- local y = r[i1]
- r[i0] = x * cosrad - y * sinrad
- r[i1] = x * sinrad + y * cosrad
- end
- end
-
- return r
- end
-
-end
-
--- We share some patterns.
-
-local p_digit = lpegpatterns.digit
-local p_hexdigit = lpegpatterns.hexdigit
-local p_space = lpegpatterns.whitespace
-
-local factors = {
- ["pt"] = 1.25,
- ["mm"] = 3.543307,
- ["cm"] = 35.43307,
- ["px"] = 1,
- ["pc"] = 15,
- ["in"] = 90,
- ["em"] = 12 * 1.25,
- ["ex"] = 8 * 1.25,
-}
-
-local percentage_r = 1/100
-local percentage_x = percentage_r
-local percentage_y = percentage_r
-
--- incredible: we can find .123.456 => 0.123 0.456 ...
-
-local p_command_x = C(S("Hh"))
-local p_command_y = C(S("Vv"))
-local p_command_xy = C(S("CcLlMmQqSsTt"))
-local p_command_a = C(S("Aa"))
-local p_command = C(S("Zz"))
-
-local p_optseparator = S("\t\n\r ,")^0
-local p_separator = S("\t\n\r ,")^1
-local p_number = (S("+-")^0 * (p_digit^0 * P(".") * p_digit^1 + p_digit^1 * P(".") + p_digit^1))
- * (P("e") * S("+-")^0 * p_digit^1)^-1
-
-local function convert (n) n = tonumber(n) return n end
-local function convert_r (n,u) n = tonumber(n) if u == true then return percentage_r * n elseif u then return u * n else return n end end
-local function convert_x (n,u) n = tonumber(n) if u == true then return percentage_x * n elseif u then return u * n else return n end end
-local function convert_y (n,u) n = tonumber(n) if u == true then return percentage_y * n elseif u then return u * n else return n end end
-local function convert_vx(n,u) n = tonumber(n) if u == true then return percentage_x * n elseif u then return u * n else return n end end
-local function convert_vy(n,u) n = - tonumber(n) if u == true then return percentage_y * n elseif u then return u * n else return n end end
-
-local p_unit = (P("p") * S("txc") + P("e") * S("xm") + S("mc") * P("m") + P("in")) / factors
-local p_percent = P("%") * Cc(true)
-
-local c_number_n = C(p_number)
-local c_number_u = C(p_number) * (p_unit + p_percent)^-1
-
-local p_number_n = c_number_n / convert
-local p_number_x = c_number_u / convert_x
-local p_number_vx = c_number_u / convert_vx
-local p_number_y = c_number_u / convert_y
-local p_number_vy = c_number_u / convert_vy
-local p_number_r = c_number_u / convert_r
-
-local function asnumber (s) return s and lpegmatch(p_number, s) or 0 end
-local function asnumber_r (s) return s and lpegmatch(p_number_r, s) or 0 end
-local function asnumber_x (s) return s and lpegmatch(p_number_x, s) or 0 end
-local function asnumber_y (s) return s and lpegmatch(p_number_y, s) or 0 end
-local function asnumber_vx(s) return s and lpegmatch(p_number_vx,s) or 0 end
-local function asnumber_vy(s) return s and lpegmatch(p_number_vy,s) or 0 end
-
-local p_number_vx_t = Ct { (p_number_vx + p_separator)^1 }
-local p_number_vy_t = Ct { (p_number_vy + p_separator)^1 }
-
-local zerotable = { 0 }
-
-local function asnumber_vx_t(s) return s and lpegmatch(p_number_vx_t,s) or zerotable end
-local function asnumber_vy_t(s) return s and lpegmatch(p_number_vy_t,s) or zerotable end
-
-local p_numbersep = p_number_n + p_separator
-local p_numbers = p_optseparator * P("(") * p_numbersep^0 * p_optseparator * P(")")
-local p_fournumbers = p_numbersep^4
-local p_path = Ct ( (
- p_command_xy * (p_optseparator * p_number_vx *
- p_optseparator * p_number_vy )^1
- + p_command_x * (p_optseparator * p_number_vx )^1
- + p_command_y * (p_optseparator * p_number_vy )^1
- + p_command_a * (p_optseparator * p_number_vx *
- p_optseparator * p_number_vy *
- p_optseparator * p_number_r *
- p_optseparator * p_number_n * -- flags
- p_optseparator * p_number_n * -- flags
- p_optseparator * p_number_vx *
- p_optseparator * p_number_vy )^1
- + p_command
- + p_separator
-)^1 )
-
--- We can actually use the svg color definitions from the tex end but maybe a user
--- doesn't want those replace the normal definitions.
---
--- local hexhash = setmetatableindex(function(t,k) local v = lpegmatch(p_hexcolor, k) t[k] = v return v end) -- per file
--- local hexhash3 = setmetatableindex(function(t,k) local v = lpegmatch(p_hexcolor3,k) t[k] = v return v end) -- per file
---
--- local function hexcolor (c) return hexhash [c] end -- directly do hexhash [c]
--- local function hexcolor3(c) return hexhash3[c] end -- directly do hexhash3[c]
-
-local colormap = false
-
-local function prepared(t)
- if type(t) == "table" then
- local mapping = t.mapping or { }
- local mapper = t.mapper
- local colormap = setmetatableindex(mapping)
- if mapper then
- setmetatableindex(colormap,function(t,k)
- local v = mapper(k)
- t[k] = v or k
- return v
- end)
- end
- return colormap
- else
- return false
- end
-end
-
-local colormaps = setmetatableindex(function(t,k)
- local v = false
- if type(k) == "string" then
- v = prepared(table.load(k)) -- todo: same path as svg file
- elseif type(k) == "table" then
- v = prepared(k)
- k = k.name or k
- end
- t[k] = v
- return v
-end)
-
-function metapost.svgcolorremapper(colormap)
- return colormaps[colormap]
-end
-
--- todo: cache colors per image / remapper
-
-local colorcomponents, withcolor, thecolor, usedcolors do
-
- local svgcolors = {
- aliceblue = 0xF0F8FF, antiquewhite = 0xFAEBD7, aqua = 0x00FFFF, aquamarine = 0x7FFFD4,
- azure = 0xF0FFFF, beige = 0xF5F5DC, bisque = 0xFFE4C4, black = 0x000000,
- blanchedalmond = 0xFFEBCD, blue = 0x0000FF, blueviolet = 0x8A2BE2, brown = 0xA52A2A,
- burlywood = 0xDEB887, cadetblue = 0x5F9EA0, hartreuse = 0x7FFF00, chocolate = 0xD2691E,
- coral = 0xFF7F50, cornflowerblue = 0x6495ED, cornsilk = 0xFFF8DC, crimson = 0xDC143C,
- cyan = 0x00FFFF, darkblue = 0x00008B, darkcyan = 0x008B8B, darkgoldenrod = 0xB8860B,
- darkgray = 0xA9A9A9, darkgreen = 0x006400, darkgrey = 0xA9A9A9, darkkhaki = 0xBDB76B,
- darkmagenta = 0x8B008B, darkolivegreen = 0x556B2F, darkorange = 0xFF8C00, darkorchid = 0x9932CC,
- darkred = 0x8B0000, darksalmon = 0xE9967A, darkseagreen = 0x8FBC8F, darkslateblue = 0x483D8B,
- darkslategray = 0x2F4F4F, darkslategrey = 0x2F4F4F, darkturquoise = 0x00CED1, darkviolet = 0x9400D3,
- deeppink = 0xFF1493, deepskyblue = 0x00BFFF, dimgray = 0x696969, dimgrey = 0x696969,
- dodgerblue = 0x1E90FF, firebrick = 0xB22222, floralwhite = 0xFFFAF0, forestgreen = 0x228B22,
- fuchsia = 0xFF00FF, gainsboro = 0xDCDCDC, ghostwhite = 0xF8F8FF, gold = 0xFFD700,
- goldenrod = 0xDAA520, gray = 0x808080, green = 0x008000, greenyellow = 0xADFF2F,
- grey = 0x808080, honeydew = 0xF0FFF0, hotpink = 0xFF69B4, indianred = 0xCD5C5C,
- indigo = 0x4B0082, ivory = 0xFFFFF0, khaki = 0xF0E68C, lavender = 0xE6E6FA,
- lavenderblush = 0xFFF0F5, lawngreen = 0x7CFC00, lemonchiffon = 0xFFFACD, lightblue = 0xADD8E6,
- lightcoral = 0xF08080, lightcyan = 0xE0FFFF, lightgoldenrodyellow = 0xFAFAD2, lightgray = 0xD3D3D3,
- lightgreen = 0x90EE90, lightgrey = 0xD3D3D3, lightpink = 0xFFB6C1, lightsalmon = 0xFFA07A,
- lightseagreen = 0x20B2AA, lightskyblue = 0x87CEFA, lightslategray = 0x778899, lightslategrey = 0x778899,
- lightsteelblue = 0xB0C4DE, lightyellow = 0xFFFFE0, lime = 0x00FF00, limegreen = 0x32CD32,
- linen = 0xFAF0E6, magenta = 0xFF00FF, maroon = 0x800000, mediumaquamarine = 0x66CDAA,
- mediumblue = 0x0000CD, mediumorchid = 0xBA55D3, mediumpurple = 0x9370DB, mediumseagreen = 0x3CB371,
- mediumslateblue = 0x7B68EE, mediumspringgreen = 0x00FA9A, mediumturquoise = 0x48D1CC, mediumvioletred = 0xC71585,
- midnightblue = 0x191970, mintcream = 0xF5FFFA, mistyrose = 0xFFE4E1, moccasin = 0xFFE4B5,
- navajowhite = 0xFFDEAD, navy = 0x000080, oldlace = 0xFDF5E6, olive = 0x808000,
- olivedrab = 0x6B8E23, orange = 0xFFA500, orangered = 0xFF4500, orchid = 0xDA70D6,
- palegoldenrod = 0xEEE8AA, palegreen = 0x98FB98, paleturquoise = 0xAFEEEE, palevioletred = 0xDB7093,
- papayawhip = 0xFFEFD5, peachpuff = 0xFFDAB9, peru = 0xCD853F, pink = 0xFFC0CB,
- plum = 0xDDA0DD, powderblue = 0xB0E0E6, purple = 0x800080, red = 0xFF0000,
- rosybrown = 0xBC8F8F, royalblue = 0x4169E1, saddlebrown = 0x8B4513, salmon = 0xFA8072,
- sandybrown = 0xF4A460, seagreen = 0x2E8B57, seashell = 0xFFF5EE, sienna = 0xA0522D,
- silver = 0xC0C0C0, skyblue = 0x87CEEB, slateblue = 0x6A5ACD, slategray = 0x708090,
- slategrey = 0x708090, snow = 0xFFFAFA, springgreen = 0x00FF7F, steelblue = 0x4682B4,
- tan = 0xD2B48C, teal = 0x008080, thistle = 0xD8BFD8, tomato = 0xFF6347,
- turquoise = 0x40E0D0, violet = 0xEE82EE, wheat = 0xF5DEB3, white = 0xFFFFFF,
- whitesmoke = 0xF5F5F5, yellow = 0xFFFF00, yellowgreen = 0x9ACD32,
- }
-
- local f_rgb = formatters['withcolor svgcolor(%.3N,%.3N,%.3N)']
- local f_cmyk = formatters['withcolor svgcmyk(%.3N,%.3N,%.3N,%.3N)']
- local f_gray = formatters['withcolor svggray(%.3N)']
- local f_rgba = formatters['withcolor svgcolor(%.3N,%.3N,%.3N) withtransparency (1,%.3N)']
- local f_graya = formatters['withcolor svggray(%.3N) withtransparency (1,%.3N)']
- local f_name = formatters['withcolor "%s"']
- local f_svgrgb = formatters['svgcolor(%.3N,%.3N,%.3N)']
- local f_svgcmyk = formatters['svgcmyk(%.3N,%.3N,%.3N,%.3N)']
- local f_svggray = formatters['svggray(%.3N)']
- local f_svgname = formatters['"%s"']
-
- local extract = bit32.extract
-
- local triplets = setmetatableindex(function(t,k)
- -- we delay building all these strings
- local v = svgcolors[k]
- if v then
- v = { extract(v,16,8)/255, extract(v,8,8)/255, extract(v,0,8)/255 }
- else
- v = false
- end
- t[k] = v
- return v
- end)
-
- local p_fraction = C(p_number) * C("%")^-1 / function(a,b) return tonumber(a) / (b and 100 or 255) end
- local p_angle = C(p_number) * P("deg")^0 / function(a) return tonumber(a) end
- local p_percent = C(p_number) * P("%") / function(a) return tonumber(a) / 100 end
- local p_absolute = C(p_number) / tonumber
-
- local p_left = P("(")
- local p_right = P(")")
- local p_a = P("a")^-1
- local p_h_a_color = p_left
- * p_angle
- * p_separator * p_percent
- * p_separator * p_percent
- * p_separator^0 * p_absolute^0
- * p_right
-
- local colors = attributes.colors
- local colorvalues = colors.values
- local colorindex = attributes.list[attributes.private('color')]
- local hsvtorgb = colors.hsvtorgb
- local hwbtorgb = colors.hwbtorgb
- local forcedmodel = colors.forcedmodel
-
- local p_splitcolor =
- P("#") * C(p_hexdigit*p_hexdigit)^1 / function(r,g,b)
- if not r then
- return "gray", 0
- elseif not (g and b) then
- return "gray", tonumber(r or "0", 16) / 255 or 0
- else
- return "rgb",
- tonumber(r or "0", 16) / 255 or 0,
- tonumber(g or "0", 16) / 255 or 0,
- tonumber(b or "0", 16) / 255 or 0
- end
- end
- + P("rgb") * p_a
- * p_left * (p_fraction + p_separator)^-3 * (p_absolute + p_separator)^0 * p_right / function(r,g,b,a)
- return "rgb", r or 0, g or 0, b or 0, a or false
- end
- + P("cmyk")
- * p_left * (p_absolute + p_separator)^0 * p_right / function(c,m,y,k)
- return "cmyk", c or 0, m or 0, y or 0, k or 0
- end
- + P("hsl") * p_a
- * p_h_a_color / function(h,s,l,a)
- local r, g, b = hsvtorgb(h,s,l,a)
- return "rgb", r or 0, g or 0, b or 0, a or false
- end
- + P("hwb") * p_a
- * p_h_a_color / function(h,w,b,a)
- local r, g, b = hwbtorgb(h,w,b)
- return "rgb", r or 0, g or 0, b or 0, a or false
- end
-
- function metapost.svgsplitcolor(color)
- if type(color) == "string" then
- local what, s1, s2, s3, s4 = lpegmatch(p_splitcolor,color)
- if not what then
- local t = triplets[color]
- if t then
- what, s1, s2, s3 = "rgb", t[1], t[2], t[3]
- end
- end
- return what, s1, s2, s3, s4
- else
- return "gray", 0, false
- end
- end
-
- local function registeredcolor(name)
- local color = colorindex[name]
- if color then
- local v = colorvalues[color]
- local t = forcedmodel(v[1])
- if t == 2 then
- return "gray", v[2]
- elseif t == 3 then
- return "rgb", v[3], v[4], v[5]
- elseif t == 4 then
- return "cmyk", v[6], v[7], v[8], v[9]
- else
- --
- end
- end
- end
-
- -- we can have a fast check for #000000
-
- local function validcolor(color)
- if usedcolors then
- usedcolors[color] = usedcolors[color] + 1
- end
- if colormap then
- local c = colormap[color]
- local t = type(c)
- if t == "table" then
- local what = t[1]
- if what == "rgb" then
- return
- what,
- tonumber(t[2]) or 0,
- tonumber(t[3]) or 0,
- tonumber(t[4]) or 0,
- tonumber(t[4]) or false
- elseif what == "cmyk" then
- return
- what,
- tonumber(t[2]) or 0,
- tonumber(t[3]) or 0,
- tonumber(t[4]) or 0,
- tonumber(t[5]) or 0
- elseif what == "gray" then
- return
- what,
- tonumber(t[2]) or 0,
- tonumber(t[3]) or false
- end
- elseif t == "string" then
- color = c
- end
- end
- local what, s1, s2, s3, s4 = registeredcolor(color)
- if what then
- return what, s1, s2, s3, s4
- end
- what, s1, s2, s3, s4 = lpegmatch(p_splitcolor,color)
- if not what then
- local t = triplets[color]
- if t then
- s1, s3, s3 = t[1], t[2], t[3]
- what = "rgb"
- end
- end
- return what, s1, s2, s3, s4
- end
-
- colorcomponents = function(color)
- local what, s1, s2, s3, s4 = validcolor(color)
- return s1, s2, s3, s4 -- so 4 means cmyk
- end
-
- withcolor = function(color)
- local what, s1, s2, s3, s4 = validcolor(color)
- -- print(color,what, s1, s2, s3, s4)
- if what == "rgb" then
- if s4 then
- if s1 == s2 and s1 == s3 then
- return f_graya(s1,s4)
- else
- return f_rgba(s1,s2,s3,s4)
- end
- else
- if s1 == s2 and s1 == s3 then
- return f_gray(s1)
- else
- return f_rgb(s1,s2,s3)
- end
- end
- elseif what == "cmyk" then
- return f_cmyk(s1,s2,s3,s4)
- elseif what == "gray" then
- if s2 then
- return f_graya(s1,s2)
- else
- return f_gray(s1)
- end
- end
- return f_name(color)
- end
-
- thecolor = function(color)
- local what, s1, s2, s3, s4 = validcolor(color)
- if what == "rgb" then
- if s4 then
- if s1 == s2 and s1 == s3 then
- return f_svggraya(s1,s4)
- else
- return f_svgrgba(s1,s2,s3,s4)
- end
- else
- if s1 == s2 and s1 == s3 then
- return f_svggray(s1)
- else
- return f_svgrgb(s1,s2,s3)
- end
- end
- elseif what == "cmyk" then
- return f_cmyk(s1,s2,s3,s4)
- elseif what == "gray" then
- if s2 then
- return f_svggraya(s1,s2)
- else
- return f_svggray(s1)
- end
- end
- return f_svgname(color)
- end
-
-end
-
--- actually we can loop faster because we can go to the last one
-
-local grabpath, grablist do
-
- local f_moveto = formatters['(%N,%N)']
- local f_curveto_z = formatters['controls(%N,%N)and(%N,%N)..(%N,%N)']
- local f_curveto_n = formatters['..controls(%N,%N)and(%N,%N)..(%N,%N)']
- local f_lineto_z = formatters['(%N,%N)']
- local f_lineto_n = formatters['--(%N,%N)']
-
- local m = { __index = function() return 0 end }
-
- grabpath = function(str)
- local p = lpegmatch(p_path,str) or { }
- local np = #p
- local all = { entries = np, closed = false, curve = false }
- if np == 0 then
- return all
- end
- setmetatable(p,m)
- local t = { } -- no real saving here if we share
- local n = 0
- local a = 0
- local i = 0
- local last = "M"
- local prev = last
- local kind = "L"
- local x, y = 0, 0
- local x1, y1 = 0, 0
- local x2, y2 = 0, 0
- local rx, ry = 0, 0
- local ar, al = 0, 0
- local as, ac = 0, nil
- local mx, my = 0, 0
- while i < np do
- i = i + 1
- local pi = p[i]
- if type(pi) ~= "number" then
- last = pi
- i = i + 1
- pi = p[i]
- end
- -- most often
- if last == "c" then
- x1 = x + pi
- i = i + 1 ; y1 = y + p[i]
- i = i + 1 ; x2 = x + p[i]
- i = i + 1 ; y2 = y + p[i]
- i = i + 1 ; x = x + p[i]
- i = i + 1 ; y = y + p[i]
- goto curveto
- elseif last == "l" then
- x = x + pi
- i = i + 1 ; y = y + p[i]
- goto lineto
- elseif last == "h" then
- x = x + pi
- goto lineto
- elseif last == "v" then
- y = y + pi
- goto lineto
- elseif last == "a" then
- x1 = x
- y1 = y
- rx = pi
- i = i + 1 ; ry = p[i]
- i = i + 1 ; ar = p[i]
- i = i + 1 ; al = p[i]
- i = i + 1 ; as = p[i]
- i = i + 1 ; x = x + p[i]
- i = i + 1 ; y = y + p[i]
- goto arc
- elseif last == "s" then
- if prev == "C" then
- x1 = 2 * x - x2
- y1 = 2 * y - y2
- else
- x1 = x
- y1 = y
- end
- x2 = x + pi
- i = i + 1 ; y2 = y + p[i]
- i = i + 1 ; x = x + p[i]
- i = i + 1 ; y = y + p[i]
- goto curveto
- elseif last == "m" then
- if n > 0 then
- a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
- end
- x = x + pi
- i = i + 1 ; y = y + p[i]
- goto moveto
- elseif last == "z" then
- goto close
- -- less frequent
- elseif last == "C" then
- x1 = pi
- i = i + 1 ; y1 = p[i]
- i = i + 1 ; x2 = p[i]
- i = i + 1 ; y2 = p[i]
- i = i + 1 ; x = p[i]
- i = i + 1 ; y = p[i]
- goto curveto
- elseif last == "L" then
- x = pi
- i = i + 1 ; y = p[i]
- goto lineto
- elseif last == "H" then
- x = pi
- goto lineto
- elseif last == "V" then
- y = pi
- goto lineto
- elseif last == "A" then
- x1 = x
- y1 = y
- rx = pi
- i = i + 1 ; ry = p[i]
- i = i + 1 ; ar = p[i]
- i = i + 1 ; al = p[i]
- i = i + 1 ; as = p[i]
- i = i + 1 ; x = p[i]
- i = i + 1 ; y = p[i]
- goto arc
- elseif last == "S" then
- if prev == "C" then
- x1 = 2 * x - x2
- y1 = 2 * y - y2
- else
- x1 = x
- y1 = y
- end
- x2 = pi
- i = i + 1 ; y2 = p[i]
- i = i + 1 ; x = p[i]
- i = i + 1 ; y = p[i]
- goto curveto
- elseif last == "M" then
- if n > 0 then
- a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
- end
- x = pi ;
- i = i + 1 ; y = p[i]
- goto moveto
- elseif last == "Z" then
- goto close
- -- very seldom
- elseif last == "q" then
- x1 = x + pi
- i = i + 1 ; y1 = y + p[i]
- i = i + 1 ; x2 = x + p[i]
- i = i + 1 ; y2 = y + p[i]
- goto quadratic
- elseif last == "t" then
- if prev == "C" then
- x1 = 2 * x - x1
- y1 = 2 * y - y1
- else
- x1 = x
- y1 = y
- end
- x2 = x + pi
- i = i + 1 ; y2 = y + p[i]
- goto quadratic
- elseif last == "Q" then
- x1 = pi
- i = i + 1 ; y1 = p[i]
- i = i + 1 ; x2 = p[i]
- i = i + 1 ; y2 = p[i]
- goto quadratic
- elseif last == "T" then
- if prev == "C" then
- x1 = 2 * x - x1
- y1 = 2 * y - y1
- else
- x1 = x
- y1 = y
- end
- x2 = pi
- i = i + 1 ; y2 = p[i]
- goto quadratic
- else
- goto continue
- end
- ::moveto::
- n = n + 1 ; t[n] = f_moveto(x,y)
- last = last == "M" and "L" or "l"
- prev = "M"
- mx = x
- my = y
- goto continue
- ::lineto::
- n = n + 1 ; t[n] = (n > 0 and f_lineto_n or f_lineto_z)(x,y)
- prev = "L"
- goto continue
- ::curveto::
- n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(x1,y1,x2,y2,x,y)
- prev = "C"
- goto continue
- ::arc::
- ac = a2c(x1,y1,rx,ry,ar,al,as,x,y)
- for i=1,#ac,6 do
- n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(
- ac[i],ac[i+1],ac[i+2],ac[i+3],ac[i+4],ac[i+5]
- )
- end
- prev = "A"
- goto continue
- ::quadratic::
- n = n + 1 ; t[n] = (n > 0 and f_curveto_n or f_curveto_z)(
- x + 2/3 * (x1-x ), y + 2/3 * (y1-y ),
- x2 + 2/3 * (x1-x2), y2 + 2/3 * (y1-y2),
- x2, y2
- )
- x = x2
- y = y2
- prev = "C"
- goto continue
- ::close::
- -- n = n + 1 ; t[n] = prev == "C" and "..cycle" or "--cycle"
- n = n + 1 ; t[n] = "--cycle"
- if n > 0 then
- a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
- end
- if i == np then
- break
- else
- i = i - 1
- end
- kind = prev
- prev = "Z"
- -- this is kind of undocumented: a close also moves back
- x = mx
- y = my
- ::continue::
- end
- if n > 0 then
- a = a + 1 ; all[a] = concat(t,"",1,n) ; n = 0
- end
- if prev == "Z" then
- all.closed = true
- end
- all.curve = (kind == "C" or kind == "A")
- return all, p
- end
-
- -- this is a bit tricky as what are points for a mark ... the next can be simplified
- -- a lot
-
- grablist = function(p)
- local np = #p
- if np == 0 then
- return nil
- end
- local t = { }
- local n = 0
- local a = 0
- local i = 0
- local last = "M"
- local prev = last
- local kind = "L"
- local x, y = 0, 0
- local x1, y1 = 0, 0
- local x2, y2 = 0, 0
- local rx, ry = 0, 0
- local ar, al = 0, 0
- local as, ac = 0, nil
- local mx, my = 0, 0
- while i < np do
- i = i + 1
- local pi = p[i]
- if type(pi) ~= "number" then
- last = pi
- i = i + 1
- pi = p[i]
- end
- -- most often
- if last == "c" then
- x1 = x + pi
- i = i + 1 ; y1 = y + p[i]
- i = i + 1 ; x2 = x + p[i]
- i = i + 1 ; y2 = y + p[i]
- i = i + 1 ; x = x + p[i]
- i = i + 1 ; y = y + p[i]
- goto curveto
- elseif last == "l" then
- x = x + pi
- i = i + 1 ; y = y + p[i]
- goto lineto
- elseif last == "h" then
- x = x + pi
- goto lineto
- elseif last == "v" then
- y = y + pi
- goto lineto
- elseif last == "a" then
- x1 = x
- y1 = y
- rx = pi
- i = i + 1 ; ry = p[i]
- i = i + 1 ; ar = p[i]
- i = i + 1 ; al = p[i]
- i = i + 1 ; as = p[i]
- i = i + 1 ; x = x + p[i]
- i = i + 1 ; y = y + p[i]
- goto arc
- elseif last == "s" then
- if prev == "C" then
- x1 = 2 * x - x2
- y1 = 2 * y - y2
- else
- x1 = x
- y1 = y
- end
- x2 = x + pi
- i = i + 1 ; y2 = y + p[i]
- i = i + 1 ; x = x + p[i]
- i = i + 1 ; y = y + p[i]
- goto curveto
- elseif last == "m" then
- x = x + pi
- i = i + 1 ; y = y + p[i]
- goto moveto
- elseif last == "z" then
- goto close
- -- less frequent
- elseif last == "C" then
- x1 = pi
- i = i + 1 ; y1 = p[i]
- i = i + 1 ; x2 = p[i]
- i = i + 1 ; y2 = p[i]
- i = i + 1 ; x = p[i]
- i = i + 1 ; y = p[i]
- goto curveto
- elseif last == "L" then
- x = pi
- i = i + 1 ; y = p[i]
- goto lineto
- elseif last == "H" then
- x = pi
- goto lineto
- elseif last == "V" then
- y = pi
- goto lineto
- elseif last == "A" then
- x1 = x
- y1 = y
- rx = pi
- i = i + 1 ; ry = p[i]
- i = i + 1 ; ar = p[i]
- i = i + 1 ; al = p[i]
- i = i + 1 ; as = p[i]
- i = i + 1 ; x = p[i]
- i = i + 1 ; y = p[i]
- goto arc
- elseif last == "S" then
- if prev == "C" then
- x1 = 2 * x - x2
- y1 = 2 * y - y2
- else
- x1 = x
- y1 = y
- end
- x2 = pi
- i = i + 1 ; y2 = p[i]
- i = i + 1 ; x = p[i]
- i = i + 1 ; y = p[i]
- goto curveto
- elseif last == "M" then
- x = pi ;
- i = i + 1 ; y = p[i]
- goto moveto
- elseif last == "Z" then
- goto close
- -- very seldom
- elseif last == "q" then
- x1 = x + pi
- i = i + 1 ; y1 = y + p[i]
- i = i + 1 ; x2 = x + p[i]
- i = i + 1 ; y2 = y + p[i]
- goto quadratic
- elseif last == "t" then
- if prev == "C" then
- x1 = 2 * x - x1
- y1 = 2 * y - y1
- else
- x1 = x
- y1 = y
- end
- x2 = x + pi
- i = i + 1 ; y2 = y + p[i]
- goto quadratic
- elseif last == "Q" then
- x1 = pi
- i = i + 1 ; y1 = p[i]
- i = i + 1 ; x2 = p[i]
- i = i + 1 ; y2 = p[i]
- goto quadratic
- elseif last == "T" then
- if prev == "C" then
- x1 = 2 * x - x1
- y1 = 2 * y - y1
- else
- x1 = x
- y1 = y
- end
- x2 = pi
- i = i + 1 ; y2 = p[i]
- goto quadratic
- else
- goto continue
- end
- ::moveto::
- n = n + 1 ; t[n] = x
- n = n + 1 ; t[n] = y
- last = last == "M" and "L" or "l"
- prev = "M"
- mx = x
- my = y
- goto continue
- ::lineto::
- n = n + 1 ; t[n] = x
- n = n + 1 ; t[n] = y
- prev = "L"
- goto continue
- ::curveto::
- n = n + 1 ; t[n] = x
- n = n + 1 ; t[n] = y
- prev = "C"
- goto continue
- ::arc::
- ac = a2c(x1,y1,rx,ry,ar,al,as,x,y)
- for i=1,#ac,6 do
- n = n + 1 ; t[n] = ac[i+4]
- n = n + 1 ; t[n] = ac[i+5]
- end
- prev = "A"
- goto continue
- ::quadratic::
- n = n + 1 ; t[n] = x2
- n = n + 1 ; t[n] = y2
- x = x2
- y = y2
- prev = "C"
- goto continue
- ::close::
- n = n + 1 ; t[n] = mx
- n = n + 1 ; t[n] = my
- if i == np then
- break
- end
- kind = prev
- prev = "Z"
- x = mx
- y = my
- ::continue::
- end
- return t
- end
-
-end
-
--- todo: viewbox helper
-
-local s_wrapped_start = "draw image ("
-local f_wrapped_stop = formatters[") shifted (0,%N) scaled %N ;"]
-
-local handletransform, handleviewbox do
-
- local sind = math.sind
-
- --todo: better lpeg
-
- local f_rotatedaround = formatters[" rotatedaround((%N,%N),%N)"]
- local f_rotated = formatters[" rotated(%N)"]
- local f_shifted = formatters[" shifted(%N,%N)"]
- local f_slanted_x = formatters[" xslanted(%N)"]
- local f_slanted_y = formatters[" yslanted(%N)"]
- local f_scaled = formatters[" scaled(%N)"]
- local f_xyscaled = formatters[" xyscaled(%N,%N)"]
- local f_matrix = formatters[" transformed bymatrix(%N,%N,%N,%N,%N,%N)"]
-
- local s_transform_start = "draw image ( "
- local f_transform_stop = formatters[")%s ;"]
-
- local function rotate(r,x,y)
- if x then
- return r and f_rotatedaround(x,-(y or x),-r)
- elseif r then
- return f_rotated(-r)
- else
- return ""
- end
- end
-
- local function translate(x,y)
- if y then
- return f_shifted(x,-y)
- elseif x then
- return f_shifted(x,0)
- else
- return ""
- end
- end
-
- local function scale(x,y)
- if y then
- return f_xyscaled(x,y)
- elseif x then
- return f_scaled(x)
- else
- return ""
- end
- end
-
- local function skewx(x)
- if x then
- return f_slanted_x(sind(-x))
- else
- return ""
- end
- end
-
- local function skewy(y)
- if y then
- return f_slanted_y(sind(-y))
- else
- return ""
- end
- end
-
- local function matrix(rx,sx,sy,ry,tx,ty)
- return f_matrix(rx or 1, sx or 0, sy or 0, ry or 1, tx or 0, - (ty or 0))
- end
-
- -- How to deal with units here? Anyway, order seems to matter.
-
- local p_transform = Cf ( Ct("") * (
- lpegpatterns.whitespace^0 * Cg(
- C("translate") * (p_numbers / translate) -- maybe xy
- + C("scale") * (p_numbers / scale)
- + C("rotate") * (p_numbers / rotate)
- + C("matrix") * (p_numbers / matrix)
- + C("skewX") * (p_numbers / skewx)
- + C("skewY") * (p_numbers / skewy)
- )
- )^1, rawset)
-
- handletransform = function(at)
- local t = at.transform
- if t then
- local e = lpegmatch(p_transform,t)
- if e then
- e = concat({
- e.rotate or "",
- e.skewX or "",
- e.skewY or "",
- e.scale or "",
- e.translate or "",
- e.matrix or "",
- }, " ")
- return s_transform_start, f_transform_stop(e), t
- end
- end
- end
-
- handleviewbox = function(v)
- if v then
- local x, y, w, h = lpegmatch(p_fournumbers,v)
- if h then
- return x, y, w, h
- end
- end
- end
-
-end
-
-local dashed do
-
- -- actually commas are mandate but we're tolerant
-
- local f_dashed_n = formatters[" dashed dashpattern (%s ) "]
- local f_dashed_y = formatters[" dashed dashpattern (%s ) shifted (%N,0) "]
-
- local p_number = p_optseparator/"" * p_number_r
- local p_on = Cc(" on ") * p_number
- local p_off = Cc(" off ") * p_number
- local p_dashed = Cs((p_on * p_off^-1)^1)
-
- dashed = function(s,o)
- if not find(s,",") then
- -- a bit of a hack:
- s = s .. " " .. s
- end
- return (o and f_dashed_y or f_dashed_n)(lpegmatch(p_dashed,s),o)
- end
-
-end
-
-do
-
- local handlers = { }
- local process = false
- local root = false
- local result = false
- local r = false
- local definitions = false
- local classstyles = false
- local tagstyles = false
-
- local tags = {
- ["a"] = true,
- -- ["altgGlyph"] = true,
- -- ["altgGlyphDef"] = true,
- -- ["altgGlyphItem"] = true,
- -- ["animate"] = true,
- -- ["animateColor"] = true,
- -- ["animateMotion"] = true,
- -- ["animateTransform"] = true,
- ["circle"] = true,
- ["clipPath"] = true,
- -- ["color-profile"] = true,
- -- ["cursor"] = true,
- ["defs"] = true,
- -- ["desc"] = true,
- ["ellipse"] = true,
- -- ["filter"] = true,
- -- ["font"] = true,
- -- ["font-face"] = true,
- -- ["font-face-format"] = true,
- -- ["font-face-name"] = true,
- -- ["font-face-src"] = true,
- -- ["font-face-uri"] = true,
- -- ["foreignObject"] = true,
- ["g"] = true,
- -- ["glyph"] = true,
- -- ["glyphRef"] = true,
- -- ["hkern"] = true,
- ["image"] = true,
- ["line"] = true,
- ["linearGradient"] = true,
- ["marker"] = true,
- -- ["mask"] = true,
- -- ["metadata"] = true,
- -- ["missing-glyph"] = true,
- -- ["mpath"] = true,
- ["path"] = true,
- -- ["pattern"] = true,
- ["polygon"] = true,
- ["polyline"] = true,
- ["radialGradient"] = true,
- ["rect"] = true,
- -- ["script"] = true,
- -- ["set"] = true,
- ["stop"] = true,
- ["style"] = true,
- ["svg"] = true,
- -- ["switch"] = true,
- ["symbol"] = true,
- ["text"] = true,
- -- ["textPath"] = true,
- -- ["title"] = true,
- ["tspan"] = true,
- ["use"] = true,
- -- ["view"] = true,
- -- ["vkern"] = true,
- }
-
- local function handlechains(c)
- if tags[c.tg] then
- local at = c.at
- local dt = c.dt
- if at and dt then
- -- at["inkscape:connector-curvature"] = nil -- cleare entry and might prevent table growth
- local estyle = rawget(at,"style")
- if estyle and estyle ~= "" then
- for k, v in gmatch(estyle,"%s*([^:]+):%s*([^;]+);?") do
- at[k] = v
- end
- end
- local eclass = rawget(at,"class")
- if eclass and eclass ~= "" then
- for c in gmatch(eclass,"[^ ]+") do
- local s = classstyles[c]
- if s then
- for k, v in next, s do
- at[k] = v
- end
- end
- end
- end
- local tstyle = tagstyles[tag]
- if tstyle then
- for k, v in next, tstyle do
- at[k] = v
- end
- end
- if trace_path and pathtracer then
- for k, v in next, pathtracer do
- at[k] = v
- end
- end
- for i=1,#dt do
- local dti = dt[i]
- if type(dti) == "table" then
- handlechains(dti)
- end
- end
- end
- end
- end
-
- local handlestyle do
-
- -- It can also be CDATA but that is probably dealt with because we only
- -- check for style entries and ignore the rest. But maybe we also need
- -- to check a style at the outer level?
-
- local p_key = C((R("az","AZ","09","__","--")^1))
- local p_spec = P("{") * C((1-P("}"))^1) * P("}")
- local p_valid = Carg(1) * P(".") * p_key + Carg(2) * p_key
- local p_grab = ((p_valid * p_space^0 * p_spec / rawset) + p_space^1 + P(1))^1
-
- local fontspecification = css.fontspecification
-
- handlestyle = function(c)
- local s = xmltext(c)
- lpegmatch(p_grab,s,1,classstyles,tagstyles)
- for k, v in next, classstyles do
- local t = { }
- for k, v in gmatch(v,"%s*([^:]+):%s*([^;]+);?") do
- if k == "font" then
- local s = fontspecification(v)
- for k, v in next, s do
- t["font-"..k] = v
- end
- else
- t[k] = v
- end
- end
- classstyles[k] = t
- end
- for k, v in next, tagstyles do
- local t = { }
- for k, v in gmatch(v,"%s*([^:]+):%s*([^;]+);?") do
- if k == "font" then
- local s = fontspecification(v)
- for k, v in next, s do
- t["font-"..k] = v
- end
- else
- t[k] = v
- end
- end
- tagstyles[k] = t
- end
- end
-
- function handlers.style()
- -- ignore
- end
-
- end
-
- -- We can have root in definitions and then do a metatable lookup but use
- -- is not used that often I guess.
-
- local function locate(id)
- local res = definitions[id]
- if res then
- return res
- end
- local ref = gsub(id,"^url%(#(.-)%)$","%1")
- local ref = gsub(ref,"^#","")
- -- we can make a fast id lookup
- local res = xmlfirst(root,"**[@id='"..ref.."']")
- if res then
- definitions[id] = res
- end
- return res
- end
-
- -- also locate
-
- local function handleclippath(at)
- local clippath = at["clip-path"]
-
- if not clippath then
- return
- end
-
- local spec = definitions[clippath] or locate(clippath)
-
- -- do we really need thsi crap
- if not spec then
- local index = match(clippath,"(%d+)")
- if index then
- spec = xmlfirst(root,"clipPath["..tostring(tonumber(index) or 0).."]")
- end
- end
- -- so far for the crap
-
- if not spec then
- report("unknown clip %a",clippath)
- return
- elseif spec.tg ~= "clipPath" then
- report("bad clip %a",clippath)
- return
- end
-
- ::again::
- for c in xmlcollected(spec,"/(path|use|g)") do
- local tg = c.tg
- if tg == "use" then
- local ca = c.at
- local id = ca["xlink:href"]
- if id then
- spec = locate(id)
- if spec then
- local sa = spec.at
- setmetatableindex(sa,ca)
- if spec.tg == "path" then
- local d = sa.d
- if d then
- local p = grabpath(d)
- p.evenodd = sa["clip-rule"] == "evenodd"
- p.close = true
- return p, clippath
- else
- return
- end
- else
- goto again
- end
- end
- end
- -- break
- elseif tg == "path" then
- local ca = c.at
- local d = ca.d
- if d then
- local p = grabpath(d)
- p.evenodd = ca["clip-rule"] == "evenodd"
- p.close = true
- return p, clippath
- else
- return
- end
- else
- -- inherit?
- end
- end
- end
-
- local s_shade_linear = ' withshademethod "linear" '
- local s_shade_circular = ' withshademethod "circular" '
- local f_shade_step = formatters['withshadestep ( withshadefraction %N withshadecolors(%s,%s) )']
- local f_shade_one = formatters['withprescript "sh_center_a=%N %N"']
- local f_shade_two = formatters['withprescript "sh_center_b=%N %N"']
-
- local f_color = formatters['withcolor "%s"']
- local f_opacity = formatters['withtransparency (1,%N)']
- local f_pen = formatters['withpen pencircle scaled %N']
-
- -- todo: gradient unfinished
- -- todo: opacity but first we need groups in mp
-
- local function gradient(id)
- local spec = definitions[id] -- no locate !
- if spec then
- local kind = spec.tg
- local shade = nil
- local n = 1
- local a = spec.at
- if kind == "linearGradient" then
- shade = { s_shade_linear }
- --
- local x1 = rawget(a,"x1")
- local y1 = rawget(a,"y1")
- local x2 = rawget(a,"x2")
- local y2 = rawget(a,"y2")
- if x1 and y1 then
- n = n + 1 ; shade[n] = f_shade_one(asnumber_vx(x1),asnumber_vy(y1))
- end
- if x2 and y2 then
- n = n + 1 ; shade[n] = f_shade_one(asnumber_vx(x2),asnumber_vy(y2))
- end
- --
- elseif kind == "radialGradient" then
- shade = { s_shade_circular }
- --
- local cx = rawget(a,"cx") -- x center
- local cy = rawget(a,"cy") -- y center
- local r = rawget(a,"r" ) -- radius
- local fx = rawget(a,"fx") -- focal points
- local fy = rawget(a,"fy") -- focal points
- --
- if cx and cy then
- -- todo
- end
- if r then
- -- todo
- end
- if fx and fy then
- -- todo
- end
- else
- report("unknown gradient %a",id)
- return
- end
- -- local gu = a.gradientUnits
- -- local gt = a.gradientTransform
- -- local sm = a.spreadMethod
- local colora, colorb
- -- startcolor ?
- for c in xmlcollected(spec,"/stop") do
- local a = c.at
- local offset = rawget(a,"offset")
- local colorb = rawget(a,"stop-color")
- local opacity = rawget(a,"stop-opacity")
- if colorb then
- colorb = thecolor(colorb)
- end
- if not colora then
- colora = colorb
- end
- -- what if no percentage
-
- local fraction = offset and asnumber_r(offset)
- if not fraction then
- -- offset = tonumber(offset)
- -- for now
- fraction = xmlcount(spec,"/stop")/100
- end
-
- if colora and colorb and color_a ~= "" and color_b ~= "" then
- n = n + 1 ; shade[n] = f_shade_step(fraction,colora,colorb)
- end
-
- colora = colorb
- end
- return concat(shade," ")
- end
- end
-
- local function drawproperties(stroke,at,opacity)
- local p = at["stroke-width"]
- if p then
- p = f_pen(asnumber_r(p))
- end
- local d = at["stroke-dasharray"]
- if d == "none" then
- d = nil
- elseif d then
- local o = at["stroke-dashoffset"]
- if o and o ~= "none" then
- o = asnumber_r(o)
- else
- o = false
- end
- d = dashed(d,o)
- end
- local c = withcolor(stroke)
- local o = at["stroke-opacity"] or (opacity and at["opacity"])
- if o == "none" then
- o = nil
- elseif o then
- o = asnumber_r(o)
- if o and o ~= 1 then
- o = f_opacity(o)
- else
- o = nil
- end
- end
- return p, d, c, o
- end
-
- local s_opacity_start = "draw image ("
- local f_opacity_stop = formatters["setgroup currentpicture to boundingbox currentpicture withtransparency (1,%N)) ;"]
-
- local function sharedopacity(at)
- local o = at["opacity"]
- if o and o ~= "none" then
- o = asnumber_r(o)
- if o and o ~= 1 then
- return s_opacity_start, f_opacity_stop(o)
- end
- end
- end
-
- local function fillproperties(fill,at,opacity)
- local c = c ~= "none" and (gradient(fill) or withcolor(fill)) or nil
- local o = at["fill-opacity"] or (opacity and at["opacity"])
- if o and o ~= "none" then
- o = asnumber_r(o)
- if o == 1 then
- return c
- elseif o then
- return c, f_opacity(o), o == 0
- end
- end
- return c
- end
-
- -- todo: clip = [ auto | rect(llx,lly,urx,ury) ]
-
- local s_offset_start = "draw image ( "
- local f_offset_stop = formatters[") shifted (%N,%N) ;"]
- local s_rotation_start = "draw image ( "
- local f_rotation_stop = formatters[") rotatedaround((0,0),-angle((%N,%N))) ;"]
- local f_rotation_angle = formatters[") rotatedaround((0,0),-%N) ;"]
-
- local function offset(at)
- local x = asnumber_vx(rawget(at,"x"))
- local y = asnumber_vy(rawget(at,"y"))
- if x ~= 0 or y ~= 0 then
- return s_offset_start, f_offset_stop(x,y)
- end
- end
-
- local s_viewport_start = "draw image ("
- local s_viewport_stop = ") ;"
- local f_viewport_shift = formatters["currentpicture := currentpicture shifted (%03N,%03N);"]
- local f_viewport_scale = formatters["currentpicture := currentpicture xysized (%03N,%03N);"]
- local f_viewport_clip = formatters["clip currentpicture to (unitsquare xyscaled (%03N,%03N));"]
-
- local function viewport(x,y,w,h,noclip,scale)
- r = r + 1 ; result[r] = s_viewport_start
- return function()
- local okay = w ~= 0 and h ~= 0
- if okay and scale then
- r = r + 1 ; result[r] = f_viewport_scale(w,h)
- end
- if x ~= 0 or y ~= 0 then
- r = r + 1 ; result[r] = f_viewport_shift(-x,y)
- end
- if okay and not noclip then
- r = r + 1 ; result[r] = f_viewport_clip(w,-h)
- end
-
- r = r + 1 ; result[r] = s_viewport_stop
- end
- end
-
- -- maybe forget about defs and just always locate (and then backtrack
- -- over <g> if needed)
-
- function handlers.defs(c)
- for c in xmlcollected(c,"/*") do
- local a = c.at
- if a then
- local id = rawget(a,"id")
- if id then
- definitions["#" .. id ] = c
- definitions["url(#" .. id .. ")"] = c
- end
- end
- end
- end
-
- function handlers.symbol(c)
- if uselevel == 0 then
- local id = rawget(c.at,"id")
- if id then
- definitions["#" .. id ] = c
- definitions["url(#" .. id .. ")"] = c
- end
- else
- handlers.g(c)
- end
- end
-
- local uselevel = 0
-
- function handlers.use(c)
- local at = c.at
- local id = rawget(at,"href") or rawget(at,"xlink:href") -- better a rawget
- local res = locate(id)
- if res then
- -- width height ?
- uselevel = uselevel + 1
- local boffset, eoffset = offset(at)
- local btransform, etransform, transform = handletransform(at)
-
- if boffset then
- r = r + 1 result[r] = boffset
- end
-
- -- local clippath = at.clippath
-
- if btransform then
- r = r + 1 result[r] = btransform
- end
-
- local _transform = transform
- local _clippath = clippath
- at["transform"] = false
- -- at["clip-path"] = false
-
- process(res,"/*")
-
- at["transform"] = _transform
- -- at["clip-path"] = _clippath
-
- if etransform then
- r = r + 1 ; result[r] = etransform
- end
-
- if eoffset then
- r = r + 1 result[r] = eoffset
- end
-
- uselevel = uselevel - 1
- else
- report("use: unknown definition %a",id)
- end
- end
-
- local f_no_draw = formatters['nodraw (%s)']
- local f_do_draw = formatters['draw (%s)']
- local f_no_fill_c = formatters['nofill (%s..cycle)']
- local f_do_fill_c = formatters['fill (%s..cycle)']
- local f_eo_fill_c = formatters['eofill (%s..cycle)']
- local f_no_fill_l = formatters['nofill (%s--cycle)']
- local f_do_fill_l = formatters['fill (%s--cycle)']
- local f_eo_fill_l = formatters['eofill (%s--cycle)']
- local f_do_fill = f_do_fill_c
- local f_eo_fill = f_eo_fill_c
- local f_no_fill = f_no_fill_c
- local s_clip_start = 'draw image ('
- local f_clip_stop_c = formatters[') ; clip currentpicture to (%s..cycle) ;']
- local f_clip_stop_l = formatters[') ; clip currentpicture to (%s--cycle) ;']
- local f_clip_stop = f_clip_stop_c
- local f_eoclip_stop_c = formatters[') ; eoclip currentpicture to (%s..cycle) ;']
- local f_eoclip_stop_l = formatters[') ; eoclip currentpicture to (%s--cycle) ;']
- local f_eoclip_stop = f_eoclip_stop_c
-
- -- could be shared and then beginobject | endobject
-
- local function flushobject(object,at,c,o)
- local btransform, etransform = handletransform(at)
- local cpath = handleclippath(at)
-
- if cpath then
- r = r + 1 ; result[r] = s_clip_start
- end
-
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
-
- r = r + 1 ; result[r] = f_do_draw(object)
-
- if c then
- r = r + 1 ; result[r] = c
- end
-
- if o then
- r = r + 1 ; result[r] = o
- end
-
- if etransform then
- r = r + 1 ; result[r] = etransform
- end
-
- r = r + 1 ; result[r] = ";"
-
- if cpath then
- local f_done = cpath.evenodd
- if cpath.curve then
- f_done = f_done and f_eoclip_stop_c or f_clip_stop_c
- else
- f_done = f_done and f_eoclip_stop_l or f_clip_stop_l
- end
- r = r + 1 ; result[r] = f_done(cpath[1])
- end
- end
-
- do
-
- local flush
-
- local f_linecap = formatters["interim linecap := %s ;"]
- local f_linejoin = formatters["interim linejoin := %s ;"]
- local f_miterlimit = formatters["interim miterlimit := %s ;"]
-
- local s_begingroup = "begingroup;"
- local s_endgroup = "endgroup;"
-
- local linecaps = { butt = "butt", square = "squared", round = "rounded" }
- local linejoins = { miter = "mitered", bevel = "beveled", round = "rounded" }
-
- local function startlineproperties(at)
- local cap = at["stroke-linecap"]
- local join = at["stroke-linejoin"]
- local limit = at["stroke-miterlimit"]
- cap = cap and linecaps [cap]
- join = join and linejoins[join]
- limit = limit and asnumber_r(limit)
- if cap or join or limit then
- r = r + 1 ; result[r] = s_begingroup
- if cap then
- r = r + 1 ; result[r] = f_linecap(cap)
- end
- if join then
- r = r + 1 ; result[r] = f_linejoin(join)
- end
- if limit then
- r = r + 1 ; result[r] = f_miterlimit(limit)
- end
- return function()
- at["stroke-linecap"] = false
- at["stroke-linejoin"] = false
- at["stroke-miterlimit"] = false
- r = r + 1 ; result[r] = s_endgroup
- at["stroke-linecap"] = cap
- at["stroke-linejoin"] = join
- at["stroke-miterlimit"] = limit
- end
- end
- end
-
- -- markers are a quite rediculous thing .. let's assume simple usage for now
-
- function handlers.marker()
- -- todo: is just a def too
- end
-
- -- kind of local svg ... so make a generic one
- --
- -- todo: combine more (offset+scale+rotation)
-
- local function makemarker(where,c,x1,y1,x2,y2,x3,y3,parentat)
- local at = c.at
- local refx = rawget(at,"refX")
- local refy = rawget(at,"refY")
- local width = rawget(at,"markerWidth")
- local height = rawget(at,"markerHeight")
- local view = rawget(at,"viewBox")
- local orient = rawget(at,"orient")
- -- local ratio = rawget(at,"preserveAspectRatio")
- local units = asnumber(at["markerUnits"] or parentat["stroke-width"]) or 1
-
- local angx = 0
- local angy = 0
- local angle = 0
-
- if where == "beg" then
- if orient == "auto" then -- unchecked
- -- no angle
- angx = abs(x2 - x3)
- angy = abs(y2 - y3)
- elseif orient == "auto-start-reverse" then -- checked
- -- points to start
- angx = -abs(x2 - x3)
- angy = -abs(y2 - y3)
- elseif orient then -- unchecked
- angle = asnumber_r(orient)
- end
- elseif where == "end" then
- -- funny standard .. bug turned feature?
- if orient == "auto" or orient == "auto-start-reverse" then
- angx = abs(x1 - x2)
- angy = abs(y1 - y2)
- elseif orient then -- unchecked
- angle = asnumber_r(orient)
- end
- elseif orient then -- unchecked
- angle = asnumber_r(orient)
- end
- -- what wins: viewbox or w/h
-
- refx = asnumber_x(refx)
- refy = asnumber_y(refy)
-
- width = (width and asnumber_x(width) or 3) * units
- height = (height and asnumber_y(height) or 3) * units
-
- local x = 0
- local y = 0
- local w = width
- local h = height
-
- -- kind of like the main svg
-
- r = r + 1 ; result[r] = s_offset_start
-
- local wrapupviewport
--- todo : better viewbox code
- local xpct, ypct, rpct
- if view then
- x, y, w, h = handleviewbox(view)
- end
-
- if width ~= 0 then
- w = width
- end
- if height ~= 0 then
- h = height
- end
-
- if h then
- xpct = percentage_x
- ypct = percentage_y
- rpct = percentage_r
- percentage_x = w / 100
- percentage_y = h / 100
- percentage_r = (sqrt(w^2 + h^2) / sqrt(2)) / 100
- wrapupviewport = viewport(x,y,w,h,true,true) -- no clip
- end
-
- -- we can combine a lot here:
-
- local hasref = refx ~= 0 or refy ~= 0
- local hasrot = angx ~= 0 or angy ~= 0 or angle ~= 0
-
- local btransform, etransform, transform = handletransform(at)
-
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
-
- if hasrot then
- r = r + 1 ; result[r] = s_rotation_start
- end
-
- if hasref then
- r = r + 1 ; result[r] = s_offset_start
- end
-
- local _transform = transform
- at["transform"] = false
-
- handlers.g(c)
-
- at["transform"] = _transform
-
- if hasref then
- r = r + 1 ; result[r] = f_offset_stop(-refx,refy)
- end
-
- if hasrot then
- if angle ~= 0 then
- r = r + 1 ; result[r] = f_rotation_angle(angle)
- else
- r = r + 1 ; result[r] = f_rotation_stop(angx,angy)
- end
- end
-
- if etransform then
- r = r + 1 ; result[r] = etransform
- end
-
- if h then
- percentage_x = xpct
- percentage_y = ypct
- percentage_r = rpct
- if wrapupviewport then
- wrapupviewport()
- end
- end
- r = r + 1 ; result[r] = f_offset_stop(x2,y2)
-
- end
-
- -- do we need to metatable the attributes here?
-
- local function addmarkers(list,begmarker,midmarker,endmarker,at)
- local n = #list
- if n > 3 then
- if begmarker then
- local m = locate(begmarker)
- if m then
- makemarker("beg",m,false,false,list[1],list[2],list[3],list[4],at)
- end
- end
- if midmarker then
- local m = locate(midmarker)
- if m then
- for i=3,n-2,2 do
- makemarker("mid",m,list[i-2],list[i-1],list[i],list[i+1],list[i+2],list[i+3],at)
- end
- end
- end
- if endmarker then
- local m = locate(endmarker)
- if m then
- makemarker("end",m,list[n-3],list[n-2],list[n-1],list[n],false,false,at)
- end
- end
- else
- -- no line
- end
- end
-
- local function flush(shape,dofill,at,list,begmarker,midmarker,endmarker)
-
- local fill = dofill and (at["fill"] or "black")
- local stroke = at["stroke"] or "none"
-
- local btransform, etransform = handletransform(at)
- local cpath = handleclippath(at)
-
- if cpath then
- r = r + 1 ; result[r] = s_clip_start
- end
-
- local has_stroke = stroke and stroke ~= "none"
- local has_fill = fill and fill ~= "none"
-
- local bopacity, eopacity
- if has_stroke and has_fill then
- bopacity, eopacity = sharedopacity(at)
- end
-
- if bopacity then
- r = r + 1 ; result[r] = bopacity
- end
-
- if has_fill then
- local color, opacity = fillproperties(fill,at,not has_stroke)
- local f_xx_fill = at["fill-rule"] == "evenodd" and f_eo_fill or f_do_fill
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
- r = r + 1 result[r] = f_xx_fill(shape)
- if color then
- r = r + 1 ; result[r] = color
- end
- if opacity then
- r = r + 1 ; result[r] = opacity
- end
- r = r + 1 ; result[r] = etransform or ";"
- end
-
- if has_stroke then
- local wrapup = startlineproperties(at)
- local pen, dashing, color, opacity = drawproperties(stroke,at,not has_fill)
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
- r = r + 1 ; result[r] = f_do_draw(shape)
- if pen then
- r = r + 1 ; result[r] = pen
- end
- if dashing then
- r = r + 1 ; result[r] = dashing
- end
- if color then
- r = r + 1 ; result[r] = color
- end
- if opacity then
- r = r + 1 ; result[r] = opacity
- end
- r = r + 1 ; result[r] = etransform or ";"
- --
- if list then
- addmarkers(list,begmarker,midmarker,endmarker,at)
- end
- --
- if wrapup then
- wrapup()
- end
- end
-
- if eopacity then
- r = r + 1 ; result[r] = eopacity
- end
-
- if cpath then
- r = r + 1 ; result[r] = (cpath.evenodd and f_eoclip_stop or f_clip_stop)(cpath[1])
- end
-
- end
-
- local f_rectangle = formatters['unitsquare xyscaled (%N,%N) shifted (%N,%N)']
- local f_rounded = formatters['roundedsquarexy(%N,%N,%N,%N) shifted (%N,%N)']
- local f_line = formatters['((%N,%N)--(%N,%N))']
- local f_ellipse = formatters['(fullcircle xyscaled (%N,%N) shifted (%N,%N))']
- local f_circle = formatters['(fullcircle scaled %N shifted (%N,%N))']
-
- function handlers.line(c)
- local at = c.at
- local x1 = rawget(at,"x1")
- local y1 = rawget(at,"y1")
- local x2 = rawget(at,"x2")
- local y2 = rawget(at,"y2")
-
- x1 = x1 and asnumber_vx(x1) or 0
- y1 = y1 and asnumber_vy(y1) or 0
- x2 = x2 and asnumber_vx(x2) or 0
- y2 = y2 and asnumber_vy(y2) or 0
-
- flush(f_line(x1,y1,x2,y2),false,at)
- end
-
- function handlers.rect(c)
- local at = c.at
- local width = rawget(at,"width")
- local height = rawget(at,"height")
- local x = rawget(at,"x")
- local y = rawget(at,"y")
- local rx = rawget(at,"rx")
- local ry = rawget(at,"ry")
-
- width = width and asnumber_x(width) or 0
- height = height and asnumber_y(height) or 0
- x = x and asnumber_vx(x) or 0
- y = y and asnumber_vy(y) or 0
-
- y = y - height
-
- if rx then rx = asnumber(rx) end
- if ry then ry = asnumber(ry) end
-
- if rx or ry then
- if not rx then rx = ry end
- if not ry then ry = rx end
- flush(f_rounded(width,height,rx,ry,x,y),true,at)
- else
- flush(f_rectangle(width,height,x,y),true,at)
- end
- end
-
- function handlers.ellipse(c)
- local at = c.at
- local cx = rawget(at,"cx")
- local cy = rawget(at,"cy")
- local rx = rawget(at,"rx")
- local ry = rawget(at,"ry")
-
- cx = cx and asnumber_vx(cx) or 0
- cy = cy and asnumber_vy(cy) or 0
- rx = rx and asnumber_r (rx) or 0
- ry = ry and asnumber_r (ry) or 0
-
- flush(f_ellipse(2*rx,2*ry,cx,cy),true,at)
- end
-
- function handlers.circle(c)
- local at = c.at
- local cx = rawget(at,"cx")
- local cy = rawget(at,"cy")
- local r = rawget(at,"r")
-
- cx = cx and asnumber_vx(cx) or 0
- cy = cy and asnumber_vy(cy) or 0
- r = r and asnumber_r (r) or 0
-
- flush(f_circle(2*r,cx,cy),true,at)
- end
-
- local f_lineto_z = formatters['(%N,%N)']
- local f_lineto_n = formatters['--(%N,%N)']
-
- local p_pair = p_optseparator * p_number_vx * p_optseparator * p_number_vy
- local p_open = Cc("(")
- local p_close = Carg(1) * P(true) / function(s) return s end
- local p_polyline = Cs(p_open * (p_pair / f_lineto_z) * (p_pair / f_lineto_n)^0 * p_close)
- local p_polypair = Ct(p_pair^0)
-
- local function poly(c,final)
- local at = c.at
- local points = rawget(at,"points")
- if points then
- local path = lpegmatch(p_polyline,points,1,final)
- local list = nil
- local begmarker = rawget(at,"marker-start")
- local midmarker = rawget(at,"marker-mid")
- local endmarker = rawget(at,"marker-end")
- if begmarker or midmarker or endmarker then
- list = lpegmatch(p_polypair,points)
- end
- flush(path,true,at,list,begmarker,midmarker,endmarker)
- end
- end
-
- function handlers.polyline(c) poly(c, ")") end
- function handlers.polygon (c) poly(c,"--cycle)") end
-
- local s_image_start = "draw image ("
- local s_image_stop = ") ;"
-
- function handlers.path(c)
- local at = c.at
- local d = rawget(at,"d")
- if d then
- local shape, l = grabpath(d)
- local fill = at["fill"] or "black"
- local stroke = at["stroke"] or "none"
- local n = #shape
-
- local btransform, etransform = handletransform(at)
- local cpath = handleclippath(at)
- if cpath then
- r = r + 1 ; result[r] = s_clip_start
- end
-
- -- todo: image (nicer for transform too)
-
- if fill and fill ~= "none" then
- local color, opacity = fillproperties(fill,at)
- local f_xx_fill = at["fill-rule"] == "evenodd"
- if shape.closed then
- f_xx_fill = f_xx_fill and f_eo_fill or f_do_fill
- elseif shape.curve then
- f_xx_fill = f_xx_fill and f_eo_fill_c or f_do_fill_c
- else
- f_xx_fill = f_xx_fill and f_eo_fill_l or f_do_fill_l
- end
- if n == 1 then
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
- r = r + 1 result[r] = f_xx_fill(shape[1])
- if color then
- r = r + 1 ; result[r] = color
- end
- if opacity then
- r = r + 1 ; result[r] = opacity
- end
- r = r + 1 ; result[r] = etransform or ";"
- else
- r = r + 1 ; result[r] = btransform or s_image_start
- for i=1,n do
- if i == n then
- r = r + 1 ; result[r] = f_xx_fill(shape[i])
- if color then
- r = r + 1 ; result[r] = color
- end
- if opacity then
- r = r + 1 ; result[r] = opacity
- end
- else
- r = r + 1 ; result[r] = f_no_fill(shape[i])
- end
- r = r + 1 ; result[r] = ";"
- end
- r = r + 1 ; result[r] = etransform or s_image_stop
- end
- end
-
- if stroke and stroke ~= "none" then
- local begmarker = rawget(at,"marker-start")
- local midmarker = rawget(at,"marker-mid")
- local endmarker = rawget(at,"marker-end")
- if begmarker or midmarker or endmarker then
- list = grablist(l)
- end
- local wrapup = startlineproperties(at)
- local pen, dashing, color, opacity = drawproperties(stroke,at)
- if n == 1 and not list then
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
- r = r + 1 result[r] = f_do_draw(shape[1])
- if pen then
- r = r + 1 ; result[r] = pen
- end
- if dashing then
- r = r + 1 ; result[r] = dashing
- end
- if color then
- r = r + 1 ; result[r] = color
- end
- if opacity then
- r = r + 1 ; result[r] = opacity
- end
- r = r + 1 result[r] = etransform or ";"
- else
- r = r + 1 result[r] = btransform or "draw image ("
- for i=1,n do
- r = r + 1 result[r] = f_do_draw(shape[i])
- if pen then
- r = r + 1 ; result[r] = pen
- end
- if dashing then
- r = r + 1 ; result[r] = dashing
- end
- if color then
- r = r + 1 ; result[r] = color
- end
- if opacity then
- r = r + 1 ; result[r] = opacity
- end
- r = r + 1 ; result[r] = ";"
- end
- if list then
- addmarkers(list,begmarker,midmarker,endmarker,at)
- end
- r = r + 1 ; result[r] = etransform or ") ;"
- end
- if wrapup then
- wrapup()
- end
- end
-
- if cpath then
- r = r + 1 ; result[r] = f_clip_stop(cpath[1])
- end
-
- end
- end
-
- end
-
- -- kind of special
-
- do
-
- -- some day:
- --
- -- specification = identifiers.jpg(data."string")
- -- specification.data = data
- -- inclusion takes from data
- -- specification.data = false
-
- local f_image = formatters[ [[figure("%s") xysized (%N,%N) shifted (%N,%N)]] ]
-
- local nofimages = 0
-
- function handlers.image(c)
- local at = c.at
- local im = rawget(at,"xlink:href")
- if im then
- local kind, data = match(im,"^data:image/([a-z]+);base64,(.*)$")
- if kind == "png" then
- -- ok
- elseif kind == "jpeg" then
- kind = "jpg"
- else
- kind = false
- end
- if kind and data then
- local w = rawget(at,"width")
- local h = rawget(at,"height")
- local x = rawget(at,"x")
- local y = rawget(at,"y")
- w = w and asnumber_x(w)
- h = h and asnumber_y(h)
- x = x and asnumber_vx(x) or 0
- y = y and asnumber_vy(y) or 0
- nofimages = nofimages + 1
- local name = "temp-svg-image-" .. nofimages .. "." .. kind
- local data = mime.decode("base64")(data)
- io.savedata(name,data)
- if not w or not h then
- local info = graphics.identifiers[kind](data,"string")
- if info then
- -- todo: keep aspect ratio attribute
- local xsize = info.xsize
- local ysize = info.ysize
- if not w then
- if not h then
- w = xsize
- h = ysize
- else
- w = (h / ysize) * xsize
- end
- else
- h = (w / xsize) * ysize
- end
- end
- end
- -- safeguard:
- if not w then w = h or 1 end
- if not h then h = w or 1 end
- luatex.registertempfile(name)
- -- done:
- flushobject(f_image(name,w,h,x,y - h),at)
- else
- -- nothing done
- end
- end
- end
-
- end
-
- -- these transform: g a text svg symbol
-
- do
-
- function handlers.a(c)
- process(c,"/*")
- end
-
- function handlers.g(c) -- much like flushobject so better split and share
- local at = c.at
-
- local btransform, etransform, transform = handletransform(at)
- local cpath, clippath = handleclippath(at)
-
- if cpath then
- r = r + 1 ; result[r] = s_clip_start
- end
-
- if btransform then
- r= r + 1 result[r] = btransform
- end
-
- local _transform = transform
- local _clippath = clippath
- at["transform"] = false
- at["clip-path"] = false
-
- process(c,"/*")
-
- at["transform"] = _transform
- at["clip-path"] = _clippath
-
- if etransform then
- r = r + 1 ; result[r] = etransform
- end
-
- if cpath then
- local f_done = cpath.evenodd
- if cpath.curve then
- f_done = f_done and f_eoclip_stop_c or f_clip_stop_c
- else
- f_done = f_done and f_eoclip_stop_l or f_clip_stop_l
- end
- r = r + 1 ; result[r] = f_done(cpath[1])
- end
- end
-
- -- this will never really work out
- --
- -- todo: register text in lua in mapping with id, then draw mapping unless overloaded
- -- using lmt_svglabel with family,style,weight,size,id passed
-
- -- nested tspans are messy: they can have displacements but in inkscape we also
- -- see x and y (inner and outer element)
-
- -- The size is a bit of an issue. I assume that the specified size relates to the
- -- designsize but we want to be able to use other fonts.
-
- do
-
- local f_styled = formatters["\\svgstyled{%s}{%s}{%s}{%s}"]
- local f_colored = formatters["\\svgcolored{%.3N}{%.3N}{%.3N}{"]
- local f_placed = formatters["\\svgplaced{%.3N}{%.3N}{}{"]
- local f_poschar = formatters["\\svgposchar{%.3N}{%.3N}{%s}"]
- local f_char = formatters["\\svgchar{%s}"]
-
- local f_scaled = formatters["\\svgscaled{%N}{%s}{%s}{%s}"]
- local f_normal = formatters["\\svgnormal{%s}{%s}{%s}"]
- local f_hashed = formatters["\\svghashed{%s}"]
-
- -- We move to the outer (x,y) and when we have an inner offset we
- -- (need to) compensate for that outer offset.
-
- -- local f_text_scaled_svg = formatters['(svgtext("%s") scaled %N shifted (%N,%N))']
- -- local f_text_normal_svg = formatters['(svgtext("%s") shifted (%N,%N))']
- -- local f_text_simple_svg = formatters['svgtext("%s")']
-
- local anchors = {
- ["start"] = "drt",
- ["end"] = "dflt",
- ["middle"] = "d",
- }
-
- local f_text_normal_svg = formatters['(textext.%s("%s") shifted (%N,%N))']
- local f_text_simple_svg = formatters['textext.%s("%s")']
-
- -- or just maptext
-
- local f_mapped_normal_svg = formatters['(svgtext("%s") shifted (%N,%N))']
- local f_mapped_simple_svg = formatters['svgtext("%s")']
-
- local cssfamily = css.family
- local cssstyle = css.style
- local cssweight = css.weight
- local csssize = css.size
-
- local usedfonts = setmetatableindex(function(t,k)
- local v = setmetatableindex("table")
- t[k] = v
- return v
- end)
-
- local p_texescape = lpegpatterns.texescape
-
- -- For now as I need it for my (some 1500) test files.
-
- local function checkedfamily(name)
- if find(name,"^.-verdana.-$") then
- name = "verdana"
- end
- return name
- end
-
- -- todo: only escape some chars and handle space
-
- local defaultsize = 10
-
- local function collect(t,c,x,y,size,scale,family,tx,ty)
- local at = c.at
- local ax = rawget(at,"x")
- local ay = rawget(at,"y")
- local dx = rawget(at,"dx")
- local dy = rawget(at,"dy")
- local v_fill = at["fill"]
- local v_family = at["font-family"]
- local v_style = at["font-style"]
- local v_weight = at["font-weight"]
- local v_size = at["font-size"]
- --
- ax = ax and asnumber_vx(ax) or x
- ay = ay and asnumber_vy(ay) or y
- dx = dx and asnumber_vx(dx) or 0
- dy = dy and asnumber_vy(dy) or 0
- --
- if v_family then v_family = cssfamily(v_family) end
- if v_style then v_style = cssstyle (v_style) end
- if v_weight then v_weight = cssweight(v_weight) end
- if v_size then v_size = csssize (v_size,factors) end
- --
- ax = ax - x
- ay = ay - y
- --
- local elayered = ax ~= 0 or ay ~= 0 or false
- local eplaced = dx ~= 0 or dy ~= 0 or false
-
- local usedsize, usedscaled
-
- if elayered then
- -- we're now at the outer level again so we need to scale
- -- back to the outer level values
- t[#t+1] = formatters["\\svgsetlayer{%0N}{%0N}{"](ax,-ay)
- usedsize = v_size or defaultsize
- usedscale = usedsize / defaultsize
- else
- -- we're nested so we can be scaled
- usedsize = v_size or size
- usedscale = (usedsize / defaultsize) / scale
- end
- --
- -- print("element ",c.tg)
- -- print(" layered ",elayered)
- -- print(" font size ",v_size)
- -- print(" parent size ",size)
- -- print(" parent scale",scale)
- -- print(" used size ",usedsize)
- -- print(" used scale ",usedscale)
- --
- if eplaced then
- t[#t+1] = f_placed(dx,dy)
- end
- --
- if not v_family then v_family = family end
- if not v_weight then v_weight = "normal" end
- if not v_style then v_style = "normal" end
- --
- if v_family then
- v_family = fonts.names.cleanname(v_family)
- v_family = checkedfamily(v_family)
- end
- --
- usedfonts[v_family][v_weight][v_style] = true
- --
--- if usedscale == 1 then
--- t[#t+1] = f_normal( v_family,v_weight,v_style)
--- else
- t[#t+1] = f_scaled(usedscale,v_family,v_weight,v_style)
--- end
- t[#t+1] = "{"
- --
- local ecolored = v_fill and v_fill ~= "" or false
- if ecolored then
- -- todo cmyk
- local r, g, b = colorcomponents(v_fill)
- if r and g and b then
- t[#t+1] = f_colored(r,g,b)
- else
- ecolored = false
- end
- end
- --
- local dt = c.dt
- local nt = #dt
- for i=1,nt do
- local di = dt[i]
- if type(di) == "table" then
- -- can be a tspan (should we pass dx too)
- collect(t,di,x,y,usedsize,usedscale,v_family)
- else
- if i == 1 then
- di = gsub(di,"^%s+","")
- end
- if i == nt then
- di = gsub(di,"%s+$","")
- end
- local chars = utfsplit(di)
- if svghash then
- di = f_hashed(svghash[di])
- elseif tx then
- for i=1,#chars do
- chars[i] = f_poschar(
- (tx[i] or 0) - x,
- (ty[i] or 0) - y,
- utfbyte(chars[i])
- )
- end
- di = "{" .. concat(chars) .. "}"
- else
- -- this needs to be texescaped ! and even quotes and newlines
- -- or we could register it but that's a bit tricky as we nest
- -- and don't know what we can expect here
- -- di = lpegmatch(p_texescape,di) or di
- for i=1,#chars do
- chars[i] = f_char(utfbyte(chars[i]))
- end
- di = concat(chars)
- end
- t[#t+1] = di
- end
- end
- --
- if ecolored then
- t[#t+1] = "}"
- end
- --
- t[#t+1] = "}"
- --
- if eplaced then
- t[#t+1] = "}"
- end
- if elayered then
- t[#t+1] = "}"
- end
- --
- return t
- end
-
- local s_startlayer = "\\svgstartlayer "
- local s_stoplayer = "\\svgstoplayer "
-
- function handlers.text(c)
- local only = fullstrip(xmltextonly(c))
- -- if metapost.processing() then
- local at = c.at
- local x = rawget(at,"x")
- local y = rawget(at,"y")
-
- local tx = asnumber_vx_t(x)
- local ty = asnumber_vy_t(y)
-
- x = tx[1] or 0 -- catch bad x/y spec
- y = ty[1] or 0 -- catch bad x/y spec
-
- local v_fill = at["fill"]
- if not v_fill or v_fill == "none" then
- v_fill = "black"
- end
- local color, opacity, invisible = fillproperties(v_fill,at)
- local anchor = anchors[at["text-anchor"] or "start"] or "drt"
- local r = metapost.remappedtext(only)
- if r then
- if x == 0 and y == 0 then
- only = f_mapped_simple_svg(r.index)
- else
- only = f_mapped_normal_svg(r.index,x,y)
- end
- flushobject(only,at,color,opacity)
- if trace_text then
- report("text: %s",only)
- end
- elseif not invisible then -- can be an option
- local scale = 1
- local textid = 0
- local result = { }
- local nx = #tx
- local ny = #ty
- --
- result[#result+1] = s_startlayer
- if nx > 1 or ny > 1 then
- concat(collect(result,c,x,y,defaultsize,1,"serif",tx,ty))
- else
- concat(collect(result,c,x,y,defaultsize,1,"serif"))
- end
- result[#result+1] = s_stoplayer
- result = concat(result)
- if x == 0 and y == 0 then
- result = f_text_simple_svg(anchor,result)
- else
- result = f_text_normal_svg(anchor,result,x,y)
- end
- flushobject(result,at,color,opacity)
- if trace_text then
- report("text: %s",result)
- end
- elseif trace_text then
- report("invisible text: %s",only)
- end
- -- elseif trace_text then
- -- report("ignored text: %s",only)
- -- end
- end
-
- function metapost.reportsvgfonts()
- for family, weights in sortedhash(usedfonts) do
- for weight, styles in sortedhash(weights) do
- for style in sortedhash(styles) do
- report("used font: %s-%s-%s",family,weight,style)
- end
- end
- end
- end
-
- statistics.register("used svg fonts",function()
- if next(usedfonts) then
- -- also in log file
- logs.startfilelogging(report,"used svg fonts")
- local t = { }
- for family, weights in sortedhash(usedfonts) do
- for weight, styles in sortedhash(weights) do
- for style in sortedhash(styles) do
- report("%s-%s-%s",family,weight,style)
- t[#t+1] = formatters["%s-%s-%s"](family,weight,style)
- end
- end
- end
- logs.stopfilelogging()
- return concat(t," ")
- end
- end)
-
- end
-
- function handlers.svg(c,x,y,w,h,noclip,notransform,normalize)
- local at = c.at
-
- local wrapupviewport
- local bhacked
- local ehacked
- local wd = w
- -- local ex, em
- local xpct, ypct, rpct
-
- local btransform, etransform, transform = handletransform(at)
-
- if trace then
- report("view: %s, xpct %N, ypct %N","before",percentage_x,percentage_y)
- end
-
- local viewbox = at.viewBox
-
- if viewbox then
- x, y, w, h = handleviewbox(viewbox)
- if trace then
- report("viewbox: x %N, y %N, width %N, height %N",x,y,w,h)
- end
- end
- if not w or not h or w == 0 or h == 0 then
- noclip = true
- end
- if h then
- --
- -- em = factors["em"]
- -- ex = factors["ex"]
- -- factors["em"] = em
- -- factors["ex"] = ex
- --
- xpct = percentage_x
- ypct = percentage_y
- rpct = percentage_r
- percentage_x = w / 100
- percentage_y = h / 100
- percentage_r = (sqrt(w^2 + h^2) / sqrt(2)) / 100
- if trace then
- report("view: %s, xpct %N, ypct %N","inside",percentage_x,percentage_y)
- end
- wrapupviewport = viewport(x,y,w,h,noclip)
- end
- -- todo: combine transform and offset here
-
- -- some fonts need this (bad transforms + viewbox)
- if v and normalize and w and wd and w ~= wd and w > 0 and wd > 0 then
- bhacked = s_wrapped_start
- ehacked = f_wrapped_stop(y or 0,wd/w)
- end
- if btransform then
- r = r + 1 ; result[r] = btransform
- end
- if bhacked then
- r = r + 1 ; result[r] = bhacked
- end
- local boffset, eoffset = offset(at)
- if boffset then
- r = r + 1 result[r] = boffset
- end
-
- at["transform"] = false
- at["viewBox"] = false
-
- process(c,"/*")
-
- at["transform"] = transform
- at["viewBox"] = viewbox
-
- if eoffset then
- r = r + 1 result[r] = eoffset
- end
- if ehacked then
- r = r + 1 ; result[r] = ehacked
- end
- if etransform then
- r = r + 1 ; result[r] = etransform
- end
- if h then
- --
- -- factors["em"] = em
- -- factors["ex"] = ex
- --
- percentage_x = xpct
- percentage_y = ypct
- percentage_r = rpct
- if wrapupviewport then
- wrapupviewport()
- end
- end
- if trace then
- report("view: %s, xpct %N, ypct %N","after",percentage_x,percentage_y)
- end
- end
-
- end
-
- process = function(x,p)
- for c in xmlcollected(x,p) do
- local tg = c.tg
- local h = handlers[c.tg]
- if h then
- h(c)
- end
- end
- end
-
- -- For huge inefficient files there can be lots of garbage to collect so
- -- maybe we should run the collector when a file is larger than say 50K.
-
- function metapost.svgtomp(specification,pattern,notransform,normalize)
- local mps = ""
- local svg = specification.data
- if type(svg) == "string" then
- svg = xmlconvert(svg)
- end
- if svg then
- local c = xmlfirst(svg,pattern or "/svg")
- if c then
- root = svg
- result = { }
- r = 0
- definitions = { }
- tagstyles = { }
- classstyles = { }
- colormap = specification.colormap
- usedcolors = trace_colors and setmetatableindex("number") or false
- for s in xmlcollected(c,"style") do -- can also be in a def, so let's play safe
- handlestyle(c)
- end
- handlechains(c)
- xmlinheritattributes(c) -- put this in handlechains
- handlers.svg (
- c,
- specification.x,
- specification.y,
- specification.width,
- specification.height,
- specification.noclip,
- notransform,
- normalize,
- specification.remap
- )
- if trace_result then
- report("result graphic:\n %\n t",result)
- end
- if usedcolors and next(usedcolors) then
- report("graphic %a uses colors: %s",specification.id or "unknown",table.sequenced(usedcolors))
- end
- mps = concat(result," ")
- root = false
- result = false
- r = false
- definitions = false
- tagstyles = false
- classstyles = false
- colormap = false
- else
- report("missing svg root element")
- end
- else
- report("bad svg blob")
- end
- return mps
- end
-
-end
-
--- These helpers might move to their own module .. some day ... also they will become
--- a bit more efficient, because we now go to mp and back which is kind of redundant,
--- but for now it will do.
-
-do
-
- local bpfactor = number.dimenfactors.bp
-
- function metapost.includesvgfile(filename,offset) -- offset in sp
- if lfs.isfile(filename) then
- context.startMPcode("doublefun")
- context('draw lmt_svg [ filename = "%s", offset = %N ] ;',filename,(offset or 0)*bpfactor)
- context.stopMPcode()
- end
- end
-
- function metapost.includesvgbuffer(name,offset) -- offset in sp
- context.startMPcode("doublefun")
- context('draw lmt_svg [ buffer = "%s", offset = %N ] ;',name or "",(offset or 0)*bpfactor)
- context.stopMPcode()
- end
-
- interfaces.implement {
- name = "includesvgfile",
- actions = metapost.includesvgfile,
- arguments = { "string", "dimension" },
- }
-
- interfaces.implement {
- name = "includesvgbuffer",
- actions = metapost.includesvgbuffer,
- arguments = { "string", "dimension" },
- }
-
- function metapost.showsvgpage(data)
- local dd = data.data
- if not dd then
- local fn = data.filename
- dd = fn and table.load(fn)
- end
- if type(dd) == "table" then
- local comment = data.comment
- local offset = data.pageoffset
- local index = data.index
- local first = math.max(index or 1,1)
- local last = math.min(index or #dd,#dd)
- for i=first,last do
- local d = setmetatableindex( {
- data = dd[i],
- comment = comment and i or false,
- pageoffset = offset or nil,
- }, data)
- metapost.showsvgpage(d)
- end
- elseif data.method == "code" then
- context.startMPcode(doublefun)
- context(metapost.svgtomp(data))
- context.stopMPcode()
- else
- context.startMPpage { instance = "doublefun", offset = data.pageoffset or nil }
- context(metapost.svgtomp(data))
- local comment = data.comment
- if comment then
- context("draw boundingbox currentpicture withcolor .6red ;")
- context('draw textext.bot("\\strut\\tttf %s") ysized (10pt) shifted center bottomboundary currentpicture ;',comment)
- end
- context.stopMPpage()
- end
- end
-
- function metapost.typesvgpage(data)
- local dd = data.data
- if not dd then
- local fn = data.filename
- dd = fn and table.load(fn)
- end
- if type(dd) == "table" then
- local index = data.index
- if index and index > 0 and index <= #dd then
- data = dd[index]
- else
- data = nil
- end
- end
- if type(data) == "string" and data ~= "" then
- buffers.assign("svgpage",data)
- context.typebuffer ({ "svgpage" }, { option = "XML", strip = "yes" })
- end
- end
-
- function metapost.svgtopdf(data,...)
- local mps = metapost.svgtomp(data,...)
- if mps then
- -- todo: special instance, only basics needed
- local pdf = metapost.simple("metafun",mps,true,false,"svg")
- if pdf then
- return pdf
- else
- -- message
- end
- else
- -- message
- end
- end
-
-end
-
-do
-
- local runner = sandbox.registerrunner {
- name = "otfsvg2pdf",
- program = "context",
- template = "--batchmode --purgeall --runs=2 %filename%",
- reporter = report_svg,
- }
-
- -- By using an independent pdf file instead of pdf streams we can use resources and still
- -- cache. This is the old method updated. Maybe a future version will just do this runtime
- -- but for now this is the most efficient method.
-
- local decompress = gzip.decompress
- local compress = gzip.compress
-
- function metapost.svgshapestopdf(svgshapes,pdftarget,report_svg)
- local texname = "temp-otf-svg-to-pdf.tex"
- local pdfname = "temp-otf-svg-to-pdf.pdf"
- local tucname = "temp-otf-svg-to-pdf.tuc"
- local nofshapes = #svgshapes
- local pdfpages = { filename = pdftarget }
- local pdfpage = 0
- local t = { }
- local n = 0
- --
- os.remove(texname)
- os.remove(pdfname)
- os.remove(tucname)
- --
- if report_svg then
- report_svg("processing %i svg containers",nofshapes)
- statistics.starttiming(pdfpages)
- end
- --
- -- can be option:
- --
- -- n = n + 1 ; t[n] = "\\nopdfcompression"
- --
- n = n + 1 ; t[n] = "\\starttext"
- n = n + 1 ; t[n] = "\\setupMPpage[alternative=offset,instance=doublefun]"
- --
- for i=1,nofshapes do
- local entry = svgshapes[i]
- local data = entry.data
- if decompress then
- data = decompress(data) or data
- end
- local specification = {
- data = xmlconvert(data),
- x = 0,
- y = 1000,
- width = 1000,
- height = 1000,
- noclip = true,
- }
- for index=entry.first,entry.last do
- if not pdfpages[index] then
- pdfpage = pdfpage + 1
- pdfpages[index] = pdfpage
- local pattern = "/svg[@id='glyph" .. index .. "']"
- n = n + 1 ; t[n] = "\\startMPpage"
- n = n + 1 ; t[n] = metapost.svgtomp(specification,pattern,true,true) or ""
- n = n + 1 ; t[n] = "\\stopMPpage"
- end
- end
- end
- n = n + 1 ; t[n] = "\\stoptext"
- io.savedata(texname,concat(t,"\n"))
- runner { filename = texname }
- os.remove(pdftarget)
- file.copy(pdfname,pdftarget)
- if report_svg then
- statistics.stoptiming(pdfpages)
- report_svg("svg conversion time %s",statistics.elapsedseconds(pdfpages))
- end
- os.remove(texname)
- os.remove(pdfname)
- os.remove(tucname)
- return pdfpages
- end
-
- function metapost.svgshapestomp(svgshapes,report_svg)
- local nofshapes = #svgshapes
- local mpshapes = { }
- if report_svg then
- report_svg("processing %i svg containers",nofshapes)
- statistics.starttiming(mpshapes)
- end
- for i=1,nofshapes do
- local entry = svgshapes[i]
- local data = entry.data
- if decompress then
- data = decompress(data) or data
- end
- local specification = {
- data = xmlconvert(data),
- x = 0,
- y = 1000,
- width = 1000,
- height = 1000,
- noclip = true,
- }
- for index=entry.first,entry.last do
- if not mpshapes[index] then
- local pattern = "/svg[@id='glyph" .. index .. "']"
- local mpcode = metapost.svgtomp(specification,pattern,true,true) or ""
- if mpcode ~= "" and compress then
- mpcode = compress(mpcode) or mpcode
- end
- mpshapes[index] = mpcode
- end
- end
- end
- if report_svg then
- statistics.stoptiming(mpshapes)
- report_svg("svg conversion time %s",statistics.elapsedseconds(mpshapes))
- end
- return mpshapes
- end
-
- function metapost.svgglyphtomp(fontname,unicode)
- if fontname and unicode then
- local id = fonts.definers.internal { name = fontname }
- if id then
- local tfmdata = fonts.hashes.identifiers[id]
- if tfmdata then
- local properties = tfmdata.properties
- local svg = properties.svg
- local hash = svg and svg.hash
- local timestamp = svg and svg.timestamp
- if hash then
- local svgfile = containers.read(fonts.handlers.otf.svgcache,hash)
- local svgshapes = svgfile and svgfile.svgshapes
- if svgshapes then
- if type(unicode) == "string" then
- unicode = utfbyte(unicode)
- end
- local chardata = tfmdata.characters[unicode]
- local index = chardata and chardata.index
- if index then
- for i=1,#svgshapes do
- local entry = svgshapes[i]
- if index >= entry.first and index <= entry.last then
- local data = entry.data
- if data then
- local root = xml.convert(gzip.decompress(data) or data)
- return metapost.svgtomp (
- {
- data = root,
- x = 0,
- y = 1000,
- width = 1000,
- height = 1000,
- noclip = true,
- },
- "/svg[@id='glyph" .. index .. "']",
- true,
- true
- )
- end
- end
- end
- end
- end
- end
- end
- end
- end
- end
-
-end