diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua | 135 |
1 files changed, 109 insertions, 26 deletions
diff --git a/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua b/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua index 74d56eeaac2..875fd896592 100644 --- a/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua +++ b/Master/texmf-dist/tex/luatex/minim-pdf/minim-pdf.lua @@ -7,9 +7,11 @@ alloc.remember('minim-pdf') -- 1 helper functions +local HLIST_NODE = node.id 'hlist' local GLUE_NODE = node.id 'glue' local GLYPH_NODE = node.id 'glyph' local RULE_NODE = node.id 'rule' +local DISC_NODE = node.id 'disc' local WHATSIT_NODE = node.id 'whatsit' local USER_DEFINED = node.subtype 'user_defined' local START_LINK = node.subtype 'pdf_start_link' @@ -51,9 +53,8 @@ local function singleton(t) return one end --- in-depth node list traversal --- returns current and parent node --- only dives into hbox, vbox and ins nodes +-- in-depth node list traversal; returns current and parent node +-- only dives into hbox, vbox and ins nodes, and in disc no-break text -- returns node and enclosing box local function full_traverse(head) return function(stack, last) @@ -178,7 +179,7 @@ local structure_types = alloc.saved_table('structure types', { -- inline structure elements ILSE Span = { type = 'inline' }, Quote = { type = 'inline' }, - Note = { type = 'inline' }, + Note = { type = 'extern' }, Reference = { type = 'inline' }, BibEntry = { type = 'inline' }, Code = { type = 'inline', attributes = { ['CSS-2.00'] = { ['white-space'] = '(pre)' } } }, @@ -198,9 +199,10 @@ local structure_type_compatibility = { none = { none=1, section=1, group=1, block=1, inline=1, contain=1 }, section = { none=1, section=1, group=1, block=1 }, group = { none=1, group=1, block=1 }, - block = { none=1, inline=1 }, - inline = { none=1, inline=1 }, - contain = { none=1, inline=1, block=1, group=1 }, + extern = { none=1, group=1, block=1 }, + block = { none=1, inline=1, extern=1 }, + inline = { none=1, inline=1, extern=1 }, + contain = { none=1, inline=1, block=1, group=1, extern=1 }, } local function check_structure_compatibility(parent, child, childtype) @@ -354,10 +356,19 @@ local function write_structure() local id_tree, id_tree_obj = { }, pdf.reserveobj() structure[1].parent = { objnum = root_obj } for _, se in ipairs(structure) do - if not se.hidden then se.objnum = pdf.reserveobj() end + if not se.hidden then + se.objnum = pdf.reserveobj() + if se.id then + id_tree[se.id] = se.objnum + end + end end -- update the document catalog - add_to_catalog('/MarkInfo << /Marked true >>') + if tex.count['pdfuaconformancelevel'] > 0 then + add_to_catalog('/MarkInfo << /Marked true /Suspects false >>') + else + add_to_catalog('/MarkInfo << /Marked true >>') + end add_to_catalog('/StructTreeRoot %s 0 R', root_obj) -- write the structure tree root pdf.immediateobj(root_obj, string.format( @@ -368,10 +379,11 @@ local function write_structure() if not se.hidden then local res = { '<<' } insert_formatted(res, '/Type/StructElem /S%s /P %d 0 R', pdf_name(se.struct), se.parent.objnum) - if se.id then id_tree[se.id] = se.objnum; insert_formatted(res, '/ID %s', pdf_string(se.id)) end - if se.lang and se.lang ~= se.parent.lang then insert_formatted(res, '/Lang (%s)', se.lang) end + if se.id then insert_formatted(res, '/ID %s', pdf_string(se.id)) end + if se.lang and se.lang ~= se.parent.lang then insert_formatted(res, '/Lang %s', pdf_string(se.lang)) end if se.alt then insert_formatted(res, '/Alt %s', pdf_string(se.alt)) end if se.actual then insert_formatted(res, '/ActualText %s', pdf_string(se.actual)) end + if se.title then insert_formatted(res, '/T %s', pdf_string(se.title)) end if #se.children > 0 then insert_formatted(res, '\n/K %s', format_K_array(se)) end if se.mainpage then insert_formatted(res, '/Pg %d 0 R', se.mainpage) end if se.class then @@ -382,6 +394,19 @@ local function write_structure() end if #se.class > 1 then table.insert(res, ']') end end + if se.ref then + table.insert(res, '/Ref') + table.insert(res, '[') + for _, c in ipairs(se.ref) do + local onum = id_tree[c] + if onum then + insert_formatted(res, '%d 0 R', onum) + else + alloc.err('Invalid structure element ID: %s', c) + end + end + table.insert(res, ']') + end if not is_empty(se.attributes) then table.insert(res, '/A') make_attributes(res, se.attributes) @@ -481,7 +506,7 @@ cb.register('uselanguage', function(name) end) local function write_language() - add_to_catalog('/Lang (%s)', structure[1].lang or 'und') + add_to_catalog('/Lang %s', pdf_string(structure[1].lang or 'und')) end -- 1 marking structure elements @@ -559,9 +584,11 @@ end alloc.luadef('tagging:startelement', function() local s = options_scanner() :string('id') + :string('ref', true) :string('type') -- 'section', 'group', 'block' etc. :argument('alt') :argument('actual') + :argument('title') :string('lang') :subtable('attr') :string('class', true) @@ -736,7 +763,7 @@ function M.mark_content_items(box) elseif marker and marker.what == 'mci_stop' then end_node = end_node and n; insert_tags(b) -- finally, see if we need to intervene between content nodes - elseif n.id == RULE_NODE or n.id == GLYPH_NODE + elseif n.id == RULE_NODE or n.id == GLYPH_NODE or n.id == DISC_NODE or marker and marker.what == 'content' then local _se, _order = n[current_struct], n[current_order] if n.id == RULE_NODE and (n.width == 0 or n.height == 0 and n.depth == 0) then @@ -768,7 +795,7 @@ cb.register('pre_shipout', function(nr) end end) --- 1 destinations +-- 1 links and destinations local dest_count = alloc.new_count('link dest count') local function new_dest_name() @@ -791,7 +818,41 @@ end alloc.luadef('newdestinationname', function() tex.sprint(new_dest_name()) end) alloc.luadef('lastdestinationname', function() tex.sprint(last_dest_name()) end) --- 1 bookmarks +-- provide the arguments to \pdfextension startlink +local link_types = { } +alloc.luadef('hyper:makelink', function() + local s = options_scanner() + :argument('alt'):argument('contents') -- same thing + :argument('attr') + :scan() + local type = token.scan_word() + local userf = link_types[type] + if not userf then + alloc.err('Unknown hyperlink type: %s', type) + userf = link_types.first + end + local user = userf() + attr = { 'attr {\\csname minim:linkattr\\endcsname' } + if s.attr then table.insert(attr, s.attr) end + if s.alt or s.contents then insert_formatted(attr, '/Contents %s', pdf_string(s.alt or s.contents)) end + insert_formatted(attr, '} user {%s}', user) + tex.sprint(table.concat(attr, ' ')) +end) + +function link_types.dest() + return string.format('/Subtype/Link /A <</S/GoTo /D %s>>', pdf_string(token.scan_argument())) +end +function link_types.url() + return string.format('/Subtype/Link /A <</S/URI /URI %s>>', pdf_string(token.scan_argument())) +end + +function link_types.user() return token.scan_argument() end +function link_types.next() return '/Subtype/Link /A <</S/Named /N/NextPage>>' end +function link_types.prev() return '/Subtype/Link /A <</S/Named /N/PrevPage>>' end +function link_types.first() return '/Subtype/Link /A <</S/Named /N/FirstPage>>' end +function link_types.last() return '/Subtype/Link /A <</S/Named /N/LastPage>>' end + +-- 1 bookmarks (outlines) -- start with a dummy top-level bookmark local bookmarks = { { count = 0 } } @@ -811,8 +872,8 @@ local function write_bookmarks() -- write bookmark objects for i=2, #bookmarks do local bm, res = bookmarks[i], { } - insert_formatted(res, '<< /Title %s\n/Parent %s 0 R /Dest (%s)', pdf_string(bm.title), - bm.parent and bm.parent.outline_obj or bookmarks[1].outline_obj, bm.dest) + insert_formatted(res, '<< /Title %s\n/Parent %s 0 R /Dest %s', pdf_string(bm.title), + bm.parent and bm.parent.outline_obj or bookmarks[1].outline_obj, pdf_string(bm.dest)) if bm.next then insert_formatted(res, '/Next %s 0 R', bm.next.outline_obj) end if bm.prev then insert_formatted(res, '/Prev %s 0 R', bm.prev.outline_obj) end if bm.struct and bm.struct.objnum then @@ -945,13 +1006,13 @@ end, 'protected') function M.mark_discretionaries(head, gc) if not spaces_enabled() then return end - for disc in node.traverse_id(7, head) do + for disc in node.traverse_id(DISC_NODE, head) do if disc.subtype ~= 2 then -- ‘automatic’ (exclude explicit hyphens) local pre, post, replace = disc.pre, disc.post, disc.replace local se, order = disc[current_struct], disc[current_order] -- get the replacement text local actual = { } - for c in node.traverse_id(29, replace) do + for c in node.traverse_id(GLYPH_NODE, replace) do table.insert(actual, c.char) end -- special case: a single hyphen @@ -982,7 +1043,7 @@ local space_attr = alloc.new_attribute('space insertion marker') function M.mark_spaces(head, gc) if not spaces_enabled() then return end - for g in node.traverse_id(12, head) do -- glue + for g in node.traverse_id(GLUE_NODE, head) do if g.prev and g.subtype == 13 or g.subtype == 14 then -- (x)spaceskip local p = g.prev p[space_attr] = p.id == GLYPH_NODE and p.font @@ -1011,7 +1072,7 @@ end function M.insert_spaces(head, gc) if not spaces_enabled() then return end - for line in node.traverse_id(0, head) do + for line in node.traverse_id(HLIST_NODE, head) do insert_spaces(line.head) end return true @@ -1085,10 +1146,10 @@ local function write_output_intents() attr = string.format('/N %d', oi.N), immediate = true } insert_formatted(res, '<< /Type/OutputIntent /DestOutputProfile %d 0 R', p) - insert_formatted(res, '/S/%s /OutputConditionIdentifier (%s)', oi.subtype, oi.id) - if oi.info then insert_formatted(res, '/Info (%s)', oi.info) end - if oi.condition then insert_formatted(res, '/OutputCondition (%s)', oi.condition) end - if oi.registry then insert_formatted(res, '/RegistryName (%s)', oi.registry) end + insert_formatted(res, '/S/%s /OutputConditionIdentifier %s', oi.subtype, pdf_string(oi.id)) + if oi.info then insert_formatted(res, '/Info %s', pdf_string(oi.info)) end + if oi.condition then insert_formatted(res, '/OutputCondition %s', pdf_string(oi.condition)) end + if oi.registry then insert_formatted(res, '/RegistryName %s', pdf_string(oi.registry)) end table.insert(res, '>>') end table.insert(res, ']') @@ -1121,9 +1182,31 @@ end alloc.luadef('minim:default:rgb:profile', function() M.add_default_rgb_output_intent() end) alloc.luadef('minim:default:cmyk:profile', function() M.add_default_cmyk_output_intent() end) +-- 1 pdf/ua + +alloc.luadef('tagging:enablepdfua', function() + add_to_catalog('/ViewerPreferences << /DisplayDocTitle true >>') + local pageattr = pdf.getpageattributes() + if pageattr then + pdf.setpageattributes(pageattr .. ' /Tabs /S') + else + pdf.setpageattributes('/Tabs /S') + end +end) + +local function perform_pdfua_checks() + local xmp = require 'minim-xmp' + if xmp.get_metadata('dc:title') == '' then + alloc.err('PDF/UA error: no dc:title metadata') + end +end + -- -cb.register ('finish_pdffile', function() +cb.register('finish_pdffile', function() + if tex.count['pdfuaconformancelevel'] > 0 then + perform_pdfua_checks() + end if tagging_enabled() then write_language() write_structure() |