diff options
Diffstat (limited to 'macros/luatex/generic')
23 files changed, 732 insertions, 262 deletions
diff --git a/macros/luatex/generic/luamplib/NEWS b/macros/luatex/generic/luamplib/NEWS index d56d4df857..3e91c9cc67 100644 --- a/macros/luatex/generic/luamplib/NEWS +++ b/macros/luatex/generic/luamplib/NEWS @@ -1,5 +1,14 @@ History of the luamplib package +2024/03/01 2.26.0 + * when \mplibcachedir{<dir>} is not set, default cache directory will + be in the following order: + $TEXMFVAR/luamplib_cache + $TEXMF_OUTPUT_DIRECTORY/luamplib_cache + ./luamplib_cache + $TEXMFOUTPUT/luamplib_cache + . + 2024/01/25 2.25.3 * protect "..." even if textextlabel is disabled (revert part of v2.25.0) diff --git a/macros/luatex/generic/luamplib/luamplib.dtx b/macros/luatex/generic/luamplib/luamplib.dtx index 3b6aad6e35..8bdbf884e4 100644 --- a/macros/luatex/generic/luamplib/luamplib.dtx +++ b/macros/luatex/generic/luamplib/luamplib.dtx @@ -85,7 +85,7 @@ See source file '\inFileName' for licencing and contact information. %<*driver> \NeedsTeXFormat{LaTeX2e} \ProvidesFile{luamplib.drv}% - [2024/01/25 v2.25.3 Interface for using the mplib library]% + [2024/03/01 v2.26.0 Interface for using the mplib library]% \documentclass{ltxdoc} \usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace} \usepackage[x11names]{xcolor} @@ -153,7 +153,7 @@ See source file '\inFileName' for licencing and contact information. % \author{Hans Hagen, Taco Hoekwater, Elie Roux, Philipp Gesang and Kim Dohyun\\ % Maintainer: LuaLaTeX Maintainers --- % Support: \email{lualatex-dev@tug.org}} -% \date{2024/01/25 v2.25.3} +% \date{2024/03/01 v2.26.0} % % \maketitle % @@ -328,8 +328,13 @@ See source file '\inFileName' for licencing and contact information. % |$TEXMFMAIN/metapost/context/base| are already registered by default. % % By default, cache files will be stored in |$TEXMFVAR/luamplib_cache| or, -% if it's not available, in the same directory as where pdf/dvi output file -% is saved. This however can be changed by the command +% if it's not available (mostly not writable), +% in the directory where output files are saved: +% to be specific, |$TEXMF_OUTPUT_DIRECTORY/luamplib_cache|, +% |./luamplib_cache|, |$TEXMFOUTPUT/luamplib_cache|, and |.| in this order. +% (|$TEXMF_OUTPUT_DIRECTORY| is normally the value of |--output-directory| +% command-line option.) +% This behavior however can be changed by the command % |\mplibcachedir{<directory path>}|, where tilde (|~|) is interpreted % as the user's home directory (on a windows machine as well). % As backslashes (|\|) should be escaped by users, it would be easier to use @@ -446,8 +451,8 @@ See source file '\inFileName' for licencing and contact information. luatexbase.provides_module { name = "luamplib", - version = "2.25.3", - date = "2024/01/25", + version = "2.26.0", + date = "2024/03/01", description = "Lua package to typeset Metapost with LuaTeX's MPLib.", } @@ -528,7 +533,7 @@ local is_writable = file.is_writable or function(name) end end end -local mk_full_path = lfs.mkdirs or function(path) +local mk_full_path = lfs.mkdirp or lfs.mkdirs or function(path) local full = "" for sub in path:gmatch("(/*[^\\/]+)") do full = full .. sub @@ -549,31 +554,24 @@ local currenttime = os.time() local outputdir if lfstouch then - local texmfvar = kpse.expand_var('$TEXMFVAR') - if texmfvar and texmfvar ~= "" and texmfvar ~= '$TEXMFVAR' then - for _,dir in next, texmfvar:explode(os.type == "windows" and ";" or ":") do - if not lfsisdir(dir) then - mk_full_path(dir) - end - if is_writable(dir) then - local cached = format("%s/luamplib_cache",dir) - lfsmkdir(cached) - outputdir = cached - break + for i,v in ipairs{'TEXMFVAR','TEXMF_OUTPUT_DIRECTORY','.','TEXMFOUTPUT'} do + local var = i == 3 and v or kpse.var_value(v) + if var and var ~= "" then + for _,vv in next, var:explode(os.type == "unix" and ":" or ";") do + local dir = format("%s/%s",vv,"luamplib_cache") + if not lfsisdir(dir) then + mk_full_path(dir) + end + if is_writable(dir) then + outputdir = dir + break + end end + if outputdir then break end end end end -if not outputdir then - outputdir = "." - for _,v in ipairs(arg) do - local t = v:match("%-output%-directory=(.+)") - if t then - outputdir = t - break - end - end -end +outputdir = outputdir or '.' function luamplib.getcachedir(dir) dir = dir:gsub("##","#") @@ -610,7 +608,7 @@ local noneedtoreplace = { ["mp-grph.mpiv"] = true, ["mp-idea.mpiv"] = true, ["mp-luas.mpiv"] = true, ["mp-mlib.mpiv"] = true, ["mp-node.mpiv"] = true, ["mp-page.mpiv"] = true, ["mp-shap.mpiv"] = true, ["mp-step.mpiv"] = true, ["mp-text.mpiv"] = true, - ["mp-tool.mpiv"] = true, + ["mp-tool.mpiv"] = true, ["mp-cont.mpiv"] = true, } luamplib.noneedtoreplace = noneedtoreplace @@ -2061,7 +2059,7 @@ luamplib.colorconverter = colorconverter \else \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luamplib} - [2024/01/25 v2.25.3 mplib package for LuaTeX] + [2024/03/01 v2.26.0 mplib package for LuaTeX] \ifx\newluafunction\@undefined \input ltluatex \fi diff --git a/macros/luatex/generic/luamplib/luamplib.pdf b/macros/luatex/generic/luamplib/luamplib.pdf Binary files differindex e46210d999..506ec49715 100644 --- a/macros/luatex/generic/luamplib/luamplib.pdf +++ b/macros/luatex/generic/luamplib/luamplib.pdf diff --git a/macros/luatex/generic/minim-mp/README b/macros/luatex/generic/minim-mp/README index 6c10eeabb2..372e9626e1 100644 --- a/macros/luatex/generic/minim-mp/README +++ b/macros/luatex/generic/minim-mp/README @@ -1,4 +1,4 @@ -Version: 2024/1.4 +Version: 2024/1.5 SUMMARY @@ -19,6 +19,18 @@ In order to obtain the typeset manual of this package, simply say: HISTORY +2025/1.5 (in development) + + New features: + + * Add a luamplib compatibility mode to the latex package. + * Make default format and math mode configurable. + + Bug fixes: + + * Make metafun not crash immediately. + * Comments (% characters) are now handled by metapost, not tex. + 2024/1.4 (5/1/2024) * Support the extended graphics state (extgstate) diff --git a/macros/luatex/generic/minim-mp/minim-mp.doc b/macros/luatex/generic/minim-mp/minim-mp.doc index 9429544d8e..0cd4ff574e 100644 --- a/macros/luatex/generic/minim-mp/minim-mp.doc +++ b/macros/luatex/generic/minim-mp/minim-mp.doc @@ -8,8 +8,8 @@ \else \startmetadata author {Esger Renkema} title {minim-mp} - date {2024-01-05} - version {2024/1.4} + date {2024-02-25} + version {2024/1.5} keywords {LuaTeX; Plain TeX; MetaPost; mplib} stopmetadata \maketitle \fi @@ -29,8 +29,8 @@ a persistent metapost instance. This will also give you more control over image extraction. See below under „Metapost instances”. The ⟦options⟧ will also be explained there (for simple cases, you will not need them). -The logging of the metapost run will be included in the regular log file. If an -error occurs, the logging will also be shown on the terminal. +The logs of the metapost run will be included in the regular log file. If an +error occurs, the log of the last snippet will also be shown on the terminal. \section As a stand-alone Metapost compiler %1 @@ -49,16 +49,26 @@ and ⟦\end{document}⟧ will be inserted automatically if omitted. \section LaTeX compatibility %1 -An experimental latex package is included in ⟦minim-mp.sty⟧. It accepts no -options, as it really is a rather thin wrapper around the plain tex package. -It does, however, provide a proper ⟦*metapost⟧ latex environment as an +An experimental latex package is included in ⟦minim-mp.sty⟧. It really is +a rather thin wrapper around the plain tex package, +but does provide a proper ⟦*metapost⟧ latex environment as an alternative to ⟦\directmetapost⟧. - -The ⟦metapost⟧ environment has no persistent backing instance. -You can create a similar environment ⟦envname⟧ that does with +The ⟦metapost⟧ environment has no persistent backing instance, +but you can create a similar environment ⟦envname⟧ that does with ⟦*\newmetapostenvironment [options] {envname}⟧. If your demands are even more complex, you should fall back to the plain tex -commands described below. +commands described in the next section. + +As in luamplib, you can use ⟦*\mpcolor {name}⟧ to insert the proper colour +values; this macro is only available inside the above environments. + +When the package is loaded with the option ⟦luamplib⟧, minim-mp will try and +act as a drop-in replacement for luamplib. The effort made is not very great +though, but it will define an ⟦mplibcode⟧ environment, as well as the +⟦\mplibcodeinherit⟧, ⟦\mplibshowlog⟧, ⟦\mplibsetformat⟧ and +⟦\mplibnumbersystem⟧ switches. +Please do note that this is not the recommended way of using minim-mp, which +remains the interface documented in the next section. \section Metapost instances %1 @@ -73,7 +83,7 @@ These are the options you can use: Option&Default&Description\cr ⟦jobname⟧&⟦':metapost:'⟧&Used in error messages.\cr ⟦format⟧&⟦'plain.mp'⟧&Format to initialise the instance with.\cr -⟦math⟧&⟦'scaled'⟧&One of ⟦scaled⟧, ⟦decimal⟧ or ⟦double⟧.\cr +⟦mathmode⟧&⟦'scaled'⟧&One of ⟦scaled⟧, ⟦decimal⟧ or ⟦double⟧.\cr ⟦seed⟧&⟦nil⟧&Random seed for this instance.\cr ⟦catcodes⟧&⟦0⟧&Catcode table for ⟦btex ... etex⟧.\cr ⟦env⟧© of ⟦_G⟧&Lua environment; see below.\cr @@ -103,7 +113,7 @@ tex can parse as a number. Say ⟦*\remainingmpimages \id⟧ for the number of images not yet retrieved. Finally, as a shorthand, ⟦*\runmetapostimage \id { code }⟧ will add -⟦beginfig ... endfig⟧ to your ⟦code⟧ and write the resulting image immediately +⟦beginfig ... endfig;⟧ to your ⟦code⟧ and write the resulting image immediately to the current list. @@ -364,7 +374,7 @@ A transparency group is a special kind of XForm, and these can be created from withing metapost: ⟦<id> =⟧ ⟦*saveboxresource (<attributes>) <picture>⟧ returns a number identifying the resource and can be fed attributes in the same way as ⟦setgstate⟧. XForms defined through metapost are available to other metapost -instances, but not to tex, though the macro painting them, ⟦*boxresource <id>⟧, +instances but not to tex, though the macro painting them, ⟦*boxresource <id>⟧, also accepts identifiers of tex-defined box resources. There remains a subtle difference, however: metapost-defined box resources are placed at their original origin. @@ -429,6 +439,11 @@ Each metapost instance is a table containing the following entries: ⟦catcodes⟧&Number of the catcode table used with ⟦btex ... etex⟧.\cr ⟦env⟧&The lua environment for ⟦runscript⟧.\cr} +Default values for the format and number system are available in the +⟦M.default_format⟧ and ⟦M.default_mathmode⟧ variables, +while ⟦M.on_line⟧ controls whether the logs are always printed to the +terminal. + \section Debugging %1 diff --git a/macros/luatex/generic/minim-mp/minim-mp.lua b/macros/luatex/generic/minim-mp/minim-mp.lua index 9069ab7147..fba6573239 100644 --- a/macros/luatex/generic/minim-mp/minim-mp.lua +++ b/macros/luatex/generic/minim-mp/minim-mp.lua @@ -228,7 +228,7 @@ end local function parse_lua_table(name, str) local f, msg = load('return '..str, name, 't') if msg then return alloc.err(msg) end - t = f() + local t = f() if not t or type(t) ~= 'table' then return alloc.err('%s attributes must be given as lua table', name) end @@ -772,9 +772,9 @@ local function get_boxresource_center (id) end end -E.set_boxresource_dimensions = set_boxresource_dimensions -E.get_boxresource_dimensions = get_boxresource_dimensions -E.get_boxresource_center = get_boxresource_center +E.set_boxresource_dimensions = set_boxresource_dimensions +E.get_boxresource_dimensions = get_boxresource_dimensions +E.get_boxresource_center = get_boxresource_center local function save_as_boxresource(pic) -- attributes @@ -1248,6 +1248,21 @@ function E.quote_for_lua(s) return E.quote(mkluastring(s)) end +-- These are here so that luametafun does not bug out immediately. Please note +-- that using metafun is not otherwise supported or recommended. +local cct_maketext = alloc.registernumber('minim:mp:catcodes:maketext') +E.catcodes = { numbers = + { inicatcodes = cct_maketext , texcatcodes = cct_maketext + , luacatcodes = cct_maketext , notcatcodes = cct_maketext + , vrbcatcodes = cct_maketext , prtcatcodes = cct_maketext + , ctxcatcodes = cct_maketext , txtcatcodes = cct_maketext +} } +E.mp = mp or { } -- all seem nonessential +E.mp.mf_path_reset = E.mp.mf_path_reset or function() end +E.mp.mf_finish_saving_data = E.mp.mf_finish_saving_data or function() end +E.mp.report = E.mp.report or function() end + + --2 typesetting with tex -- The result of the maketext function is fed back into metapost; on that side, @@ -1330,7 +1345,6 @@ function M.make_glyph(glyphname, fnt) alloc.err('Font not found: %s (id %s)', fnt, fontid) return { }, 10 end - local fontname = thefont.psname local gid = luaotfload.aux.gid_of_name(fontid, glyphname) if not gid then alloc.err('Font %s has no glyph named %s', thefont.psname, glyphname) @@ -1393,6 +1407,9 @@ M.get_contours = E.get_contours --2 opening, running and and closing instances +M.default_format = 'plain.mp' +M.default_mathmode = 'scaled' + local function apply_default_instance_opts(t) return { ini_version = true @@ -1401,7 +1418,7 @@ local function apply_default_instance_opts(t) , find_file = new_file_finder() --, script_error = ... , job_name = t.jobname - , math_mode = t.math or 'scaled' + , math_mode = t.mathmode or t.math or M.default_mathmode , random_seed = t.seed or nil -- , file_line_error_style , make_text = maketext @@ -1440,14 +1457,18 @@ function M.run (nr, code) save_image_list(self, picts) end -M.init_code = { 'let dump=endinput;', 'input INIT;', 'input minim-mp.mp;', 'input minim.mp;' } +M.init_code = { 'let dump=endinput;' + , 'boolean mplib; mplib:=true;' + , 'input INIT;' + , 'input minim-mp.mp;' + , 'input minim.mp;' } local maketext_catcodes = alloc.registernumber('minim:mp:catcodes:maketext') function M.open (t) local nr = #instances + 1 t.jobname = t.jobname or ':metapost:' -- creating instance options - local init = string.gsub(table.concat(M.init_code, ''), 'INIT', t.format or 'plain.mp') + local init = string.gsub(table.concat(M.init_code, ''), 'INIT', t.format or M.default_format) local opts = apply_default_instance_opts(t) local instance = mplib.new(opts) -- adding the instance diff --git a/macros/luatex/generic/minim-mp/minim-mp.pdf b/macros/luatex/generic/minim-mp/minim-mp.pdf Binary files differindex 9ea6a51612..eae0cd360e 100644 --- a/macros/luatex/generic/minim-mp/minim-mp.pdf +++ b/macros/luatex/generic/minim-mp/minim-mp.pdf diff --git a/macros/luatex/generic/minim-mp/minim-mp.sty b/macros/luatex/generic/minim-mp/minim-mp.sty index 250e86668e..6240c80b9a 100644 --- a/macros/luatex/generic/minim-mp/minim-mp.sty +++ b/macros/luatex/generic/minim-mp/minim-mp.sty @@ -29,7 +29,7 @@ \newmetapostinstance[#1]\mnm@tmp \expandafter\let\csname #2@instance\endcsname\mnm@tmp \newenvironment{#2}{% - \begingroup \catcode`\#=12 + \begingroup \catcode`\#=12 \catcode`\%=12 \let\mpcolor = \minimpcolor \csname:#2:\endcsname} {\csname end:#2:\endcsname \endgroup}% @@ -50,3 +50,33 @@ \newtoks\mpcolorspectoks \newtoks\mpcolorruntoks \newtoks\mpcolorvaltoks \mpcolorruntoks{\expandafter\extractcolorspec\the\mpcolorspectoks\mptmpcolor \expandafter\mpcolorvaltoks\expandafter\@gobble\mptmpcolor} + +% as a rather fragile drop-in replacement for luamplib +\DeclareOption{luamplib}{% + \def\mplibtextextlabel#1{} % always enabled + \def\mplibshowlog#1{\directlua{ + require 'minim-mp'.on_line = % + \ifcsname minimp@switch@#1\endcsname + true \else false \fi }} + \newmetapostenvironment[jobname='\jobname']{@mplibcode} + \def\mplibcodeinherit#1{% + \ifcsname minimp@switch@#1\endcsname + \let\mplibcode=\@mplibcode + \let\endmplibcode=\@mplibcode + \else + \let\mplibcode=\metapost + \let\endmplibcode=\endmetapost + \fi} + \mplibcodeinherit{disable} + \def\mplibsetformat#1{\directlua{ + require 'minim-mp'.default_format = '#1.mp' }} + \def\mplibnumbersystem#1{\directlua{ + require 'minim-mp'.default_math = '#1' }} +} \ProcessOptions* + +\let\minimp@switch@@enable=\relax +\let\minimp@switch@@enabled=\relax +\let\minimp@switch@@true=\relax +\let\minimp@switch@@yes=\relax + + diff --git a/macros/luatex/generic/minim-mp/minim-mp.tex b/macros/luatex/generic/minim-mp/minim-mp.tex index 6d95334677..7b8cef6c6b 100644 --- a/macros/luatex/generic/minim-mp/minim-mp.tex +++ b/macros/luatex/generic/minim-mp/minim-mp.tex @@ -16,6 +16,7 @@ \newcatcodetable \minim:mp:catcodes:mpcode {\catcodetable\minim:initcatcodes \catcode`\{=1 \catcode`\}=2 \catcode9=10 + \catcode`\#=12 \catcode`\%=12 \catcode`\~=12 \savecatcodetable\csname minim:mp:catcodes:mpcode\endcsname} \directlua { require('minim-mp') } diff --git a/macros/luatex/generic/minim-pdf/README b/macros/luatex/generic/minim-pdf/README index 0626208f2f..c449a9a2c4 100644 --- a/macros/luatex/generic/minim-pdf/README +++ b/macros/luatex/generic/minim-pdf/README @@ -1,4 +1,4 @@ -Version: 2024/1.4 +Version: 2024/1.5 SUMMARY @@ -27,26 +27,39 @@ In order to obtain the typeset manual of this package, simply say: HISTORY +2024/1.5 (in development) + + New features: + + * Add \autotagplainoutput and \sectionstructure. + * Allow bookmarks that are not associated to structure elements. + * The characters ~#% need no longer be escaped in a \hyperlink url. + + Bug fixes: + + * When writing the document structure is disabled, emit a warning instead + of an error for incompatible structure types. + 2024/1.4 (5/1/2024) Breaking changes: - * Table cells and headers now connect automatically in \automarktable + * 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 + * 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: - * Add \markcolumnspan and \markrowspan - * Add \markcolumnhead and \markrowhead + * Add \markcolumnspan and \markrowspan. + * Add \markcolumnhead and \markrowhead. 2023/1.2 (3/3/2023) diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.doc b/macros/luatex/generic/minim-pdf/minim-pdf.doc index 111d8c1423..3dc0349aca 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.doc +++ b/macros/luatex/generic/minim-pdf/minim-pdf.doc @@ -14,8 +14,8 @@ all other features of the package are described here. \else \startmetadata author {Esger Renkema} title {minim-pdf} - date {2024-01-05} - version {2024/1.4} + date {2024-02-25} + version {2024/1.5} keywords {LuaTeX; Plain TeX; PDF/A; Tagged PDF; accessibility; a11y; hypertext; bookmarks; document outline; associated files} stopmetadata @@ -40,9 +40,16 @@ Hyperlinks can be made with 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). +After the ⟦url⟧ action, the characters \Uchar`\~, \# and \% need not be escaped. +(Of course, this does nothing for already-tokenised text; be aware of this when +you wrap ⟦\hyperlink⟧ into another macro.) +Any spaces after the ⟦<action>⟧ will be ignored. + 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. +The ⟦alt⟧ options sets the ⟦/Contents⟧ key that is required by PDF/UA (where it +has the purpose of an alt text). A named destination can be created with ⟦*\nameddestination {...}⟧ (also in horizontal mode, unlike the backend primitive) and if you cannot think of @@ -53,18 +60,24 @@ generated name. \section Bookmarks %1 -Bookmarks (also known as outlines) can be added with ⟦*\outline [open|closed] -[dest {name}] {title}⟧. +Bookmarks (also known as outlines) can be added with +⟦*\outline [level n] [open|closed] [dest {name}] {title}⟧. Add ⟦open⟧ or ⟦closed⟧ to have the bookmark appear initially open or closed -(default), and +(the 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). -A bookmark is automatically associated with the current structure element and -the hierarchy of structure elements determines the nesting of bookmarks. -Therefore, if you want nested bookmarks, you \emph{must} precede the ⟦\outline⟧ -command with a declaration of the current structure element, even if you have -otherwise disabled tagging. See the next chapter on how to do this. +In the absence of the ⟦level⟧ option, the bookmark is automatically associated +with the current structure element and the hierarchy of structure elements +determines the nesting of bookmarks. This works even if you have otherwise +disabled tagging and is the recommended way of generating outlines. +(You can find all relevant macros in next chapter under ‘Document structure’ +and ‘Structure element aliases’.) + +As a fallback, outlines specified with the ⟦level n⟧ option will be inserted at +the end of the current outline list at the specified level (${\tt n}≥1$ and +need not be contiguous). Both methods can be intertwined, but please use the +document structure if you can. \section Page labels %1 @@ -201,6 +214,46 @@ For a more general introduction to and discussion of tagged pdf, please read the (excellent) manual of latex’s tagpdf package. +\section Quick-start guide % 1 + +The minimal setup for producing tagged pdf from plain tex documents is +something like the following: + +⟦% first update all fonts to TrueType (ttf) or OpenType (otf) +\input luaotfload.sty +% ... font redefinitions omitted ... +\input minim-mp +\pdfalevel 2a % declare pdf/a conformance, enable tagging +\autotagplainoutput % update the output routine +% create section markers and counters +\sectionstructure { subsection, section, chapter }⟧ + +You can then update your sectioning macros to look like this: + +⟦\def\section#1\par{% + % space above + \bigskip \goodbreak + % structure and outline (this is the new part) + \marksection \outline open {#1} + % section header + \noindent {\bf \the\chapternr.\the\sectionnr. #1} + % space below + \smallskip \noindent}⟧ + +Other macros you might have for laying out structural elements, such as tables +or lists, should of course also be updated. The rest of this chapter describes +the tools you can use. + +Please be advised that producing tagged pdf will likely forever remain +a fragile and error-prone process. +You should always validate the resulting pdf. +An easy-to-use and free validator is \hyperlink alt {The veraPDF website.} +url {https://verapdf.org/home}veraPDF\endlink. +For inspecting the document structure, you can use the ⟦pdfinfo⟧ utility that +comes with the \hyperlink alt {The Poppler website.} +url {https://poppler.freedesktop.org/}Poppler\endlink\ pdf library. + + \section Purpose, limitations and pitfalls %1 The main purpose of this package is semi-automatically marking up the @@ -213,7 +266,7 @@ Furthermore, while the macros in this package are sophisticated enough that tagging can be done without any manual intervantion, it is quite possible and rather easy to generate the wrong document structure, or even cause syntax errors in the resulting pdf code. -You should always check the result in an external application. +You should always inspect and validate the result. This is the full list of limitations, pitfalls and shortcomings: \startlist @@ -223,15 +276,13 @@ can mark out-of-order content explicitly if you know what you are doing; see below). \item2. The contents of ⟦\localleftbox⟧ and ⟦\localrightbox⟧ must be marked manually, probably as artifact. -\item3. You must mark page header, page footer and footnote rule yourself; no -default is set. -\item4. There currently is no way of marking xforms or other pdf objects as +\item3. There currently is no way of marking xforms or other pdf objects as content items of themselves. -\item5. The content of xforms (i.e. pdf objects created by ⟦\useboxresource⟧) +\item4. The content of xforms (i.e. pdf objects created by ⟦\useboxresource⟧) should not contain tagging commands. -\item6. Likewise, you should be careful with box reuse: it might work, but you +\item5. Likewise, you should be careful with box reuse: it might work, but you should check. -\item7. This package currently only supports pdf~1.7 tagging and is not yet +\item6. This package currently only supports pdf~1.7 tagging and is not yet ready for use with pdf~2.0. \stoplist @@ -279,7 +330,7 @@ Use of ⟦ActualText⟧, ⟦Alt⟧ or ⟦Lang⟧ attribute on MCIs, while allowe pdf standard, is not supported by this package. You should set these on the structure element instead. -The begining and ending of a content item can be forced with +The beginning and ending of a content item can be forced with ⟦*\startcontentitem⟧ and ⟦*\stopcontentitem⟧, while ⟦*\ensurecontentitem⟧ will only open a new content item if you are currently outside any. If you need some part to be a single content item, you can use @@ -336,17 +387,10 @@ All these helpful features can be disabled by setting will have to be closed by an explicit closing tag, as in xml. In this case, ⟦\stopelement⟧ and ⟦\ensurestopelement⟧ will be equivalent. -By default, ⟦P⟧ structure elements are inserted automatically at the start of -every paragraph. The tag can be changed with ⟦*\nextpartag {Tag}⟧; leaving the -argument empty will prevent marking the next paragraph. -Keep in mind that the reassignment is local: if a paragraph marked with -⟦\nextpartag⟧ starts inside a group, it will not reset. -Auto-marking paragraphs can be (locally) disabled or enabled by saying -⟦*\markparagraphsfalse⟧ or ⟦*\markparagraphstrue⟧. - You can query the place in the document structure of any point with ⟦*\showdocumentstructure⟧. + \section Structure element aliases %1 New structure element tags can be created with @@ -360,6 +404,33 @@ Any aliases you declare will be written to the pdf’s ⟦RoleMap⟧ only if the have actually been used. +\section Automatic tagging of paragraphs %1 + +By default, ⟦P⟧ structure elements are inserted automatically at the start of +every paragraph. The tag can be changed with ⟦*\nextpartag {Tag}⟧; leaving the +argument empty will prevent marking the next paragraph. +Keep in mind that the (internal) reassignment is local: if a paragraph marked +with ⟦\nextpartag⟧ starts inside a group, it will not reset. +Hence, to avoid surprises, you should have ⟦\nextpartag⟧ and the start of your +paragraph at the same grouping level. + +Useful structure elements for ⟦\nextpartag⟧ include ⟦H⟧ for headings and ⟦LI⟧ +for list items. Since minim-pdf produces strongly-structured documents, the +tags ⟦H1⟧, ⟦H2⟧, ⟦H3⟧ etc. should not be used. + +Please also note that if you add ⟦\hbox⟧es directly to a vertical list +(this includes ⟦\line⟧, ⟦\centerline⟧ and the like), +the ⟦\everypar⟧ token list is not inserted and no new paragraph structure element +will be opened. +The contents of the ⟦\hbox⟧ will be added to the current structure element, and +this may result in an invalid structure hierarchy (and an error messsage +reading ‘Structure type mismatch’). +You can make your intentions clear by inserting ⟦\startelement{P}⟧ at the +appropriate place (see above). + +Auto-marking paragraphs can be (locally) disabled or enabled by saying +⟦*\markparagraphsfalse⟧ or ⟦*\markparagraphstrue⟧. + \section Manipulating the logical order %1 With the process outlined above, the logical order of structure elements has to @@ -504,7 +575,7 @@ list item. Of course, this only works with two-column tables. Cells spanning multiple cells or rows can be marked with ⟦*\markcolumnspan {width}⟧ and ⟦*\markrowspan {height}⟧; these statements may not occur before ⟦\marktablecell⟧. Note that while ⟦\markcolumnspan⟧ properly -increases the (internal) column number, ⟦\markcolumnrow⟧ does nothing of the +increases the (internal) column number, ⟦\markrowspan⟧ does nothing of the sort (and indeed, no general logic can be given in the latter case). Always proceed with caution when using cells spanning multiple rows, and inspect the resulting structure carefully. @@ -529,6 +600,46 @@ For tagging (foot)notes, ⟦*\marknoteref{*}⟧ and ⟦*\marknotelbl{*}⟧, when around the footnote markers, will insert the proper ⟦Ref⟧, ⟦Note⟧ and ⟦Lbl⟧ tags. +Helper macros for tagging sections can be setup quickly with +⟦*\sectionstructure { <section list> }⟧. The ⟦<section list>⟧ should be an +increasing comma-separated list of section types, e.g. +⟦{subsection, section, chapter}⟧. This will first reserve the ⟦\count⟧ +registers ⟦\subsectionnr⟧ etc, then create the structure aliases ⟦/Subsection⟧ +etc. and finally define the helper macros ⟦\marksubsection⟧ etc, which will do +the following: +\startlist +\item 1. Call ⟦\ensurestopelement⟧ on all lower section types. +\item 2. Set all lower section number counts to zero. +\item 3. Increase the current section type number by one. +\item 4. Call ⟦\startelement⟧ for the current section type. +\item 5. Set the ⟦\nextpartag⟧ to ⟦H⟧. +\stoplist +The proper place for these helper macros is just before the section heading; +inbetween those two may come an ⟦\outline⟧ statement (see the previous +chapter). You can set the ⟦title⟧ option to the internal ⟦\startelement⟧ +statement with an optional argument (e.g. +⟦\marksection [Section \the\sectionnr]⟧). + +\section Tagging the output routine %1 + +The command ⟦*\autotagplainoutput⟧ will try and update plain tex’s output routine +to produce tagged page artifacts and footnotes. +It redefines ⟦\makeheadline⟧, ⟦\makefootline⟧, +⟦\footnoterule⟧, ⟦\footnote⟧ and ⟦\vfootnote⟧. +Headline and footline will not be marked as artifacts if their contents equal +⟦\hfil⟧; the footnote macros are edited to include the ⟦\marknoteref⟧ and +⟦\marknotelbl⟧ macros described above. +Note that the ⟦\topinsert⟧, ⟦\midinsert⟧ and ⟦\pageinsert⟧ macros are left +untouched; you will have to mark those explicitly. + +You can make some changes to the affected macros before calling +⟦\autotagplainoutput⟧, as it tries to be smart about it. +Though the redefinitions involve a full expansion, +most conditionals and common typesetting instructions (⟦\line⟧, ⟦\quad⟧, +⟦\strut⟧ etc.) are safe-to-use and will not be expanded. +If you include custom macros of your own, however, it is wise to have those +⟦\protected⟧. + % \endmanual diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.lua b/macros/luatex/generic/minim-pdf/minim-pdf.lua index 875fd89659..101a548e4b 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.lua +++ b/macros/luatex/generic/minim-pdf/minim-pdf.lua @@ -229,7 +229,13 @@ local function determine_parent_node(current, se) end end if not c then - alloc.err('Structure type mismatch: %s in %s', se.struct, current.struct) + if tagging_enabled() then + alloc.err('Structure type mismatch: %s in %s', + pdf_name(se.struct), pdf_name(current.struct)) + else + alloc.msg('Warning (minim-pdf): Structure type mismatch (%s in %s)', + pdf_name(se.struct), pdf_name(current.struct)) + end end return c or current end @@ -820,6 +826,13 @@ alloc.luadef('lastdestinationname', function() tex.sprint(last_dest_name()) end) -- provide the arguments to \pdfextension startlink local link_types = { } +local function make_link_attr(s) + local 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 + table.insert(attr, '}') + return attr +end alloc.luadef('hyper:makelink', function() local s = options_scanner() :argument('alt'):argument('contents') -- same thing @@ -832,18 +845,32 @@ alloc.luadef('hyper:makelink', function() 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) + local attr = make_link_attr(s) tex.sprint(table.concat(attr, ' ')) + tex.sprint('user {') + tex.write(user) + tex.sprint('}\\ignorespaces') end) +local function scan_escaped_argument(chars_to_escape) + local stored = { } + for char in string.utfvalues(chars_to_escape) do + stored[char] = tex.getcatcode(char) + tex.setcatcode(char, 12) + end + local res = token.scan_argument() + for char, code in pairs(stored) do + tex.setcatcode(char, code) + end + return res +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())) + local url = scan_escaped_argument ('~#$%^&_') + return string.format('/Subtype/Link /A <</S/URI /URI %s>>', pdf_string(url)) end function link_types.user() return token.scan_argument() end @@ -855,7 +882,7 @@ function link_types.last() return '/Subtype/Link /A <</S/Named /N/LastPage>>' en -- 1 bookmarks (outlines) -- start with a dummy top-level bookmark -local bookmarks = { { count = 0 } } +local bookmarks = { { count = 0, level = 0 } } structure[1].bookmark = bookmarks[1] local function write_bookmarks() @@ -870,13 +897,13 @@ local function write_bookmarks() ' <</Type /Outlines /First %s 0 R /Last %s 0 R /Count %s >> ', bookmarks[2].outline_obj, bookmarks[1].last.outline_obj, #bookmarks - 1)) -- write bookmark objects - for i=2, #bookmarks do + for i=2, #bookmarks do -- skip the root (i=1) 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, 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 + if bm.struct and bm.struct.objnum then -- false if no structures are written out insert_formatted(res, '/SE %s 0 R', bm.struct.objnum) end if bm.count > 0 then @@ -888,37 +915,51 @@ local function write_bookmarks() end function M.add_bookmark(bm) - local se = current_structure_element() - bm.parent, bm.count = false, 0 - bm.dest = bm.dest or write_new_dest() - se.bookmark, bm.struct = bm, se - -- find and update the parent bookmarks - local p = se.parent - while p do - local pbm = p.bookmark - if pbm then - if not bm.parent then - if pbm.last then pbm.last.next = bm end - bm.parent, bm.prev = pbm, pbm.last - pbm.first, pbm.last = pbm.first or bm, bm + local pbm + if bm.level then + for i = #bookmarks, 1, -1 do + pbm = bookmarks[i] + if pbm.level < bm.level then + break end - -- update the count until the first closed bookmark - pbm.count = pbm.count + 1 - p = pbm.open and pbm.parent - else - p = p.parent end + else + local se = current_structure_element() + if se.bookmark then + return alloc.err('Structure element %s already has a bookmark', pdf_name(se.struct)) + end + se.bookmark, bm.struct = bm, se + -- derive parent from strucrure + pbm = se.parent + while not pbm.bookmark do + pbm = pbm.parent + end + pbm = pbm.bookmark end + -- initial values; links to other bookmarks + bm.parent, bm.count = false, 0 + bm.dest = bm.dest or write_new_dest() + if pbm.last then pbm.last.next = bm end + bm.parent, bm.prev = pbm, pbm.last + pbm.first, pbm.last = pbm.first or bm, bm + bm.level = bm.level or (pbm.level + 1) + -- update the count until the first closed bookmarks + repeat + pbm.count = pbm.count + 1 + pbm = pbm.open and pbm.parent + until not pbm + -- append to the list table.insert(bookmarks, bm) end alloc.luadef('outline', function() - local s = options_scanner() + local bm = options_scanner() :keyword('open', 'closed') :string('dest') + :int('level') :scan() - s.title = token.scan_string() - M.add_bookmark(s) + bm.title = token.scan_string() + M.add_bookmark(bm) end, 'protected') @@ -987,7 +1028,7 @@ alloc.luadef('embedfile', function() t.name = t.file or alloc.err('No name specified for embedded file stream') end t.name = pdf_string(t.name or '(unnamed)') - local fs, ef = M.embed_file(t, t.global) + local fs, _ = M.embed_file(t, t.global) -- where to attach? if t.global then table.insert(attached_files, { name = t.name, objnum = fs } ) diff --git a/macros/luatex/generic/minim-pdf/minim-pdf.pdf b/macros/luatex/generic/minim-pdf/minim-pdf.pdf Binary files differindex 2b2ea69c42..2056601eec 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 8a5f738063..d5300fd58f 100644 --- a/macros/luatex/generic/minim-pdf/minim-pdf.tex +++ b/macros/luatex/generic/minim-pdf/minim-pdf.tex @@ -323,6 +323,84 @@ \markelement{Lbl}{{\stopformulatagging#1}}} \def\tagging:ensurestopNote{\ensurestopelement{Note}} +% \sectionstructure { subsection, section, chapter } +\def\sectionstructure{% + \newtoks\tag:sect:resets \tag:sect:resets{}% + \splitcommalist\tag:sect:a} +\def\tag:sect:a#1{\expandafter\tag:sect:b + \expandafter{\Uchar\uccode`#1}{#1}} +\def\tag:sect:b#1#2{% Section section + \addstructuretype Sect #1\relax + \expandafter\newcount\csname #2nr\endcsname + \expandafter\edef\csname mark#2\endcsname{% + \noexpand\withoptions[]% + \expandafter\noexpand\csname #2:domark\endcsname}% + \expandafter\edef\csname #2:domark\endcsname[##1]{% + \unexpanded{\ifhmode\errhelp{You should place section markers + in the vertical list, before the header.}\errmessage + {Section marker in horizontal list}\fi}% + \global\advance\expandafter\noexpand + \csname #2nr\endcsname1 + \noexpand\the\expandafter\noexpand + \csname #2:resets\endcsname + \startelement\noexpand\ifx=##1=\noexpand\else + title{##1}\noexpand\fi{#1}% + \nextpartag{H}\ignorespaces}% + \expandafter\newtoks\csname #2:resets\endcsname + \csname #2:resets\endcsname \tag:sect:resets + \etoksapp\tag:sect:resets{% + \noexpand\ensurestopelement{#1}% + \global\expandafter\noexpand\csname#2nr\endcsname0 }} + +% tagging the plain output routine +\begingroup\catcode`\@=11 +\gdef\autotagplainoutput{% + \let \vfootnot = \tag:vfootnote + \begingroup + % these should not be expanded + \tag:mkrelax + \ifhmode \ifvmode \ifnum \ifdim \ifodd \ifeven + \ifcondition \unless \else \fi \next \tmp + \strut \strutbox \footstrut + \the \number \empty \folio + \line \leftline \rightline \centerline + \nointerlineskip \offinterlineskip + \textindent \narrower \hang \llap \rlap + \enspace \thinspace \hidewidth + \quad \qquad \enskip + \@sf \fo@t \footins + \tag:mkrelax + % redefine head- and footlines + \def\footline{\taggedfootline}% + \def\headline{\taggedheadline}% + \xdef\makeheadline{\makeheadline}% + \xdef\makefootline{\makefootline}% + \xdef\footnoterule{\markartifact{Layout}\footnoterule} + % redefine vfootnote + \def\textindent##1{\noexpand\nextpartag{}% + \noexpand\marknotelbl{\noexpand\textindent{##1}}% + \noexpand\startelement{P}}% + \xdef\vfootnote##1{\vfootnote{##1}}% + % redefine footnote + \let\vfootnote=\relax \let\textindent=\relax + \def\fi##1\@sf{\noexpand\fi\noexpand\marknoteref{##1}\@sf} + \xdef\footnote##1{\footnote{##1}}% + \endgroup} +\endgroup +\def\tag:mkrelax#1{\ifx\tag:mkrelax#1\tag:else + \let#1=\relax\expandafter\tag:mkrelax\tag:fi} +\let\tag:else=\else \let\tag:fi=\fi +\newtoks \taggedheadline \newtoks \taggedfootline +\taggedheadline{\tagtoks:nonempty{Pagination /Subtype/Header}\headline} +\taggedfootline{\tagtoks:nonempty{Pagination /Subtype/Footer}\footline} +\def\tagtoks:nonempty#1#2{% + \edef\tagtoks:tmp{\the#2}% + \ifx\tagtoks:tmp\tagtoks:hfil + \the#2\else + \markartifact{#1}{\the#2}\fi} +\def\tagtoks:hfil{\hfil} + + % 1 tagging formulas % \autotagformulas @@ -408,7 +486,7 @@ % \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\endlink{\pdfextension endlink\ensurestopelement{Link}\relax} \protected\def\startlink{\startelement{Link}\pdfextension startlink} \protected\def\hyperlink{\quitvmode\startlink\hyper:makelink} diff --git a/macros/luatex/generic/minim/README b/macros/luatex/generic/minim/README index 4aa90c7cd8..a61e0f74ec 100644 --- a/macros/luatex/generic/minim/README +++ b/macros/luatex/generic/minim/README @@ -1,4 +1,4 @@ -Version: 2024/1.4 +Version: 2024/1.5 SUMMARY @@ -31,6 +31,10 @@ the packages mentioned above. HISTORY +2024/1.4 (25/2/2024) + + No changes. + 2024/1.4 (5/1/2024) * Documented the pdf resource management module diff --git a/macros/luatex/generic/minim/minim-doc.sty b/macros/luatex/generic/minim/minim-doc.sty index 93d807f16a..6f662dced6 100644 --- a/macros/luatex/generic/minim/minim-doc.sty +++ b/macros/luatex/generic/minim/minim-doc.sty @@ -42,7 +42,7 @@ \font\tenrm {Latin Modern Roman:script=latn;protrusion=default;} at 10pt \font\tenbf {Latin Modern Roman/B:script=latn;} at 10pt \font\tenit {Latin Modern Roman/I:script=latn;} at 10pt -\font\tentt {Latin Modern Mono:script=latn;+smcp;} at 10pt +\font\tentt {Latin Modern Mono:script=latn;} at 10pt \font\title {Latin Modern Roman/B:script=latn;} at 12pt \font\Title {Latin Modern Roman/B:script=latn;} at 16pt @@ -122,25 +122,8 @@ endfig;} % 1 document structure -% page artifacts -\edef\tmp{\markartifact{Pagination /Subtype/Footer}{\the\footline}} -\footline\expandafter{\tmp} - -% footnotes -\edef\footnoterule{\markartifact{Layout}\footnoterule} -\catcode`\@=11 -\def\footnote#1{\let\@sf\empty % parameter #2 (the text) is read later - \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi - \marknoteref{#1}\@sf\vfootnote{#1}} -\def\vfootnote#1{% - \insert\footins\bgroup - \interlinepenalty\interfootnotelinepenalty - \splittopskip\ht\strutbox % top baseline for broken footnotes - \splitmaxdepth\dp\strutbox \floatingpenalty\@MM - \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip - \nextpartag{}\marknotelbl{#1}\enspace\startelement{P}% - \footstrut\futurelet\next\fo@t} -\catcode`\@=12 +\autotagplainoutput +\def\textindent#1{\noindent#1\enspace\ignorespaces} % \startlist \item x. ... \stoplist \def\listskip{\vskip 3pt plus 2pt\vskip-\parskip} @@ -151,31 +134,26 @@ endfig;} \hbox to 2em{\hss\startelement{Lbl}#1.~~}% \startelement{LBody}\ignorespaces} +\sectionstructure{section, chapter} + % \chapter Title \par -\addstructuretype Sect Chapter \protected\def\chapter#1 \par{% \vfil\break - \ensurestopelement{Section}% - \global\advance\chapterno1 \global\sectionno0 - \startelement title{Chapter \the\chapterno}{Chapter}% + \markchapter[Chapter \the\chapternr]% \outline open {#1}% \addtotoc{\chapter{#1}{\lastdestinationname}}% \nextpartag{H}\quitvmode \red{\Title#1\hfill\copy\notehead}\bigskip\nobreak} -\newcount \chapterno % \section Title \par -\addstructuretype Sect Section \protected\def\section#1 \par{% \bigskip\penalty-50\relax - \global\advance\sectionno1 - \startelement title{Section \the\chapterno.\the\sectionno}{Section}% + \marksection[Section \the\chapternr.\the\sectionnr]% \outline closed {#1}% \addtotoc{\section{#1}{\lastdestinationname}}% \nextpartag{H}\quitvmode \red{\title#1}% \par\nobreak} -\newcount \sectionno % table of contents \newtoks\toc \newif\iftoc \toctrue diff --git a/macros/luatex/generic/minim/minim-pdfresources.lua b/macros/luatex/generic/minim/minim-pdfresources.lua index 468da03b48..66d168f4ec 100644 --- a/macros/luatex/generic/minim/minim-pdfresources.lua +++ b/macros/luatex/generic/minim/minim-pdfresources.lua @@ -121,7 +121,7 @@ local removal_regex = { local function update_page_resources(shippingout) if M.self_destruct then return end local resources = shippingout and pdf.getpageresources() or pdf.getxformresources() or '' - for kind, used in pairs(page_resources) do + for kind, _ in pairs(page_resources) do resources = string.gsub(resources, removal_regex[kind], '') local entry = make_resource_entry(kind) if entry then diff --git a/macros/luatex/generic/minim/minim.doc b/macros/luatex/generic/minim/minim.doc index e975718b1e..ef35a8ea4e 100644 --- a/macros/luatex/generic/minim/minim.doc +++ b/macros/luatex/generic/minim/minim.doc @@ -4,8 +4,8 @@ \startmetadata author {Esger Renkema} title {minim} - date {2024-01-05} - version {2024/1.4} + date {2024-02-25} + version {2024/1.5} keywords {LuaTeX; Plain TeX; MetaPost; PDF/A; Tagged PDF; accessibility; a11y; Unicode mathematics; XMP; metadata; hypertext; bookmarks} stopmetadata @@ -39,11 +39,8 @@ For building your own format files, you can re-use the file \red{minim.ini}: if you define ⟦\fmtname⟧ before inputting this file, no ⟦\dump⟧ will be performed. - -\bigskip - -\tocfalse \section Contents \par \bigskip -\begingroup \parskip1pt +\tocfalse \section Contents \par +\smallskip \begingroup \parskip1pt \protected\def\chapter#1#2#3{% \stopelement{TOC}\smallskip \nextpartag{}\red{\tenbf\marktocentry{#2}{}{\strut#1}{\hfill~}{#3}}% @@ -60,11 +57,9 @@ performed. One central design goal of minim is to be as unobtrusive as possible: you should be able to safely ignore any function you do not want to use. Please get in touch if you find this not the case. - Particular care has been taken to be compatible with \red{ltluatex}. All overlapping functions should produce the same results and ltluatex can be loaded either before or after minim. -Earlier incompatibilities with \red{pgf} have been resolved. \licencesection diff --git a/macros/luatex/generic/minim/minim.pdf b/macros/luatex/generic/minim/minim.pdf Binary files differindex 3848cf5103..e632089dfa 100644 --- a/macros/luatex/generic/minim/minim.pdf +++ b/macros/luatex/generic/minim/minim.pdf diff --git a/macros/luatex/generic/penlightplus/penlightplus.lua b/macros/luatex/generic/penlightplus/penlightplus.lua index 6aa1331fdd..a831c6721d 100644 --- a/macros/luatex/generic/penlightplus/penlightplus.lua +++ b/macros/luatex/generic/penlightplus/penlightplus.lua @@ -1,6 +1,6 @@ --% Kale Ewasiuk (kalekje@gmail.com) ---% 2023-12-08 ---% Copyright (C) 2021-2023 Kale Ewasiuk +--% 2024-02-29 +--% Copyright (C) 2021-2024 Kale Ewasiuk --% --% Permission is hereby granted, free of charge, to any person obtaining a copy --% of this software and associated documentation files (the "Software"), to deal @@ -143,8 +143,9 @@ function penlight.tex.wrth(s1, s2) -- helpful printing, makes it easy to debug, end wrt2('\n^^^^^\n') end +penlight.wrth = penlight.tex.wrth +penlight.tex.help_wrt = penlight.tex.wrth penlight.help_wrt = penlight.tex.wrth -penlight.wrth = penlight.help_wrt function penlight.tex.prt_array2d(t) for _, r in ipairs(t) do @@ -456,22 +457,18 @@ function str_mt.__index.subpar(s, r) end -function str_mt.__index.fmt(s, t, fmt) -- format a $1 string with an array or table and formats - -- formats can be a luakeys string, or a table - fmt = fmt or {} - if type(fmt) == 'string' then - fmt = penlight.tablex.strinds(penlight.luakeys.parse(fmt, {naked_as_value=true})) - end +function str_mt.__index.fmt(s, t, fmt) -- format a $1 and $k string with an array or table and formats + -- formats can be a luakeys string, or a table and are applied to table before string is formatted if type(t) ~= 'table' then t = {t} end t = penlight.tablex.strinds(t) - for k, f in pairs(fmt) do -- apply formats - t[k] = string.format('%'..f, t[tostring(k)]) - end + t = penlight.tablex.fmt(t, fmt, true) return s % t end - - +function str_mt.__index.parsekv(s, t) -- parsekv string + if type(t) ~= 'table' then t = penlight.luakeys.parse(t) end + return penlight.luakeys.parse(s, t) +end -- -- -- -- function stuff @@ -532,6 +529,37 @@ function penlight.tablex.strinds(t) -- convert indices that are numbers to strin end +function penlight.tablex.listcontains(t, v) + return penlight.tablex.find(t, v) ~= nil +end + + +-- format contents of a table +function penlight.tablex.fmt(t, fmt, strinds) + if fmt == nil then + return t + end + strinds = strinds or false -- if your fmt table should use string indexes + if type(fmt) == 'string' then + if not fmt:find('=') then -- if no = assume format all same + for k, v in pairs(t) do -- apply same format to all + if tonumber(v) ~= nil then -- only apply to numeric values + t[k] = string.format("%"..fmt, v) + end + end + return t + else + fmt = fmt:parsekv('naked_as_value') -- make fmt a table from keyval str + end + end + if strinds then fmt = penlight.tablex.strinds(fmt) end -- convert int inds to str inds + for k, f in pairs(fmt) do -- apply formatting to table + t[k] = string.format("%"..f, tostring(t[k])) + end + return t +end + + function penlight.tablex.map_slice(func, T, j1, j2) if type(j1) == 'string' then return penlight.array2d.map_slice(func, {T}, ','..j1)[1] @@ -556,24 +584,25 @@ function penlight.tablex.filterstr(t, exp, case) end end - +--todo add doc function penlight.utils.filterfiles(...) -- f1 is a series of filtering patterns, or condition -- f2 is a series of filtering patters, or condition -- (f1_a or f2_...) and (f2 .. ) must match local args = table.pack(...) - -- todo -- check where boolean is for recursive or not, set starting argument + -- dir, recursive[bool], filt1, filt2 etc... + -- OR recursive[bool], filt1, filt2, etc.. + -- OR filt1, filt2, filt3, etc.. -- this could allow one to omit dir - -- todo if no boolean at all, assume dir = '.' and r = false - -- if boolean given, assume dir = '.' + -- if boolean given ar arg 1, assume dir = '.' local nstart = 3 - local r = args[2] - local dir = args[1] + local r = args[2] -- recursive + local dir = args[1] -- start dir if type(args[1]) == 'boolean' then dir = '.' r = args[1] nstart = 2 - elseif type(args[2]) ~= 'boolean' then + elseif type(args[2]) ~= 'boolean' then -- if boolean given ar arg 1, assume dir = '.' dir = '.' r = false nstart = 1 @@ -591,7 +620,6 @@ end - -- -- -- -- -- -- -- -- functions below extend the array2d module @@ -808,6 +836,18 @@ function penlight.tex.split2items(s, d) end +function penlight.toggle_luaexpr(expr) + if expr then + tex.sprint('\\toggletrue{luaexpr}') + else + tex.sprint('\\togglefalse{luaexpr}') + end +end + + + + + penlight.tbls = {} penlight.rec_tbl = '' @@ -820,6 +860,11 @@ function penlight.get_tbl_name(s) return s end +function penlight.get_tbl(s) + s = penlight.get_tbl_name(s) + return penlight.tbls[s] +end + function penlight.get_tbl_index(s) local tbl = '' local key = '' @@ -830,6 +875,7 @@ function penlight.get_tbl_index(s) elseif s:find('/') then local tt = s:split('/') tbl = tt[1] + if tbl == '' then tbl = penlight.rec_tbl end key = tonumber(tonumber(tt[2])) if key < 0 then key = #penlight.tbls[tbl]+1+key end else @@ -837,6 +883,7 @@ function penlight.get_tbl_index(s) key = tonumber(s) or s if type(key) == 'number' and key < 0 then key = #penlight.tbls[tbl]+1+key end end + if tbl == '' then tbl = penlight.rec_tbl end if penlight.tbls[tbl] == nil or penlight.tbls[tbl][key] == nil then penlight.tex.pkgerror('penlightplus', 'Invalid index of tbl using: "'..s..'"') end @@ -881,10 +928,26 @@ function penlight.check_recent_tbl_undefault() end end +function penlight.def_tbl(ind, def, g) + local _tbl, _key = penlight.get_tbl_index(ind) + if def == '' then def = 'dtbl'.._tbl.._key end + token.set_macro(def, tostring(penlight.tbls[_tbl][_key]), g) +end + +function penlight.def_tbl_all(ind, def) + local _tbl = penlight.get_tbl_name(ind) + if def == '' then def = 'dtbl'.._tbl end + for k, v in pairs(penlight.tbls[_tbl]) do + token.set_macro(def..k, tostring(v)) + end +end + -- TODO TODO TODO get error working xy def, and referene which table for key-vals penlight.tbl_xysep = '%s+' -- spaces separate x-y coordinates -function penlight.def_tbl_coords(str, def) - -- todo could definitely make this flexible for a table... +function penlight.def_tbl_coords(ind, def) + local tbl, key = penlight.get_tbl_index(ind) + local str = penlight.tbls[tbl][key] + if def == '' then def = 'dtbl'..tbl..key end local x, y = str:strip():splitv(penlight.tbl_xysep) if (not penlight.hasval(x)) or (not penlight.hasval(y)) then penlight.tex.pkgerror('penlightplus', 'def_tbl_coords function could not parse coordiantes given as "'..str..'" ensure two numbers separated by space are given!', '', true) @@ -894,7 +957,14 @@ function penlight.def_tbl_coords(str, def) end --- + + + + + + + +-- global setting type stuff function penlight.make_tex_global() for k,v in pairs(penlight.tex) do -- make tex functions global diff --git a/macros/luatex/generic/penlightplus/penlightplus.pdf b/macros/luatex/generic/penlightplus/penlightplus.pdf Binary files differindex 17d95268eb..8f4fcb9717 100644 --- a/macros/luatex/generic/penlightplus/penlightplus.pdf +++ b/macros/luatex/generic/penlightplus/penlightplus.pdf diff --git a/macros/luatex/generic/penlightplus/penlightplus.sty b/macros/luatex/generic/penlightplus/penlightplus.sty index f5ffc0f176..ee7ff3ad11 100644 --- a/macros/luatex/generic/penlightplus/penlightplus.sty +++ b/macros/luatex/generic/penlightplus/penlightplus.sty @@ -1,6 +1,6 @@ % Kale Ewasiuk (kalekje@gmail.com) -% 2023-12-08 -% Copyright (C) 2021-2023 Kale Ewasiuk +% 2024-02-29 +% Copyright (C) 2021-2024 Kale Ewasiuk % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,7 @@ % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE % OR OTHER DEALINGS IN THE SOFTWARE. -\ProvidesPackage{penlightplus}[2023-12-08] +\ProvidesPackage{penlightplus}[2024-02-29] \RequirePackage{luacode} \RequirePackage{luakeys} @@ -84,22 +84,16 @@ \newtoggle{luaexpr}\togglefalse{luaexpr} \begin{luacode*} - function toggle_luaexpr(expr) - if expr then - tex.sprint('\\toggletrue{luaexpr}') - else - tex.sprint('\\togglefalse{luaexpr}') - end - end + \end{luacode*} -\NewDocumentCommand{\ifluax}{m m O{}}{% - \luadirect{toggle_luaexpr(#1)}% +\NewDocumentCommand{\ifluax}{m m O{}}{% if lua expression is true do {m} if not [o] + \luadirect{penlight.toggle_luaexpr(#1)}% \iftoggle{luaexpr}{#2}{#3}% \togglefalse{luaexpr}% safety set to false } -\NewDocumentCommand{\ifluaxv}{m m O{}}{\ifluax{penlight.hasval(#1)}{#2}[#3]} +\NewDocumentCommand{\ifluaxv}{m m O{}}{\ifluax{penlight.hasval(#1)}{#2}[#3]}% if lua expression is truthy do {m} else [o] @@ -116,6 +110,10 @@ penlight.rec_tbl = \luastring{#1} }} +\NewDocumentCommand{\tblfrkvCD}{m +m O{}}{\tblfrkv{#1}{#2}[#3]\tblkvundefcheck\tbldefall{}{}} +%% tbl from key-vals, then check defaults, then define all keys using default format +\NewDocumentCommand{\tblfrkvNCD}{m +m O{}}{\tblfrkvN{#1}{#2}[#3]\tblkvundefcheck\tbldefall{}{}} + \NewDocumentCommand{\tblfrkvN}{m +m O{}}{\luadirect{% penlight.rec_tbl_opts = penlight.luakeys.parse(\luastring{#3}) penlight.tbls[\luastring{#1}] = penlight.luakeys.parse(string.subpar(\luastringN{#2}), penlight.rec_tbl_opts) @@ -124,9 +122,38 @@ \NewDocumentCommand{\tblfrcsv}{m +m O{}}{\tblfrkv{#1}{#2}[naked_as_value=true,#3]} +\NewDocumentCommand{\tblfrcsvN}{m +m O{}}{\tblfrkvN{#1}{#2}[naked_as_value=true,#3]} + \NewDocumentCommand{\tblkvundefcheck}{}{\luadirect{penlight.check_recent_tbl_undefault()}}% check defaults list and throw error if foreign keys were used + + +\NewDocumentCommand{\tblapp}{m m}{\luadirect{% append to a table (ie using integer index) with a value (second arg) # todo option for string or number + __tbl__ = penlight.get_tbl_name(\luastring{#1}) + table.insert(penlight.tbls[__tbl__], \luastring{#2}) +}} + +\NewDocumentCommand{\tblcon}{m m}{\luadirect{% concatenate to a table (ie using integer index) with a list of comma separated values (second arg) # + __tbl__ = penlight.get_tbl_name(\luastring{#1}) + for k, v in ipairs(penlight.luakeys.parse(string.subpar(\luastring{#2}), {naked_as_value=true})) do + table.insert(penlight.tbls[__tbl__], v) + end +}} + +\NewDocumentCommand{\tbladd}{m m m}{\luadirect{% add a kv pair to a table + __tbl__ = penlight.get_tbl_name(\luastring{#1}) + penlight.tbls[__tbl__][\luastring{#2}] = \luastring{#3} +}} + +\NewDocumentCommand{\tbladdN}{m m m}{\luadirect{% add a kv pair to a table + __tbl__ = penlight.get_tbl_name(\luastring{#1}) + penlight.tbls[__tbl__][\luastring{#2}] = \luastringN{#3} +}} + + + + \NewDocumentCommand{\tblchg}{ m }{\luadirect{% change recent table penlight.rec_tbl = \luastring{#1} }} @@ -140,36 +167,21 @@ penlight.set_tbl_item(\luastring{#1}, \luastring{#2}) }} -% todo if no definition is provided, assume <tblname><key> -\NewDocumentCommand{\tbldef}{ m m }{\luadirect{% define a {table key}{def} - local _tbl, _key = penlight.get_tbl_index(\luastring{#1}) - local _tbl_def_ = \luastring{#2} - if _tbl_def_ == '' then _tbl_def_ = 'dtbl'.._tbl.._key end - token.set_macro(_tbl_def_, tostring(penlight.tbls[_tbl][_key])) -}} -\NewDocumentCommand{\tbldefall}{ m m }{\luadirect{% define all {table} keys to {table} \table<key1> - % todo maybe change tbldef {def} to [def] and make same as table name - local _tbl = penlight.get_tbl_name(\luastring{#1}) - local _tbl_def_ = \luastring{#2} - if _tbl_def_ == '' then _tbl_def_ = 'dtbl'.._tbl end - for k, v in pairs(penlight.tbls[_tbl]) do - token.set_macro(_tbl_def_..k, tostring(v)) - end -}} +\NewDocumentCommand{\tbldef}{ m m }{\luadirect{penlight.def_tbl(\luastring{#1}, \luastring{#2})}} +% define a table, use * to make global definition +\NewDocumentCommand{\tblgdef}{ m m }{\luadirect{penlight.def_tbl(\luastring{#1}, \luastring{#2}, 'global')}} -\NewDocumentCommand{\tblgdef}{ m m }{\luadirect{% define a table, use * to make global definition - token.set_macro(\luastring{#2}, tostring(penlight.get_tbl_item(\luastring{#1})), 'global') -}} - -\NewDocumentCommand{\tbldefxy}{ m m }{\luadirect{penlight.def_tbl_coords(penlight.get_tbl_item(\luastring{#1}), \luastring{#2})}}% define #2x and #2y from a space delimited x-y pair +\NewDocumentCommand{\tbldefall}{ m m }{\luadirect{penlight.def_tbl_all(\luastring{#1}, \luastring{#2})}} +\NewDocumentCommand{\tbldefxy}{ m m }{\luadirect{penlight.def_tbl_coords(\luastring{#1}, \luastring{#2})}}% define #2x and #2y from a space delimited x-y pair \NewDocumentCommand{\tblif}{m m O{}}{\ifluax{penlight.get_tbl_item(\luastring{#1})}{#2}[#3]} \NewDocumentCommand{\tblifv}{m m O{}}{\ifluaxv{penlight.get_tbl_item(\luastring{#1})}{#2}[#3]} +\NewDocumentCommand{\tblprt}{m}{\luadirect{penlight.wrth(penlight.get_tbl(\luastring{#1}),'penlightplus table: '..\luastring{#1})}} % legacy code, delete this \let\kvtblundefcheck\tblkvundefcheck diff --git a/macros/luatex/generic/penlightplus/penlightplus.tex b/macros/luatex/generic/penlightplus/penlightplus.tex index 988a51bb2a..53488619bf 100644 --- a/macros/luatex/generic/penlightplus/penlightplus.tex +++ b/macros/luatex/generic/penlightplus/penlightplus.tex @@ -1,6 +1,6 @@ % Kale Ewasiuk (kalekje@gmail.com) -% 2023-12-08 -% Copyright (C) 2021-2023 Kale Ewasiuk +% 2024-02-29 +% Copyright (C) 2021-2024 Kale Ewasiuk % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the "Software"), to deal @@ -35,11 +35,11 @@ \setlength\ResultBoxRule{0mm} \lstset{ language=[LaTeX]TeX, - basicstyle=\ttfamily\small, - commentstyle=\ttfamily\small\color{gray}, + basicstyle=\ttfamily\footnotesize, + commentstyle=\ttfamily\footnotesize\color{gray}, frame=none, numbers=left, - numberstyle=\ttfamily\small\color{gray}, + numberstyle=\ttfamily\footnotesize\color{gray}, prebreak=\raisebox{0ex}[0ex][0ex]{\color{gray}\ensuremath{\hookleftarrow}}, extendedchars=true, breaklines=true, @@ -53,7 +53,7 @@ \date{\today} -\RequirePackage{penlightplus} +\RequirePackage[pl,globals]{penlightplus} \title{penlightplus} \subtitle{Additions to the Penlight Lua Libraries} @@ -97,14 +97,28 @@ The gloals flags below are taken care of in the package options:\\ \cmd{__PL_NO_HYPERREF__} a flag used to change the behaviour of a function, depending on if you don't use the hyperref package\\ \cmd{__PDFmetadata__} a table used to store PDF meta-data + +\subsubsection*{global extras} +If the package option \cmd{globals} is used, many additional globals are set for easier scripting. +\cmd{pl.hasval}, \cmd{pl.COMP}, \cmd{pl.utils.kpairs}, \cmd{pl.utils.npairs} become globals. +\cmd{pl.tablex} is aliased as \cmd{pl.tbx and tbx} (which also includes all native Lua table functions), and +\cmd{pl.array2d} is aliased as \cmd{pl.a2d and a2d}. + +If you want global \cmd{pl.tex} funcs and vars, call \cmd{pl.make_tex_global()}\\ + + \subsubsection*{penlight additions} Some functionality is added to penlight and Lua. \llcmd{pl.hasval(x)} Python-like boolean testing\\ \llcmd{COMP'xyz'()} Python-like comprehensions:\\\url{https://lunarmodules.github.io/Penlight/libraries/pl.comprehension.html}\\ + +\cmd{clone_function(f)} returns a cloned function\\ +\cmd{operator.strgt(a,b)} compares strings a greater than b (useful for sorting)\\ +\cmd{operator.strlt(a,b)} compares strings a less than b (useful for sorting)\\ + \llcmd{math.mod(n,d)}, \cmd{math.mod2(n)} math modulous\\ -\\ \llcmd{string.}\cmd{upfirst(s)} uppercase first letter\\ \llcmd{string.}\cmd{delspace(s)} delete all spaces\\ @@ -123,6 +137,16 @@ Some functionality is added to penlight and Lua. a few improvements. \cmd{t} can be an array (reference items like \cmd{\$1} in the string), and \cmd{fmt} can be a table of formats (keys correspond to those in \cmd{t}), or a string that is processed by luakeys.\\ +\llcmd{string.}\cmd{parsekv(s, opts)} parse a string using \cmd{penlight.luakeys}. A string or table can be used for opts. + +\llcmd{tablex.}\cmd{fmt(t, f)} format a table with table or key-value string f\\ +\llcmd{tablex.}\cmd{strinds(t)} convert integer indexes to string indices (1 -> '1')\\ +\llcmd{tablex.}\cmd{filterstr(t,e,case)} keep only values in table t that contain expression e, case insensitive by default.\\ +\llcmd{tablex.}\cmd{mapslice(f,t,i1,i2)} map a function to elements between i1 and i2\\ +\llcmd{tablex.}\cmd{listcontains(t,v)} checks if a value is in a array-style list \\ + + + \llcmd{pl.}\cmd{char(n)} return letter corresponding to 1=a, 2=b, etc.\\ \llcmd{pl.}\cmd{Char(n)} return letter corresponding to 1=A, 2=B, etc.\\ @@ -156,17 +180,13 @@ to a package warning saying \cmd{'cs' was declared and used in document, but nev is true, it will overwrite an existing command (using \cmd{defcmd}), otherwise, it will throw error like \cmd{newcmd}.\\ \llcmd{get_ref_info(l)}accesses the \cmd{\r@label} and returns a table\\ +\subsubsection*{Recording latex input} +\cmd{penlight.tex.startrecording()} start recording input buffer without printing to latex\\ +\cmd{penlight.tex.stoprecording()} stop recording input buffer\\ +\cmd{penlight.tex.readbuf()} internal-use function that interprets the buffer. This will ignore an environment ending (eg. \cmd{end{envir}})\\\\ +\cmd{penlight.tex.recordedbuf} the string variable where the recorded buffer is stored\\ -\subsubsection*{global extras} -If the package option \cmd{globals} is used, many additional globals are set for easier scripting. -\cmd{pl.hasval}, \cmd{pl.COMP}, \cmd{pl.utils.kpairs}, \cmd{pl.utils.npairs} become globals. -\cmd{pl.tablex} is aliased as \cmd{pl.tbx and tbx} (which also includes all native Lua table functions), and -\cmd{pl.array2d} is aliased as \cmd{pl.a2d and a2d}. - -If you want global \cmd{pl.tex} funcs and vars, call \cmd{pl.make_tex_global()} -\\ - \subsection*{Macro helpers} @@ -224,17 +244,27 @@ no, once, twice, or full expansion. For example, we can control the expansion of \cmd{\tblchg{t}} changes the 'recent' table\\ \cmd{\tblfrkv{t}{key-val string}[luakeys opts]} new table from key-vals using \cmd{luakeys} \\ \cmd{\tblfrkvN{t}{key-val string}[luakeys opts]} does not expand key-val string \cmd{luakeys} \\ - \cmd{\tblfrcsv} a shorthand \cmd{\tblfrkv{t}{csv}[naked_as_value=true,opts]}, a good way to convert a comma-separated list to an array\\ +\cmd{\tblfrkvCD{t}{key-val string}[luakeys opts]} define tbl from key-val, +check if any were not defined as defaults (see below), and then push all to definitions\\ +\cmd{\tblkvundefcheck} will throw an error if you use define a table from key-values +and use a key that was not specified in the luakeys parse options via \cmd{opts.defaults} or \cmd{opts.defs}. + \cmd{\tblfrcsv{t}{csv}} a shorthand \cmd{\tblfrkv{t}{csv}[naked_as_value=true,opts]}, a good way to convert a comma-separated list to an array\\ + \cmd{\tblfrcsvN{t}{csv}} same as above, but the csv is not expanded. \cmd{\tblset{i}{v}} sets a value of the table/index \cmd{i} to \cmd{v}\\ -\cmd{\tblget{i}} gets the value and \cmd{tex.sprint()}'s it\\ +\cmd{\tblget{i}} gets the value and \cmd{tex.sprint()}s it\\ +\cmd{\tbladd{i}{v}} add a new value to a table using index method\\ +\cmd{\tbladdN{i}{v}} above, but don't expand the value argument\\ +\cmd{\tblcon{t}{csv}} concatenate an array-style csv\\ +\cmd{\tblapp{t}{v}} append a value (integer-wise) to a table\\ \cmd{\tbldef{i}{d}} pushes the value to macro \cmd{d}\\ -\cmd{\tbldefall{t}{d}} define all item in table \cmd{t} (use recent if blank) with format \cmd{d<key>}, if \cmd{d} is blank, commands are defined as \cmd{tbl<t><k>} \\ -\cmd{\tblgdef{i}{d}} pushes the value to a global\\ +\cmd{\tbldefall{t}{d}} define all item in table \cmd{t} (use recent if blank) with format \cmd{d<key>} where d is your prefix. If d is blank, keys will be defined as \cmd{\dtbl<t><k>} +\cmd{\tblgdef{i}{d}} pushes the defined value to a global\\ \cmd{\tbldefxy{i}{d}} splits the value of item by spaces creates two definitions \cmd{\dx} and \cmd{\dy}. Useful for pasing tikz coordinates like \cmd{xy=0 5}\\ +For definiting tables, if \cmd{d} is blank, commands are defined as \cmd{dtbl<t><k>}\\ \cmd{\iftbl{i}{tr}[fa]} runs code \cmd{ta} if the item is true else \cmd{fr}\\ \cmd{\iftblv{i}{tr}[fa]} runs code \cmd{ta} if the item is truthy else \cmd{fr}\\ -\cmd{\tblkvundefcheck} will throw an error if you use define a table from key-values -and use a key that was not specified in the luakeys parse options via \cmd{opts.defaults} or \cmd{opts.defs}. + +\cmd{\tblprt{t}} print the table in console There are 3 ways to use the index (placeholder \cmd{{i}} above). \cmd{t.key} where \cmd{t} is the table name and \cmd{key} is a string key, @@ -248,6 +278,7 @@ or simply use \cmd{ind} without the table name, where the assumed table is the l \tblset{a}{tRuE!!} \tblget{a}\\ \tblget{my.x}\\ +\tblget{.x}\\ \tblif{n}{tr}[fa]\\ \tblifv{n}{TR}[FA]\\ \tblif{my.y}{Tr}[Fa]\\ @@ -263,14 +294,21 @@ or simply use \cmd{ind} without the table name, where the assumed table is the l \tblset{my.a}{12 36} \tbldefxy{my.a}{coord} (\coordx,\coordy) +\tbldefxy{my.a}{} (\dtblmyax,\dtblmyay) +\tbldefxy{a}{} (\dtblmyax,\dtblmyay) \tblfrcsv{me}{a,b,"c,see",d,e} \tblget{me/1},\tblget{2}\\ \tblget{3}\\ -\tblset{me/4}{D}\tblget{me/4}\\ +\tblset{me/4}{D}\tblget{me/4}\tblget{/4}\\ \tblset{5}{E}\tblget{5}\\ \tblget{-2},\tblget{me/-1}\\ +\tblget{/-3}\\ %% \tblget{k} % would throw error + +\tblfrkvCD{M}{a=A,b=B,d=D}[defaults={a,b,c,d}] +\dtblMa \dtblMb \dtblMc \dtblMd + \end{LTXexample} @@ -287,6 +325,38 @@ Note: for this versions: all latex tbl commands are now prefixed with \cmd{tbl}, Old-style commands eg. \cmd{gettbl} will be kept as aliases for a few more releases then removed. +\subsubsection*{A practical tbl example} + +\begin{LTXexample} +\begin{luacode*} + function prt_pyth() + t = pl.tbls.pyth + if not t.a then + pl.tex.pkgerror('must pass a= to \\pyth') + elseif not t.b then + t.b = (tonumber(t.c)^2 - + tonumber(t.a)^2)^0.5 + elseif not t.c then + t.c = (tonumber(t.a)^2 + + tonumber(t.b)^2)^0.5 + end + local t = pl.tbx.fmt(t,'.'..t.d..'f') -- format table according to d decimals + s = 'Right-angle sides a=$a and b=$b form a hypotenuse of c=$c' + pl.tex.prt(s:fmt(t)) + end +\end{luacode*} +\NewDocumentCommand{\pyth}{m}{% + \tblfrkv{pyth}{#1}[defaults={a=false,b=false,c=false,d=0,e=extras}] + \luadirect{prt_pyth()}% +} + +\pyth{a=3,c=5}\\ +\pyth{a=3.2,b=4.2,d=2}\\ +C: \tblget{c} + +\end{LTXexample} + + \subsection*{Splitting strings} Splitting text (or a cmd) into oxford comma format via: \cmd{\splittocomma[expansion level]{text}{text to split on}}: @@ -314,9 +384,20 @@ You can do a similar string split but to \cmd{\item} instead of commas with \cmd +\subsubsection*{PDF meta data (for pdfx package)} +\cmd{\writePDFmetadatakv*{m}} Take a key-value string (eg. \cmd{title=whatever, author=me}) and writes to the \cmd{jobname.xmpdata} file, to be used by pdfx. \cmd{*} will first clear the data\\ +\cmd{\writePDFmetadata} runs the lua function \cmd{penlight.tex.writePDFmetadata()}, +which pushes the lua variable \cmd{__PDFmetadata__} (a table) to the xmpdata file. + + +%\pyth{} % erro +%\begin{luacode*} +% pl.tex.wrth(('a,b,c=3'):parsekv('!naked_as_value'), 'dumy') +%\end{luacode*} + % \tblfrkv{tbl_def}{kale=cool,paul=gay,craig=fun} % \tblfrkv{tbl}{kale,paul=gay} %[naked_as_value=true] % @@ -338,24 +419,25 @@ You can do a similar string split but to \cmd{\item} instead of commas with \cmd % % \THINg[color=blue,size=tiny]{Kale} % -%\begin{luacode*} -%penlight.wrth(('$1'):fmt(5.5,'.8f','XYZ')) -%function prttol() -% penlight.tbls.tol[3] = penlight.tbls.tol[3] or 3 -% penlight.tbls.tol[4] = penlight.tbls.tol[1]*(1.0-penlight.tbls.tol[3]/100.0) + 0.0 -% penlight.tbls.tol[5] = penlight.tbls.tol[1]*(1.0+penlight.tbls.tol[3]/100.0) + 0.0 -% penlight.tbls.tol['k'] = 'fuckboi' -% penlight.wrth(('$k $1\\$2 (\\pmpct{$3} tolerance, $4\\ndash$5\\$2)'):fmt(penlight.tbls.tol, '4=.1f, 5=.1f'), 'XYZ') -%end -%\end{luacode*} -%\NewDocumentCommand{\prttol}{ m }{% {50.0,kV,3} % 50\us (\pmpct{20} tolerance, 40=--60\us) -%\tblfrcsv{tol}{#1}\luadirect{prttol()}} -% -%\prttol{50,kV,3.21} -% -%\begin{luacode*} -%--pl.wrth(('2.23'):gnum()) -%\end{luacode*} +\begin{luacode*} +function prttol() + local dec = penlight.tbls.tol[4] or 1 + penlight.wrth(dec,'??') + penlight.tbls.tol[3] = penlight.tbls.tol[3] or 3 + penlight.tbls.tol[4] = penlight.tbls.tol[1]*(1.0-penlight.tbls.tol[3]/100.0) + 0.0 + penlight.tbls.tol[5] = penlight.tbls.tol[1]*(1.0+penlight.tbls.tol[3]/100.0) + 0.0 + --penlight.tbls.tol['k'] = 'fuckboi' + --ttt = pl.tbx.fmt(penlight.tbls.tol, '.3f') + penlight.wrth(('$1\\$2 (\\pmpct{$3} tolerance, $4\\ndash$5\\$2)'):fmt(penlight.tbls.tol, '4=.'..dec..'f, 5=.'..dec..'f'), 'XYZ') +end +\end{luacode*} +\NewDocumentCommand{\prttol}{ m }{\tblfrcsv{tol}{#1}\luadirect{prttol()}}% {50.0,kV,3,P} % 50\us (\pmpct{20} tolerance, 40=--60\us), P is optional and precision of the range (number of decimals) + +\prttol{50,kV,3} + +\begin{luacode*} + pl.wrth(pl.filterfiles('.',true,'.*%.tex'), 'FF') +\end{luacode*} \end{document}
\ No newline at end of file |