summaryrefslogtreecommitdiff
path: root/Build/source
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2023-11-24 21:16:55 +0000
committerKarl Berry <karl@freefriends.org>2023-11-24 21:16:55 +0000
commit64fba4d8d03a09b824a446b3dd7ae978ab0c42e3 (patch)
tree475309783b89c2f78aaf9627e737e18e9c94110b /Build/source
parent49b3498f25b7813dde26d170282a5b34d5f49ae3 (diff)
texblend (24nov23)
git-svn-id: svn://tug.org/texlive/trunk@68961 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source')
-rw-r--r--Build/source/texk/texlive/linked_scripts/Makefile.am1
-rw-r--r--Build/source/texk/texlive/linked_scripts/Makefile.in1
-rw-r--r--Build/source/texk/texlive/linked_scripts/scripts.lst1
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/texblend/texblend280
4 files changed, 283 insertions, 0 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.am b/Build/source/texk/texlive/linked_scripts/Makefile.am
index 9102aee218b..acc1382f6dd 100644
--- a/Build/source/texk/texlive/linked_scripts/Makefile.am
+++ b/Build/source/texk/texlive/linked_scripts/Makefile.am
@@ -225,6 +225,7 @@ texmf_other_scripts = \
sty2dtx/sty2dtx.pl \
svn-multi/svn-multi.pl \
tex4ebook/tex4ebook \
+ texblend/texblend \
texcount/texcount.pl \
texdef/texdef.pl \
texdiff/texdiff \
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.in b/Build/source/texk/texlive/linked_scripts/Makefile.in
index 3e350511bc0..1e3b225408d 100644
--- a/Build/source/texk/texlive/linked_scripts/Makefile.in
+++ b/Build/source/texk/texlive/linked_scripts/Makefile.in
@@ -442,6 +442,7 @@ texmf_other_scripts = \
sty2dtx/sty2dtx.pl \
svn-multi/svn-multi.pl \
tex4ebook/tex4ebook \
+ texblend/texblend \
texcount/texcount.pl \
texdef/texdef.pl \
texdiff/texdiff \
diff --git a/Build/source/texk/texlive/linked_scripts/scripts.lst b/Build/source/texk/texlive/linked_scripts/scripts.lst
index 3342efc3356..ee789b44617 100644
--- a/Build/source/texk/texlive/linked_scripts/scripts.lst
+++ b/Build/source/texk/texlive/linked_scripts/scripts.lst
@@ -166,6 +166,7 @@ srcredact/srcredact.pl
sty2dtx/sty2dtx.pl
svn-multi/svn-multi.pl
tex4ebook/tex4ebook
+texblend/texblend
texcount/texcount.pl
texdef/texdef.pl
texdiff/texdiff
diff --git a/Build/source/texk/texlive/linked_scripts/texblend/texblend b/Build/source/texk/texlive/linked_scripts/texblend/texblend
new file mode 100755
index 00000000000..bb5c9d06610
--- /dev/null
+++ b/Build/source/texk/texlive/linked_scripts/texblend/texblend
@@ -0,0 +1,280 @@
+#!/usr/bin/env texlua
+kpse.set_program_name "luatex"
+
+-- TeXBlend -- Compile Segments of LaTeX Documents
+-- Copyright: Michal Hoftich <michal.h21@gmail.com> (2023)
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+-- http://www.latex-project.org/lppl.txt
+-- and version 1.3 or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status `maintained'.
+--
+-- The Current Maintainer of this work is Michal Hoftich
+
+local lapp = require "lapp-mk4"
+-- mkutils needs the logging library
+logging = require "make4ht-logging"
+local mkutils = require "mkutils"
+local log = logging.new("TeXBlend")
+
+local version_info = "TeXBlend version v0.1"
+local cmd_options = [[TeXBlend: Compile Segments of LaTeX Documents
+
+This tool compiles individual files that are included as parts of larger documents.
+It utilizes the preamble of the main document but disregards all other included files.
+
+Usage:
+
+$ texblend [options] filename
+
+Available options:
+-e,--expand Expand variables in the template
+-h,--help Print the help message
+-H,--HTML Compile to HTML using TeX4ht
+-l,--lualatex Select lualatex as compiler explicitly
+-o,--output (default "") Set the output file name
+-O,--texoptions (default "") Extra options that should be passed to the LaTeX compiler
+-p,--pdflatex Select pdflatex as compiler explicitly
+-P,--print Don't compile the document, print the expanded template instead
+-s,--shell-escape Enable execution of external commands in LaTeX
+-t,--template (default "") Use explicit template instead of the one provided in the input file
+-v,--version Print version info
+-x,--xelatex Select xelatex as compiler explicitly
+<filename> (string) Use "-" if you want to pass document from the standard input
+
+Documentation: https://www.kodymirus.cz/texblend/
+Bug reports and development: https://github.com/michal-h21/texblend
+]]
+
+
+local default_template = [[\documentclass{article}
+\begin{document}
+\end{document}
+]]
+
+
+
+local function exit_on_error(status, msg)
+ -- function that tests function results
+ -- it prints error messages and exits if there was an error
+ if not status then
+ log:error( msg)
+ os.exit(1)
+ end
+end
+
+local function get_preamble(text)
+ -- try to get the document preamble
+ local preamble = {}
+ local found_begin = false
+ for line in text:gmatch("([^\n]*)") do
+ -- \begin{document} must be on a separate line, preceded only by whitespace
+ if line:match("^%s*\\begin{document}") then
+ found_begin = true
+ break
+ end
+ preamble[#preamble + 1] = line
+ end
+ if found_begin then
+ return table.concat(preamble, "\n")
+ end
+ return nil, "Cannot find \\begin{document} in the template. It must be placed on a separate line."
+end
+
+--
+local function prepare_template(document, template)
+ -- try to read document preamble from the template
+ local preamble, msg = get_preamble(template)
+ if not preamble then return nil, msg end
+ -- fill template with the document
+ return string.format("%s\n\\begin{document}\n%s\n\\end{document}\n", preamble, document)
+end
+
+local function get_metadata(content)
+ -- for now, we support only TeXShop's style of metadata:
+ -- % !TeX program = program
+ -- % !TeX root = root
+ -- also variant with % !TEX TS-var is supported
+ -- program is used for the compilation and root is the template document
+ local metadata = {}
+ for key, value in content:gmatch("%%%s*![Tt][Ee][Xx] T?S?%-?(.-)%s*=%s*([^\n]+)") do
+ metadata[key] = value
+ end
+ metadata.program = metadata.program or "pdflatex"
+ -- remove possible white space at the end of root filename
+ if metadata.root then metadata.root = metadata.root:gsub("%s*$", "") end
+ return metadata
+end
+
+local function use_arguments(metadata, arguments)
+ -- this function modifies metadata using command line arguments
+ -- remove empty strings from arguments
+ for k, v in pairs(arguments) do
+ if v == '""' then arguments[k] = nil end
+ end
+ -- handle output file name
+ if arguments.output then
+ metadata.output = mkutils.remove_extension(arguments.output)
+ else
+ -- base output filename on the input filename if we don't get explicit filename
+ metadata.output = mkutils.remove_extension(arguments.filename)
+ end
+ -- handle program
+ metadata.program = arguments.pdflatex and "pdflatex" or metadata.program
+ metadata.program = arguments.lualatex and "lualatex" or metadata.program
+ metadata.program = arguments.xelatex and "xelatex" or metadata.program
+ -- handle the template
+ metadata.root = arguments.template or metadata.root
+ -- pass other useful flags
+ metadata.shell_escape = arguments["shell-escape"]
+ metadata.html = arguments.HTML
+ metadata.options = arguments.texoptions
+ return metadata
+end
+
+local function expand(metadata, template)
+ -- expand {{variablename}} tags in the template. The variablename key must exitst
+ -- in the metadata table. If the variable doesn't exist, nothing is rendered
+ local expanded = template:gsub("{{([%a%d}]-)}}", function(key)
+ return metadata[key] or ""
+ end)
+ return expanded
+end
+
+local function load_file(filename)
+ -- universal function to read files
+ if not filename then return nil, "No filename passed" end
+ local f = io.open(filename, "r")
+ if not f then return nil, string.format("Cannot open file: %s", filename) end
+ local content = f:read("*all")
+ f:close()
+ return content
+end
+
+local function load_template(filename)
+ -- with kpse, we can use root files in the local TEXMF tree, like ~/tex/latex/tpl/template.tex
+ local filename = kpse.find_file(filename, "tex")
+ return load_file(filename)
+end
+
+local function load_document(filename)
+ -- load the input file and its metadata
+ local content, msg = load_file(filename)
+ if not content then return content, msg end
+ return get_metadata(content), content
+end
+
+
+local function prepare_command(metadata,arguments)
+ -- prepare CLI program to be executed
+ local options = {}
+ local command
+ -- we support HTML creation thanks to make4ht
+ if metadata.html then
+ command = "make4ht"
+ options[#options + 1] = "-j " .. metadata.output
+ options[#options + 1] = metadata.shell_escape and "-s" or nil
+ if metadata.program == "lualatex" then
+ options[#options + 1] = "-l"
+ elseif metadata.program == "xelatex" then
+ options[#options + 1] = "-x"
+ end
+ local texoptions = metadata.options
+ options[#options+1] = texoptions
+ texoptions = texoptions or ""
+ -- if texoptions already contain the - character, don't add it
+ if not texoptions:match("%-%s") and not texoptions:match("%-$") then
+ options[#options+1] = "-"
+ end
+ else
+ command = metadata.program
+ options[#options + 1] = "-jobname=" .. metadata.output
+ options[#options + 1] = metadata.shell_escape and "-shell-escape" or nil
+ options[#options+1] = metadata.options
+ end
+ return string.format("%s %s", command, table.concat(options, " "))
+end
+
+local function compile(command, document)
+ -- execute the command
+ local cmd = io.popen(command, "w")
+ if not cmd then
+ exit_on_error(nil, "Cannot run command")
+ end
+ cmd:write(document)
+ cmd:close()
+end
+
+local arguments
+-- you can set the _G.test=true if you want to load texblend as a library for testing
+if not __test__ then
+ -- process command line options
+ arguments = lapp(cmd_options)
+
+ -- handle non file actions
+ -- arguments.help is handled automatically by lapp
+ if arguments.version then
+ print(version_info)
+ os.exit()
+ end
+
+
+ local metadata, document
+ local filename = arguments.filename
+ if filename == "-" then
+ -- read from the standard input if the filename is -
+ document = io.read("*all")
+ metadata = get_metadata(document)
+ else
+ -- otherwise, load the input file and get metadata
+ metadata, document = load_document(filename)
+ end
+
+ exit_on_error(metadata, document)
+
+ -- enrich metadata with command line arguments
+ metadata = use_arguments(metadata, arguments)
+
+ -- user needs to add explicit output filename when dealing with the standard input
+ if metadata.output == "-" then
+ exit_on_error(nil, "Empty output filename. Use the -o option")
+ end
+
+ metadata.template, msg = metadata.root and load_template(metadata.root) or default_template
+
+ exit_on_error(metadata.template, msg)
+
+ if arguments.expand then
+ metadata.template = expand(metadata,metadata.template)
+ end
+
+ local full_document, msg = prepare_template(document, metadata.template)
+
+ exit_on_error(full_document, msg)
+
+ if arguments.print then
+ print(full_document)
+ os.exit()
+ end
+
+ local command = prepare_command(metadata, arguments)
+
+ log:info(command)
+ compile(command, full_document)
+else
+ return {
+ prepare_command = prepare_command
+ ,prepare_template = prepare_template
+ ,get_metadata = get_metadata
+ ,use_arguments = use_arguments
+ ,get_preamble = get_preamble
+ ,expand = expand
+ }
+end
+
+