diff options
Diffstat (limited to 'tex/context/base/mlib-pdf.lua')
-rw-r--r-- | tex/context/base/mlib-pdf.lua | 268 |
1 files changed, 152 insertions, 116 deletions
diff --git a/tex/context/base/mlib-pdf.lua b/tex/context/base/mlib-pdf.lua index 344052607..e8abf7087 100644 --- a/tex/context/base/mlib-pdf.lua +++ b/tex/context/base/mlib-pdf.lua @@ -13,12 +13,35 @@ local abs, sqrt, round = math.abs, math.sqrt, math.round metapost = metapost or { } metapost.multipass = false metapost.n = 0 +metapost.optimize = true -- false + +--~ Because in MKiV we always have two passes, we save the objects. When an extra +--~ mp run is done (due to for instance texts identifier in the parse pass), we +--~ get a new result table and the stored objects are forgotten. Otherwise they +--~ are reused. + +local function getobjects(result,figure,f) + if metapost.optimize then + local objects = result.objects + if not objects then + result.objects = { } + end + objects = result.objects[f] + if not objects then + objects = figure:objects() + result.objects[f] = objects + end + return objects + else + return figure:objects() + end +end function metapost.convert(result, trialrun, flusher, multipass) if trialrun then metapost.multipass = false metapost.parse(result, flusher) - if multipass and not metapost.multipass then + if multipass and not metapost.multipass and metapost.optimize then metapost.flush(result, flusher) -- saves a run else return false @@ -60,107 +83,20 @@ function metapost.flushers.pdf.textfigure(font,size,text,width,height,depth) -- sprint(tex.ctxcatcodes,format("\\MPLIBtextext{%s}{%s}{%s}{%s}{%s}",font,size,text,0,-number.dimenfactors.bp*depth)) end --- the pen calculations are taken from metapost, first converted by --- taco from c to lua, and then optimized by hans, so all errors are his - -local function pyth(a,b) - return sqrt(a*a + b*b) -- much faster than sqrt(a^2 + b^2) -end - -local aspect_bound = 10/65536 -local aspect_default = 1/65536 local bend_tolerance = 131/65536 -local eps = 0.0001 - -local function coord_range_x(h, dz) -- direction x - local zlo, zhi = 0, 0 - for i=1, #h do - local p = h[i] - local z = p.x_coord - if z < zlo then zlo = z elseif z > zhi then zhi = z end - z = p.right_x - if z < zlo then zlo = z elseif z > zhi then zhi = z end - z = p.left_x - if z < zlo then zlo = z elseif z > zhi then zhi = z end - end - return (zhi - zlo <= dz and aspect_bound) or aspect_default -end - -local function coord_range_y(h, dz) -- direction y - local zlo, zhi = 0, 0 - for i=1, #h do - local p = h[i] - local z = p.y_coord - if z < zlo then zlo = z elseif z > zhi then zhi = z end - z = p.right_y - if z < zlo then zlo = z elseif z > zhi then zhi = z end - z = p.left_y - if z < zlo then zlo = z elseif z > zhi then zhi = z end - end - return (zhi - zlo <= dz and aspect_bound) or aspect_default -end local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 local function pen_characteristics(object) - local p = object.pen[1] - local x_coord, y_coord, left_x, left_y, right_x, right_y = p.x_coord, p.y_coord, p.left_x, p.left_y, p.right_x, p.right_y - local wx, wy, width - if right_x == x_coord and left_y == y_coord then - wx = abs(left_x - x_coord) - wy = abs(right_y - y_coord) - else - wx = pyth(left_x - x_coord, right_x - x_coord) - wy = pyth(left_y - y_coord, right_y - y_coord) - end - if wy/coord_range_x(object.path, wx) >= wx/coord_range_y(object.path, wy) then - width = wy - else - width = wx - end - sx, rx, ry, sy, tx, ty = left_x, left_y, right_x, right_y, x_coord, y_coord - sx, rx, ry, sy = (sx-tx), (rx-ty), (ry-tx), (sy-ty) -- combine with previous - if width ~= 1 then - if width == 0 then - sx, sy = 1, 1 - else - rx, ry, sx, sy = rx/width, ry/width, sx/width, sy/width - end - end - -- sx rx ry sy tx ty -> 1 0 0 1 0 0 is ok, but 0 0 0 0 0 0 not - if true then - if abs(sx) < eps then sx = eps end - if abs(sy) < eps then sy = eps end + if mplib.pen_info then + local t = mplib.pen_info(object) + rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty + divider = sx*sy - rx*ry + return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width else - -- this block looks complicated but it only captures invalid transforms - -- to be checked rx vs sx and so - local det = sx/sy - ry/rx - local aspect = 4*aspect_bound + aspect_default - if abs(det) < aspect then - local s - if det >= 0 then - s, aspect = 1, aspect - det - else - s, aspect = -1, -aspect - det -- - ? - end - local absrx, absry, abssy, abssx = abs(rx), abs(ry), abs(sy), abs(sx) - if abssx + abssy >= absry + absrx then -- was yy - if abssx > abssy then - sy = sy + (aspect + s*abssx) / sx - else - sx = sx + (aspect + s*abssy) / sy - end - else - if absry > absrx then - rx = rx + (aspect + s*absry) / ry - else - ry = ry + (aspect + s*absrx) / rx - end - end - end + rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 + return false, 1 end - divider = sx*sy - rx*ry - return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), width end local function concat(px, py) -- no tx, ty here @@ -196,8 +132,12 @@ local function flushnormalpath(path, t, open) if curved(pth,one) then t[#t+1] = format("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) else - t[#t+1] = format("%f %f l",one.x_coord,one.y_coord) + t[#t+1] = format("%f %f l",one.x_coord,one.y_coord) end + elseif #path == 1 then + -- special case .. draw point + local one = path[1] + t[#t+1] = format("%f %f l",one.x_coord,one.y_coord) end return t end @@ -225,8 +165,12 @@ local function flushconcatpath(path, t, open) local c, d = concat(one.left_x,one.left_y) t[#t+1] = format("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord)) else - t[#t+1] = format("%f %f l",concat(one.x_coord,one.y_coord)) + t[#t+1] = format("%f %f l",concat(one.x_coord,one.y_coord)) end + elseif #path == 1 then + -- special case .. draw point + local one = path[1] + t[#t+1] = format("%f %f l",concat(one.x_coord,one.y_coord)) end return t end @@ -247,23 +191,18 @@ function metapost.flush(result,flusher) -- pdf flusher, table en dan concat is s local colorhandler = metapost.colorhandler for f=1, #figures do local figure = figures[f] ---~ local objects = figure:objects() -local objects = result.objects -if not objects then - objects = figure:objects() - result.objects = objects -end - local fignum = tonumber((figure:filename()):match("([%d]+)$") or 0) + local objects = getobjects(result,figure,f) + local fignum = tonumber((figure:filename()):match("([%d]+)$") or figure:charcode() or 0) local t = { } local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false local bbox = figure:boundingbox() local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4] -- faster than unpack if urx < llx then -- invalid - flusher.startfigure(fignum,0,0,0,0,"invalid") + flusher.startfigure(fignum,0,0,0,0,"invalid",figure) flusher.stopfigure() else - flusher.startfigure(fignum,llx,lly,urx,ury,"begin") + flusher.startfigure(fignum,llx,lly,urx,ury,"begin",figure) t[#t+1] = "q" if objects then for o=1,#objects do @@ -328,7 +267,7 @@ end t[#t+1], cr = colorconverter(cs) end -- - if before then object, t = before() end + if before then currentobject, t = before() end local ml = currentobject.miterlimit if ml and ml ~= miterlimit then miterlimit = ml @@ -355,13 +294,13 @@ end t[#t+1] = "[] 0 d" dashed = false end - if inbetween then object, t = inbetween() end + if inbetween then currentobject, t = inbetween() end local path = currentobject.path local transformed, penwidth = false, 1 local open = path and path[1].left_type and path[#path].right_type -- at this moment only "end_point" local pen = currentobject.pen if pen then - if pen.type=='elliptical' then + if pen.type == 'elliptical' then transformed, penwidth = pen_characteristics(object) -- boolean, value t[#t+1] = format("%f w",penwidth) -- todo: only if changed if objecttype == 'fill' then @@ -415,7 +354,7 @@ end if cr then t[#t+1] = cr end - if after then object, t = after() end + if after then currentobject, t = after() end end end end @@ -434,12 +373,7 @@ function metapost.parse(result) if figures then for f=1, #figures do local figure = figures[f] ---~ local objects = figure:objects() -local objects = result.objects -if not objects then - objects = figure:objects() - result.objects = objects -end + local objects = getobjects(result,figure,f) if objects then for o=1,#objects do local object = objects[o] @@ -524,3 +458,105 @@ function metapost.colorconverter() end end end + +--~ -- obsolete code +--~ +--~ -- the pen calculations are taken from metapost, first converted by +--~ -- taco from c to lua, and then optimized by hans, so all errors are his +--~ +--~ local aspect_bound = 10/65536 +--~ local aspect_default = 1/65536 +--~ local eps = 0.0001 +--~ +--~ local function pyth(a,b) +--~ return sqrt(a*a + b*b) -- much faster than sqrt(a^2 + b^2) +--~ end +--~ +--~ local function coord_range_x(h, dz) -- direction x +--~ local zlo, zhi = 0, 0 +--~ for i=1, #h do +--~ local p = h[i] +--~ local z = p.x_coord +--~ if z < zlo then zlo = z elseif z > zhi then zhi = z end +--~ z = p.right_x +--~ if z < zlo then zlo = z elseif z > zhi then zhi = z end +--~ z = p.left_x +--~ if z < zlo then zlo = z elseif z > zhi then zhi = z end +--~ end +--~ return (zhi - zlo <= dz and aspect_bound) or aspect_default +--~ end +--~ +--~ local function coord_range_y(h, dz) -- direction y +--~ local zlo, zhi = 0, 0 +--~ for i=1, #h do +--~ local p = h[i] +--~ local z = p.y_coord +--~ if z < zlo then zlo = z elseif z > zhi then zhi = z end +--~ z = p.right_y +--~ if z < zlo then zlo = z elseif z > zhi then zhi = z end +--~ z = p.left_y +--~ if z < zlo then zlo = z elseif z > zhi then zhi = z end +--~ end +--~ return (zhi - zlo <= dz and aspect_bound) or aspect_default +--~ end +--~ +--~ local function pen_characteristics(object) +--~ local p = object.pen[1] +--~ local x_coord, y_coord, left_x, left_y, right_x, right_y = p.x_coord, p.y_coord, p.left_x, p.left_y, p.right_x, p.right_y +--~ local wx, wy, width +--~ if right_x == x_coord and left_y == y_coord then +--~ wx = abs(left_x - x_coord) +--~ wy = abs(right_y - y_coord) +--~ else +--~ wx = pyth(left_x - x_coord, right_x - x_coord) +--~ wy = pyth(left_y - y_coord, right_y - y_coord) +--~ end +--~ if wy/coord_range_x(object.path, wx) >= wx/coord_range_y(object.path, wy) then +--~ width = wy +--~ else +--~ width = wx +--~ end +--~ sx, rx, ry, sy, tx, ty = left_x, left_y, right_x, right_y, x_coord, y_coord +--~ sx, rx, ry, sy = (sx-tx), (rx-ty), (ry-tx), (sy-ty) -- combine with previous +--~ if width ~= 1 then +--~ if width == 0 then +--~ sx, sy = 1, 1 +--~ else +--~ rx, ry, sx, sy = rx/width, ry/width, sx/width, sy/width +--~ end +--~ end +--~ -- sx rx ry sy tx ty -> 1 0 0 1 0 0 is ok, but 0 0 0 0 0 0 not +--~ if true then +--~ if abs(sx) < eps then sx = eps end +--~ if abs(sy) < eps then sy = eps end +--~ else +--~ -- this block looks complicated but it only captures invalid transforms +--~ -- to be checked rx vs sx and so +--~ local det = sx/sy - ry/rx +--~ local aspect = 4*aspect_bound + aspect_default +--~ if abs(det) < aspect then +--~ local s +--~ if det >= 0 then +--~ s, aspect = 1, aspect - det +--~ else +--~ s, aspect = -1, -aspect - det -- - ? +--~ end +--~ local absrx, absry, abssy, abssx = abs(rx), abs(ry), abs(sy), abs(sx) +--~ if abssx + abssy >= absry + absrx then -- was yy +--~ if abssx > abssy then +--~ sy = sy + (aspect + s*abssx) / sx +--~ else +--~ sx = sx + (aspect + s*abssy) / sy +--~ end +--~ else +--~ if absry > absrx then +--~ rx = rx + (aspect + s*absry) / ry +--~ else +--~ ry = ry + (aspect + s*absrx) / rx +--~ end +--~ end +--~ end +--~ end +--~ divider = sx*sy - rx*ry +--~ return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), width +--~ end |