diff options
Diffstat (limited to 'macros/luatex/generic/minim-pdf')
-rw-r--r-- | macros/luatex/generic/minim-pdf/README | 16 | ||||
-rw-r--r-- | macros/luatex/generic/minim-pdf/minim-pdf.doc | 99 | ||||
-rw-r--r-- | macros/luatex/generic/minim-pdf/minim-pdf.lua | 135 | ||||
-rw-r--r-- | macros/luatex/generic/minim-pdf/minim-pdf.pdf | bin | 116670 -> 126824 bytes | |||
-rw-r--r-- | macros/luatex/generic/minim-pdf/minim-pdf.tex | 53 |
5 files changed, 244 insertions, 59 deletions
diff --git a/macros/luatex/generic/minim-pdf/README b/macros/luatex/generic/minim-pdf/README index eb9a1bd114..0626208f2f 100644 --- a/macros/luatex/generic/minim-pdf/README +++ b/macros/luatex/generic/minim-pdf/README @@ -1,4 +1,4 @@ -Version: 2023/1.3 +Version: 2024/1.4 SUMMARY @@ -27,6 +27,20 @@ In order to obtain the typeset manual of this package, simply say: HISTORY +2024/1.4 (5/1/2024) + + Breaking changes: + + * Table cells and headers now connect automatically in \automarktable + + New features: + + * Add support for pdf/ua + * Re-write \hyperlink, adding many options + * Add ref and title options to structure elements + * New helper macros for footnotes + * Add \withpdfresource + 2023/1.3 (20/10/2023) New features: diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.doc b/macros/luatex/generic/minim-pdf/minim-pdf.doc index e04deb31ab..111d8c1423 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.doc +++ b/macros/luatex/generic/minim-pdf/minim-pdf.doc @@ -3,7 +3,7 @@ \manual{minim-pdf} -\ifchapter \chapter Hypertext +\ifchapter \chapter Advanced PDF features This chapter and the next document the support of the modern pdf features provided by the minim-pdf package. @@ -14,8 +14,8 @@ all other features of the package are described here. \else \startmetadata author {Esger Renkema} title {minim-pdf} - date {2023-10-20} - version {2023/1.3} + date {2024-01-05} + version {2024/1.4} keywords {LuaTeX; Plain TeX; PDF/A; Tagged PDF; accessibility; a11y; hypertext; bookmarks; document outline; associated files} stopmetadata @@ -33,10 +33,16 @@ all other features will be covered first. \section Hyperlinks %1 -For most simple cases, you can use -⟦*\hyperlink [name {...} | url {...}] ... \endlink⟧ for linking to a named -destination in your own document or to an external hyperlink respectively. -There is no support for nonsimple cases. +Hyperlinks can be made with +⟦*\hyperlink [alt {...}] [attr {...}] <action> ... \endlink⟧, where the +⟦<action>⟧ must be one of +⟦name {...} | url {...} | name {...} | next | prev | first | last⟧ +With the ⟦name⟧ action, a named destination must be used (see below), while the +⟦user⟧ action will be passed directly to the back-end (as with the pdftex +primitive). +The ⟦*\hyperlinkstyle⟧ token list can be used so set common (pdf) link +attributes; it defaults to ⟦/Border [0 0 0]⟧. The contents of the optional +⟦attr⟧ parameter will be appended to these. A named destination can be created with ⟦*\nameddestination {...}⟧ (also in horizontal mode, unlike the backend primitive) and if you cannot think of @@ -47,9 +53,10 @@ generated name. \section Bookmarks %1 -Bookmarks can be added with ⟦*\outline [open|closed] [dest {name}] {title}⟧. -Add ⟦open⟧ or ⟦closed⟧ to have the bookmark appear initially open or -closed (default), and +Bookmarks (also known as outlines) can be added with ⟦*\outline [open|closed] +[dest {name}] {title}⟧. +Add ⟦open⟧ or ⟦closed⟧ to have the bookmark appear initially open or closed +(default), and say ⟦dest {name}⟧ for having it refer to a specific named destination (otherwise, a new one will be created where the ⟦\outline⟧ command appears). @@ -101,6 +108,48 @@ are enabled by default. You can disable them by setting ⟦*\writehyphensandspaces⟧ to a nonpositive value. +\section PDF/UA %1 + +You can claim pdf/ua conformance with ⟦*\pdfualevel 1⟧. +By itself, this will do very little: + +\startlist +\tagattribute List ListNumbering /Decimal +\item 1. The ⟦pdfuaid:part⟧ metadata key will be set. +\item 2. A conforming ⟦ViewerPreferences⟧ dictionary will be added to the document catalog. +\item 3. The ⟦/Suspects⟧ key of the ⟦MarkInfo⟧ dictionary will be set to ⟦false⟧. +\item 4. ⟦/Tab /S⟧ will be added to the page attributes. +\stoplist + +Also making your document pdf/a-compliant, however, will relieve you of a few +additional worries: + +\startlist +\tagattribute List ListNumbering /Decimal +\tagattribute List ContinuedList true +\item 5. Fonts will be included properly. +\item 6. The (natural) language of every element will be known. +\item 7. Headings will be strongly-structured. +\item 8. Table headers will have their ⟦Scope⟧ set properly. +\item 9. A document outline will be generated automatically. +\stoplist + +This leaves the following for you to provide before your document can be +pdf/ua-compliant: + +\startlist +\tagattribute List ListNumbering /Decimal +\tagattribute List ContinuedList true +\item 10. Figure and Formula structure elements must have alt texts. +\item 11. Hyperlinks must have alternate descriptions. +\item 12. Lists must have the ⟦ListNumbering⟧ attribute set. +\item 13. Tables must have headers that are tagged as such. +\item 14. Page headers and footers must be marked as header or footer artifacts. +\item 15. Document section structure elements should have their ⟦title⟧ set. +\item 16. All embedded files must have a description. +\stoplist + + \section Embedded files %1 You can attach (associate) files with ⟦*\embedfile <options>⟧. @@ -110,7 +159,9 @@ document catalog. Arguments consisting of a single word can be given without braces and exactly one of the options ⟦file⟧ or ⟦string⟧ must be present. -\smallskip\marktableaslist \halign{#\quad\hfil&#\hfil\cr +\smallskip\marktableaslist +\tagattribute List ListNumbering /Description +\halign{#\quad\hfil&#\hfil\cr ⟦file ⟧\hfill⟦{...}⟧ & The file to embed.\cr ⟦string ⟧\hfill⟦{...}⟧ & The string to embed.\cr ⟦global⟧ & Attach to the document catalog.\cr @@ -166,6 +217,7 @@ You should always check the result in an external application. This is the full list of limitations, pitfalls and shortcomings: \startlist +\tagattribute List ListNumbering /Decimal \item1. Document content must be seen by tex in its logical order (although you can mark out-of-order content explicitly if you know what you are doing; see below). @@ -342,9 +394,16 @@ Note that for the ⟦owner⟧ and ⟦key⟧ the initial slash must be omitted; t ⟦value⟧ on the other hand will be written to the pdf verbatim. Any number of attributes can be added. -An identifier can be set with the ⟦id⟧ option, or after the fact with +An identifier can be set with the ⟦id {...}⟧ option, or after the fact with ⟦*\settagid {...}⟧. This identifier will be added to the ⟦IDTree⟧ and is entirely optional; you will probably already know when you need it. +The ⟦ref {...}⟧ option lets a structure element refer to another (the ⟦/Ref⟧ +option in the structure element dictionary). Its argument should be the ⟦id⟧ of +the other structure element. + +The title of the structure element (corresponding to the ⟦/T⟧ entry in the +structure element dictionary) can be set with the ⟦title {...}⟧ option. The +pdf/ua standard requires this key for all document sections. Finally, structure element classes can be given with the ⟦class <classname>⟧ keyword, which can be repeated. @@ -450,12 +509,13 @@ sort (and indeed, no general logic can be given in the latter case). Always proc with caution when using cells spanning multiple rows, and inspect the resulting structure carefully. -Marking a table header (either manually or with ⟦\automarktable⟧) will not -connect normal cells with their headers; you will have to connect these -manually by including ⟦*\markcolumnhead⟧ or ⟦*\markrowhead⟧ in the appropriate -header cells. This must be done \emph{after} ⟦\markcolumnspan⟧ if the latter -appplies. If properly setup like this, other cells of the table (including -header cells) will be assigned to matching row or column headers automatically. +Marking up a table header (except if done through ⟦\automarktable⟧) +will not connect normal table cells with their headers; you will have to +connect these manually by including ⟦*\markcolumnhead⟧ or ⟦*\markrowhead⟧ in +the appropriate header cells. This must be done \emph{after} ⟦\markcolumnspan⟧ +if the latter appplies. If properly setup like this, other cells of the table +(including header cells) will be assigned to matching row or column headers +automatically. \section Other helper macros %1 @@ -465,6 +525,9 @@ For marking up an entry in a table of contents, you can use the macro tags in the correct way. (The ⟦dest⟧ is a link destination and can be empty; the ⟦lbl⟧ is a section number and can also be empty.) +For tagging (foot)notes, ⟦*\marknoteref{*}⟧ and ⟦*\marknotelbl{*}⟧, when placed +around the footnote markers, will insert the proper ⟦Ref⟧, ⟦Note⟧ and ⟦Lbl⟧ +tags. % diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.lua b/macros/luatex/generic/minim-pdf/minim-pdf.lua index 74d56eeaac..875fd89659 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.lua +++ b/macros/luatex/generic/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() diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.pdf b/macros/luatex/generic/minim-pdf/minim-pdf.pdf Binary files differindex a6e22ee194..2b2ea69c42 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.pdf +++ b/macros/luatex/generic/minim-pdf/minim-pdf.pdf diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.tex b/macros/luatex/generic/minim-pdf/minim-pdf.tex index d8b06255ba..8a5f738063 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.tex +++ b/macros/luatex/generic/minim-pdf/minim-pdf.tex @@ -167,8 +167,8 @@ \newcount\tagging:tablecolspan \tagging:tablecolspan 1 \newcount\tagging:tablerowspan \tagging:tablerowspan 1 \newcount\tagging:i -\def\tagging:TH{TH} -\def\tagging:TD{TD} +\def\tagging:TH{{TH}\markcolumnhead} +\def\tagging:TD{{TD}} % initialising variables \def\marktable{\startelement{Table}% \global\advance\tagging:tablec1 @@ -205,7 +205,7 @@ \def\marktablecell{% \global\advance\tagging:tablecol1 \continueelementfrom\tagging:row - \startelement{\tagging:cell}% + \expandafter\startelement\tagging:cell% \tagging:assignheaders} % spans @@ -301,13 +301,27 @@ \def\marktocentry#1#2#3#4#5{% \ifx\marktocentry#1\marktocentry \def\marktocentry:link##1{##1}\else - \def\marktocentry:link##1{\hyperlink dest{#1}##1\endlink}\fi + \def\marktocentry:link##1{\hyperlink + alt{\csname minim:str:page\endcsname#5} + dest{#1}##1\endlink}\fi \markelement{TOCI}{\nextpartag{}\quitvmode \ifx\marktocentry#2\marktocentry\else \markelement{Lbl}{\marktocentry:link{#2}}\fi \markelement{Reference}{\marktocentry:link{#3% \ifx\marktocentry#4\marktocentry\else \markartifact{Layout}{#4}\fi#5}}}} +\def\minim:str:page{Page } + +% \marknoteref {*} +\def\marknoteref#1{% + \startelement ref{\newdestinationname}{Reference}% + {\stopformulatagging#1}\stopelement{Reference}} +% \marknotelbl {*} +\def\marknotelbl#1{% + \startelement id{\lastdestinationname}{Note}% + \aftergroup\tagging:ensurestopNote + \markelement{Lbl}{{\stopformulatagging#1}}} +\def\tagging:ensurestopNote{\ensurestopelement{Note}} % 1 tagging formulas @@ -351,7 +365,7 @@ \def\formulasource:set#1{% \begingroup % strip \setalttext, \setactualtext - \def\setalttext#1{}\def\setactualtext#1{}% + \def\setalttext##1{}\def\setactualtext##1{}% \xdef\formulasource:thesource{#1}% \endgroup \expandafter\splitcommalist @@ -370,6 +384,7 @@ relation Source desc {Equation source} name {\formulafilename.tex} string {\tagging:formulasource{#1}}\fi} +\def\formulasource:none#1{} \newtoks\includeformulasources \includeformulasources{actualtext,attachment} @@ -390,17 +405,18 @@ \def\linkdest:h#1{\vadjust pre{\linkdest:v{#1}}} \def\linkdest:v#1{\pdfextension dest name {#1} xyz\nobreak} -% \hyperlink dest {name} ... \endlink -% \hyperlink url {url} ... \endlink +% \hyperlink <args> ... \endlink +% args: alt {...} attr {...} <type> +% type: dest {name} | url {url} | next | prev | first | last \protected\def\endlink{\pdfextension endlink\stopelement{Link}\relax} \protected\def\startlink{\startelement{Link}\pdfextension startlink} -\def\hyperlink#1#{\quitvmode\hyperlink:rmspace#1 \hyperlink:rmspace} -\def\hyperlink:rmspace#1 #2\hyperlink:rmspace{% - \startlink attr {\minim:linkattr}% - \csname hyperlink:#1\endcsname} -\def\hyperlink:dest#1{user {/Subtype/Link /F 4 /A <</S/GoTo /D (#1)>>}} -\def\hyperlink:url#1{user {/Subtype/Link /F 4 /A <</S/URI /URI (#1)>>}} -\def\minim:linkattr{/Border [0 0 0]} +\protected\def\hyperlink{\quitvmode\startlink\hyper:makelink} + +% \hyperlinkstyle { ... } +\def\minim:linkattr{\the\hyperlinkstyle\the\minim:pdfalinkattr} +\newtoks\hyperlinkstyle +\newtoks\minim:pdfalinkattr +\hyperlinkstyle{/Border [0 0 0]} % 1 languages @@ -449,9 +465,12 @@ \newcount \pdfaconformancelevel \pdfaconformancelevel = 0 +\newcount \pdfuaconformancelevel +\pdfuaconformancelevel = 0 % \pdfalevel 2b \def\pdfalevel#1#2{% + \minim:pdfalinkattr{ /F 4}% hyperlinks must be printed \global\pdfaconformancelevel=#1\relax \ifcsname minim:pdfa:#1#2\endcsname \lastnamedcs\else \errmessage{Unknown pdf/a standard pdf/a-#1}\fi} @@ -474,6 +493,12 @@ \expandafter\def\csname minim:pdfa:3b\endcsname{\minim:pdfasettings 7B3} \expandafter\def\csname minim:pdfa:3u\endcsname{\minim:pdfasettings 7U3} +% \pdfualavel 1 +\def\pdfualevel{\input minim-xmp + \setmetadata pdfuaid:part {1} + \tagging:enablepdfua + \global\pdfuaconformancelevel= } + % \catcode`\: = \minimpdfloaded |