From 99403d9f58e59544100f624e73684c27c7725d7a Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 30 Dec 2021 21:19:07 +0000 Subject: luapstricks (30dec21) git-svn-id: svn://tug.org/texlive/trunk@61446 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/doc/lualatex/luapstricks/README.md | 2 +- .../tex/lualatex/luapstricks/luapstricks.lua | 208 ++++++++++++++++++--- 2 files changed, 188 insertions(+), 22 deletions(-) (limited to 'Master/texmf-dist') diff --git a/Master/texmf-dist/doc/lualatex/luapstricks/README.md b/Master/texmf-dist/doc/lualatex/luapstricks/README.md index d7c32789835..69a01740570 100644 --- a/Master/texmf-dist/doc/lualatex/luapstricks/README.md +++ b/Master/texmf-dist/doc/lualatex/luapstricks/README.md @@ -8,7 +8,7 @@ Some advanced features require the `pdfmanagement-testphase` package to be loade 1. Make sure that you have the latest pstricks version installed. 2. Run `l3build install` *or* copy `luapstricks.lua` from this repository into the directory of your TeX files or another directory in your TeX search path. 3. Compile your document with `lualatex`. - 4. Given the current state of development of this project, the previous stage probably failed. Write a bug report to the author. Otherwise go to step 7. + 4. Given that most people immediately stop reading after they get a result and you are still here, the previous stage probably failed. Write a bug report to the author. Otherwise go to step 7. 5. Wait for a fix. 6. Go back to step 2 and try again. 7. It worked? That's great. Feel free to inform the author anyway and share the awesome images you created. diff --git a/Master/texmf-dist/tex/lualatex/luapstricks/luapstricks.lua b/Master/texmf-dist/tex/lualatex/luapstricks/luapstricks.lua index 7da1c9c1339..e505f5458aa 100644 --- a/Master/texmf-dist/tex/lualatex/luapstricks/luapstricks.lua +++ b/Master/texmf-dist/tex/lualatex/luapstricks/luapstricks.lua @@ -18,8 +18,8 @@ if luatexbase then luatexbase.provides_module { name = 'luapstricks', - version = 'v0.3', - date = '2021-11-12', + version = 'v0.4', + date = '2021-12-29', description = 'PSTricks backend for LuaLaTeX', } end @@ -502,8 +502,16 @@ end local execute_ps, execute_tok local dictionary_stack + +-- About the bbox entry: +-- - If the bounding box is not currently tracked, it is set to nil +-- - Otherwise it's a linked list linked with the .next field. Every entry is a "matrix level" +-- - if .bbox[1] is nil, the current matrix level does not have a set bounding box yet +-- - Otherside it's {min_x, min_y, max_x, max_y} +-- - If a .bbox.matrix entry is present then it describes the matrix which should be applied before the bbox gets added to the next "matrix level" local graphics_stack = {{ matrix = {10, 0, 0, 10, 0, 0}, -- Chosen for consistency with GhostScript's pdfwrite. Must be the same as defaultmatrix + bbox = nil, linewidth = nil, current_path = nil, current_point = nil, @@ -654,6 +662,12 @@ local function flush_delayed_table(delayed, state, force_start) delayed_matrix[5], delayed_matrix[6]) if cm_string == "1.00000 0.00000 0.00000 1.00000 0.00000 0.00000 cm" then cm_string = nil + else + local bbox = state.bbox + if bbox then + state.bbox = { matrix = delayed_matrix, next = bbox } + delayed.matrix = {} -- Will be initialized in reset_delayed + end end -- Before flushing, make sure that the current graphics state has started. @@ -686,6 +700,47 @@ local function flush_delayed(force_start) return flush_delayed_table(delayed, graphics_stack[#graphics_stack], force_start) end +local function register_point_bbox(bbox, x, y) + local min_x, min_y, max_x, max_y = bbox[1], bbox[2], bbox[3], bbox[4] + if min_x then + if x < min_x then + bbox[1] = x + elseif x > max_x then + bbox[3] = x + end + if y < min_y then + bbox[2] = y + elseif y > max_y then + bbox[4] = y + end + else + bbox[1], bbox[2], bbox[3], bbox[4] = x, y, x, y + end +end + +-- Only call after flush_delayed +local function register_point(state, x, y) + local bbox = state.bbox + if not bbox then return end + return register_point_bbox(bbox, x, y) +end + +local function merge_bbox(bbox, after) + if bbox[1] then + local matrix = bbox.matrix + if matrix then + register_point_bbox(after, matrix_transform(bbox[1], bbox[2], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6])) + register_point_bbox(after, matrix_transform(bbox[1], bbox[4], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6])) + register_point_bbox(after, matrix_transform(bbox[3], bbox[2], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6])) + register_point_bbox(after, matrix_transform(bbox[3], bbox[4], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6])) + else + register_point_bbox(after, bbox[1], bbox[2]) + register_point_bbox(after, bbox[3], bbox[4]) + end + end + return after +end + function drawarc(xc, yc, r, a1, a2) a1, a2 = math.rad(a1), math.rad(a2) local dx, dy = r*math.cos(a1), r*math.sin(a1) @@ -876,6 +931,7 @@ local function generic_show(str, ax, ay) local w = 0 if fonttype == 0x1CA then local characters = assert(font.getfont(fid)).characters + local max_d, max_h = 0, 0 flush_delayed() if pdfprint ~= gobble then vf.push() @@ -890,12 +946,29 @@ local function generic_show(str, ax, ay) end end local char = characters[b] - w = w + (char and char.width or 0) + if char then + w = w + (char.width or 0) + if char.depth and char.depth > max_d then + max_d = char.depth + end + if char.height and char.height > max_h then + max_h = char.height + end + end end + w = w/65781.76 if pdfprint ~= gobble then + max_d = max_d/65781.76 + max_h = max_h/65781.76 + register_point(state, 0, -max_d) + if ax then + local count = #str + register_point(state, w + count * ax, max_h + count * ay) + else + register_point(state, w, max_h) + end vf.pop() end - w = w/65781.76 elseif fonttype == 3 then for b in string.bytes(str) do systemdict.value.gsave() @@ -1061,7 +1134,7 @@ systemdict = {kind = 'dict', value = { break end end - if not mark_pos then error'Unmatched mark' end + if not mark_pos then error'unmatchedmark' end local dict = lua.newtable(0, (#operand_stack-mark_pos) // 2) for i = mark_pos + 1, #operand_stack - 1, 2 do push(operand_stack[i]) @@ -1084,14 +1157,14 @@ systemdict = {kind = 'dict', value = { return push(height-i) end end - error'Unmatched mark' + error'unmatchedmark' end, cleartomark = function() local entry repeat entry = pop() until (not entry) or type(entry) == 'table' and entry.kind == 'mark' - if not entry then error'Unmatched mark' end + if not entry then error'unmatchedmark' end end, ['if'] = function() @@ -1316,7 +1389,11 @@ systemdict = {kind = 'dict', value = { local after = node.direct.getnext(n) local head = node.direct.getlist(parent) node.direct.setnext(n, nil) - node.direct.setlist(parent, subbox.box) + node.direct.setlist(parent, n) + local state = graphics_stack[#graphics_stack] + local w, h, d = node.direct.dimensions(n) + register_point(state, 0, -d/65781.76) + register_point(state, w/65781.76, h/65781.76) vf.node(parent) node.direct.setnext(n, after) node.direct.setlist(parent, head) @@ -2234,12 +2311,20 @@ systemdict = {kind = 'dict', value = { local current_path = state.current_path if not current_path then return end current_path[#current_path+1] = 'f*' + flush_delayed() + local x for i = 1, #current_path do - if type(current_path[i]) == 'number' then - current_path[i] = string.format('%.5f', current_path[i]) + local value = current_path[i] + if type(value) == 'number' then + current_path[i] = string.format('%.5f', value) + if x then + register_point(state, x, value) + x = nil + else + x = value + end end end - flush_delayed() pdfprint((table.concat(current_path, ' '):gsub('%.?0+ ', ' '))) state.current_path, state.current_point = nil end, @@ -2248,12 +2333,20 @@ systemdict = {kind = 'dict', value = { local current_path = state.current_path if not current_path then return end current_path[#current_path+1] = 'f' + flush_delayed() + local x for i = 1, #current_path do - if type(current_path[i]) == 'number' then - current_path[i] = string.format('%.5f', current_path[i]) + local value = current_path[i] + if type(value) == 'number' then + current_path[i] = string.format('%.5f', value) + if x then + register_point(state, x, value) + x = nil + else + x = value + end end end - flush_delayed() pdfprint((table.concat(current_path, ' '):gsub('%.?0+ ', ' '))) state.current_path, state.current_point = nil end, @@ -2262,12 +2355,20 @@ systemdict = {kind = 'dict', value = { local current_path = state.current_path if not current_path then return end current_path[#current_path+1] = 'S' + flush_delayed() + local x for i = 1, #current_path do - if type(current_path[i]) == 'number' then - current_path[i] = string.format('%.5f', current_path[i]) + local value = current_path[i] + if type(value) == 'number' then + current_path[i] = string.format('%.5f', value) + if x then + register_point(state, x, value) + x = nil + else + x = value + end end end - flush_delayed() pdfprint((table.concat(current_path, ' '):gsub('%.?0+ ', ' '))) state.current_path, state.current_point = nil end, @@ -2340,6 +2441,9 @@ systemdict = {kind = 'dict', value = { local y = pop_num() local x = pop_num() pdfprint((string.format('%.5f %.5f %.5f %.5f re S', x, y, w, h):gsub('%.?0+ ', ' '))) + local state = graphics_stack[#graphics_stack] + register_point(state, x, y) + register_point(state, x + w, y + h) else error'Unsupported rectstroke variant' end @@ -2356,6 +2460,9 @@ systemdict = {kind = 'dict', value = { local y = pop_num() local x = pop_num() pdfprint((string.format('%.5f %.5f %.5f %.5f re f', x, y, w, h):gsub('%.?0+ ', ' '))) + local state = graphics_stack[#graphics_stack] + register_point(state, x, y) + register_point(state, x + w, y + h) else error'Unsupported rectfill variant' end @@ -2799,7 +2906,8 @@ systemdict = {kind = 'dict', value = { end, gsave = function() - graphics_stack[#graphics_stack+1] = table.copy(graphics_stack[#graphics_stack]) + local bbox = graphics_stack[#graphics_stack].bbox + graphics_stack[#graphics_stack+1] = table.copy(graphics_stack[#graphics_stack], bbox and {[bbox] = {}}) graphics_stack[#graphics_stack].saved_delayed = delayed delayed = { text = {}, @@ -2807,13 +2915,22 @@ systemdict = {kind = 'dict', value = { } end, grestore = function() - local saved_delayed = graphics_stack[#graphics_stack].saved_delayed + local state = graphics_stack[#graphics_stack] + local saved_delayed = state.saved_delayed if saved_delayed then delayed = saved_delayed else pdfprint'Q' reset_delayed(delayed) end + local upper_state = graphics_stack[#graphics_stack-1] + local upper_bbox = upper_state.bbox + if upper_bbox then + local bbox = assert(state.bbox) + while upper_bbox ~= bbox do + bbox = merge_bbox(bbox, bbox.next or upper_bbox) + end + end graphics_stack[#graphics_stack] = nil end, @@ -3364,6 +3481,33 @@ systemdict = {kind = 'dict', value = { end end, + ['.trackbbox'] = function() + local state = graphics_stack[#graphics_stack] + flush_delayed() + state.bbox = { next = state.bbox, start = true } + end, + -- Trackedbbox should only be invoked if the current matrix is essentially the same + -- as in the corresponding .trackbbox, otherwise everything gets messed up. + -- This isn't checked, mostly because we don't want a check to be too sensitive. + ['.trackedbbox'] = function() + local state = graphics_stack[#graphics_stack] + local bbox = state.bbox + if not bbox then + error'trackedbbox without matching trackbbox' + end + while not bbox.start do + if not bbox.next then + error'Illegal nesting of trackbbox/trackedbbox and gsave/grestore' + end + bbox = merge_bbox(bbox, bbox.next) + end + state.bbox = bbox.next and merge_bbox(bbox, bbox.next) + push(bbox[1] or 0) + push(bbox[2] or 0) + push(bbox[3] or 0) + push(bbox[4] or 0) + end, + revision = 1000, ['true'] = true, ['false'] = false, @@ -3726,7 +3870,16 @@ local register_texbox do function register_texbox(box) id = id + 1 box = setmetatable({box = node.direct.todirect(box)}, meta) - local op = function() flush_delayed() vf.push() vf.node(box.box) vf.pop() end + local op = function() + flush_delayed() + local state = graphics_stack[#graphics_stack] + local w, h, d = node.direct.dimensions(box.box) + register_point(state, 0, -d/65781.76) + register_point(state, w/65781.76, h/65781.76) + vf.push() + vf.node(box.box) + vf.pop() + end lua_node_lookup[op] = box dict[id] = op return id @@ -3898,9 +4051,11 @@ local fid = font.define{ TeXDict.ocount = height operand_stack[height + 1], operand_stack[height + 2] = ps_pos_x/65781.76, ps_pos_y/65781.76 ps_pos_x, ps_pos_y = nil + local graphics_height if direct then systemdict.value.moveto() else + graphics_height = #graphics_stack systemdict.value.gsave() systemdict.value.translate() end @@ -3917,6 +4072,16 @@ local fid = font.define{ flush_delayed() if not direct then systemdict.value.grestore() + if graphics_height ~= #graphics_stack then + if graphics_height < #graphics_stack then + texio.write_nl"luapstricks: PS block contains unbalanced gsave. grestore will be executed to compensate." + repeat + systemdict.value.grestore() + until graphics_height == #graphics_stack + else + texio.write_nl"luapstricks: PS block contains unbalanced grestore." + end + end height = TeXDict.ocount or height local new_height = #operand_stack assert(new_height >= height) @@ -3977,7 +4142,8 @@ do local ps_rgb = 'rgb ' * l.C(l.P(1)^0) * l.Cc' setrgbcolor' * l.Cc'rgb ' local ps_cmyk = 'cmyk ' * l.C(l.P(1)^0) * l.Cc' setcmykcolor' * l.Cc'cmyk ' local ps_gray = 'gray ' * l.C(l.P(1)^0) * l.Cc' setgray' * l.Cc'gray ' - local pscolor = ps_rgb + ps_gray + ps_gray + local ps_hsb = 'hsb ' * l.C(l.P(1)^0) * l.Cc' sethsbcolor' * l.Cc'hsb ' + local pscolor = ps_rgb + ps_gray + ps_gray + ps_hsb local pdf_rgb = l.Cmt(l.C(number * whitespace * number * whitespace * number / 0) * whitespace * 'rg' * whitespace * l.C(number * whitespace * number * whitespace * number / 0) * whitespace * 'RG' * -1, function(s, p, a, b) if a == b then -- cgit v1.2.3