diff options
author | Norbert Preining <norbert@preining.info> | 2021-07-27 03:01:57 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2021-07-27 03:01:57 +0000 |
commit | cef58753eba8ad7e2071195dbd92454febe82d32 (patch) | |
tree | df909696e42fb25ce108e30258c7f935ded86079 /support | |
parent | 84465d50d746dfed1bfbe7bb5065279665fa87de (diff) |
CTAN sync 202107270301
Diffstat (limited to 'support')
23 files changed, 1095 insertions, 396 deletions
diff --git a/support/make4ht/README b/support/make4ht/README index 490b9c4a63..89cfac9141 100644 --- a/support/make4ht/README +++ b/support/make4ht/README @@ -175,6 +175,19 @@ cross-references work. It can save quite a lot of the compilation time: $ make4ht -um draft filename.tex +Another buil-in mode is `clean`. It executes the `Make:clean()` command to +remove all generated and temporary files from the current directory. +No \LaTeX\ compilation happens in this mode. + +It should be used in this way: + + # copy generated files to a direcory + $ make4ht -d outdir filename.tex + # remove all generated files in the current dir + # the -a info option will print files that are removed + $ make4ht -m clean -a info filename.tex + + More information about the build files can be found in section \ref{sec:buildfiles}. ## Handling of the generated files @@ -248,7 +261,7 @@ common\_domfilters : clean the HTML file using DOM filters. It is more powerful than `common_filters`. Used DOM filters are `fixinlines`, `idcolons`, -`joincharacters`, and `tablerows`. +`joincharacters`, `sectionid` and `tablerows`. detect\_engine @@ -416,6 +429,12 @@ detect compilation errors in the TeX log file. : One call to the TeX engine with special configuration for loading of the `tex4ht.sty` package. +`Make:clean` + +: This command removes all generated files, including images, HTML files and + various auxilary files, from the current directory. It keeps files whose + file names don't match the input file name. + `Make:httex` : Variant of `Make:htlatex` suitable for Plain \TeX. @@ -653,6 +672,12 @@ mathmlfixes : fix common issues for MathML. +sectionid + +: create `id` attribute for HTML sectioning elements derived from the section + title. It also updates links to these sections. Use the `notoc` command line + option to prevent that. + t4htlinks : fix hyperlinks in the ODT format. @@ -935,7 +960,7 @@ options `toc_query` -: CSS selector for selecting the table of contents container. +: CSS selector for selection of element that contains the table of contents. `title_query` @@ -945,12 +970,33 @@ options : table containing a hierarchy of classes used in TOC +`max_depth` + +: set detph of displayed children TOC levels + Default values: filter_settings "collapsetoc" { toc_query = ".tableofcontents", - title_query = ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a", - toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} + title_query = "h1 a, h2 a, h3 a, h4 a, h5 a, h6 a", + max_depth = 1, + toc_levels = { + tocpart = 1, + toclikepart = 1, + tocappendix = 1, + toclikechapter = 2, + tocchapter = 2, + tocsection = 3, + toclikesection = 3, + tocsubsection = 4, + toclikesubsection = 4, + tocsubsubsection = 5, + toclikesubsubsection = 5, + tocparagraph = 6, + toclikeparagraph = 6, + tocsubparagraph = 7, + toclikesubparagraph = 7, + } } ## The `fixinlines` dom filter @@ -1034,15 +1080,22 @@ header table value is a function, it is executed with current parameters and HTML page DOM object as arguments. +remove\_maketitle + +: the `staticsite` extension removes text produced by the `\maketitle` command by default. Set this +option to `false` to disable the removal. + Example: + -- set the environmental variable 'blog_root' with path to + -- the directory that should hold the generated HTML files local outdir = os.getenv "blog_root" filter_settings "staticsite" { site_root = outdir, map = { - [".css$"] = "../css/" + [".css$"] = "/css/" }, header = { layout="post", @@ -1262,6 +1315,20 @@ Another option is to add a space before the `tex4ht` options: The former way is preferable, though. +## Table of Contents points to a wrong destination + +The `sectionid` DOM filter creates better link destinations for sectioning commands. +In some cases, for example if you use Pandoc, the document may already contain the +link destination with the same name. In such cases the original destination is preserved +in the file. In this case links to the section will point to that place, instead of +correct destination in the section. This may happen for example if you use Pandoc for +the Markdown to \LaTeX\ conversion. It creates `\hypertarget` commands that are placed +just before section. The links points to that place, instead of the actual section. + +In this case you don't want to update links. Use the `notoc` option to prevent that. + + + ## Filenames containing spaces `tex4ht` command cannot handle filenames containing spaces. to fix this issue, `make4ht` diff --git a/support/make4ht/changelog.tex b/support/make4ht/changelog.tex index ead753239a..61dbcdaa3b 100644 --- a/support/make4ht/changelog.tex +++ b/support/make4ht/changelog.tex @@ -3,6 +3,140 @@ \begin{itemize} \item + 2021/07/25 + + \begin{itemize} + \tightlist + \item + version \texttt{0.3h} released. + \end{itemize} +\item + 2021/07/25 + + \begin{itemize} + \tightlist + \item + use current directory as default output dir in \texttt{staticsite} + extension. + \end{itemize} +\item + 2021/07/23 + + \begin{itemize} + \tightlist + \item + fixed detection of single paragraphs inside + \texttt{\textless{}li\textgreater{}} in the \texttt{itemparagraphs} + DOM filter. + \end{itemize} +\item + 2021/07/18 + + \begin{itemize} + \tightlist + \item + remove elements produced by \texttt{\textbackslash{}maketitle} in + the \texttt{staticsite} extension. + \end{itemize} +\item + 2021/07/05 + + \begin{itemize} + \tightlist + \item + sort colors alphabetically in the \texttt{joincolors} DOM filter to + enable reproducible builds. + \end{itemize} +\item + 2021/06/26 + + \begin{itemize} + \tightlist + \item + rewrote the \texttt{collapsetoc} DOM filter. + \end{itemize} +\item + 2021/06/20 + + \begin{itemize} + \tightlist + \item + test for the \texttt{svg} picture mode in the \texttt{tex4ht} + command. Use the \texttt{-g.svg} option if it is detected. This is + necessary for correct support of pictorial characters. + \end{itemize} +\item + 2021/06/16 + + \begin{itemize} + \tightlist + \item + better handling of duplicate ID attributes in \texttt{sectionid} DOM + filter. + \item + support \texttt{notoc} option in \texttt{sectionid}. + \end{itemize} +\item + 2021/06/13 + + \begin{itemize} + \tightlist + \item + added \texttt{itemparagraphs} DOM filter. It removes unnecessary + paragraphs from \texttt{\textless{}li\textgreater{}} elements. + \end{itemize} +\item + 2021/05/06 + + \begin{itemize} + \tightlist + \item + remove \texttt{\textless{}hr\textgreater{}} elements in + \texttt{.hline} rows in \texttt{tablerows} DOM filter. + \end{itemize} +\item + 2021/05/01 + + \begin{itemize} + \tightlist + \item + added function \texttt{mkutils.isModuleAvailable}. It checks if Lua + library is available. + \item + check for \texttt{char-def} library in \texttt{sectionid} DOM + filter. + \end{itemize} +\item + 2021/04/08 + + \begin{itemize} + \tightlist + \item + removed \texttt{build\_changed}. New script, + \href{https://github.com/michal-h21/siterebuild}{siterebuild}, + should be used instead. + \item + new DOM filter, \texttt{sectionid}. It uses sanitized titles instead + of automatically generated numbers as section IDs. + \item + added \texttt{sectionid} to \texttt{common\_domfilters}. + \item + use \texttt{context} in the Docker file, because it contains the + \texttt{char-def.lua} file. + \end{itemize} +\item + 2021/03/20 + + \begin{itemize} + \tightlist + \item + use \texttt{kpse} library when files are copied to the output + directory. + \item + added \texttt{clean} mode. It removes all generated, temporary and + auxilary files. + \end{itemize} +\item 2021/03/19 \begin{itemize} diff --git a/support/make4ht/domfilters/make4ht-collapsetoc.lua b/support/make4ht/domfilters/make4ht-collapsetoc.lua index 0b77b9dbbb..ede6e1f908 100644 --- a/support/make4ht/domfilters/make4ht-collapsetoc.lua +++ b/support/make4ht/domfilters/make4ht-collapsetoc.lua @@ -1,198 +1,249 @@ +-- mini TOC support for make4ht local domobject = require "luaxml-domobject" local filter = require "make4ht-filter" local log = logging.new "collapsetoc" - - -local toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} - -local debug_print = function(...) log:debug(...) end - - --- return toc element type and it's id -local function get_id(el) - local name = el:get_attribute "class" - local id - local a = el:query_selector "a" or {} - local first = a[1] - if first then - local href = first:get_attribute "href" - id = href:match("#(.+)$") +local mktuils = require "mkutils" + + +-- assign levels to entries in the .4tc file +local toc_levels = { + tocpart = 1, + toclikepart = 1, + tocappendix = 2, + toclikechapter = 2, + tocchapter = 2, + tocsection = 3, + toclikesection = 3, + tocsubsection = 4, + toclikesubsection = 4, + tocsubsubsection = 5, + toclikesubsubsection = 5, + tocparagraph = 6, + toclikeparagraph = 6, + tocsubparagraph = 7, + toclikesubparagraph = 7, +} + +-- number of child levels to be kept +-- the depth of 1 ensures that only direct children of the current sectioning command +-- will be kept in TOC +local max_depth = 1 + + +-- debugging function to test correct structure of the TOC tree +local function print_tree(tree, level) + local level = level or 0 + log:debug(string.rep(" ", level) .. (tree.type or "root"), tree.id) + for k, v in pairs(tree.children) do + print_tree(v, level + 2) end - return name, id end -local function remove_sections(part_elements, currentpart) - -- we need to remove toc entries from the previous part if the - -- current document isn't part of it - if currentpart == false then - for _, part in ipairs(part_elements) do - part:remove_node() +-- convert the parsed toc entries to a tree structure +local function make_toc_tree(tocentries, lowestlevel, position, tree) + local position = position or 1 + local tree = tree or { + level = lowestlevel - 1, + children = {} + } + local stack = {tree} + if position > #tocentries then return tree, position end + -- loop over TOC entries and make a tree + for i = 1, #tocentries do + -- initialize new child + local element = tocentries[i] + element.children = element.children or {} + local parent = stack[#stack] + local level_diff = element.level - parent.level + if level_diff == 0 then -- entry is sibling of parent + -- current parent is sibling of the current elemetn, true parent is + -- sibling's parent + parent = parent.parent + -- we must replace sibling element with the current element in stact + -- so the child elements get correct parent + table.remove(stack) + table.insert(stack, element) + elseif level_diff > 0 then -- entry is child of parent + for x = 1, level_diff do + table.insert(stack, element) + end + else + -- we must remove levels from the stack to get the correct parent + for x =1 , level_diff, -1 do + if #stack > 0 then + parent = table.remove(stack) + end + end + -- we must reinsert parent back to stack, place the current element to stact too + table.insert(stack, parent) + table.insert(stack, element) end + table.insert(parent.children, element) + element.parent = parent end + print_tree(tree) + return tree end -local function make_toc_selector(toc_levels) - local level_classes = {} - for _, l in ipairs(toc_levels) do - level_classes[#level_classes+1] = "." .. l +-- find first sectioning element in the current page +local function find_headers(dom, header_levels) + -- we need to find id attributes in <a> elements that are children of sectioning elements + local ids = {} + for _, header in ipairs(dom:query_selector(header_levels)) do + local id = header:get_attribute "id" + if id then ids[#ids+1] = id end end - return table.concat(level_classes, ", ") + return ids end -local function find_toc_levels(toc) - -- find toc levels used in the document - -- it ecpects that sectioning levels appears in the TOC in the descending order - local levels, used = {}, {} - local level = 1 - -- we still expect the standard class names - local toc_selector = make_toc_selector(toc_levels) - for _, el in ipairs(toc:query_selector(toc_selector)) do - local class = el:get_attribute("class") - if not used[class] then - table.insert(levels, class) - used[class] = level - level = level + 1 + +-- process list of ids and find those that should be kept: +-- siblings, children, parents and top level +local function find_toc_entries_to_keep(ids, tree) + local tree = tree or {} + -- all id in TOC tree that we want to kepp are saved in this table + local ids_to_keep = {} + -- find current id in the TOC tree + local function find_id(id, tree) + if tree.id == id then return tree end + if not tree.children or #tree.children == 0 then return false end + for k,v in pairs(tree.children) do + local found_id = find_id(id, v) + if found_id then return found_id end end + return false end - return levels, used -end - -local function remove_levels(toc, matched_ids) - -- sort the matched sections according to their levels - local levels, level_numbers = find_toc_levels(toc) - debug_print("remove levels", #levels) - -- for _, level in ipairs(levels) do - -- print(level, level_numbers[level], matched_ids[level]) - -- end - local keep_branch = false - local matched_levels = {} - local toc_selector = make_toc_selector(toc_levels) - for _, el in ipairs(toc:query_selector(toc_selector)) do - local name, id = get_id(el) - -- get the current toc hiearchy level - local level = level_numbers[name] - -- get the matched id for the current level - local level_id = matched_ids[name] - local matched = level_id == id - local remove = true - -- we will use this for toc elements at lower hiearchy than is the top sectioning level on the page - if matched then keep_branch = true end - -- find the parent level to the current section level - local parent_level = toc_levels[level - 1] - local parent_matched = matched_levels[parent_level] - if matched then - debug_print("match",name, id, level_id, level, #levels) - keep_branch = true - remove = false - elseif level==1 then - -- don't remove the top level - debug_print("part",name, id, level_id, level) - remove = false - matched_levels = {} - if not matched then keep_branch = false end - elseif keep_branch then - -- if level >= (#levels - 1) then - if level > matched_ids._levels then - debug_print("level",name, id, level_id, level, parent_level, parent_matched) - remove = false - elseif matched_ids.ids[id] then - debug_print("matched id",name, id, level_id, level, parent_level, parent_matched) - remove = false - elseif parent_matched then - debug_print("parent_matched",name, id, level_id, level, parent_level, parent_matched) - keep_branch = false - remove = false - end - -- else - -- print("remove", name, id, level_id, level, #matched_ids) - -- end - elseif parent_matched then - debug_print("parent_matched alternative",name, id, level_id, level, parent_level, parent_matched) - remove = false - else - debug_print("else",name, id, level_id, level, keep_branch) - keep_branch = false + -- always keep top level of the hiearchy + local function keep_toplevel(tree) + for _, el in ipairs(tree.children) do + ids_to_keep[el.id] = true end - matched_levels[name] = matched - if remove then - el:remove_node() - --print(name,id, level_id, matched) + end + -- we want to keep all children in TOC hiearchy + local function keep_children(element, depth) + local depth = depth or 1 + local max_depth = max_depth or 1 + -- stop processing when there are no children + for _, el in pairs(element.children or {}) do + if el.id then ids_to_keep[el.id] = true end + -- by default, we keep just direct children of the current sectioning element + if depth < max_depth then + keep_children(el, depth + 1) + end end end - + -- also keep all siblings + local function keep_siblings(element) + local parent = element.parent + for k, v in pairs(parent.children or {}) do + ids_to_keep[v.id] = true + end + end + -- and of course, keep all parents + local function keep_parents(element) + local parent = element.parent + if parent and parent.id then + ids_to_keep[parent.id] = true + -- we should keep siblings of all parents as well + keep_siblings(parent) + keep_parents(parent) + end + end + -- always keep the top-level TOC hiearchy, even if we cannot find any sectioning element on the page + keep_toplevel(tree) + for _, id in ipairs(ids) do + -- keep the current id + ids_to_keep[id] = true + local found_element = find_id(id, tree) + if found_element then + keep_children(found_element) + keep_siblings(found_element) + keep_parents(found_element) + end + end + return ids_to_keep end +-- process the .4tc file and convert entries to a tree structure +-- based on the sectioning level +local function parse_4tc(parameters, toc_levels) + local tcfilename = parameters.input .. ".4tc" + if not mkutils.file_exists(tcfilename) then + log:warning("Cannot find TOC: " .. tcfilename) + return {} + end + local tocentries = {} + local f = io.open(tcfilename, "r") + -- we need to find the lowest level used in the TOC + local lowestlevel = 999 + for line in f:lines() do + -- entries looks like: \doTocEntry\tocsubsection{1.2.2}{\csname a:TocLink\endcsname{5}{x5-60001.2.2}{QQ2-5-6}{aaaa}}{7}\relax + -- we want do extract tocsubsection and x5-60001.2.2 + local toctype, id = line:match("\\doTocEntry\\(.-){.-}{.-{.-}{(.-)}") + if toctype then + local level = toc_levels[toctype] + if not level then + log:warning("Cannot find TOC level for: " .. toctype) + else + lowestlevel = level < lowestlevel and level or lowestlevel + table.insert(tocentries, {type = toctype, id = id, level = level}) + end + end + end + f:close() + local toc = make_toc_tree(tocentries, lowestlevel) + return toc +end +local function remove_levels(toc, matched_ids) + -- remove links that aren't in the TOC hiearchy that should be kept + for _, link in ipairs(toc:query_selector("a")) do + local href = link:get_attribute("href") + -- find id in the href + local id = href:match("#(.+)") + if id and not matched_ids[id] then + -- toc links are in <span> elements that can contain the section number + -- we must remove them too + local parent = link:get_parent() + if parent:get_element_name() == "span" then + parent:remove_node() + else + -- if the parent node isn't <span>, remove at least the link itself + link:remove_node() + end + end + end +end - --- local process = filter{ function(s) - -- local dom = domobject.parse(s) -local function collapse_toc(dom, par) +local function collapsetoc(dom, parameters) -- set options + local par = parameters local options = get_filter_settings "collapsetoc" + -- query to find the TOC element in DOM local toc_query = par.toc_query or options.toc_query or ".tableofcontents" - local title_query = par.title_query or options.title_query or ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a" - toc_levels = par.toc_levels or options.toc_levels or toc_levels - -- keep track of current id of each sectioning level - local current_ids, matched_ids = {}, {_levels = 0, ids = {}} - -- search sectioning elements - local titles = dom:query_selector(title_query) - local section_ids = {} - for _, x in ipairs(titles) do - -- get their id attributes and save them in a table - section_ids[#section_ids+1] = x:get_attribute("id") - end - - -- we need to retrieve the first table of contents - local toctables = dom:query_selector(toc_query) or {} - -- process only when we got a TOC - debug_print("toc query", toc_query, #toctables) - if #toctables > 0 then - local tableofcontents = toctables[1] - -- all toc entries are in span elements - local toc = tableofcontents:query_selector("span") - local currentpart = false - local part_elements = {} - for _, el in ipairs(toc) do - -- get sectioning level and id of the current TOC entry - local name, id = get_id(el) - -- set the id of the current sectioning level - current_ids[name] = id - for _, sectid in ipairs(section_ids) do - -- detect if the current TOC entry match some sectioning element in the current document - if id == sectid then - currentpart = true - -- save the current id as a matched id - matched_ids.ids[id] = true - -- copy the TOC hiearchy for the current toc level - for i, level in ipairs(toc_levels) do - -- print("xxx",i, level, current_ids[level]) - matched_ids[level] = current_ids[level] - -- set the maximum matched level - if i > matched_ids._levels then matched_ids._levels = i end - if level == name then break end - end - debug_print("match", id) - end - end - end - remove_levels(tableofcontents, matched_ids) - - -- remove sections from the last part - -- remove_sections(part_elements,currentpart) - -- remove unneeded br elements - local br = tableofcontents:query_selector("br") - for _, el in ipairs(br) do el:remove_node() end - -- remove unneded whitespace - for _, el in ipairs(tableofcontents:get_children()) do - if el:is_text() then el:remove_node() end - end + -- query to select sectioning elements with id's + local title_query = par.title_query or options.title_query or "h1 a, h2 a, h3 a, h4 a, h5 a, h6 a" + -- level of child levels to be kept in TOC + max_depth = par.max_depth or options.max_depth or max_depth + -- set level numbers for particular TOC entry types + local user_toc_levels = par.toc_levels or options.toc_levels or {} + -- join user's levels with default + for k,v in pairs(user_toc_levels) do toc_levels[k] = v end + -- parse the .4tc file to get TOC tree + toc = toc or parse_4tc(parameters, toc_levels) + -- find sections in the current html file + local ids = find_headers(dom, title_query) + log:debug("Ids", table.concat(ids, ",")) + local ids_to_keep = find_toc_entries_to_keep(ids, toc) + local toc_dom = dom:query_selector(toc_query)[1] + if toc_dom then + remove_levels(toc_dom, ids_to_keep) + else + log:warning("Cannot find TOC element using query: " .. toc_query) end return dom -end - -return collapse_toc +end --- Make:match("html$", process) +return collapsetoc diff --git a/support/make4ht/domfilters/make4ht-itemparagraphs.lua b/support/make4ht/domfilters/make4ht-itemparagraphs.lua new file mode 100644 index 0000000000..d8616c564a --- /dev/null +++ b/support/make4ht/domfilters/make4ht-itemparagraphs.lua @@ -0,0 +1,26 @@ +-- TeX4ht puts contents of all \item commands into paragraphs. We are not +-- able to detect if it contain only one paragraph, or more. If just one, +-- we can remove the paragraph and put the contents directly to <li> element. +return function(dom) + for _, li in ipairs(dom:query_selector("li")) do + local is_single_par = false + -- count elements and paragraphs that are direct children of <li> + -- remove the paragraph only if it is the only child element + local el_count, par_count = 0, 0 + local par = {} + for _, el in ipairs(li._children) do + if el:is_element() then + el_count = el_count + 1 + if el:get_element_name() == "p" then + par[#par+1] = el + end + end + end + if #par == 1 and el_count == 1 then + -- place paragraph children as direct children of <li>, this + -- efectivelly removes <p> + li._children = par[1]._children + end + end + return dom +end diff --git a/support/make4ht/domfilters/make4ht-joincolors.lua b/support/make4ht/domfilters/make4ht-joincolors.lua index 730f66a7a0..1c86481010 100644 --- a/support/make4ht/domfilters/make4ht-joincolors.lua +++ b/support/make4ht/domfilters/make4ht-joincolors.lua @@ -25,6 +25,7 @@ local function extract_colors(csscontent) for class, color in pairs(used_colors) do t[#t+1] = string.format(".%s{color:%s;}", class, color) end + table.sort(t) return csscontent .. table.concat(t, "\n") end diff --git a/support/make4ht/domfilters/make4ht-sectionid.lua b/support/make4ht/domfilters/make4ht-sectionid.lua new file mode 100644 index 0000000000..c1629181bb --- /dev/null +++ b/support/make4ht/domfilters/make4ht-sectionid.lua @@ -0,0 +1,156 @@ +local mkutils = require "mkutils" +local log = logging.new("tocid") +-- Unicode data distributed with ConTeXt +-- defines "characters" table +if not mkutils.isModuleAvailable("char-def") then + log:warning("char-def module not found") + log:warning("cannot fix section id's") + return function(dom) return dom end +end +require "char-def" +local chardata = characters.data or {} + + +local toc = nil + +local function is_letter(info) + -- test if character is letter + local category = info.category or "" + return category:match("^l") +end + +local function is_space(info) + local category = info.category or "" + return category == "zs" +end + +local uchar = utf8.char +local function normalize_letter(char, result) + local info = chardata[char] or {} + -- first get lower case of the letter + local lowercase = info.lccode or char + -- remove accents. the base letter is in the shcode field + local lowerinfo = chardata[lowercase] or {} + -- when no shcode, use the current lowercase char + local shcode = lowerinfo.shcode or lowercase + -- shcode can be table if it contains multiple characters + -- normaliz it to a table, so we can add all letters to + -- the resulting string + if type(shcode) ~= "table" then shcode = {shcode} end + for _, x in ipairs(shcode) do + result[#result+1] = uchar(x) + end +end + +local escape_name = function(name) + local result = {} + for _,char in utf8.codes(name) do + local info = chardata[char] or {} + if is_space(info) then + result[#result+1] = " " + elseif is_letter(info) then + normalize_letter(char, result) + end + end + --- convert table with normalized characters to string + local name = table.concat(result) + -- remove spaces + return name:gsub("%s+", "-") +end + +local function parse_toc_line(line) + -- the section ids and titles are saved in the following format: + -- \csname a:TocLink\endcsname{1}{x1-20001}{QQ2-1-2}{Nazdar světe} + -- ............................... id ................. title ... + local id, name = line:match("a:TocLink.-{.-}{(.-)}{.-}{(.-)}") + if id then + return id, escape_name(name) + end +end + +local used = {} + +local function parse_toc(filename) + local toc = {} + if not mkutils.file_exists(filename) then return nil, "Cannot open TOC file " .. filename end + for line in io.lines(filename) do + local id, name = parse_toc_line(line) + local orig_name = name + -- not all lines in the .4tc file contains TOC entries + if id then + -- test if the same name was used already. user should be notified + if used[name] then + -- update + name = name .. used[name] + log:debug("Duplicate id found: ".. orig_name .. ". New id: " .. name) + end + used[orig_name] = (used[orig_name] or 0) + 1 + toc[id] = name + end + end + return toc +end + +-- we don't want to change the original id, as there may be links to it from the outside +-- so we will set it to the parent element (which should be h[1-6]) +local function set_id(el, id) + local section = el:get_parent() + local section_id = section:get_attribute("id") + if section_id and section_id~=id then -- if it already has id, we don't override it, but create dummy child instead + local new = section:create_element("span", {id=id}) + section:add_child_node(new,1) + else + section:set_attribute("id", id) + end + +end + + + +return function(dom, par) + local msg + toc, msg = toc or parse_toc(par.input .. ".4tc") + msg = msg or "Cannot load TOC" + -- don't do anyting if toc cannot be found + if not toc then + log:warning(msg) + return dom + end + -- if user selects the "notoc" option on the command line, we + -- will not update href links + local notoc = false + if par["tex4ht_sty_par"]:match("notoc") then notoc = true end + -- the HTML file can already contain ID that we want to assign + -- we will not set duplicate id from TOC in that case + local toc_ids = {} + for _, el in ipairs(dom:query_selector("[id]")) do + local id = el:get_attribute("id") + toc_ids[id] = true + end + -- process all elements with id atribute or <a href> + for _, el in ipairs(dom:query_selector "[id],a[href]") do + local id, href = el:get_attribute("id"), el:get_attribute("href") + if id then + local name = toc[id] + -- replace id with new section id + if name and not toc_ids[name] then + set_id(el, name) + else + if name then + log:debug("Document already contains id: " .. name) + end + end + end + if href and notoc == false then + -- replace links to sections with new id + local base, anchor = href:match("^(.*)%#(.+)") + local name = toc[anchor] + if name then + el:set_attribute("href", base .. "#" .. name) + end + end + end + return dom + end + + diff --git a/support/make4ht/domfilters/make4ht-tablerows.lua b/support/make4ht/domfilters/make4ht-tablerows.lua index 3e3931b368..ef36a85750 100644 --- a/support/make4ht/domfilters/make4ht-tablerows.lua +++ b/support/make4ht/domfilters/make4ht-tablerows.lua @@ -40,6 +40,12 @@ return function(dom) -- that matches this pattern, so we should keep the row if we match them too) return not css:match(search_term) end + local hline_hr = function(row) + -- remove <hr> elements from "hline" rows + for _, hr in ipairs(row:query_selector(".hline hr")) do + hr:remove_node() + end + end local load_css_files = function() -- the empty rows can be styled using CSS, for example configuration for -- Booktabs does that. We shouldn't remove such rows. @@ -62,6 +68,7 @@ return function(dom) -- find the empty rows for _, row in ipairs(tbl:query_selector("tr")) do if is_empty_row(row) and is_not_styled(row, css) then row:remove_node() end + hline_hr(row) end end diff --git a/support/make4ht/extensions/make4ht-ext-build_changed.lua b/support/make4ht/extensions/make4ht-ext-build_changed.lua deleted file mode 100644 index e0fc5ec6eb..0000000000 --- a/support/make4ht/extensions/make4ht-ext-build_changed.lua +++ /dev/null @@ -1,68 +0,0 @@ --- this make4ht build file tries to recompile modified blog article sources --- - --- disable any compilation -Make.build_seq = {} -Make:add("tex4ht", "") -Make:add("t4ht", "") - -local log = logging.new "compile newest" - - - --- construct the name of the generated HTML file from the .published file -local function get_generated_html_name(published_file, directory, file_pattern) - local f = io.open(directory .. "/" .. published_file, "r") - local content = f:read("*all") - f:close() - local timestamp = tonumber(content) - local basename = mkutils.remove_extension(published_file) - local tex_file = basename .. ".tex" - -- expand fillename in the file_pattern - local expanded = file_pattern % {input = basename} - -- expand date in the file_pattern - expanded = os.date(expanded, timestamp) - log:status("found source files :", directory, basename, expanded) - return {directory = directory, tex_file = tex_file, generated = expanded .. ".html"} -end - --- process subdirectories of the basedir and look for the filename.published files -local function find_published(basedir, file_pattern) - local published = {} - for f in lfs.dir(basedir) do - local fullname = basedir .. "/" .. f - local attributes = lfs.attributes(fullname) - -- process directories, but ignore . and .. - if attributes.mode == "directory" and f ~= "." and f~= ".." then - for name in lfs.dir(fullname) do - if name:match("published$") then - published[#published + 1] = get_generated_html_name(name, fullname, file_pattern) - end - - end - end - end - return published -end - --- find tex files that were modified later than the generated HTML files -local function find_changed(published, site_root) - local keep = {} - for _, entry in ipairs(published) do - local source_attributes = lfs.attributes(entry.directory .. "/" .. entry.tex_file) - local dest_attributes = lfs.attributes(site_root .. "/" .. entry.generated) - -- - print(entry.tex_file, entry.generated, source_attributes.change < dest_attributes.change) - end -end - - -Make:add("rebuild", function(par) - local config = get_filter_settings "staticsite" - -- how the generated HTML files are named - local file_pattern = config.file_pattern or "%Y-%m-%d-${input}" - local published = find_published(par.tex_dir, file_pattern) - local changed = find_changed(published, config.site_root) -end) - -Make:rebuild{tex_dir = "posts"} diff --git a/support/make4ht/extensions/make4ht-ext-common_domfilters.lua b/support/make4ht/extensions/make4ht-ext-common_domfilters.lua index 802902d48d..6219c95fbd 100644 --- a/support/make4ht/extensions/make4ht-ext-common_domfilters.lua +++ b/support/make4ht/extensions/make4ht-ext-common_domfilters.lua @@ -26,7 +26,7 @@ function M.modify_build(make) make:match("4om$", process, {charclasses= charclasses}) count = 2 else - local process = filter {"fixinlines", "idcolons", "joincharacters", "mathmlfixes", "tablerows","booktabs"} + local process = filter {"fixinlines", "idcolons", "joincharacters", "mathmlfixes", "tablerows","booktabs", "sectionid", "itemparagraphs"} make:match("html?$", process) count = 1 end diff --git a/support/make4ht/extensions/make4ht-ext-staticsite.lua b/support/make4ht/extensions/make4ht-ext-staticsite.lua index 5704959970..a9a5bcece7 100644 --- a/support/make4ht/extensions/make4ht-ext-staticsite.lua +++ b/support/make4ht/extensions/make4ht-ext-staticsite.lua @@ -16,7 +16,7 @@ local function get_slug(settings) local f = io.open(published_name, "r") local readtime = f:read("*line") time = tonumber(readtime) - log:info("Already pubslished", slug) + log:info("Already pubslished", os.date("%Y-%m-%d %H:%M", time)) f:close() else -- escape @@ -76,6 +76,22 @@ local function insert_filter(make, pattern, fn) }) end +local function remove_maketitle(make) + -- use DOM filter to remove \maketitle block + local domfilter = require "make4ht-domfilter" + local process = domfilter { + function(dom) + local maketitles = dom:query_selector(".maketitle") + for _, el in ipairs(maketitles) do + log:debug("removing maketitle") + el:remove_node() + end + return dom + end + } + make:match("html$", process) +end + local function copy_files(filename, par) local function prepare_path(dir, subdir) @@ -84,7 +100,7 @@ local function copy_files(filename, par) end -- get extension settings local site_settings = get_filter_settings "staticsite" - local site_root = site_settings.site_root + local site_root = site_settings.site_root or "./" local map = site_settings.map or {} -- default path without subdir, will be used if the file is not matched -- by any pattern in the map @@ -107,6 +123,14 @@ function M.modify_build(make) local process = filter { "staticsite" } + + -- detect if we should remove maketitle + local site_settings = get_filter_settings "staticsite" + -- \maketitle is removed by default, set `remove_maketitle=false` setting to disable that + if site_settings.remove_maketitle ~= false then + remove_maketitle(make) + end + local settings = make.params -- get the published file name local slug = get_slug(settings) diff --git a/support/make4ht/make4ht b/support/make4ht/make4ht index 3ae56f049a..d2bf27194f 100755 --- a/support/make4ht/make4ht +++ b/support/make4ht/make4ht @@ -29,7 +29,7 @@ make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"] -- set version number. the template should be replaced by the -- actual version number by the build script -local version = "v0.3g" +local version = "v0.3h" mkparams.version_number = version local args = mkparams.get_args() @@ -76,6 +76,9 @@ make.params = parameters if make:length() < 1 then if mode == "draft" then make:htlatex() + elseif mode == "clean" then + make:clean() + make.no_dvi_process = true else make:htlatex() make:htlatex() @@ -84,7 +87,7 @@ if make:length() < 1 then end -if not args["no-tex4ht"] then +if not args["no-tex4ht"] and not make.no_dvi_process then make:tex4ht() end @@ -92,7 +95,11 @@ local ext = args.xetex and "xdv" or "dvi" if #make.image_patterns > 0 then make.params.t4ht_par = make.params.t4ht_par .. " -p" end -make:t4ht {ext = ext} + +if not make.no_dvi_process then + make:t4ht {ext = ext} +end + -- run extensions which modify the build sequence if #extensions > 0 then make = mkutils.extensions_modify_build(extensions, make) diff --git a/support/make4ht/make4ht-doc.pdf b/support/make4ht/make4ht-doc.pdf Binary files differindex 6643f67203..cc0d7d1b50 100644 --- a/support/make4ht/make4ht-doc.pdf +++ b/support/make4ht/make4ht-doc.pdf diff --git a/support/make4ht/make4ht-odtfilter.lua b/support/make4ht/make4ht-odtfilter.lua deleted file mode 100644 index 97f0b8df67..0000000000 --- a/support/make4ht/make4ht-odtfilter.lua +++ /dev/null @@ -1,33 +0,0 @@ -local mkutils = require "mkutils" -local zip = require "zip" - - --- use function to change contents of the ODT file -local function update_odt(odtfilename, file_path, fn) - -- get name of the odt file - local odtname = mkutils.remove_extension(odtfilename) .. ".odt" - -- open and read contents of the requested file inside ODT file - local odtfile = zip.open(odtname) - local local_file = odtfile:open(file_path) - local content = local_file:read("*all") - local_file:close() - odtfile:close() - -- update the content using user function - content = fn(content) - -- write the updated file - local local_file_file = io.open(file_path,"w") - local_file_file:write(content) - local_file_file:close() - os.execute("zip " .. odtname .. " " .. file_path) - os.remove(file_path) -end - -Make:match("tmp$", function(name, par) - update_odt(name, "content.xml", function(content) - return content:gsub("%&%#x([A-Fa-f0-9]+);", function(entity) - -- convert hexadecimal entity to Unicode - print(entity,utfchar(tonumber(entity, 16))) - return utfchar(tonumber(entity, 16)) - end) - end) -end) diff --git a/support/make4ht/mkutils.lua b/support/make4ht/mkutils.lua index c182bfd9dd..6bcc4787c3 100644 --- a/support/make4ht/mkutils.lua +++ b/support/make4ht/mkutils.lua @@ -55,13 +55,31 @@ function remove_extension(path) end -- - +-- check if file exists function file_exists(file) local f = io.open(file, "rb") if f then f:close() end return f ~= nil end +-- check if Lua module exists +-- source: https://stackoverflow.com/a/15434737/2467963 +function isModuleAvailable(name) + if package.loaded[name] then + return true + else + for _, searcher in ipairs(package.searchers or package.loaders) do + local loader = searcher(name) + if type(loader) == 'function' then + package.preload[name] = loader + return true + end + end + return false + end +end + + -- searching for converted images function parse_lg(filename) log:info("Parse LG") @@ -115,6 +133,10 @@ local cp_func = os.type == "unix" and "cp" or "copy" -- in reality it isn't. -- local cp_func = os.type == "unix" and "mv" or "move" function cp(src,dest) + if not file_exists(src) then + -- try to find file using kpse library if it cannot be found + src = kpse.find_file(src) or src + end local command = string.format('%s "%s" "%s"', cp_func, src, dest) if cp_func == "copy" then command = command:gsub("/",'\\') end log:info("Copy: "..command) @@ -362,9 +384,77 @@ end, {correct_exit= 0}) -env.Make:add("tex4ht","tex4ht ${tex4ht_par} \"${input}.${dvi}\"", nil, 1) +-- env.Make:add("tex4ht","tex4ht ${tex4ht_par} \"${input}.${dvi}\"", nil, 1) +env.Make:add("tex4ht",function(par) + -- detect if svg output is used + -- if yes, we need to pass the -g.svg option to tex4ht command + -- to support svg images for character pictures + local logfile = par.input .. ".log" + if file_exists(logfile) then + for line in io.lines(logfile) do + local options = line:match("TeX4ht package options:(.+)") + if options then + log:info(options) + if options:match("svg") then + par.tex4ht_par = (par.tex4ht_par or "") .. " -g.svg" + end + break + end + end + end + local command = "tex4ht ${tex4ht_par} \"${input}.${dvi}\"" % par + log:info("executing: " .. command) + return execute(command) +end + , nil, 1) env.Make:add("t4ht","t4ht ${t4ht_par} \"${input}.${ext}\"",{ext="dvi"},1) +env.Make:add("clean", function(par) + -- remove all functions that process produced files + -- we will provide only one function, that remove all of them + Make.matches = {} + local main_name = par.input + local remove_file = function(filename) + if file_exists(filename) then + log:info("removing file: " .. filename) + os.remove(filename) + end + end + -- try to find if the last converted file was in the ODT format + local lg_file = parse_lg(main_name .. ".lg") + local is_odt = false + if lg_file and lg_file.files then + for _, x in ipairs(lg_file.files) do + is_odt = x:match("odt$") or is_odt + end + end + if is_odt then + Make:match("4om$",function(filename) + -- math temporary file + local to_remove = filename:gsub("4om$", "tmp") + remove_file(to_remove) + return false + end) + Make:match("4og$", remove_file) + end + Make:match("tmp$", function() + -- remove temporary and auxilary files + for _,ext in ipairs {"aux", "xref", "tmp", "4tc", "4ct", "idv", "lg","dvi", "log"} do + remove_file(main_name .. "." .. ext) + end + end) + Make:match(".*", function(filename, par) + -- remove only files that start with the input file basename + -- this should prevent removing of images. this also means that + -- images shouldn't be names as <filename>-hello.png for example + if filename:find(main_name, 1,true) then + -- log:info("Matched file", filename) + remove_file(filename) + end + end) + +end) + -- enable extension in the config file -- the following two functions must be here and not in make4ht-lib.lua -- because of the access to env.settings diff --git a/support/make4ht/readme.tex b/support/make4ht/readme.tex index 3810dbad36..1938c98a0a 100644 --- a/support/make4ht/readme.tex +++ b/support/make4ht/readme.tex @@ -232,6 +232,21 @@ compilation time: $ make4ht -um draft filename.tex \end{verbatim} +Another buil-in mode is \texttt{clean}. It executes the +\texttt{Make:clean()} command to remove all generated and temporary +files from the current directory. No \LaTeX~compilation happens in this +mode. + +It should be used in this way: + +\begin{verbatim} +# copy generated files to a direcory +$ make4ht -d outdir filename.tex +# remove all generated files in the current dir +# the -a info option will print files that are removed +$ make4ht -m clean -a info filename.tex +\end{verbatim} + More information about the build files can be found in section \ref{sec:buildfiles}. @@ -328,7 +343,8 @@ clean the output HTML files using filters. \item[common\_domfilters] clean the HTML file using DOM filters. It is more powerful than \texttt{common\_filters}. Used DOM filters are \texttt{fixinlines}, -\texttt{idcolons}, \texttt{joincharacters}, and \texttt{tablerows}. +\texttt{idcolons}, \texttt{joincharacters}, \texttt{sectionid} and +\texttt{tablerows}. \item[detect\_engine] detect engine and format necessary for the document compilation from the magic comments supported by \LaTeX~editors such as TeXShop or TeXWorks. @@ -512,6 +528,10 @@ the TeX log file. \item[\texttt{Make:htlatex}] One call to the TeX engine with special configuration for loading of the \texttt{tex4ht.sty} package. +\item[\texttt{Make:clean}] +This command removes all generated files, including images, HTML files +and various auxilary files, from the current directory. It keeps files +whose file names don't match the input file name. \item[\texttt{Make:httex}] Variant of \texttt{Make:htlatex} suitable for Plain \TeX. \item[\texttt{Make:latexmk}] @@ -723,6 +743,10 @@ format. remove spurious rows from HTML tables. \item[mathmlfixes] fix common issues for MathML. +\item[sectionid] +create \texttt{id} attribute for HTML sectioning elements derived from +the section title. It also updates links to these sections. Use the +\texttt{notoc} command line option to prevent that. \item[t4htlinks] fix hyperlinks in the ODT format. \end{description} @@ -1023,12 +1047,15 @@ filter}{The collapsetoc dom filter}}\label{the-collapsetoc-dom-filter}} \begin{description} \item[\texttt{toc\_query}] -CSS selector for selecting the table of contents container. +CSS selector for selection of element that contains the table of +contents. \item[\texttt{title\_query}] CSS selector for selecting all elements that contain the section ID attribute. \item[\texttt{toc\_levels}] table containing a hierarchy of classes used in TOC +\item[\texttt{max\_depth}] +set detph of displayed children TOC levels \end{description} Default values: @@ -1036,8 +1063,25 @@ Default values: \begin{verbatim} filter_settings "collapsetoc" { toc_query = ".tableofcontents", - title_query = ".partHead a, .chapterHead a, .sectionHead a, .subsectionHead a", - toc_levels = {"partToc", "chapterToc", "sectionToc", "subsectionToc", "subsubsectionToc"} + title_query = "h1 a, h2 a, h3 a, h4 a, h5 a, h6 a", + max_depth = 1, + toc_levels = { + tocpart = 1, + toclikepart = 1, + tocappendix = 1, + toclikechapter = 2, + tocchapter = 2, + tocsection = 3, + toclikesection = 3, + tocsubsection = 4, + toclikesubsection = 4, + tocsubsubsection = 5, + toclikesubsubsection = 5, + tocparagraph = 6, + toclikeparagraph = 6, + tocsubparagraph = 7, + toclikesubparagraph = 7, + } } \end{verbatim} @@ -1145,17 +1189,23 @@ pattern \texttt{\%Y-\%m-\%d-\$\{input\}} creates names in the form of table with variables to be set in the YAML header in HTML files. If the table value is a function, it is executed with current parameters and HTML page DOM object as arguments. +\item[remove\_maketitle] +the \texttt{staticsite} extension removes text produced by the +\texttt{\textbackslash{}maketitle} command by default. Set this option +to \texttt{false} to disable the removal. \end{description} Example: \begin{verbatim} +-- set the environmental variable 'blog_root' with path to +-- the directory that should hold the generated HTML files local outdir = os.getenv "blog_root" filter_settings "staticsite" { site_root = outdir, map = { - [".css$"] = "../css/" + [".css$"] = "/css/" }, header = { layout="post", @@ -1405,6 +1455,24 @@ $ make4ht hello.tex "customcfg,charset=utf-8" " -cunihtf -utf8" -d foo The former way is preferable, though. +\hypertarget{table-of-contents-points-to-a-wrong-destination}{% +\subsection{Table of Contents points to a wrong +destination}\label{table-of-contents-points-to-a-wrong-destination}} + +The \texttt{sectionid} DOM filter creates better link destinations for +sectioning commands. In some cases, for example if you use Pandoc, the +document may already contain the link destination with the same name. In +such cases the original destination is preserved in the file. In this +case links to the section will point to that place, instead of correct +destination in the section. This may happen for example if you use +Pandoc for the Markdown to \LaTeX~conversion. It creates +\texttt{\textbackslash{}hypertarget} commands that are placed just +before section. The links points to that place, instead of the actual +section. + +In this case you don't want to update links. Use the \texttt{notoc} +option to prevent that. + \hypertarget{filenames-containing-spaces}{% \subsection{Filenames containing spaces}\label{filenames-containing-spaces}} diff --git a/support/tex4ebook/README b/support/tex4ebook/README index 3634974ab8..f87bd02f42 100644 --- a/support/tex4ebook/README +++ b/support/tex4ebook/README @@ -46,7 +46,17 @@ But it is optional. You shouldn't need to modify your \TeX\ files ## Available commands -- `\coverimage{coverimage.name}` - include cover image to the document. +- `\coverimage[<graphicx options>]{coverimage.name}` - include cover image to + the document. You can pass the same options as to `\includegraphics` command + in the optional argument. + +For example: + + \thispagestyle{empty} + \begin{document} + \coverimage[scale=0.8]{coverimage.name} % include scaled cover image + ... + \pagestyle{headings} # Command line options @@ -212,6 +222,42 @@ Add item to `<manifest>` section in the `OPF` file. Add `XML` name space to the generated `XHTML` files. Useful in `EPUB 3`. +### Example config file + + \Preamble{xhtml} + \begin{document} + \Configure{DocumentLanguage}{de} + % Use following lines if your document has ISBN: + % \Configure{OpfScheme}{ISBN} + % \Configure{UniqueIdentifier}{3-0000-1111-X} + % Another possibility is URI that points for example to the ebook homepage: + \Configure{OpfScheme}{URI} + \Configure{UniqueIdentifier} + {https://de.wikipedia.org/wiki/Der_achte_Schöpfungstag} + \Configure{CoverMimeType}{image/jpeg} + % If you don't use \coverimage in the document text, + % add cover image using this command: + \CoverMetadata{coverimage.jpg} + % You can also add more authors to your ebook metadata: + \Configure{OpfMetadata} + {\HCode{<dc:publisher>Deutscher Bücherbund</dc:publisher>}} + \Configure{OpfMetadata} + {\HCode{<dc:contributor>Image Artist</dc:contributor>}} + \Configure{OpfMetadata} + {\HCode{<dc:contributor>Trans Lator</dc:contributor>}} + \Configure{OpfMetadata} + {\HCode{<dc:date opf:event='original-publication'>1888</dc:date>}} + \EndPreamble + +Remarks: + +- Leading percent signs in the `.cfg` file introduce comments +- If the unique identifier is a URI which contains diacritical characters, the + equivalent HTML code needs to be inserted. `UTF8` is not recognized at that place. +- `UTF8` characters may be used in the `OpfMetadata` sections. + + + ## Commands available in the config file @@ -229,6 +275,7 @@ Add `XML` name space to the generated `XHTML` files. Useful in `EPUB 3`. *Where do you start an ePUB and what is the `<guide>` section of the `.OPF` file?*^[http://epubsecrets.com/where-do-you-start-an-epub-and-what-is-the-guide-section-of-the-opf-file.php] for some details. Note that `<guide>` is deprecated in `EPUB 3`. + ## Build files `tex4ebook` uses `make4ht`^[https://github.com/michal-h21/make4ht] as a build @@ -254,10 +301,14 @@ See the `make4ht` documentation for an example and more information. The basic support for the Fixed layout EPUB 3 can be enabled using the following configurations: - \Configure{OpfMetadata}{\HCode{<meta property="rendition:layout">pre-paginated</meta>}} - \Configure{OpfMetadata}{\HCode{<meta property="rendition:orientation">landscape</meta>}} - \Configure{OpfMetadata}{\HCode{<meta property="rendition:spread">none</meta>}} - \Configure{@HEAD}{\HCode{<meta name="viewport" content="width=1920, height=1080"/>\Hnewline}} + \Configure{OpfMetadata} + {\HCode{<meta property="rendition:layout">pre-paginated</meta>}} + \Configure{OpfMetadata} + {\HCode{<meta property="rendition:orientation">landscape</meta>}} + \Configure{OpfMetadata} + {\HCode{<meta property="rendition:spread">none</meta>}} + \Configure{@HEAD} + {\HCode{<meta name="viewport" content="width=1920, height=1080"/>\Hnewline}} Modify the dimensions in the `<meta name="viewport>` element according to your needs. diff --git a/support/tex4ebook/changelog.tex b/support/tex4ebook/changelog.tex index 3d0d6c1c6c..f415f81c8c 100644 --- a/support/tex4ebook/changelog.tex +++ b/support/tex4ebook/changelog.tex @@ -3,6 +3,61 @@ \begin{itemize} \item + 2021/07/26 + + \begin{itemize} + \tightlist + \item + released version \texttt{0.3d} + \end{itemize} +\item + 2021/05/29 + + \begin{itemize} + \tightlist + \item + renamed \texttt{DeclareLanguage} to + \texttt{\textbackslash{}DeclareLanguageEbook}. Fixes + \href{https://github.com/michal-h21/tex4ebook/issues/78}{issue 78}. + \end{itemize} +\item + 2021/05/15 + + \begin{itemize} + \tightlist + \item + use \texttt{assert} in checking of existence of the \texttt{zip} + command. + \item + replace colons in \texttt{OPF} \texttt{id} attributes and add + trailing \texttt{x} if the \texttt{id} starts with number. It should + fix some validation issues. + \end{itemize} +\item + 2021/05/02 + + \begin{itemize} + \tightlist + \item + added more examples of configuration to the documentation. + \end{itemize} +\item + 2020/11/09 + + \begin{itemize} + \tightlist + \item + set exit status + \end{itemize} +\item + 2020/11/09 + + \begin{itemize} + \tightlist + \item + don't redefine \texttt{PicDisplay} configuration. + \end{itemize} +\item 2020/11/06 \begin{itemize} diff --git a/support/tex4ebook/readme.tex b/support/tex4ebook/readme.tex index 0fdb16c1c9..428ac6c68c 100644 --- a/support/tex4ebook/readme.tex +++ b/support/tex4ebook/readme.tex @@ -53,10 +53,22 @@ But it is optional. You shouldn't need to modify your \TeX~files \begin{itemize} \tightlist \item - \texttt{\textbackslash{}coverimage\{coverimage.name\}} - include cover - image to the document. + \texttt{\textbackslash{}coverimage{[}\textless{}graphicx\ options\textgreater{}{]}\{coverimage.name\}} + - include cover image to the document. You can pass the same options + as to \texttt{\textbackslash{}includegraphics} command in the optional + argument. \end{itemize} +For example: + +\begin{verbatim} +\thispagestyle{empty} +\begin{document} +\coverimage[scale=0.8]{coverimage.name} % include scaled cover image +... +\pagestyle{headings} +\end{verbatim} + \hypertarget{command-line-options}{% \section{Command line options}\label{command-line-options}} @@ -246,6 +258,52 @@ Add item to \texttt{\textless{}manifest\textgreater{}} section in the Add \texttt{XML} name space to the generated \texttt{XHTML} files. Useful in \texttt{EPUB\ 3}. +\hypertarget{example-config-file}{% +\subsubsection{Example config file}\label{example-config-file}} + +\begin{verbatim} +\Preamble{xhtml} +\begin{document} +\Configure{DocumentLanguage}{de} +% Use following lines if your document has ISBN: +% \Configure{OpfScheme}{ISBN} +% \Configure{UniqueIdentifier}{3-0000-1111-X} +% Another possibility is URI that points for example to the ebook homepage: +\Configure{OpfScheme}{URI} +\Configure{UniqueIdentifier} +{https://de.wikipedia.org/wiki/Der_achte_Schöpfungstag} +\Configure{CoverMimeType}{image/jpeg} +% If you don't use \coverimage in the document text, +% add cover image using this command: +\CoverMetadata{coverimage.jpg} +% You can also add more authors to your ebook metadata: +\Configure{OpfMetadata} +{\HCode{<dc:publisher>Deutscher Bücherbund</dc:publisher>}} +\Configure{OpfMetadata} +{\HCode{<dc:contributor>Image Artist</dc:contributor>}} +\Configure{OpfMetadata} +{\HCode{<dc:contributor>Trans Lator</dc:contributor>}} +\Configure{OpfMetadata} +{\HCode{<dc:date opf:event='original-publication'>1888</dc:date>}} +\EndPreamble +\end{verbatim} + +Remarks: + +\begin{itemize} +\tightlist +\item + Leading percent signs in the \texttt{.cfg} file introduce comments +\item + If the unique identifier is a URI which contains diacritical + characters, the\\ + equivalent HTML code needs to be inserted. \texttt{UTF8} is not + recognized at that place. +\item + \texttt{UTF8} characters may be used in the \texttt{OpfMetadata} + sections. +\end{itemize} + \hypertarget{commands-available-in-the-config-file}{% \subsection{Commands available in the config file}\label{commands-available-in-the-config-file}} @@ -301,10 +359,14 @@ The basic support for the Fixed layout EPUB 3 can be enabled using the following configurations: \begin{verbatim} -\Configure{OpfMetadata}{\HCode{<meta property="rendition:layout">pre-paginated</meta>}} -\Configure{OpfMetadata}{\HCode{<meta property="rendition:orientation">landscape</meta>}} -\Configure{OpfMetadata}{\HCode{<meta property="rendition:spread">none</meta>}} -\Configure{@HEAD}{\HCode{<meta name="viewport" content="width=1920, height=1080"/>\Hnewline}} +\Configure{OpfMetadata} +{\HCode{<meta property="rendition:layout">pre-paginated</meta>}} +\Configure{OpfMetadata} +{\HCode{<meta property="rendition:orientation">landscape</meta>}} +\Configure{OpfMetadata} +{\HCode{<meta property="rendition:spread">none</meta>}} +\Configure{@HEAD} +{\HCode{<meta name="viewport" content="width=1920, height=1080"/>\Hnewline}} \end{verbatim} Modify the dimensions in the diff --git a/support/tex4ebook/tex4ebook b/support/tex4ebook/tex4ebook index 91df031ffb..2b2b38b113 100755 --- a/support/tex4ebook/tex4ebook +++ b/support/tex4ebook/tex4ebook @@ -67,7 +67,7 @@ else end if args.version then - print "tex4ebook v0.3c" + print "tex4ebook v0.3d" return end @@ -201,4 +201,5 @@ executor.run(input,params) executor.writeContainer() executor.clean() log:status("Conversion finished") +logging.exit_status() --print(args[1]) diff --git a/support/tex4ebook/tex4ebook-doc.pdf b/support/tex4ebook/tex4ebook-doc.pdf Binary files differindex 3546db973f..170a8fc4f1 100644 --- a/support/tex4ebook/tex4ebook-doc.pdf +++ b/support/tex4ebook/tex4ebook-doc.pdf diff --git a/support/tex4ebook/tex4ebook-exec_epub.lua b/support/tex4ebook/tex4ebook-exec_epub.lua index 75304e2494..539cf930ab 100644 --- a/support/tex4ebook/tex4ebook-exec_epub.lua +++ b/support/tex4ebook/tex4ebook-exec_epub.lua @@ -157,6 +157,10 @@ function make_opf() local id=table.concat(dir_part,"-")..fname.."_"..ext -- remove invalid characters from id start id = id:gsub("^[%.%-]*","") + -- remove colons + id = id:gsub(":", "_") + -- id cannot start by number, add trailing "x" character + id = id:gsub("^([%d])", "x%1") return "<item id='"..id .. "' href='"..item.."' media-type='"..mimetype.."' />",id end local find_all_files= function(s,r) @@ -277,9 +281,9 @@ function make_opf() end end local function find_zip() - if io.popen("zip -v","r"):close() then + if assert(io.popen("zip -v","r"):close()) then return "zip" - elseif io.popen("miktex-zip -v","r"):close() then + elseif assert(io.popen("miktex-zip -v","r"):close()) then return "miktex-zip" end log:warning "It appears you don't have zip command installed. I can't pack the ebook" diff --git a/support/tex4ebook/tex4ebook.4ht b/support/tex4ebook/tex4ebook.4ht index 6ee87ed3ae..0132463917 100644 --- a/support/tex4ebook/tex4ebook.4ht +++ b/support/tex4ebook/tex4ebook.4ht @@ -193,10 +193,6 @@ href="#1" media-type="\a:CoverMimeType" />}} % Support for SVG isn't ready %\Configure{Picture}{.svg} \Configure{Picture}{.png} -\Configure{PicDisplay} - {\edef\MathPar{\ifvmode par-\fi}\IgnorePar\endgraf\EndP - \HCode{<div class="\MathPar math-display">}} - {\HCode{</div>}} {} {class="\MathPar math-display" } \Configure{()}{$}{$} \fi \:CheckOption{kindle} \if:Option diff --git a/support/tex4ebook/tex4ebook.sty b/support/tex4ebook/tex4ebook.sty index 1b0114748c..4d8cb1db09 100644 --- a/support/tex4ebook/tex4ebook.sty +++ b/support/tex4ebook/tex4ebook.sty @@ -11,7 +11,7 @@ \def\opftable{} %Declare RFC3066 language code for babel language name -\def\DeclareLanguage#1#2{% +\def\DeclareLanguageEbook#1#2{% \csgdef{rfclang#1}{#2}% } \def\GetLanguage{% @@ -21,96 +21,96 @@ \fi% } -\DeclareLanguage{UKenglish}{en-GB} -\DeclareLanguage{USenglish}{en-US} -\DeclareLanguage{acadian}{fr} -\DeclareLanguage{albanian}{sq} -\DeclareLanguage{american}{en-US} -\DeclareLanguage{amharic}{am} -\DeclareLanguage{arabic}{ar} -\DeclareLanguage{armenian}{hy} -\DeclareLanguage{australian}{en-US} -\DeclareLanguage{austrian}{de} -\DeclareLanguage{basque}{eu} -\DeclareLanguage{bengali}{bn} -\DeclareLanguage{brazilian}{pt} -\DeclareLanguage{brazil}{pt} -\DeclareLanguage{breton}{br} -\DeclareLanguage{british}{en-GB} -\DeclareLanguage{bulgarian}{bg} -\DeclareLanguage{canadian}{en-US} -\DeclareLanguage{canadien}{fr} -\DeclareLanguage{catalan}{ca} -\DeclareLanguage{croatian}{hr} -\DeclareLanguage{czech}{cs-CZ} -\DeclareLanguage{danish}{da} -\DeclareLanguage{divehi}{dv} -\DeclareLanguage{dutch}{nl} -\DeclareLanguage{english}{en} -\DeclareLanguage{esperanto}{eo} -\DeclareLanguage{estonian}{et} -\DeclareLanguage{finnish}{f\/i} -\DeclareLanguage{francais}{fr} -\DeclareLanguage{french}{fr} -\DeclareLanguage{galician}{gl} -\DeclareLanguage{germanb}{de} -\DeclareLanguage{german}{de} -\DeclareLanguage{greek}{el} -\DeclareLanguage{hebrew}{he} -\DeclareLanguage{hindi}{hi} -\DeclareLanguage{hungarian}{hu} -\DeclareLanguage{icelandic}{is} -\DeclareLanguage{interlingua}{ia} -\DeclareLanguage{irish}{ga} -\DeclareLanguage{italian}{it} -\DeclareLanguage{kannada}{kn} -\DeclareLanguage{khmer}{km} -\DeclareLanguage{korean}{ko} -\DeclareLanguage{lao}{lo} -\DeclareLanguage{latin}{la} -\DeclareLanguage{latvian}{lv} -\DeclareLanguage{lithuanian}{lt} -\DeclareLanguage{lowersorbian}{dsb} -\DeclareLanguage{magyar}{hu} -\DeclareLanguage{malayalam}{ml} -\DeclareLanguage{marathi}{mr} -\DeclareLanguage{naustrian}{de} -\DeclareLanguage{newzealand}{en} -\DeclareLanguage{ngerman}{de} -\DeclareLanguage{norsk}{no} -\DeclareLanguage{norwegiannynorsk}{nn} -\DeclareLanguage{nynorsk}{no} -\DeclareLanguage{occitan}{oc} -\DeclareLanguage{oldchurchslavonic}{cu} -\DeclareLanguage{persian}{fa} -\DeclareLanguage{polish}{pl-PL} -\DeclareLanguage{polutonikogreek}{el} -\DeclareLanguage{portuges}{pt} -\DeclareLanguage{portuguese}{pt} -\DeclareLanguage{romanian}{ro} -\DeclareLanguage{romansh}{rm} -\DeclareLanguage{russian}{ru} -\DeclareLanguage{samin}{se} -\DeclareLanguage{sanskrit}{sa} -\DeclareLanguage{scottish}{gd} -\DeclareLanguage{serbian}{sr} -\DeclareLanguage{serbo-croatian}{sh} -\DeclareLanguage{slovak}{sk} -\DeclareLanguage{slovene}{sl} -\DeclareLanguage{slovenian}{sl} -\DeclareLanguage{spanish}{es} -\DeclareLanguage{swedish}{sv} -\DeclareLanguage{tamil}{ta} -\DeclareLanguage{telugu}{te} -\DeclareLanguage{thai}{th} -\DeclareLanguage{tibetan}{bo} -\DeclareLanguage{turkish}{tr} -\DeclareLanguage{turkmen}{tk} -\DeclareLanguage{ukrainian}{uk} -\DeclareLanguage{uppersorbian}{hsb} -\DeclareLanguage{urdu}{ur} -\DeclareLanguage{vietnamese}{vi} -\DeclareLanguage{welsh}{cy} +\DeclareLanguageEbook{UKenglish}{en-GB} +\DeclareLanguageEbook{USenglish}{en-US} +\DeclareLanguageEbook{acadian}{fr} +\DeclareLanguageEbook{albanian}{sq} +\DeclareLanguageEbook{american}{en-US} +\DeclareLanguageEbook{amharic}{am} +\DeclareLanguageEbook{arabic}{ar} +\DeclareLanguageEbook{armenian}{hy} +\DeclareLanguageEbook{australian}{en-US} +\DeclareLanguageEbook{austrian}{de} +\DeclareLanguageEbook{basque}{eu} +\DeclareLanguageEbook{bengali}{bn} +\DeclareLanguageEbook{brazilian}{pt} +\DeclareLanguageEbook{brazil}{pt} +\DeclareLanguageEbook{breton}{br} +\DeclareLanguageEbook{british}{en-GB} +\DeclareLanguageEbook{bulgarian}{bg} +\DeclareLanguageEbook{canadian}{en-US} +\DeclareLanguageEbook{canadien}{fr} +\DeclareLanguageEbook{catalan}{ca} +\DeclareLanguageEbook{croatian}{hr} +\DeclareLanguageEbook{czech}{cs-CZ} +\DeclareLanguageEbook{danish}{da} +\DeclareLanguageEbook{divehi}{dv} +\DeclareLanguageEbook{dutch}{nl} +\DeclareLanguageEbook{english}{en} +\DeclareLanguageEbook{esperanto}{eo} +\DeclareLanguageEbook{estonian}{et} +\DeclareLanguageEbook{finnish}{f\/i} +\DeclareLanguageEbook{francais}{fr} +\DeclareLanguageEbook{french}{fr} +\DeclareLanguageEbook{galician}{gl} +\DeclareLanguageEbook{germanb}{de} +\DeclareLanguageEbook{german}{de} +\DeclareLanguageEbook{greek}{el} +\DeclareLanguageEbook{hebrew}{he} +\DeclareLanguageEbook{hindi}{hi} +\DeclareLanguageEbook{hungarian}{hu} +\DeclareLanguageEbook{icelandic}{is} +\DeclareLanguageEbook{interlingua}{ia} +\DeclareLanguageEbook{irish}{ga} +\DeclareLanguageEbook{italian}{it} +\DeclareLanguageEbook{kannada}{kn} +\DeclareLanguageEbook{khmer}{km} +\DeclareLanguageEbook{korean}{ko} +\DeclareLanguageEbook{lao}{lo} +\DeclareLanguageEbook{latin}{la} +\DeclareLanguageEbook{latvian}{lv} +\DeclareLanguageEbook{lithuanian}{lt} +\DeclareLanguageEbook{lowersorbian}{dsb} +\DeclareLanguageEbook{magyar}{hu} +\DeclareLanguageEbook{malayalam}{ml} +\DeclareLanguageEbook{marathi}{mr} +\DeclareLanguageEbook{naustrian}{de} +\DeclareLanguageEbook{newzealand}{en} +\DeclareLanguageEbook{ngerman}{de} +\DeclareLanguageEbook{norsk}{no} +\DeclareLanguageEbook{norwegiannynorsk}{nn} +\DeclareLanguageEbook{nynorsk}{no} +\DeclareLanguageEbook{occitan}{oc} +\DeclareLanguageEbook{oldchurchslavonic}{cu} +\DeclareLanguageEbook{persian}{fa} +\DeclareLanguageEbook{polish}{pl-PL} +\DeclareLanguageEbook{polutonikogreek}{el} +\DeclareLanguageEbook{portuges}{pt} +\DeclareLanguageEbook{portuguese}{pt} +\DeclareLanguageEbook{romanian}{ro} +\DeclareLanguageEbook{romansh}{rm} +\DeclareLanguageEbook{russian}{ru} +\DeclareLanguageEbook{samin}{se} +\DeclareLanguageEbook{sanskrit}{sa} +\DeclareLanguageEbook{scottish}{gd} +\DeclareLanguageEbook{serbian}{sr} +\DeclareLanguageEbook{serbo-croatian}{sh} +\DeclareLanguageEbook{slovak}{sk} +\DeclareLanguageEbook{slovene}{sl} +\DeclareLanguageEbook{slovenian}{sl} +\DeclareLanguageEbook{spanish}{es} +\DeclareLanguageEbook{swedish}{sv} +\DeclareLanguageEbook{tamil}{ta} +\DeclareLanguageEbook{telugu}{te} +\DeclareLanguageEbook{thai}{th} +\DeclareLanguageEbook{tibetan}{bo} +\DeclareLanguageEbook{turkish}{tr} +\DeclareLanguageEbook{turkmen}{tk} +\DeclareLanguageEbook{ukrainian}{uk} +\DeclareLanguageEbook{uppersorbian}{hsb} +\DeclareLanguageEbook{urdu}{ur} +\DeclareLanguageEbook{vietnamese}{vi} +\DeclareLanguageEbook{welsh}{cy} \AtEndDocument{% \ncxtable |