diff --git a/crates/renderflow-core/src/strategies/pdf.rs b/crates/renderflow-core/src/strategies/pdf.rs index 394c330..61f11fd 100644 --- a/crates/renderflow-core/src/strategies/pdf.rs +++ b/crates/renderflow-core/src/strategies/pdf.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use std::collections::HashMap; use std::path::Path; use tracing::info; @@ -38,6 +39,27 @@ impl PdfStrategy { } Ok(()) } + + /// Add paths owned by Renderflow's template bundle without overriding + /// explicit profile variables. This keeps shared LaTeX components and + /// local font assets discoverable when pandoc stages its intermediate + /// document in a temporary directory. + fn template_variables(&self, variables: &HashMap) -> HashMap { + let mut resolved = variables.clone(); + let template_root = Path::new(&self.template_dir); + + for (key, directory) in [ + ("renderflow-style-root", template_root.join("latex")), + ("renderflow-font-root", template_root.join("fonts")), + ] { + if !resolved.contains_key(key) && directory.is_dir() { + let path = directory.canonicalize().unwrap_or(directory); + resolved.insert(key.to_string(), path.to_string_lossy().into_owned()); + } + } + + resolved + } } impl OutputStrategy for PdfStrategy { @@ -70,6 +92,7 @@ impl OutputStrategy for PdfStrategy { None }; + let variables = self.template_variables(ctx.variables); let builder = PandocArgs::new( ctx.input_format.as_pandoc_format(), ctx.input_path, @@ -80,7 +103,7 @@ impl OutputStrategy for PdfStrategy { Some(ref path) => builder.with_template(path.as_str()), None => builder, } - .with_variables(ctx.variables) + .with_variables(&variables) .build(); let args_refs: Vec<&str> = args.iter().map(String::as_str).collect(); diff --git a/docs/user-guide/latex-components.md b/docs/user-guide/latex-components.md new file mode 100644 index 0000000..438ef7f --- /dev/null +++ b/docs/user-guide/latex-components.md @@ -0,0 +1,123 @@ +# LaTeX component library + +Renderflow's PDF path keeps document meaning in publication profiles and keeps +reusable presentation in a shared LaTeX component library. The current flow is: + +```text +publication profile -> Pandoc template -> Renderflow styles -> Tectonic +``` + +The built-in research template at `templates/research/research.tex` is the +reference composition. Selecting it through the normal PDF target automatically +injects the absolute `renderflow-style-root` for Pandoc. There is no separate +LaTeX executor or manual package installation step. + +## Component boundaries + +The aggregate package is `templates/latex/renderflow-core.sty`. It centralizes +the design tokens and loads these cohesive components: + +| Component | Shared responsibility | +|---|---| +| `renderflow-color.sty` | semantic colors, links, print-safe link behavior | +| `renderflow-typography.sty` | font roles, heading rhythm, quotations | +| `renderflow-layout.sty` | geometry, paragraph rhythm, lists, running furniture | +| `renderflow-figures.sty` | image bounds, placement, captions | +| `renderflow-tables.sty` | booktabs/longtable support and table rhythm | +| `renderflow-code.sty` | Pandoc highlighting containers and listings defaults | +| `renderflow-callouts.sty` | callout and aside presentation primitives | +| `renderflow-metadata.sty` | title/author/date roles and citation-list primitives | + +Shared packages own visual treatment. A publication profile or its Pandoc +template still owns article order, front matter, title placement, column model, +editorial section meaning, and which components appear. Do not add research, +magazine, or article semantics to a `.sty` file. + +## Compose a profile + +A target selects the profile template through the existing artifact graph: + +```yaml +targets: + exact: + - id: research-pdf + role: publication/research + format: pdf + template: research/research.tex +variables: + renderflow-color-accent: "4058A6" + renderflow-margin-inner: "28mm" +``` + +The template declares any overrides before loading the aggregate package: + +```tex +\def\RenderflowStyleRoot{templates/latex} +\def\RenderflowColorAccent{4058A6} +\input{\RenderflowStyleRoot/renderflow-core.sty} +``` + +Profiles can override the supported Pandoc variables without forking the style +library: + +- `renderflow-color-accent`, `renderflow-color-ink`, + `renderflow-color-muted`, `renderflow-color-rule`, and + `renderflow-color-surface` accept six-digit hexadecimal colors. +- `renderflow-margin-top`, `renderflow-margin-bottom`, + `renderflow-margin-inner`, `renderflow-margin-outer`, and + `renderflow-paragraph-skip` accept LaTeX dimensions. +- `mainfont`, `sansfont`, and `monofont` select semantic font-family roles. +- `renderflow-main-font-file`, `renderflow-sans-font-file`, and + `renderflow-mono-font-file` select local files under `renderflow-font-root`. + +Profiles that need deeper changes may redefine a documented `Renderflow...` +token before loading `renderflow-core.sty`. A change that benefits multiple +profiles belongs in the smallest existing component that owns the concern. Add +a new package only when the concern is cohesive and independently reusable, +then load it from `renderflow-core.sty`. + +## Fonts and deterministic fallback + +The three font roles default to Latin Modern. Tectonic carries the TeX support +bundle needed for that deterministic fallback, so Renderflow does not assume a +developer workstation font. When `templates/fonts/` exists, the PDF strategy +also injects it as `renderflow-font-root`. A user-provided root remains +authoritative. + +Local font variables name files rather than host-installed families. If a +declared file cannot be found, the style layer emits a package warning and uses +the corresponding Latin Modern role. Renderflow does not ship third-party font +binaries; publication owners remain responsible for font licenses and for PDF +font-embedding validation required by their publication contract. + +## Pandoc and Tectonic compatibility + +The library uses ordinary LaTeX2e packages available to the current Tectonic +toolchain. The research template exposes Pandoc's highlighting macros, table and +figure output, header includes, table of contents, metadata, and supported +Natbib/BibLaTeX hooks. `renderflow-style-root` is an implementation variable; +profile authors normally do not set it. + +The synthetic fixture exercises title metadata, headings, prose, a quotation, +callout, table, figure/caption, highlighted code, footnote, and link: + +```bash +renderflow build \ + --config "tests/fixtures/latex-components/renderflow.yaml" +``` + +When Tectonic is unavailable, the Pandoc composition can still be inspected +without producing a PDF: + +```bash +pandoc "tests/fixtures/latex-components/showcase.md" \ + --from "markdown" \ + --to "latex" \ + --template "templates/research/research.tex" \ + --variable "renderflow-style-root=$PWD/templates/latex" \ + --output "/tmp/renderflow-component-showcase.tex" +``` + +Keep custom templates close to Pandoc's current template contract. Pandoc may +add generated commands over time, so profile templates should be checked when +the supported Pandoc tool version changes. diff --git a/mkdocs.yml b/mkdocs.yml index fdf6a68..f5efb22 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,7 @@ nav: - Adapter Ecosystem: user-guide/adapter-ecosystem.md - EPUB and KEPUB Derivatives: user-guide/ebook-derivatives.md - Magazine Publications: user-guide/magazine-publications.md + - LaTeX Components: user-guide/latex-components.md - Lulu Publication Pack: user-guide/lulu-publication-pack.md - Whole-file Video: handbrake-adapter.md - Super Resolution: user-guide/super-resolution.md diff --git a/templates/latex/renderflow-callouts.sty b/templates/latex/renderflow-callouts.sty new file mode 100644 index 0000000..e8afc08 --- /dev/null +++ b/templates/latex/renderflow-callouts.sty @@ -0,0 +1,21 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-callouts.sty}[2026/09/11 Renderflow callouts] + +\RequirePackage{framed} +\newenvironment{RenderflowCallout}[1][Note]{% + \def\FrameCommand{% + {\color{RenderflowAccent}\vrule width \RenderflowCalloutRuleWeight}% + \hspace{0.8em}% + }% + \MakeFramed{\advance\hsize-\width\FrameRestore}% + \noindent\textbf{\color{RenderflowAccent}#1}\par\smallskip +}{% + \endMakeFramed +} +\newenvironment{RenderflowAside}{% + \begin{RenderflowCallout}[Aside]\small\color{RenderflowMuted} +}{% + \end{RenderflowCallout} +} + +\endinput diff --git a/templates/latex/renderflow-code.sty b/templates/latex/renderflow-code.sty new file mode 100644 index 0000000..70cfeaf --- /dev/null +++ b/templates/latex/renderflow-code.sty @@ -0,0 +1,22 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-code.sty}[2026/09/11 Renderflow code presentation] + +\RequirePackage{fancyvrb} +\RequirePackage{listings} +\RequirePackage{framed} +\definecolor{shadecolor}{HTML}{\RenderflowColorSurface} +\providecommand{\passthrough}[1]{#1} +\newenvironment{RenderflowShaded}{\begin{snugshade}}{\end{snugshade}} +\lstset{ + basicstyle=\small\ttfamily, + backgroundcolor=\color{RenderflowSurface}, + rulecolor=\color{RenderflowRule}, + breaklines=true, + frame=single, + framerule=\RenderflowRuleWeight, + xleftmargin=0.5em, + xrightmargin=0.5em, + showstringspaces=false +} + +\endinput diff --git a/templates/latex/renderflow-color.sty b/templates/latex/renderflow-color.sty new file mode 100644 index 0000000..fad6afe --- /dev/null +++ b/templates/latex/renderflow-color.sty @@ -0,0 +1,24 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-color.sty}[2026/09/11 Renderflow color roles] + +\RequirePackage{xcolor} +\definecolor{RenderflowInk}{HTML}{\RenderflowColorInk} +\definecolor{RenderflowMuted}{HTML}{\RenderflowColorMuted} +\definecolor{RenderflowAccent}{HTML}{\RenderflowColorAccent} +\definecolor{RenderflowRule}{HTML}{\RenderflowColorRule} +\definecolor{RenderflowSurface}{HTML}{\RenderflowColorSurface} +\color{RenderflowInk} + +\PassOptionsToPackage{hyphens}{url} +\RequirePackage{hyperref} +\IfFileExists{xurl.sty}{\RequirePackage{xurl}}{} +\hypersetup{ + colorlinks=true, + linkcolor=RenderflowAccent, + citecolor=RenderflowAccent, + urlcolor=RenderflowAccent, + pdfcreator={Renderflow via Pandoc and LaTeX} +} +\urlstyle{same} + +\endinput diff --git a/templates/latex/renderflow-core.sty b/templates/latex/renderflow-core.sty new file mode 100644 index 0000000..bb61189 --- /dev/null +++ b/templates/latex/renderflow-core.sty @@ -0,0 +1,32 @@ +%% Renderflow shared LaTeX presentation layer. +%% Profiles set semantic token commands before loading this file. +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-core.sty}[2026/09/11 v1.0 Renderflow components] + +%% Central design tokens. Profiles may define any token before this package is +%% loaded, or use \renewcommand after loading where LaTeX permits it. +\providecommand{\RenderflowColorInk}{1F2937} +\providecommand{\RenderflowColorMuted}{5F6B7A} +\providecommand{\RenderflowColorAccent}{334E9E} +\providecommand{\RenderflowColorRule}{CBD5E1} +\providecommand{\RenderflowColorSurface}{F3F6FA} +\providecommand{\RenderflowMarginTop}{25mm} +\providecommand{\RenderflowMarginBottom}{25mm} +\providecommand{\RenderflowMarginInner}{30mm} +\providecommand{\RenderflowMarginOuter}{30mm} +\providecommand{\RenderflowParagraphSkip}{0.65em} +\providecommand{\RenderflowHeadingBefore}{1.5ex} +\providecommand{\RenderflowHeadingAfter}{0.8ex} +\providecommand{\RenderflowRuleWeight}{0.4pt} +\providecommand{\RenderflowCalloutRuleWeight}{2pt} + +\input{\RenderflowStyleRoot/renderflow-color.sty} +\input{\RenderflowStyleRoot/renderflow-typography.sty} +\input{\RenderflowStyleRoot/renderflow-layout.sty} +\input{\RenderflowStyleRoot/renderflow-figures.sty} +\input{\RenderflowStyleRoot/renderflow-tables.sty} +\input{\RenderflowStyleRoot/renderflow-code.sty} +\input{\RenderflowStyleRoot/renderflow-callouts.sty} +\input{\RenderflowStyleRoot/renderflow-metadata.sty} + +\endinput diff --git a/templates/latex/renderflow-figures.sty b/templates/latex/renderflow-figures.sty new file mode 100644 index 0000000..1acf6a4 --- /dev/null +++ b/templates/latex/renderflow-figures.sty @@ -0,0 +1,20 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-figures.sty}[2026/09/11 Renderflow figures] + +\RequirePackage{graphicx} +\RequirePackage{caption} +\makeatletter +\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi} +\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi} +\def\fps@figure{htbp} +\makeatother +\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio} +\captionsetup{ + font=small, + labelfont=bf, + textfont={color=RenderflowMuted}, + labelsep=period, + skip=0.5em +} + +\endinput diff --git a/templates/latex/renderflow-layout.sty b/templates/latex/renderflow-layout.sty new file mode 100644 index 0000000..6302ad5 --- /dev/null +++ b/templates/latex/renderflow-layout.sty @@ -0,0 +1,30 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-layout.sty}[2026/09/11 Renderflow page layout] + +\RequirePackage[ + top=\RenderflowMarginTop, + bottom=\RenderflowMarginBottom, + inner=\RenderflowMarginInner, + outer=\RenderflowMarginOuter +]{geometry} +\RequirePackage{parskip} +\RequirePackage{enumitem} +\RequirePackage{fancyhdr} + +\setlength{\parskip}{\RenderflowParagraphSkip} +\setlength{\parindent}{0pt} +\setlength{\emergencystretch}{3em} +\setlist{noitemsep,topsep=0.5em} +\setlength{\headheight}{14pt} + +\providecommand{\RenderflowRunningTitle}{} +\providecommand{\RenderflowRunningAuthor}{} +\fancyhf{} +\fancyhead[L]{\small\itshape\color{RenderflowMuted}\RenderflowRunningTitle} +\fancyhead[R]{\small\itshape\color{RenderflowMuted}\RenderflowRunningAuthor} +\fancyfoot[C]{\small\color{RenderflowMuted}\thepage} +\renewcommand{\headrulewidth}{\RenderflowRuleWeight} +\renewcommand{\footrulewidth}{0pt} +\pagestyle{fancy} + +\endinput diff --git a/templates/latex/renderflow-metadata.sty b/templates/latex/renderflow-metadata.sty new file mode 100644 index 0000000..3547226 --- /dev/null +++ b/templates/latex/renderflow-metadata.sty @@ -0,0 +1,43 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-metadata.sty}[2026/09/11 Renderflow metadata primitives] + +\providecommand{\RenderflowDocumentTitle}{} +\providecommand{\RenderflowDocumentSubtitle}{} +\providecommand{\RenderflowDocumentAuthors}{} +\providecommand{\RenderflowDocumentDate}{} +\newcommand{\RenderflowMakeTitle}{% + \begin{center} + {\LARGE\bfseries\color{RenderflowInk}\RenderflowDocumentTitle\par} + \ifx\RenderflowDocumentSubtitle\empty\else + \vspace{0.45em}{\large\color{RenderflowMuted}\RenderflowDocumentSubtitle\par} + \fi + \ifx\RenderflowDocumentAuthors\empty\else + \vspace{0.8em}{\large\RenderflowDocumentAuthors\par} + \fi + \ifx\RenderflowDocumentDate\empty\else + \vspace{0.35em}{\normalsize\color{RenderflowMuted}\RenderflowDocumentDate\par} + \fi + \end{center} + \vspace{1em} +} + +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} + +\newlength{\cslhangindent} +\setlength{\cslhangindent}{1.5em} +\newlength{\csllabelwidth} +\setlength{\csllabelwidth}{3em} +\newlength{\cslentryspacingunit} +\setlength{\cslentryspacingunit}{\parskip} +\newenvironment{CSLReferences}[2]{% + \setlength{\parindent}{0pt}% + \setlength{\parskip}{#2\cslentryspacingunit}% + \ifodd #1\let\oldpar\par\def\par{\hangindent=\cslhangindent\oldpar}\fi +}{} +\newcommand{\CSLBlock}[1]{#1\hfill\break} +\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}} +\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth-\csllabelwidth}{#1}\break} +\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1} + +\endinput diff --git a/templates/latex/renderflow-tables.sty b/templates/latex/renderflow-tables.sty new file mode 100644 index 0000000..13149ab --- /dev/null +++ b/templates/latex/renderflow-tables.sty @@ -0,0 +1,21 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-tables.sty}[2026/09/11 Renderflow tables] + +\RequirePackage{booktabs} +\RequirePackage{longtable} +\RequirePackage{array} +\RequirePackage{calc} +\RequirePackage{etoolbox} +\renewcommand{\arraystretch}{1.15} +\setlength{\tabcolsep}{0.6em} +\makeatletter +\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} +\makeatother +\IfFileExists{footnotehyper.sty}{ + \RequirePackage{footnotehyper} +}{ + \RequirePackage{footnote} +} +\makesavenoteenv{longtable} + +\endinput diff --git a/templates/latex/renderflow-typography.sty b/templates/latex/renderflow-typography.sty new file mode 100644 index 0000000..4c15029 --- /dev/null +++ b/templates/latex/renderflow-typography.sty @@ -0,0 +1,65 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{renderflow-typography.sty}[2026/09/11 Renderflow typography roles] + +\RequirePackage{iftex} +\providecommand{\RenderflowMainFont}{Latin Modern Roman} +\providecommand{\RenderflowSansFont}{Latin Modern Sans} +\providecommand{\RenderflowMonoFont}{Latin Modern Mono} +\providecommand{\RenderflowFontRoot}{.} +\providecommand{\RenderflowMainFontFile}{} +\providecommand{\RenderflowSansFontFile}{} +\providecommand{\RenderflowMonoFontFile}{} +\RequirePackage{etoolbox} + +\ifPDFTeX + \RequirePackage[T1]{fontenc} + \RequirePackage[utf8]{inputenc} + \RequirePackage{textcomp} + \RequirePackage{lmodern} +\else + \RequirePackage{fontspec} + \defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase} + \ifdefempty{\RenderflowMainFontFile}{ + \setmainfont{\RenderflowMainFont} + }{ + \IfFileExists{\RenderflowFontRoot/\RenderflowMainFontFile}{ + \setmainfont[Path=\RenderflowFontRoot/]{\RenderflowMainFontFile} + }{ + \PackageWarning{renderflow-typography}{Local main font missing; using deterministic fallback} + \setmainfont{\RenderflowMainFont} + } + } + \ifdefempty{\RenderflowSansFontFile}{ + \setsansfont{\RenderflowSansFont} + }{ + \IfFileExists{\RenderflowFontRoot/\RenderflowSansFontFile}{ + \setsansfont[Path=\RenderflowFontRoot/]{\RenderflowSansFontFile} + }{ + \PackageWarning{renderflow-typography}{Local sans font missing; using deterministic fallback} + \setsansfont{\RenderflowSansFont} + } + } + \ifdefempty{\RenderflowMonoFontFile}{ + \setmonofont{\RenderflowMonoFont} + }{ + \IfFileExists{\RenderflowFontRoot/\RenderflowMonoFontFile}{ + \setmonofont[Path=\RenderflowFontRoot/]{\RenderflowMonoFontFile} + }{ + \PackageWarning{renderflow-typography}{Local mono font missing; using deterministic fallback} + \setmonofont{\RenderflowMonoFont} + } + } +\fi + +\IfFileExists{microtype.sty}{\RequirePackage{microtype}}{} +\IfFileExists{upquote.sty}{\RequirePackage{upquote}}{} +\RequirePackage{titlesec} +\titleformat{\section}{\Large\bfseries\color{RenderflowInk}}{\thesection}{0.8em}{}[\color{RenderflowRule}\titlerule] +\titleformat{\subsection}{\large\bfseries\color{RenderflowInk}}{\thesubsection}{0.8em}{} +\titleformat{\subsubsection}{\normalsize\bfseries\color{RenderflowInk}}{\thesubsubsection}{0.8em}{} +\titlespacing*{\section}{0pt}{\RenderflowHeadingBefore}{\RenderflowHeadingAfter} +\titlespacing*{\subsection}{0pt}{\RenderflowHeadingBefore}{\RenderflowHeadingAfter} +\titlespacing*{\subsubsection}{0pt}{\RenderflowHeadingBefore}{\RenderflowHeadingAfter} +\AtBeginEnvironment{quote}{\color{RenderflowMuted}\itshape} + +\endinput diff --git a/templates/research/research.tex b/templates/research/research.tex index 63f8d44..e102745 100644 --- a/templates/research/research.tex +++ b/templates/research/research.tex @@ -1,122 +1,171 @@ -%% research.tex — Pandoc-compatible LaTeX template for research papers -%% -%% Usage: -%% pandoc input.md --template templates/research/research.tex -o output.pdf -%% -%% Required metadata (YAML front matter): -%% title: "My Paper" -%% author: "Alan" -%% date: "2026" -%% -%% Optional metadata: -%% abstract: "A short summary of the paper." -%% toc: true - +%% research.tex — Pandoc composition for the shared Renderflow LaTeX library. +%% Document semantics stay here; presentation primitives live in templates/latex. \documentclass[$if(fontsize)$$fontsize$$else$11pt$endif$,$if(papersize)$$papersize$$else$a4paper$endif$]{article} -%% ── Core packages ──────────────────────────────────────────────────────────── -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{lmodern} % scalable vector fonts -\usepackage{microtype} % improved character spacing -\usepackage{parskip} % paragraph spacing instead of indentation - -%% ── Page geometry ──────────────────────────────────────────────────────────── -\usepackage[ - $if(geometry)$$geometry$$else$ - top=2.5cm, bottom=2.5cm, - left=3cm, right=3cm - $endif$ -]{geometry} - -%% ── Graphics & colour ──────────────────────────────────────────────────────── -\usepackage{graphicx} -\usepackage{xcolor} - -%% ── Hyperlinks ─────────────────────────────────────────────────────────────── -\usepackage[ - colorlinks=true, - linkcolor=black, - citecolor=black, - urlcolor=blue -]{hyperref} - -%% ── Section styling ────────────────────────────────────────────────────────── -\usepackage{titlesec} -\titleformat{\section}{\large\bfseries}{\thesection}{1em}{}[\titlerule] -\titleformat{\subsection}{\normalsize\bfseries}{\thesubsection}{1em}{} -\titleformat{\subsubsection}{\normalsize\itshape}{\thesubsubsection}{1em}{} - -%% ── Headers and footers ────────────────────────────────────────────────────── -\usepackage{fancyhdr} -\pagestyle{fancy} -\fancyhf{} -\fancyhead[L]{\small\itshape $if(title)$$title$$endif$} -\fancyhead[R]{\small\itshape $if(author)$$author$$endif$} -\fancyfoot[C]{\small\thepage} -\renewcommand{\headrulewidth}{0.4pt} - -%% ── Lists ──────────────────────────────────────────────────────────────────── -\usepackage{enumitem} -\setlist{noitemsep, topsep=4pt} - -%% ── Tables ─────────────────────────────────────────────────────────────────── -\usepackage{booktabs} -\usepackage{longtable} - -%% ── Code blocks ────────────────────────────────────────────────────────────── -\usepackage{fancyvrb} -\usepackage{listings} -\lstset{ - basicstyle=\small\ttfamily, - breaklines=true, - frame=single, - framerule=0.4pt, - xleftmargin=0.5em, - xrightmargin=0.5em -} +%% Renderflow injects these roots from the active template directory. The +%% defaults also support direct pandoc use from the repository root. +$if(renderflow-style-root)$ +\edef\RenderflowStyleRoot{\detokenize{$renderflow-style-root$}} +$else$ +\def\RenderflowStyleRoot{templates/latex} +$endif$ +$if(renderflow-font-root)$ +\edef\RenderflowFontRoot{\detokenize{$renderflow-font-root$}} +$endif$ + +%% Profile-level design token overrides. These remain semantic values rather +%% than package implementation details. +$if(renderflow-color-accent)$ +\def\RenderflowColorAccent{$renderflow-color-accent$} +$endif$ +$if(renderflow-color-ink)$ +\def\RenderflowColorInk{$renderflow-color-ink$} +$endif$ +$if(renderflow-color-muted)$ +\def\RenderflowColorMuted{$renderflow-color-muted$} +$endif$ +$if(renderflow-color-rule)$ +\def\RenderflowColorRule{$renderflow-color-rule$} +$endif$ +$if(renderflow-color-surface)$ +\def\RenderflowColorSurface{$renderflow-color-surface$} +$endif$ +$if(renderflow-margin-top)$ +\def\RenderflowMarginTop{$renderflow-margin-top$} +$endif$ +$if(renderflow-margin-bottom)$ +\def\RenderflowMarginBottom{$renderflow-margin-bottom$} +$endif$ +$if(renderflow-margin-inner)$ +\def\RenderflowMarginInner{$renderflow-margin-inner$} +$endif$ +$if(renderflow-margin-outer)$ +\def\RenderflowMarginOuter{$renderflow-margin-outer$} +$endif$ +$if(renderflow-paragraph-skip)$ +\def\RenderflowParagraphSkip{$renderflow-paragraph-skip$} +$endif$ -%% ── Mathematics ────────────────────────────────────────────────────────────── -\usepackage{amsmath} -\usepackage{amssymb} +%% Semantic font roles use deterministic Latin Modern defaults. A profile may +%% select another bundled family or local-first file without changing styles. +$if(mainfont)$ +\def\RenderflowMainFont{$mainfont$} +$endif$ +$if(sansfont)$ +\def\RenderflowSansFont{$sansfont$} +$endif$ +$if(monofont)$ +\def\RenderflowMonoFont{$monofont$} +$endif$ +$if(renderflow-main-font-file)$ +\edef\RenderflowMainFontFile{\detokenize{$renderflow-main-font-file$}} +$endif$ +$if(renderflow-sans-font-file)$ +\edef\RenderflowSansFontFile{\detokenize{$renderflow-sans-font-file$}} +$endif$ +$if(renderflow-mono-font-file)$ +\edef\RenderflowMonoFontFile{\detokenize{$renderflow-mono-font-file$}} +$endif$ + +\input{\RenderflowStyleRoot/renderflow-core.sty} +\RequirePackage{amsmath,amssymb} + +$if(highlighting-macros)$ +$highlighting-macros$ +\renewenvironment{Shaded}{\begin{RenderflowShaded}}{\end{RenderflowShaded}} +$endif$ -%% ── User-supplied header includes ──────────────────────────────────────────── $for(header-includes)$ $header-includes$ $endfor$ -%% ── Pandoc-generated definitions (tight lists, etc.) ───────────────────────── -\providecommand{\tightlist}{% - \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +$if(title)$ +\renewcommand{\RenderflowDocumentTitle}{$title$} +\renewcommand{\RenderflowRunningTitle}{$title$} +$endif$ +$if(subtitle)$ +\renewcommand{\RenderflowDocumentSubtitle}{$subtitle$} +$endif$ +$if(author)$ +\renewcommand{\RenderflowDocumentAuthors}{$for(author)$$author$$sep$; $endfor$} +\renewcommand{\RenderflowRunningAuthor}{$for(author)$$author$$sep$; $endfor$} +$endif$ +$if(date)$ +\renewcommand{\RenderflowDocumentDate}{$date$} +$endif$ + +\hypersetup{ +$if(title-meta)$ + pdftitle={$title-meta$}, +$endif$ +$if(author-meta)$ + pdfauthor={$author-meta$}, +$endif$ +$if(subject)$ + pdfsubject={$subject$}, +$endif$ +$if(keywords)$ + pdfkeywords={$for(keywords)$$keywords$$sep$, $endfor$}, +$endif$ + pdfcreator={Renderflow via Pandoc and Tectonic} +} + +$if(numbersections)$ +\setcounter{secnumdepth}{$if(secnumdepth)$$secnumdepth$$else$5$endif$} +$else$ +\setcounter{secnumdepth}{-\maxdimen} +$endif$ + +$if(natbib)$ +\RequirePackage[$natbiboptions$]{natbib} +\bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$} +$endif$ +$if(biblatex)$ +\RequirePackage[$if(biblio-style)$style=$biblio-style$,$endif$$for(biblatexoptions)$$biblatexoptions$$sep$,$endfor$]{biblatex} +$for(bibliography)$ +\addbibresource{$bibliography$} +$endfor$ +$endif$ -%% ───────────────────────────────────────────────────────────────────────────── \begin{document} -%% ── Title block ────────────────────────────────────────────────────────────── $if(title)$ -\begin{center} - {\LARGE\bfseries $title$}\\[0.8em] - $if(author)${\large $author$}\\[0.4em]$endif$ - $if(date)${\normalsize $date$}$endif$ -\end{center} -\vspace{1em} +\RenderflowMakeTitle $endif$ -%% ── Abstract ───────────────────────────────────────────────────────────────── $if(abstract)$ \begin{abstract} - \noindent $abstract$ +$abstract$ \end{abstract} \vspace{1em} $endif$ -%% ── Table of contents ──────────────────────────────────────────────────────── +$for(include-before)$ +$include-before$ +$endfor$ + $if(toc)$ +$if(toc-title)$ +\renewcommand*\contentsname{$toc-title$} +$endif$ +\setcounter{tocdepth}{$if(toc-depth)$$toc-depth$$else$3$endif$} \tableofcontents \newpage $endif$ -%% ── Document body ──────────────────────────────────────────────────────────── $body$ +$if(natbib)$ +$if(bibliography)$ +\bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$} +$endif$ +$endif$ +$if(biblatex)$ +\printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$ +$endif$ + +$for(include-after)$ +$include-after$ +$endfor$ + \end{document} diff --git a/tests/fixtures/latex-components/renderflow.yaml b/tests/fixtures/latex-components/renderflow.yaml new file mode 100644 index 0000000..37d5d2a --- /dev/null +++ b/tests/fixtures/latex-components/renderflow.yaml @@ -0,0 +1,30 @@ +schema: renderflow/v2 +sources: + - id: component-showcase + role: manuscript + path: showcase.md + format: markdown +targets: + exact: + - id: research-pdf + role: publication/research + format: pdf + template: research/research.tex +variables: + renderflow-color-accent: "4058A6" + renderflow-margin-inner: "28mm" + renderflow-margin-outer: "28mm" +execution: + requirements: + deterministic: true + local_only: true + offline: true + network: deny + ai: deny + validation: + required: true + failure_mode: fatal + allow_unavailable: false +output: + bundle_root: output + naming_template: "{target.role}.{ext}" diff --git a/tests/fixtures/latex-components/showcase.md b/tests/fixtures/latex-components/showcase.md new file mode 100644 index 0000000..45d2b42 --- /dev/null +++ b/tests/fixtures/latex-components/showcase.md @@ -0,0 +1,50 @@ +--- +title: Shared Component Showcase +subtitle: Synthetic research profile fixture +author: + - Renderflow Contributors +date: "2026-09-11" +abstract: | + A redistribution-safe document that exercises the shared Renderflow LaTeX + presentation layer without relying on external or licensed assets. +toc: true +--- + +# Typography and prose + +This paragraph demonstrates body copy, a [local-safe link](https://example.com), +and a footnote with inspectable synthetic content.[^fixture] + +> A quotation remains semantic Markdown while the shared typography component +> controls its presentation. + +\begin{RenderflowCallout}[Reproducibility] +This callout is raw LaTeX by design: profiles opt into a presentation primitive +without moving the surrounding document structure into the style package. +\end{RenderflowCallout} + +## Table + +| Primitive | Shared concern | Profile concern | +|---|---|---| +| Typography | role styling | editorial hierarchy | +| Figure | caption treatment | asset selection | +| Callout | visual treatment | callout meaning | + +## Figure + +\begin{figure} +\centering +\fbox{\rule{0pt}{24mm}\rule{0.72\linewidth}{0pt}} +\caption{A synthetic figure placeholder with no external asset dependency.} +\end{figure} + +## Code + +```rust +fn artifact_is_reproducible(digest: &str) -> bool { + !digest.is_empty() +} +``` + +[^fixture]: The fixture uses only original prose and a geometric placeholder.