--- Code related to LaTeX generation. -- @module advtrains_doc_integration.latex local latex = {} local utils = advtrains_doc_integration.utils --- Escape string in LaTeX. -- @tparam string str The string to escape. -- @treturn string The escaped string. function latex.escape(str) return (string.gsub(str, ".", { ["&"] = [[\&]], ["%"] = [[\%]], ["$"] = [[\$]], ["#"] = [[\#]], ["_"] = [[\_]], ["{"] = [[\{]], ["}"] = [[\}]], ["~"] = [[\textasciitilde]], ["^"] = [[\textasciicircum]], ["\\"] = [[\textbackslash]], })) end --- Escape a translated string in LaTeX. -- @tparam string lang The language to use for server-side translation -- @tparam string str The string to escape. -- @treturn The escaped string. function latex.escape_translated(lang, str) return latex.escape(minetest.get_translated_string(lang, str)) end local function SL(str) return latex.escape_translated("en", str) end --- Describe a color in LaTeX. -- @tparam string cstr The color string. -- @treturn string The string describing the color. function latex.describe_color(cstr) local color = string.match(cstr,"^#(%x%x%x%x%x%x)$") cstr = SL(cstr) if color then color = SL(string.upper(color)) return string.format([[\tikz \definecolor{c}{HTML}{%s} \draw[fill=c] (0,0) rectangle (1em,1em); \texttt{%s}]], color, cstr) else return string.format([[\texttt{%s}]], cstr) end end --- Describe a coupler in LaTeX. -- @tparam string coupler The name of the coupler. -- @treturn string The string describing the coupler. function latex.describe_coupler(coupler) local name = utils.get_coupler_name(coupler) if name then return ([[%s (\texttt{%s})]]):format(SL(name), SL(coupler)) else return ([[\texttt{%s}]]):format(SL(coupler)) end end --- Describe a wagon prototype in LaTeX. -- @tparam string itemname The item name of the wagon prototype. -- @treturn string The description of the prototype. function latex.describe_wagon_prototype(itemname) local prototype = advtrains_doc_integration.prototypes[itemname] local wname = ItemStack(itemname):get_short_description() local st = {string.format([[ \documentclass{article} \usepackage[a4paper,margin=1in,bottom=1.5in]{geometry} \usepackage[T1]{fontenc} \usepackage{tikz} \usepackage{booktabs,multirow,tabularx} \renewcommand{\arraystretch}{1.5} \usepackage{hyperref} \hypersetup{pdftitle={Wagon Datasheet: %s}} \title{Wagon Datasheet} \author{%s} \setlength{\parindent}{0pt} \begin{document} \maketitle ]], SL(wname), SL(wname))} table.insert(st, [[\section{Basic Information}]]) if prototype.longdesc then table.insert(st, SL(prototype.longdesc) .. "\n") end table.insert(st, [[\begin{tabularx}{\textwidth}{l X}]]) table.insert(st, string.format([[Itemstring & \texttt{%s}\\]], SL(itemname))) if prototype.drives_on then local i0 = #st+1 local count = 0 for k in pairs(prototype.drives_on) do table.insert(st, string.format([[& \texttt{%s}\\]], SL(k))) count = count + 1 end if count > 0 then st[i0] = string.format([[Drives on %s]], st[i0]) end end if prototype.wagon_span then table.insert(st, string.format([[Wagon span & %d mm\\]], prototype.wagon_span*2000)) end if prototype.max_speed then table.insert(st, string.format([[Maximum speed & %d m/s\\]], prototype.max_speed)) end table.insert(st, string.format([[Motive power & %s\\]], prototype.is_locomotive and "Present" or "Absent")) if prototype.horn_sound then table.insert(st, string.format([[Horn sound & \texttt{%s}\\]], SL(prototype.horn_sound.name))) else table.insert(st, [[Horn sound & Undefined\\]]) end if prototype.mesh then table.insert(st, string.format([[Mesh & \texttt{%s}\\]], SL(prototype.mesh))) end if prototype.textures then local i0 = #st+1 local count = 0 for _, i in pairs(prototype.textures) do table.insert(st, string.format([[& \texttt{%s}\\]], SL(i))) count = count + 1 end if count > 0 then st[i0] = string.format([[Textures %s]], st[i0]) end end do local i0 = #st+1 local count = 0 for _, i in ipairs(prototype.drops or {}) do local item = ItemStack(i) if not item:is_empty() then local desc = string.format([[\texttt{%s}]], SL(item:get_name())) if item:is_known() then desc = SL(item:get_short_description()) end table.insert(st, string.format([[& %s: %d\\]], desc, item:get_count())) count = count + 1 end end if count > 0 then st[i0] = [[Drops ]] .. st[i0] else table.insert(st, [[Drops & Nothing \\]]) end end table.insert(st, [[\end{tabularx}]]) table.insert(st, [[\section{Coupler Compatibility}]]) do local fcouplers = prototype.coupler_types_front local rcouplers = prototype.coupler_types_back local ccouplers = {} local lcouplers = {} local couplerid = {} local flim, rlim for k in pairs(fcouplers or {}) do flim = true ccouplers[k] = true end for k in pairs(rcouplers or {}) do rlim = true ccouplers[k] = true end for k in pairs(ccouplers) do local desc = latex.describe_coupler(k) table.insert(lcouplers, desc) couplerid[desc] = k end table.sort(lcouplers) table.insert(st, [[ \begin{tabularx}{\textwidth}{X c c} \toprule \multirow[t]{2}{*}{\bfseries Coupler Type} & \multicolumn{2}{c}{\bfseries Compatibility}\\ \cmidrule(lr){2-3} & {\bfseries Front Coupler} & {\bfseries Rear Coupler}\\\midrule ]]) if not (fcouplers and rcouplers) then local fd = fcouplers and "" or [[$\bullet$]] local rd = rcouplers and "" or [[$\bullet$]] table.insert(st, string.format([[\textit{Universal}&%s&%s\\]], fd, rd)) end for i = 1, #lcouplers do local cdesc = lcouplers[i] local cid = couplerid[cdesc] local fd, rd = "", "" if flim then fd = fcouplers[cid] and [[$\bullet$]] or "" elseif not fcouplers then fd = [[$\Uparrow$]] end if rlim then rd = rcouplers[cid] and [[$\bullet$]] or "" elseif not rcouplers then rd = [[$\Uparrow$]] end table.insert(st, string.format([[%s&%s&%s\\]], cdesc, fd, rd)) end table.insert(st, [[\bottomrule]]) table.insert(st, [[\end{tabularx}]]) end local hasinv = prototype.has_inventory local hasseats = prototype.max_seats>0 local taliquid = prototype.techage_liquid_capacity or 0 if hasinv or hasseats or taliquid>0 then table.insert(st, [[\section{Wagon Capacity}]]) if hasseats then table.insert(st, [[ \begin{tabularx}{\textwidth}{X c c} \toprule {\bfseries Seat Group} & {\bfseries Driver Stand} & {\bfseries Seat Count}\\\midrule ]]) for _, d in pairs(prototype.seat_groups) do table.insert(st, string.format([[%s & %s & %d\\]], SL(d.name), d.driving_ctrl_access and [[$\bullet$]] or "", d.count)) end table.insert(st, [[\bottomrule]]) table.insert(st, [[\end{tabularx}]]) end if hasinv then if next(prototype.inventory_list_sizes or {}) ~= nil then table.insert(st, [[ \begin{tabularx}{\textwidth}{X c} \toprule {\bfseries Inventory Name} & {\bfseries Capacity}\\\midrule ]]) for k, v in pairs(prototype.inventory_list_sizes) do table.insert(st, string.format([[\texttt{%s} & %d\\]], SL(k), v)) end table.insert(st, [[\bottomrule]]) table.insert(st, [[\end{tabularx}]]) else table.insert(st, [[This wagon has an inventory of unknown size.]]) end end if taliquid > 0 then table.insert(st, string.format([[ \begin{tabularx}{\textwidth}{X l} {Liquid Capacity (Techage)} & %d \end{tabularx} ]], taliquid)) end end if prototype.set_livery then if prototype.livery_definition then table.insert(st, [[\section{Multi-Component Liveries}]]) local components = prototype.livery_definition.components local presets = prototype.livery_definition.presets table.insert(st, [[\subsection*{Components}]]) table.insert(st, [[\begin{itemize}]]) for _, c in ipairs(components) do table.insert(st, string.format([[\item %s]], SL(c.description))) end table.insert(st, [[\end{itemize}]]) for _, p in ipairs(presets) do table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(p.description))) table.insert(st, [[ \begin{tabularx}{\textwidth}{X c} \toprule {\bfseries Component} & {\bfseries Color} \\\midrule ]]) for _, c in ipairs(p.livery_stack.layers) do local cdesc = SL(components[c.component].description) table.insert(st, string.format([[%s & %s\\]], cdesc, latex.describe_color(c.color))) end table.insert(st, [[ \bottomrule \end{tabularx} ]]) end else table.insert(st, [[\section{Livery System (Bike Painter)}]]) table.insert(st, [[This wagon can be painted by the bike painter.]]) end end local dlxlivdef = prototype.dlxtrains_livery if dlxlivdef then table.insert(st, [[ \section{DlxTrains Livery Sytem} This wagon can be customized with DlxTrains' livery system. ]]) end local atlivdef = prototype.advtrains_livery_tools if atlivdef then table.insert(st, [[\section{Advtrains Livery Tool (Marnack)}]]) for _, tname in ipairs(atlivdef.template_names) do local tdef = atlivdef.templates[tname] table.insert(st, string.format([[\subsection*{Template: %s}]], SL(tname))) table.insert(st, SL(tdef.notes)) table.insert(st, "") if #tdef.overlays > 0 then table.insert(st, "This template contains the following components:") table.insert(st, [[\begin{itemize}]]) for _, overlay in ipairs(tdef.overlays) do table.insert(st, string.format([[\item %s]], SL(overlay.name))) end table.insert(st, [[\end{itemize}]]) else table.insert(st, "This template does not appear to contain any (customizable) component.") end end for _, lname in ipairs(atlivdef.livery_names) do local ldef = atlivdef.liveries[lname] local tname = ldef.livery_template_name table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(lname))) table.insert(st, string.format([[Template: %s]], SL(tname))) table.insert(st, "") table.insert(st, [[ \begin{tabularx}{\textwidth}{X c} \toprule {\bfseries Component} & {\bfseries Color}\\\midrule]]) for _, overlay in pairs(ldef.overlays) do local cname = atlivdef.templates[tname].overlays[overlay.id].name table.insert(st, string.format([[%s & %s\\]], SL(cname), latex.describe_color(overlay.color))) end table.insert(st, [[ \bottomrule \end{tabularx} ]]) end end table.insert(st, [[ \end{document} ]]) return table.concat(st, "\n") end return latex