diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e65a66f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/latex +/build +/pdf +.obsidian +.trash diff --git a/converters/bookletify.latex b/converters/bookletify.latex new file mode 100644 index 0000000..06df170 --- /dev/null +++ b/converters/bookletify.latex @@ -0,0 +1,9 @@ +\documentclass[a4paper]{article} + +\usepackage{pdfpages} + +\begin{document} + +\includepdf[pages=-, nup=2x1, frame, signature*=4, landscape, angle=180]{?pdf?} + +\end{document} diff --git a/converters/diagrams.lua b/converters/diagrams.lua new file mode 100644 index 0000000..a74b82e --- /dev/null +++ b/converters/diagrams.lua @@ -0,0 +1,644 @@ +--[[ +diagram – create images and figures from code blocks. + +MIT License + +Copyright © 2019-2023 Albert Krewinkel +Copyright © 2019 Thorsten Sommer +Copyright © 2018 Florian Schätzig +Copyright © 2018 John MacFarlane + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] +-- The filter uses the Figure AST element, which was added in pandoc 3. +PANDOC_VERSION:must_be_at_least '3.0' + +local version = pandoc.types.Version '1.2.0' + +-- Report Lua warnings to stderr if the `warn` function is not plugged into +-- pandoc's logging system. +if not warn then + -- fallback + warn = function(...) io.stderr:write(table.concat({ ... })) end +elseif PANDOC_VERSION < '3.1.4' then + -- starting with pandoc 3.1.4, warnings are reported to pandoc's logging + -- system, so no need to print warnings to stderr. + warn '@on' +end + +local io = require 'io' +local pandoc = require 'pandoc' +local system = require 'pandoc.system' +local utils = require 'pandoc.utils' +local List = require 'pandoc.List' +local stringify = utils.stringify +local with_temporary_directory = system.with_temporary_directory +local with_working_directory = system.with_working_directory + +--- Returns a filter-specific directory in which cache files can be +--- stored, or nil if no such directory is available. +local function cachedir () + local cache_home = os.getenv 'XDG_CACHE_HOME' + if not cache_home or cache_home == '' then + local user_home = system.os == 'windows' + and os.getenv 'USERPROFILE' + or os.getenv 'HOME' + + if not user_home or user_home == '' then + return nil + end + cache_home = pandoc.path.join{user_home, '.cache'} or nil + end + + -- Create filter cache directory + return pandoc.path.join{cache_home, 'pandoc-diagram-filter'} +end + +--- Path holding the image cache, or `nil` if the cache is not used. +local image_cache = nil + +local mimetype_for_extension = { + jpeg = 'image/jpeg', + jpg = 'image/jpeg', + pdf = 'application/pdf', + png = 'image/png', + svg = 'image/svg+xml', +} + +local extension_for_mimetype = { + ['application/pdf'] = 'pdf', + ['image/jpeg'] = 'jpg', + ['image/png'] = 'png', + ['image/svg+xml'] = 'svg', +} + +--- Converts a list of format specifiers to a set of MIME types. +local function mime_types_set (tbl) + local set = {} + local mime_type + for _, image_format_spec in ipairs(tbl) do + mime_type = mimetype_for_extension[image_format_spec] or image_format_spec + set[mime_type] = true + end + return set +end + +--- Reads the contents of a file. +local function read_file (filepath) + local fh = io.open(filepath, 'rb') + local contents = fh:read('a') + fh:close() + return contents +end + +--- Writes the contents into a file at the given path. +local function write_file (filepath, content) + local fh = io.open(filepath, 'wb') + fh:write(content) + fh:close() +end + +--- Like `pandoc.pipe`, but allows "multi word" paths: +-- Supplying a list as the first argument will use the first element as +-- the executable path and prepend the remaining elements to the list of +-- arguments. +local function pipe (command, args, input) + local cmd + if pandoc.utils.type(command) == 'List' then + command = command:map(stringify) + cmd = command:remove(1) + args = command .. args + else + cmd = stringify(command) + end + return pandoc.pipe(cmd, args, input) +end + + +-- +-- Diagram Engines +-- + +-- PlantUML engine; assumes that there's a `plantuml` binary. +local plantuml = { + line_comment_start = [[']], + mime_types = mime_types_set{'pdf', 'png', 'svg'}, + compile = function (self, puml) + local mime_type = self.mime_type or 'image/svg+xml' + -- PlantUML format identifiers correspond to common file extensions. + local format = extension_for_mimetype[mime_type] + if not format then + format, mime_type = 'svg', 'image/svg+xml' + end + local args = {'-t' .. format, "-pipe", "-charset", "UTF8"} + return pipe(self.execpath or 'plantuml', args, puml), mime_type + end, +} + +--- GraphViz engine for the dot language +local graphviz = { + line_comment_start = '//', + mime_types = mime_types_set{'jpg', 'pdf', 'png', 'svg'}, + mime_type = 'image/svg+xml', + compile = function (self, code) + local mime_type = self.mime_type + -- GraphViz format identifiers correspond to common file extensions. + local format = extension_for_mimetype[mime_type] + if not format then + format, mime_type = 'svg', 'image/svg+xml' + end + return pipe(self.execpath or 'dot', {"-T"..format}, code), mime_type + end, +} + +--- Mermaid engine +local mermaid = { + line_comment_start = '%%', + mime_types = mime_types_set{'pdf', 'png', 'svg'}, + compile = function (self, code) + local mime_type = self.mime_type or 'image/svg+xml' + local file_extension = extension_for_mimetype[mime_type] + return with_temporary_directory("diagram", function (tmpdir) + return with_working_directory(tmpdir, function () + local infile = 'diagram.mmd' + local outfile = 'diagram.' .. file_extension + write_file(infile, code) + pipe( + self.execpath or 'mmdc', + {"--pdfFit", "--input", infile, "--output", outfile}, + '' + ) + return read_file(outfile), mime_type + end) + end) + end, +} + +--- TikZ +-- + +--- LaTeX template used to compile TikZ images. +local tikz_template = pandoc.template.compile [[ +\documentclass{standalone} +\usepackage{tikz} +$for(header-includes)$ +$it$ +$endfor$ +$additional-packages$ +\begin{document} +$body$ +\end{document} +]] + +--- The TikZ engine uses pdflatex to compile TikZ code to an image +local tikz = { + line_comment_start = '%%', + + mime_types = { + ['application/pdf'] = true, + }, + + --- Compile LaTeX with TikZ code to an image + compile = function (self, src, user_opts) + return with_temporary_directory("tikz", function (tmpdir) + return with_working_directory(tmpdir, function () + -- Define file names: + local file_template = "%s/tikz-image.%s" + local tikz_file = file_template:format(tmpdir, "tex") + local pdf_file = file_template:format(tmpdir, "pdf") + + -- Treat string values as raw LaTeX + local meta = { + ['header-includes'] = user_opts['header-includes'], + ['additional-packages'] = {pandoc.RawInline( + 'latex', + stringify(user_opts['additional-packages'] or '') + )}, + } + local tex_code = pandoc.write( + pandoc.Pandoc({pandoc.RawBlock('latex', src)}, meta), + 'latex', + {template = tikz_template} + ) + write_file(tikz_file, tex_code) + + -- Execute the LaTeX compiler: + local success, result = pcall( + pipe, + self.execpath or 'pdflatex', + { '-interaction=nonstopmode', '-output-directory', tmpdir, tikz_file }, + '' + ) + if not success then + warn(string.format( + "The call\n%s\nfailed with error code %s. Output:\n%s", + result.command, + result.error_code, + result.output + )) + end + return read_file(pdf_file), 'application/pdf' + end) + end) + end +} + +--- Asymptote diagram engine +local asymptote = { + line_comment_start = '%%', + mime_types = { + ['application/pdf'] = true, + }, + compile = function (self, code) + return with_temporary_directory("asymptote", function(tmpdir) + return with_working_directory(tmpdir, function () + local pdf_file = "pandoc_diagram.pdf" + local args = {'-tex', 'pdflatex', "-o", "pandoc_diagram", '-'} + pipe(self.execpath or 'asy', args, code) + return read_file(pdf_file), 'application/pdf' + end) + end) + end, +} + +--- Cetz diagram engine +local cetz = { + line_comment_start = '%%', + mime_types = mime_types_set{'jpg', 'pdf', 'png', 'svg'}, + mime_type = 'image/svg+xml', + compile = function (self, code) + local mime_type = self.mime_type + local format = extension_for_mimetype[mime_type] + if not format then + format, mime_type = 'svg', 'image/svg+xml' + end + local preamble = [[ +#import "@preview/cetz:0.3.4" +#set page(width: auto, height: auto, margin: .5cm) +]] + + local typst_code = preamble .. code + + return with_temporary_directory("diagram", function (tmpdir) + return with_working_directory(tmpdir, function () + local outfile = 'diagram.' .. format + local execpath = self.execpath + if not execpath and quarto and quarto.version >= '1.4' then + -- fall back to the Typst exec shipped with Quarto. + execpath = List{'quarto', 'typst'} + end + pipe( + execpath or 'typst', + {"compile", "-f", format, "-", outfile}, + typst_code + ) + return read_file(outfile), mime_type + end) + end) + end, +} + +local default_engines = { + asymptote = asymptote, + dot = graphviz, + mermaid = mermaid, + plantuml = plantuml, + tikz = tikz, + cetz = cetz, +} + +-- +-- Configuration +-- + +--- Options for the output format of the given name. +local function format_options (name) + local pdf2svg = name ~= 'latex' and name ~= 'context' + local is_office_format = name == 'docx' or name == 'odt' + -- Office formats seem to work better with PNG than with SVG. + local preferred_mime_types = is_office_format + and pandoc.List{'image/png', 'application/pdf'} + or pandoc.List{'application/pdf', 'image/png'} + -- Prefer SVG for non-PDF output formats, except for Office formats + if is_office_format then + preferred_mime_types:insert('image/svg+xml') + elseif pdf2svg then + preferred_mime_types:insert(1, 'image/svg+xml') + end + return { + name = name, + pdf2svg = pdf2svg, + preferred_mime_types = preferred_mime_types, + best_mime_type = function (self, supported_mime_types, requested) + return self.preferred_mime_types:find_if(function (preferred) + return supported_mime_types[preferred] and + (not requested or + (pandoc.utils.type(requested) == 'List' and + requested:includes(preferred)) or + (pandoc.utils.type(requested) == 'table' and + requested[preferred]) or + + -- Assume string, Inlines, and Blocks values specify the only + -- acceptable MIME type. + stringify(requested) == preferred) + end) + end + } +end + +--- Returns a configured diagram engine. +local function get_engine (name, engopts, format) + local engine = default_engines[name] or + select(2, pcall(require, stringify(engopts.package))) + + -- Sanity check + if not engine then + warn(PANDOC_SCRIPT_FILE, ": No such engine '", name, "'.") + return nil + elseif engopts == false then + -- engine is disabled + return nil + elseif engopts == true then + -- use default options + return engine + end + + local execpath = engopts.execpath or os.getenv(name:upper() .. '_BIN') + + local mime_type = format:best_mime_type( + engine.mime_types, + engopts['mime-type'] or engopts['mime-types'] + ) + if not mime_type then + warn(PANDOC_SCRIPT_FILE, ": Cannot use ", name, " with ", format.name) + return nil + end + + return { + execpath = execpath, + compile = engine.compile, + line_comment_start = engine.line_comment_start, + mime_type = mime_type, + opt = engopts or {}, + } +end + +--- Returns the diagram engine configs. +local function configure (meta, format_name) + local conf = meta.diagram or {} + local format = format_options(format_name) + meta.diagram = nil + + -- cache for image files + if conf.cache then + image_cache = conf['cache-dir'] + and stringify(conf['cache-dir']) + or cachedir() + pandoc.system.make_directory(image_cache, true) + end + + -- engine configs + local engine = {} + for name, engopts in pairs(conf.engine or default_engines) do + engine[name] = get_engine(name, engopts, format) + end + + return { + engine = engine, + format = format, + cache = image_cache and true, + image_cache = image_cache, + } +end + +-- +-- Format conversion +-- + +--- Converts a PDF to SVG. +local pdf2svg = function (imgdata) + -- Using `os.tmpname()` instead of a hash would be slightly cleaner, but the + -- function causes problems on Windows (and wasm). See, e.g., + -- https://github.com/pandoc-ext/diagram/issues/49 + local pdf_file = 'diagram-' .. pandoc.utils.sha1(imgdata) .. '.pdf' + write_file(pdf_file, imgdata) + local args = { + '--export-type=svg', + '--export-plain-svg', + '--export-filename=-', + pdf_file + } + return pandoc.pipe('inkscape', args, ''), os.remove(pdf_file) +end + +local function properties_from_code (code, comment_start) + local props = {} + local pattern = comment_start:gsub('%p', '%%%1') .. '| ' .. + '([-_%w]+): ([^\n]*)\n' + for key, value in code:gmatch(pattern) do + if key == 'fig-cap' then + props['caption'] = value + else + props[key] = value + end + end + return props +end + +local function diagram_options (cb, comment_start) + local attribs = comment_start + and properties_from_code(cb.text, comment_start) + or {} + for key, value in pairs(cb.attributes) do + attribs[key] = value + end + + local alt + local caption + local fig_attr = {id = cb.identifier} + local filename + local image_attr = {} + local user_opt = {} + + for attr_name, value in pairs(attribs) do + if attr_name == 'alt' then + alt = value + elseif attr_name == 'caption' then + -- Read caption attribute as Markdown + caption = attribs.caption + and pandoc.read(attribs.caption).blocks + or nil + elseif attr_name == 'filename' then + filename = value + elseif attr_name == 'label' then + fig_attr.id = value + elseif attr_name == 'name' then + fig_attr.name = value + else + -- Check for prefixed attributes + local prefix, key = attr_name:match '^(%a+)%-(%a[-%w]*)$' + if prefix == 'fig' then + fig_attr[key] = value + elseif prefix == 'image' or prefix == 'img' then + image_attr[key] = value + elseif prefix == 'opt' then + user_opt[key] = value + else + -- Use as image attribute + image_attr[attr_name] = value + end + end + end + + return { + ['alt'] = alt or + (caption and pandoc.utils.blocks_to_inlines(caption)) or + {}, + ['caption'] = caption, + ['fig-attr'] = fig_attr, + ['filename'] = filename, + ['image-attr'] = image_attr, + ['opt'] = user_opt, + } +end + +local function get_cached_image (hash, mime_type) + if not image_cache then + return nil + end + local filename = hash .. '.' .. extension_for_mimetype[mime_type] + local imgpath = pandoc.path.join{image_cache, filename} + local success, imgdata = pcall(read_file, imgpath) + if success then + return imgdata, mime_type + end + return nil +end + +local function cache_image (codeblock, imgdata, mimetype) + -- do nothing if caching is disabled or not possible. + if not image_cache then + return + end + local ext = extension_for_mimetype[mimetype] + local filename = pandoc.sha1(codeblock.text) .. '.' .. ext + local imgpath = pandoc.path.join{image_cache, filename} + write_file(imgpath, imgdata) +end + +-- Executes each document's code block to find matching code blocks: +local function code_to_figure (conf) + return function (block) + -- Check if a converter exists for this block. If not, return the block + -- unchanged. + local diagram_type = block.classes[1] + if not diagram_type then + return nil + end + + local engine = conf.engine[diagram_type] + if not engine then + return nil + end + + -- Unified properties. + local dgr_opt = diagram_options(block, engine.line_comment_start) + for optname, value in pairs(engine.opt or {}) do + dgr_opt.opt[optname] = dgr_opt.opt[optname] or value + end + + local run_pdf2svg = engine.mime_type == 'application/pdf' + and conf.format.pdf2svg + + -- Try to retrieve the image data from the cache. + local imgdata, imgtype + if conf.cache then + imgdata, imgtype = get_cached_image( + pandoc.sha1(block.text), + run_pdf2svg and 'image/svg+xml' or engine.mime_type + ) + end + + if not imgdata or not imgtype then + -- No cached image; call the converter + local success + success, imgdata, imgtype = + pcall(engine.compile, engine, block.text, dgr_opt.opt) + + -- Bail if an error occurred; imgdata contains the error message + -- when that happens. + if not success then + warn(PANDOC_SCRIPT_FILE, ': ', tostring(imgdata)) + return nil + elseif not imgdata then + warn(PANDOC_SCRIPT_FILE, ': Diagram engine returned no image data.') + return nil + elseif not imgtype then + warn(PANDOC_SCRIPT_FILE, ': Diagram engine did not return a MIME type.') + return nil + end + + -- Convert SVG if necessary. + if imgtype == 'application/pdf' and conf.format.pdf2svg then + imgdata, imgtype = pdf2svg(imgdata), 'image/svg+xml' + end + + -- If we got here, then the transformation went ok and `img` contains + -- the image data. + cache_image(block, imgdata, imgtype) + end + + -- Use the block's filename attribute or create a new name by hashing the + -- image content. + local basename, _extension = pandoc.path.split_extension( + dgr_opt.filename or pandoc.sha1(imgdata) + ) + local fname = basename .. '.' .. extension_for_mimetype[imgtype] + + -- Store the data in the media bag: + pandoc.mediabag.insert(fname, imgtype, imgdata) + + -- Create the image object. + local image = pandoc.Image(dgr_opt.alt, fname, "", dgr_opt['image-attr']) + + -- Create a figure if the diagram has a caption; otherwise return + -- just the image. + return dgr_opt.caption and + pandoc.Figure( + pandoc.Plain{image}, + dgr_opt.caption, + dgr_opt['fig-attr'] + ) or + pandoc.Plain{image} + end +end + +return setmetatable( + {{ + Pandoc = function (doc) + local conf = configure(doc.meta, FORMAT) + return doc:walk { + CodeBlock = code_to_figure(conf), + } + end + }}, + { + version = version, + } +) diff --git a/converters/include-files.lua b/converters/include-files.lua new file mode 100644 index 0000000..e2264d7 --- /dev/null +++ b/converters/include-files.lua @@ -0,0 +1,127 @@ +--- include-files.lua – filter to include Markdown files +--- +--- Copyright: © 2019–2021 Albert Krewinkel +--- License: MIT – see LICENSE file for details + +-- Module pandoc.path is required and was added in version 2.12 +PANDOC_VERSION:must_be_at_least '2.12' + +local List = require 'pandoc.List' +local path = require 'pandoc.path' +local system = require 'pandoc.system' + +--- Get include auto mode +local include_auto = false +function get_vars (meta) + if meta['include-auto'] then + include_auto = true + end +end + +--- Keep last heading level found +local last_heading_level = 0 +function update_last_level(header) + last_heading_level = header.level +end + +--- Update contents of included file +local function update_contents(blocks, shift_by, include_path) + local update_contents_filter = { + -- Shift headings in block list by given number + Header = function (header) + if shift_by then + header.level = header.level + shift_by + end + return header + end, + -- If link paths are relative then prepend include file path + Link = function (link) + if path.is_relative(link.target) and string.sub(path.filename(link.target), 1, 1) ~= '#' then + link.target = path.normalize(path.join({include_path, link.target})) + end + return link + end, + -- If image paths are relative then prepend include file path + Image = function (image) + if path.is_relative(image.src) then + image.src = path.normalize(path.join({include_path, image.src})) + end + return image + end, + -- Update path for include-code-files.lua filter style CodeBlocks + CodeBlock = function (cb) + if cb.attributes.include and path.is_relative(cb.attributes.include) then + cb.attributes.include = + path.normalize(path.join({include_path, cb.attributes.include})) + end + return cb + end + } + + return pandoc.walk_block(pandoc.Div(blocks), update_contents_filter).content +end + +--- Filter function for code blocks +local transclude +function transclude (cb) + -- ignore code blocks which are not of class "include". + if not cb.classes:includes 'include' then + return + end + + -- Markdown is used if this is nil. + local format = cb.attributes['format'] + + -- Attributes shift headings + local shift_heading_level_by = 0 + local shift_input = cb.attributes['shift-heading-level-by'] + if shift_input then + shift_heading_level_by = tonumber(shift_input) + else + if include_auto then + -- Auto shift headings + shift_heading_level_by = last_heading_level + end + end + + --- keep track of level before recusion + local buffer_last_heading_level = last_heading_level + + local blocks = List:new() + for line in cb.text:gmatch('[^\n]+') do + if line:sub(1,2) ~= '//' then + local fh = io.open(line) + if not fh then + io.stderr:write("Cannot open file " .. line .. " | Skipping includes\n") + else + -- read file as the given format with global reader options + local contents = pandoc.read( + fh:read '*a', + format, + PANDOC_READER_OPTIONS + ).blocks + last_heading_level = 0 + -- recursive transclusion + contents = system.with_working_directory( + path.directory(line), + function () + return pandoc.walk_block( + pandoc.Div(contents), + { Header = update_last_level, CodeBlock = transclude } + ) + end).content + --- reset to level before recursion + last_heading_level = buffer_last_heading_level + blocks:extend(update_contents(contents, shift_heading_level_by, + path.directory(line))) + fh:close() + end + end + end + return blocks +end + +return { + { Meta = get_vars }, + { Header = update_last_level, CodeBlock = transclude } +} diff --git a/converters/mdToLatex.sh b/converters/mdToLatex.sh new file mode 100644 index 0000000..60b3480 --- /dev/null +++ b/converters/mdToLatex.sh @@ -0,0 +1,85 @@ +MD_FILE="$1" + +BASE_DIR="$(pwd)" +TEX_FILE="${BASE_DIR}/latex/$(basename "$MD_FILE" | sed -e 's/\.md$/.latex/')" +BUILD_DIR="${BASE_DIR}/build/$(basename "$MD_FILE" | sed -e 's/\.md$//')" +TEMP_MD_FILE="$BUILD_DIR/$(basename "$MD_FILE")" +TEMP_TEX_FILE="$BUILD_DIR/$(basename "$MD_FILE" | sed -e 's|md$|latex|')" +mkdir -p "$(dirname "$TEMP_MD_FILE")" + +cp "$MD_FILE" "$TEMP_MD_FILE" + +function download_images() { + echo "download images for $1" + for line in $(grep '!\[.*\](https://.*\.png)' "$1" | sed -e 's/ /%20;/g') + do + src=$(echo "$line" | sed -e 's/^.*(//' -e 's/).*$//' -e 's/%20;/ /g') + echo "remote image found: $src" + + mkdir -p "${BASE_DIR}/latex/images" + name=$(echo "$src" | sed -e 's|^.*/\([^/]*\)$|\1|') + curl "$src" > "${BASE_DIR}/latex/images/$name" + done + echo "download done" +} + +for line in $(grep '^!\[.*\](.*\.md)$' "$TEMP_MD_FILE" | sed -e 's/ /%20;/g') +do + md_src=$(echo "$line" | sed -e 's/^.*(//' -e 's/).*$//' -e 's/%20;/ /g') + echo "include found: $md_src" + + download_images "$(pwd)$md_src" + + cp "$(pwd)$md_src" "$BUILD_DIR/$(basename "$md_src")" + sed -i "$BUILD_DIR/$(basename "$md_src")" \ + -e 's|\[toc\]||' \ + -e 's|^\[parent\].*$||' \ + -e 's|^# |\\newpage\n# |' \ + -e 's|^## |\\newpage\n## |' \ + -e 's|\[\([^]]*\)\](#\([^)]*\))|[\1](#\L\2)|' \ + -e "s|https://live.kladjes.nl/uploads|${BASE_DIR}/latex/images|" \ + -e "s|\`\`\`mermaid|\`\`\`{.mermaid loc=${BASE_DIR}/latex/images/$(basename "$md_src")}|" + + sed -i "$TEMP_MD_FILE" \ + -e "s|^\!\[.*\]($md_src)\$|\`\`\`\\{.include\\}\n$(basename "$md_src")\n\`\`\`|" + +done + +download_images "$TEMP_MD_FILE" + +title="$(grep '^# ' "$MD_FILE" | head -n 1 | sed 's|^# ||')" + +sed -i "$TEMP_MD_FILE" \ + -e 's|\[toc\]|\\tableofcontents|' \ + -e 's|^\[parent\].*$||' \ + -e 's|^# .*$||' \ + -e 's|^#||' \ + -e 's|^# |\\newpage\n# |' \ + -e 's|\[\([^]]*\)\](#\([^)]*\))|[\1](#\L\2)|' \ + -e "s|https://live.kladjes.nl/uploads|${BASE_DIR}/latex/images|" \ + -e "s|\`\`\`mermaid|\`\`\`{.mermaid loc=${BASE_DIR}/latex/images/$(basename "$MD_FILE")}|" + + # -e 's|```\([^ \t]\)=|```\1 {linenumbers}|' + +mkdir -p ${BASE_DIR}/latex/images/$(basename "$MD_FILE") +cd "$BUILD_DIR" +pandoc --lua-filter=../../converters/include-files.lua \ + --to=latex \ + --from=markdown+abbreviations \ + --template "${BASE_DIR}/converters/template.latex" \ + -o "$TEX_FILE" \ + --dpi 300 \ + "$(basename "$TEMP_MD_FILE")" +cd "$BASE_DIR" + +# for line in $(grep '^!\[.*\](.*\.md)$' "$TEMP_MD_FILE" | sed 's/ /%20;/g') +# do +# src=$(echo "$line" | sed -e 's/^.*(//' -e 's/).*$//' -e 's/%20;/ /g') + +# sed -i "$TEMP_MD_FILE" \ +# -e "s/^!\[.*\]($src)\$/\\include{$src}/" +# done + +sed --in-place \ + -e "s|?title?|$title|" \ + "$TEX_FILE" diff --git a/converters/template.latex b/converters/template.latex new file mode 100644 index 0000000..eb554bb --- /dev/null +++ b/converters/template.latex @@ -0,0 +1,142 @@ +\documentclass[11pt]{article} +\usepackage[a4paper, portrait, includehead, includefoot, margin=1.5cm]{geometry} + +\usepackage[english]{babel} + +\usepackage{pdfpages} + +\usepackage{xcolor} +\usepackage{makecell} +\usepackage{tabularx} +\usepackage{adjustbox} + +\usepackage{framed} + +\usepackage{longtable} +\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +\usepackage{booktabs} + +\usepackage{fontspec} +\usepackage{xunicode} +\usepackage{xltxtra} + +\newfontfamily\fontRoboto[]{Roboto} +\newfontfamily\fontUbuntu[]{Ubuntu} +\setmainfont{Roboto} + +% \usepackage[style=ieee]{biblatex} +% \usepackage{csquotes} +% \addbibresource{bibliography.bib} + +% header and footer +\usepackage{fancyhdr} +\renewcommand{\headrule}{} + +\usepackage[nodayofweek]{datetime} + +\definecolor{darkishyellow}{rgb}{177, 179, 173} + +% for images +\usepackage{graphbox} +\usepackage{sectsty} +\sectionfont{\clearpage} + +\setkeys{Gin}{width=.99\linewidth} + +% add bookmarks with \hypertarget +\usepackage{bookmark} +\usepackage{hyperref} + +% heading numberging +\setcounter{secnumdepth}{3} +\renewcommand\thesection{{\fontUbuntu\arabic{section}}} +\renewcommand\thesubsection{{\fontUbuntu\arabic{section}.\arabic{subsection}}} +\renewcommand\thesubsubsection{{\fontUbuntu\arabic{section}.\arabic{subsection}.\arabic{subsubsection}}} +\usepackage{sectsty} +\allsectionsfont{\fontUbuntu} +\setlength{\headheight}{14pt} + +% no indent at paragraphs +\usepackage{parskip} +\usepackage{setspace} +\setstretch{1.1} +\let\tmpitem\itemize +\let\tmpenditem\enditemize +\renewenvironment{itemize}{\tmpitem\setlength\itemsep{-.4em}}{\tmpenditem} + +$if(highlighting-macros)$ +$highlighting-macros$ +$endif$ + +\begin{document} +\raggedright +\pagecolor{darkishyellow} + +\begin{titlepage} + . + \vskip 10em + \begin{center} + {\Huge\fontUbuntu ?title? \par} + \vskip 3em + {\huge\fontUbuntu $sub_title$ \par} + \end{center} + \null\vfill + { + \large + \lineskip .75em + \begin{tabular}{r l} + Auther: $for(auther)$& $auther.name$ <$auther.email$> \\ + $endfor$\\ + Class code: & ELERTS10 \\\\ + Exported on: &\today + + \end{tabular} + } +\end{titlepage} + +\pagestyle{fancy} +\fancyhead{} % clear all header fields +\fancyhead[LO]{\color{gray}\fontUbuntu ?title?} +\fancyhead[RO]{\color{gray}\fontUbuntu $sub_title$} +\fancyfoot{} % clear all footer fields +\fancyfoot[LO]{\color{gray}\fontUbuntu $for(auther)$$auther.name_short$${sep}, $endfor$} +\fancyfoot[CO]{\color{gray}\fontUbuntu } +\fancyfoot[RO]{\color{gray}\fontUbuntu \thepage} + + +$if(lof)$ +\listoffigures +$endif$ +$if(lot)$ +\listoftables +$endif$ +$if(linestretch)$ +\setstretch{$linestretch$} +$endif$ + + +$body$ + + +$if(nocite-ids)$ +\nocite{$for(nocite-ids)$$it$$sep$, $endfor$} +$endif$ +$if(natbib)$ +$if(bibliography)$ +$if(biblio-title)$ +$if(has-chapters)$ +\renewcommand\bibname{$biblio-title$} +$else$ +\renewcommand\refname{$biblio-title$} +$endif$ +$endif$ +\bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$} + +$endif$ +$endif$ +$if(biblatex)$ +\printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$ + +$endif$ + +\end{document} diff --git a/makefile b/makefile new file mode 100644 index 0000000..ed5f732 --- /dev/null +++ b/makefile @@ -0,0 +1,99 @@ + +all: all_booklets all_docduments + +all_docduments: prepare pdf/assambly_report.pdf pdf/c_report.pdf +all_booklets: prepare pdf/assambly_report.booklet.pdf pdf/c_report.booklet.pdf +prepare: + mkdir -p latex pdf + +clean: + rm -r build latex + +clean_all: + rm -r build latex pdf + +install_arch: + mkdir -p build/install + pacman -Sy --noconfirm --needed curl unzip texlive-basic texlive-langeuropean pandoc + + test -e build/install/ubuntu.zip || curl https://assets.ubuntu.com/v1/0cef8205-ubuntu-font-family-0.83.zip -o build/install/ubuntu.zip + test -d build/install/ubuntu && rm -r build/install/ubuntu || echo + mkdir build/install/ubuntu + unzip build/install/ubuntu.zip -d build/install/ubuntu + mkdir -p /usr/share/fonts/ubuntu + cp build/install/ubuntu/ubuntu-font-family-0.83/*.ttf /usr/share/fonts/ubuntu/ + chmod 0775 /usr/share/fonts/ubuntu + chmod 0664 /usr/share/fonts/ubuntu/* + + test -e build/install/roboto.zip || curl https://dl.dafont.com/dl/?f=roboto -o build/install/roboto.zip + test -d build/install/roboto && rm -r build/install/roboto || echo + mkdir build/install/roboto + unzip build/install/roboto.zip -d build/install/roboto + mkdir -p /usr/share/fonts/roboto + cp build/install/roboto/*.ttf /usr/share/fonts/roboto/ + chmod 0775 /usr/share/fonts/roboto + chmod 0664 /usr/share/fonts/roboto/* + +install_ubuntu: + mkdir -p build/install + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + curl unzip texlive texlive-lang-european texlive-lang-greek texlive-xetex pandoc + + test -e build/install/ubuntu.zip || curl https://assets.ubuntu.com/v1/0cef8205-ubuntu-font-family-0.83.zip -o build/install/ubuntu.zip + test -d build/install/ubuntu && rm -r build/install/ubuntu || echo + mkdir build/install/ubuntu + unzip build/install/ubuntu.zip -d build/install/ubuntu + mkdir -p /usr/share/fonts/ubuntu + cp build/install/ubuntu/ubuntu-font-family-0.83/*.ttf /usr/share/fonts/ubuntu/ + chmod 0775 /usr/share/fonts/ubuntu + chmod 0664 /usr/share/fonts/ubuntu/* + + test -e build/install/roboto.zip || curl https://dl.dafont.com/dl/?f=roboto -o build/install/roboto.zip + test -d build/install/roboto && rm -r build/install/roboto || echo + mkdir build/install/roboto + unzip build/install/roboto.zip -d build/install/roboto + mkdir -p /usr/share/fonts/roboto + cp build/install/roboto/*.ttf /usr/share/fonts/roboto/ + chmod 0775 /usr/share/fonts/roboto + chmod 0664 /usr/share/fonts/roboto/* + +# ======================================= +# === latex generation ================== +# ======================================= + +latex/assambly_report.latex: converters/mdToLatex.sh converters/template.latex report-1/*.md + mkdir -p build/assambly_report + bash converters/mdToLatex.sh report-1/assambly_report.md latex/assambly_report.latex + +latex/c_report.latex: converters/mdToLatex.sh converters/template.latex report-1/*.md + mkdir -p build/c_report + bash converters/mdToLatex.sh report-1/c_report.md latex/c_report.latex + +# ======================================= +# === pdf generation ==================== +# ======================================= + +pdf/assambly_report.pdf: latex/assambly_report.latex + cd build/assambly_report && xelatex ../../latex/assambly_report.latex + cd build/assambly_report && xelatex ../../latex/assambly_report.latex + cd build/assambly_report && xelatex ../../latex/assambly_report.latex + mv build/assambly_report/assambly_report.pdf pdf/assambly_report.pdf + +pdf/assambly_report.booklet.pdf: converters/bookletify.latex pdf/assambly_report.pdf + mkdir -p build/assambly_report.booklet + sed -e 's|?pdf?|../../pdf/assambly_report.pdf|' converters/bookletify.latex >build/assambly_report.booklet/bookletify.latex + pdflatex -interaction=nonstopmode -output-directory="build/assambly_report.booklet" "build/assambly_report.booklet/bookletify.latex" + mv build/assambly_report.booklet/bookletify.pdf pdf/assambly_report.booklet.pdf + +pdf/c_report.pdf: latex/c_report.latex + cd build/c_report && xelatex ../../latex/c_report.latex + cd build/c_report && xelatex ../../latex/c_report.latex + cd build/c_report && xelatex ../../latex/c_report.latex + mv build/c_report/c_report.pdf pdf/c_report.pdf + +pdf/c_report.booklet.pdf: converters/bookletify.latex pdf/c_report.pdf + mkdir -p build/c_report.booklet + sed -e 's|?pdf?|../../pdf/c_report.pdf|' converters/bookletify.latex >build/c_report.booklet/bookletify.latex + pdflatex -interaction=nonstopmode -output-directory="build/c_report.booklet" "build/c_report.booklet/bookletify.latex" + mv build/c_report.booklet/bookletify.pdf pdf/c_report.booklet.pdf diff --git a/report-1/assambly_assignments.md b/report-1/assambly_assignments.md index 8467771..110fa97 100644 --- a/report-1/assambly_assignments.md +++ b/report-1/assambly_assignments.md @@ -1,12 +1,11 @@ --- path: /elektro/hr/rts10/ tags: kladjes, elektro, elektro/hr, elektro/hr/rts10 -auther: - - Finley van Reenen (0964590@hr.nl) -auther_short: "E.L.F. van Reenen" --- -# Assembly Assignments +[parent](/lLnsoW2yQlWpblaVBRFsYw) + +# Assembly Assignment This is the first report for _realtime systems 10_. I needed to do the following assignments: @@ -38,8 +37,8 @@ I applied the same fix as last year. In the debug config, replace the `\` with a The algorithm in question is given in C code. -```c-like= -unsigned int multiply(unsigned int a , unsigned int b) +```c {.numberLines} +unsigned int multiply(unsigned int a, unsigned int b) { unsigned int m = 0; for (unsigned int i = 0; i != a; i ++) @@ -52,8 +51,8 @@ unsigned int multiply(unsigned int a , unsigned int b) This code can be simplified without changing the algorithm by replacing the variable `i` with `b`. This will result in the following code. -```c-like= -unsigned int multiply ( unsigned int a , unsigned int b) +```c {.numberLines} +unsigned int multiply(unsigned int a, unsigned int b) { unsigned int m = 0; for (; b > 0; b --) @@ -66,7 +65,7 @@ unsigned int multiply ( unsigned int a , unsigned int b) This is also the code I have implemented. The compiler puts _a_ in `R0` and _b_ in `R1`, I accepted them in place and used `R2` for _m_. At the end _m_ (`R2`) is moved to `R0` to set it as the return value. -```ass= +```asm {.numberLines} .cpu cortex-m4 .thumb .syntax unified @@ -88,8 +87,8 @@ mul_exit: a better representation of the essembly code in C would be: -```c-like= -unsigned int multiply ( unsigned int a , unsigned int b) +```c {.numberLines} +unsigned int multiply(unsigned int a, unsigned int b) { unsigned int m = 0; if (b == 0) { @@ -98,7 +97,7 @@ unsigned int multiply ( unsigned int a , unsigned int b) do { - m = m + b; + m = m + a; b --; } while (b > 0) @@ -117,7 +116,7 @@ It does not take the branch at line 10 and at line 14 it takes the branch 65535 Now a smarter version. -```c-like= +```c {.numberLines} unsigned int multiply(unsigned int a , unsigned int b) { unsigned int m = 0; @@ -134,7 +133,7 @@ unsigned int multiply(unsigned int a , unsigned int b) } ``` -```asm= +```asm {.numberLines} .cpu cortex-m4 .thumb .syntax unified @@ -162,8 +161,8 @@ bmul_exit: a better representation of the essembly code in C would be: -```c-like= -unsigned int multiply ( unsigned int a , unsigned int b) +```c {.numberLines} +unsigned int multiply(unsigned int a, unsigned int b) { unsigned int m = 0; if (b == 0) { @@ -192,7 +191,7 @@ There are 7 instructions in the loop where one only runs every `1` bit in `b`. T ## Assignment 10: Using your multiply function to calculate the dot product of two vectors -```c-like= +```c {.numberLines} unsigned int dotProduct(unsigned int a[], unsigned int b[], size_t n) { unsigned int p = 0; @@ -206,7 +205,7 @@ unsigned int dotProduct(unsigned int a[], unsigned int b[], size_t n) This function needs to call another and remember 5 variables (`*a`, `*b`, `n`, `p` and `i`). `R0` through⁣ `R3` are expected to change with a function call. There are only `R4` through `R7` left, only four spots. To solve this problem, I reversed the for loop by using n and decrementing it. This will not change the result since addition is not order sensitive. The code I implemented is the following. -```c-like= +```c {.numberLines} unsigned int dotProduct(unsigned int a[] , unsigned int b[], size_t n) { unsigned int p = 0; @@ -225,7 +224,7 @@ unsigned int dotProduct(unsigned int a[] , unsigned int b[], size_t n) My assembly code: -```asm= +```asm {.numberLines} .cpu cortex-m4 .thumb .syntax unified @@ -252,5 +251,4 @@ dotp_loop: dotp_exit: MOVS.N R0, R7 // move m to R0 POP.N {R4, R5, R6, R7, PC} - BX.N LR ``` diff --git a/report-1/assambly_report.md b/report-1/assambly_report.md new file mode 100644 index 0000000..38e7f77 --- /dev/null +++ b/report-1/assambly_report.md @@ -0,0 +1,16 @@ +--- +sub_title: "Real Time Systems 10" +auther: + - name: "Finley van Reenen (0964590)" + email: "mail@lailatheelf.nl" + name_short: "E.L.F. van Reenen" +--- + +# Assambly Report + +[toc] + +![](/report-1/assambly_assignments.md) + +![](/report-1/week_1.2.md) + diff --git a/report-1/week_1.2.md b/report-1/week_1.2.md index 7ca7084..d4a60d6 100644 --- a/report-1/week_1.2.md +++ b/report-1/week_1.2.md @@ -146,7 +146,9 @@ while ((FLASH->ACR & /* ... */ ) == 0); Finaly we configur the PLL itself. $$ -f_{(VCO\ clock)} = f_{(PLL\ clock\ input)} \cdot (\frac{PLLN}{PLLM}) \\ +f_{(VCO\ clock)} = f_{(PLL\ clock\ input)} \cdot (\frac{PLLN}{PLLM}) +$$ +$$ f_{(PLL\ general\ clock\ output)} = \frac{f_{(VCO\ clock)}}{PLLP} $$ diff --git a/report-2/c_report.md b/report-2/c_report.md new file mode 100644 index 0000000..5ecf0af --- /dev/null +++ b/report-2/c_report.md @@ -0,0 +1,13 @@ +--- +sub_title: "Real Time Systems 10" +auther: + - name: "Finley van Reenen (0964590)" + email: "mail@lailatheelf.nl" + name_short: "E.L.F. van Reenen" +--- + +# C Report + +[toc] + +![](/report-2/week_1.3.md)