PDF export (fully automated with make target)
Generate tex files directly with python, not relying on lektor
This commit is contained in:
26
Makefile
26
Makefile
@@ -1,4 +1,5 @@
|
||||
PROJDIR := src
|
||||
TEXER := lualatex
|
||||
|
||||
help:
|
||||
@echo
|
||||
@@ -11,7 +12,7 @@ help:
|
||||
@echo ' server - Start lektor server with live change updates'
|
||||
@echo ' build - Build deployable website into ./bin'
|
||||
@echo ' deploy - Custom rsync command to sync ./bin to remote server'
|
||||
@echo
|
||||
@echo ' pdf - Generate PDF from tex (after build)'
|
||||
@echo
|
||||
@echo ' find-links - Search for cross reference between recipes'
|
||||
@echo
|
||||
@@ -42,15 +43,14 @@ clean-all: clean plugins
|
||||
# Build
|
||||
|
||||
server:
|
||||
@cd '$(PROJDIR)' && lektor server
|
||||
@cd '$(PROJDIR)' && lektor server # -f ENABLE_PDF_EXPORT
|
||||
|
||||
build:
|
||||
@cd '$(PROJDIR)' && \
|
||||
lektor build --output-path ../bin --buildstate-path ../build-state -f ENABLE_APPCACHE
|
||||
lektor build --output-path ../bin --buildstate-path ../build-state -f ENABLE_APPCACHE -f ENABLE_PDF_EXPORT
|
||||
@echo
|
||||
@echo 'Checking dead links ...'
|
||||
@python3 extras/find-dead-links.py
|
||||
@echo
|
||||
|
||||
deploy:
|
||||
@echo
|
||||
@@ -59,6 +59,24 @@ deploy:
|
||||
@echo # --dry-run
|
||||
rsync -rclzv --exclude=.lektor --exclude=.DS_Store --delete bin/ vps:/srv/http/recipe-lekture
|
||||
|
||||
pdf:
|
||||
@echo
|
||||
@echo 'Generating PDF from tex source ...'
|
||||
@echo 'Check if $(TEXER) exists'
|
||||
@which $(TEXER)
|
||||
@cd extras/pdf-export/ && \
|
||||
SECONDS=0; \
|
||||
for i in 1 2; do \
|
||||
for alt in de en; do \
|
||||
fname="pdf-$${alt}.tex"; \
|
||||
echo "$$ $(TEXER) $${fname} [$${i}]"; \
|
||||
$(TEXER) $${fname} > /dev/null; \
|
||||
done; \
|
||||
done; \
|
||||
echo "done. finished after $${SECONDS}s."
|
||||
rm -rf extras/pdf-export/*.{aux,log,out,toc}
|
||||
mv extras/pdf-export/pdf-*.pdf bin/static
|
||||
|
||||
# Helper methods on all recipes
|
||||
|
||||
find-links:
|
||||
|
||||
8
extras/pdf-export/.gitignore
vendored
8
extras/pdf-export/.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
/*.aux
|
||||
/*.log
|
||||
/*.out
|
||||
/*.toc
|
||||
/dyn-*.tex
|
||||
/pdf-de.pdf
|
||||
/pdf-en.pdf
|
||||
/setup-builddir.tex
|
||||
/pdf-en.pdf
|
||||
@@ -1,7 +1,7 @@
|
||||
\documentclass[a4paper,12pt,twoside]{article}
|
||||
\usepackage[ngerman]{babel}
|
||||
\include{setup}
|
||||
\include{setup-builddir} % load \def\builddir{}
|
||||
\include{dyn-builddir} % load \def\builddir{}
|
||||
|
||||
\def\tIngredients{Zutaten}
|
||||
\def\tRecipe{Rezept}
|
||||
@@ -9,6 +9,5 @@
|
||||
|
||||
\begin{document}
|
||||
\makefrontmatter
|
||||
\graphicspath{{\builddir/de/}}
|
||||
\include{\builddir/de/pdf-export}
|
||||
\include{dyn-recipes-de}
|
||||
\end{document}
|
||||
@@ -1,7 +1,7 @@
|
||||
\documentclass[letter,12pt,twoside]{article}
|
||||
\usepackage[english]{babel}
|
||||
\include{setup}
|
||||
\include{setup-builddir} % load \def\builddir{}
|
||||
\include{dyn-builddir} % load \def\builddir{}
|
||||
|
||||
\def\tIngredients{Ingredients}
|
||||
\def\tRecipe{recipe}
|
||||
@@ -9,6 +9,5 @@
|
||||
|
||||
\begin{document}
|
||||
\makefrontmatter
|
||||
\graphicspath{{\builddir/en/}}
|
||||
\include{\builddir/en/pdf-export}
|
||||
\include{dyn-recipes-en}
|
||||
\end{document}
|
||||
@@ -9,45 +9,20 @@
|
||||
\usepackage{tocloft} % \cftbeforesecskip
|
||||
\usepackage{ragged2e} % \RaggedRight
|
||||
\usepackage{fontspec} % \setmainfont, \setsansfont, \setmonofont
|
||||
\usepackage{microtype}
|
||||
\usepackage{xurl}
|
||||
\PassOptionsToPackage{hyphens}{url}
|
||||
\usepackage[hidelinks]{hyperref}
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Defines & Geometry
|
||||
%-------------------------------------------
|
||||
\graphicspath{{\builddir/en/}}
|
||||
|
||||
\definecolor{red2}{HTML}{AA203A}
|
||||
\definecolor{red3}{HTML}{EE6A84}
|
||||
\usepackage{xurl}
|
||||
\PassOptionsToPackage{hyphens}{url}
|
||||
\usepackage{hyperref}
|
||||
\renewcommand{\UrlFont}{\normalfont}
|
||||
|
||||
%% Use this if the font is _not_ installed on the system
|
||||
%% but it requires the download of the additional fonts package
|
||||
%% \setmonofont[Path=fonts/]{FiraSans-Light}
|
||||
\setmainfont[
|
||||
Path = fonts/,
|
||||
BoldFont = Crimson-Bold,
|
||||
ItalicFont = Crimson-Italic,
|
||||
BoldItalicFont = Crimson-BoldItalic
|
||||
]{Crimson-Roman}
|
||||
\setsansfont[
|
||||
Path = fonts/,
|
||||
Scale = 0.95,
|
||||
BoldFont = FiraSans-Medium,
|
||||
ItalicFont = FiraSans-LightItalic,
|
||||
BoldItalicFont = FiraSans-MediumItalic
|
||||
]{FiraSans-Regular}
|
||||
|
||||
%% Use this if the font is installed on the system
|
||||
%% this does not require additional fonts package
|
||||
% \setmainfont[
|
||||
% BoldFont = {Fira Sans Medium},
|
||||
% ItalicFont = {Fira Sans Light Italic},
|
||||
% BoldItalicFont = {Fira Sans Medium Italic}
|
||||
% ]{Fira Sans Regular}
|
||||
% \setmainfont[
|
||||
% BoldFont = {Crimson Bold},
|
||||
% ItalicFont = {Crimson Italic},
|
||||
% BoldItalicFont = {Crimson Bold Italic}
|
||||
% ]{Crimson Roman}
|
||||
|
||||
% Defines & Geometry
|
||||
\def\marginwidth{60mm}
|
||||
\def\marginsep{8.2mm}
|
||||
\def\marginwoverflow{67mm} % -1.2mm for makebox
|
||||
@@ -66,56 +41,117 @@
|
||||
includemp,
|
||||
% showframe
|
||||
}
|
||||
% \renewcommand{\footnotesep}{2.5ex}
|
||||
\renewcommand{\footnoterule}{
|
||||
\kern -2pt
|
||||
\hrule width \textwidth height .2pt
|
||||
\kern 1.8pt}
|
||||
% spacing before & after
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Penalties
|
||||
%-------------------------------------------
|
||||
\doublehyphendemerits=10000 % No consecutive line hyphens
|
||||
\brokenpenalty=10000 % No broken words across columns/pages
|
||||
\widowpenalty=9999 % Almost no widows at bottom of page
|
||||
\clubpenalty=9999 % Almost no orphans at top of page
|
||||
\interfootnotelinepenalty=9999 % Almost never break footnotes
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Fonts
|
||||
%-------------------------------------------
|
||||
\renewcommand{\UrlFont}{\normalfont}
|
||||
%% Use this if the font is _not_ installed on the system
|
||||
%% but it requires the download of the additional fonts package
|
||||
%% \setmonofont[Path=fonts/]{FiraSans-Light}
|
||||
\setmainfont[
|
||||
Path = fonts/,
|
||||
BoldFont = {Crimson-Bold},
|
||||
ItalicFont = {Crimson-Italic},
|
||||
BoldItalicFont = {Crimson-BoldItalic}
|
||||
]{Crimson-Roman}
|
||||
\setsansfont[
|
||||
Path = fonts/,
|
||||
Scale = 0.95,
|
||||
BoldFont = {FiraSans-Medium},
|
||||
ItalicFont = {FiraSans-LightItalic},
|
||||
BoldItalicFont = {FiraSans-MediumItalic}
|
||||
]{FiraSans-Regular}
|
||||
|
||||
%% Use this if the font is installed on the system
|
||||
%% this does not require additional fonts package
|
||||
% \setmainfont[
|
||||
% BoldFont = {Fira Sans Medium},
|
||||
% ItalicFont = {Fira Sans Light Italic},
|
||||
% BoldItalicFont = {Fira Sans Medium Italic}
|
||||
% ]{Fira Sans Regular}
|
||||
% \setmainfont[
|
||||
% BoldFont = {Crimson Bold},
|
||||
% ItalicFont = {Crimson Italic},
|
||||
% BoldItalicFont = {Crimson Bold Italic}
|
||||
% ]{Crimson Roman}
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Spacing in sections
|
||||
%-------------------------------------------
|
||||
\makeatletter
|
||||
% footer
|
||||
\renewcommand\@makefntext[1]{\noindent\makebox[0.5em][l]{\@makefnmark}#1}
|
||||
% subsubsection
|
||||
\renewcommand{\subsubsection}{%
|
||||
\@startsection{subsubsection}{3}%
|
||||
{\z@}{-2ex \@plus -1ex \@minus -1ex}{.2ex \@plus 1ex \@minus .2ex}%
|
||||
{\normalfont\large\bfseries}%
|
||||
}
|
||||
% paragraph
|
||||
\renewcommand{\paragraph}{%
|
||||
\@startsection{paragraph}{4}%
|
||||
{\z@}{1.5ex \@plus 1ex \@minus .2ex}{-.75em}%
|
||||
{\z@}{1.5ex \@plus 1ex \@minus .2ex}{-.75em \@plus -1em \@minus -.5em}%
|
||||
{\normalfont\normalsize\bfseries}%
|
||||
}
|
||||
% footer
|
||||
\renewcommand\@makefntext[1]{\noindent\makebox[0.5em][l]{\@makefnmark}#1}
|
||||
\makeatother
|
||||
% adjust description environment
|
||||
\setlist[description,enumerate,itemize]{%
|
||||
% \titleformat*{\section}{\LARGE\bfseries} # titlesec
|
||||
\setlength{\cftbeforesecskip}{.5ex} % spacing in TOC
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Spacing in enumerations
|
||||
%-------------------------------------------
|
||||
\setlist[description]{%
|
||||
topsep=0.7\baselineskip,
|
||||
itemsep=0pt,
|
||||
% labelsep=*,
|
||||
% itemindent=-2em,
|
||||
% listparindent=-2em,
|
||||
% leftmargin=0em,
|
||||
font={\sffamily}
|
||||
}
|
||||
\setlist[enumerate,itemize]{leftmargin=1em,topsep=0\baselineskip,parsep=0pt}
|
||||
\widowpenalty10000
|
||||
\clubpenalty10000
|
||||
\setlist[enumerate,itemize]{
|
||||
topsep=0\baselineskip,
|
||||
leftmargin=1em,
|
||||
itemsep=1ex,
|
||||
parsep=0pt
|
||||
}
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Section & footnote numbering
|
||||
%-------------------------------------------
|
||||
% disable section numbering
|
||||
\setcounter{secnumdepth}{0}
|
||||
\setcounter{tocdepth}{1}
|
||||
% \titleformat*{\section}{\LARGE\bfseries} # titlesec
|
||||
\setlength{\cftbeforesecskip}{0.2em} % spacing in TOC
|
||||
|
||||
% \renewcommand{\footnotesep}{2.5ex}
|
||||
\renewcommand{\footnoterule}{
|
||||
\kern -2pt
|
||||
\hrule width \textwidth height .2pt
|
||||
\kern 1.8pt}
|
||||
% Footer page numbering
|
||||
\renewpagestyle{plain}{%
|
||||
\setfoot[{\makebox[-\marginwoverflow][r]{\thepage}}][][]{}{}{{\makebox[-\marginwoverflow][l]{\thepage}}}
|
||||
}
|
||||
\pagestyle{plain}
|
||||
|
||||
% \graphicspath{{../src/content/}}
|
||||
|
||||
%-------------------------------------------
|
||||
% Title page
|
||||
%-------------------------------------------
|
||||
\newcommand{\makefrontmatter}{
|
||||
\begin{titlepage}
|
||||
\setcounter{page}{0} % sets the number 0 on the first page in preview
|
||||
\newgeometry{top=2.5cm,bottom=2.5cm,left=4cm,right=4cm}
|
||||
\centering
|
||||
\vspace*{.06\textheight}
|
||||
@@ -134,7 +170,10 @@
|
||||
\setcounter{page}{1}
|
||||
}
|
||||
|
||||
|
||||
%-------------------------------------------
|
||||
% Custom commands
|
||||
%-------------------------------------------
|
||||
\newcommand{\meta}[2]{%
|
||||
\vspace{-0.75em}%
|
||||
~\includegraphics[height=2ex]{misc/icon-time}\enspace\ifx\relax#1\relax—\else#1\fi%
|
||||
@@ -146,8 +185,7 @@
|
||||
\ifx\relax#1\relax\else{%
|
||||
\enlargethispage{5.75ex}
|
||||
\let\thefootnote\relax\footnotetext{%
|
||||
\hspace{-.6em} Source: %
|
||||
\ifx\relax#2\relax#1\else\href{#1}{#2}\fi%
|
||||
\hspace{-.6em} Source: \ifx\relax#2\relax#1\else\href{#1}{#2}\fi%
|
||||
}\fi%
|
||||
}
|
||||
}
|
||||
@@ -156,7 +194,7 @@
|
||||
\newcommand{\ingGroup}[1]{\vspace{.2ex}\item{\bfseries\color{red2}\hspace{-1em} #1}}
|
||||
\newcommand{\ingName}[1]{{\color{red3}#1}}
|
||||
\newcommand{\ingDetail}[1]{\emph{\footnotesize,#1}}
|
||||
\newcommand{\pagelink}[1]{\tRecipe \tPagePrefix\,{\color{red2}\pageref{#1}}}
|
||||
\newcommand{\pagelink}[1]{\tRecipe{} \tPagePrefix\,{\color{red2}\pageref{#1}}}
|
||||
\newcommand{\recipelink}[2]{#2 (\tPagePrefix\,\pageref{#1})}
|
||||
\newcommand{\external}[2]{#2\footnote{\href{#1}{#1}}}
|
||||
% \newcommand{\external}[2]{#2\footnote{\url{#1}}}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
_template: makepdf.tex
|
||||
---
|
||||
_model: none
|
||||
@@ -210,7 +210,9 @@ class HelperPlugin(Plugin):
|
||||
repFrac = self.settings[alt]['replFrac']
|
||||
|
||||
for line in recipe['ingredients']:
|
||||
line = tex.raw_text_to_tex(line).strip()
|
||||
line = line.strip()
|
||||
if mode == 'tex':
|
||||
line = tex.raw_text_to_tex(line)
|
||||
if not line:
|
||||
continue
|
||||
elif line.endswith(':'):
|
||||
|
||||
@@ -50,13 +50,12 @@ s?<it>\([^<]*\)</it>?{\\it \1}?g
|
||||
s?<em>\([^<]*\)</em>?{\\it \1}?g
|
||||
s?<b>\([^<]*\)</b>?{\\bf \1}?g
|
||||
s?<strong>\([^<]*\)</strong>?{\\bf \1}?g
|
||||
# old unused, because lektor generated internally other urls then output
|
||||
# s?<a href="\.\.[^"]*/\([^"/][^"/]*\)/*">[^<]*</a>?\\recipelink{\1}?g
|
||||
# recipe specific
|
||||
s?<a href="recipes/\([^"/]*\)/*">\([^<]*\)</a>?\\recipelink{\1}{\2}?g
|
||||
s?<a href="../\([^"/]*\)/*">\([^<]*\)</a>?\\recipelink{\1}{\2}?g
|
||||
# Get rid of Anchors
|
||||
s?<a href="\(http[^"]*\)">\([^<]*\)</a>?\\external{\1}{\2}?g
|
||||
s?<a[^>]*>??g
|
||||
s?</a>??g
|
||||
# after href replace
|
||||
# quotes (replace after href)
|
||||
s?\([[:space:]]\)"\([^[:space:]]\)?\1``\2?g
|
||||
s?"?''?g
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from lektor.pluginsystem import Plugin # , get_plugin
|
||||
import mistune
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import subprocess as shell
|
||||
import lektor_helper as helper
|
||||
import lektor_time_duration as timedur
|
||||
|
||||
my_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
f_sed_a = os.path.join(my_dir, 'html2latex.sed')
|
||||
|
||||
|
||||
def sed_repl_content(f_sed, content):
|
||||
with subprocess.Popen(('echo', content), stdout=subprocess.PIPE) as ps:
|
||||
o = subprocess.check_output(['sed', '-f', f_sed], stdin=ps.stdout)
|
||||
def sed_repl_tex(f_sed, content):
|
||||
with shell.Popen(('echo', content), stdout=shell.PIPE) as ps:
|
||||
o = shell.check_output(['sed', '-f', f_sed], stdin=ps.stdout)
|
||||
ps.wait()
|
||||
return o.decode('utf-8')
|
||||
|
||||
|
||||
def html_to_tex(html):
|
||||
return sed_repl_content(f_sed_a, html)
|
||||
return sed_repl_tex(f_sed_a, html)
|
||||
|
||||
|
||||
def raw_text_to_tex(text):
|
||||
@@ -24,23 +28,104 @@ def raw_text_to_tex(text):
|
||||
if c in text:
|
||||
text = text.replace(c, '\\' + c)
|
||||
return text
|
||||
# return sed_repl_content(f_sed_b, text)
|
||||
# return sed_repl_tex(f_sed_b, text)
|
||||
|
||||
|
||||
class RecipeToTex(object):
|
||||
def __init__(self, enumerator):
|
||||
super(RecipeToTex, self).__init__()
|
||||
bases = tuple([]) + (mistune.Renderer,)
|
||||
renderer_cls = type('renderer_cls', bases, {})
|
||||
renderer = renderer_cls(escape=False)
|
||||
self.mdparser = mistune.Markdown(renderer, escape=False)
|
||||
self.enumerator = enumerator
|
||||
|
||||
def make(self, recipe, alt):
|
||||
ingredients = ''
|
||||
for x in self.enumerator(recipe, alt, mode='tex'):
|
||||
ingredients += '\n' + self.process_ingredient(x, alt)
|
||||
ingredients = ingredients[1:] or '\\item '
|
||||
self.mdparser.renderer.record = recipe
|
||||
instructions_html = self.mdparser(recipe['directions'].source)
|
||||
instructions = sed_repl_tex(f_sed_a, instructions_html)
|
||||
return self.process_recipe(recipe, alt, ingredients, instructions)
|
||||
pass
|
||||
|
||||
def process_recipe(self, recipe, alt, ingredients, instructions):
|
||||
img = helper.title_image(recipe)
|
||||
if img:
|
||||
img = img.path[:-4] + '@200x150_crop' + img.path[-4:]
|
||||
duration = recipe['time']
|
||||
host = recipe['source'].host
|
||||
time = timedur.to_duration(duration, alt) if duration else ''
|
||||
srcUrl = raw_text_to_tex(str(recipe['source'])) or ''
|
||||
srcHost = raw_text_to_tex(host) if host else ''
|
||||
return f'''
|
||||
\\newrecipe{{{recipe['_slug']}}}{{{raw_text_to_tex(recipe['name'])}}}
|
||||
\\meta{{{time}}}{{{recipe['yield'] or ''}}}
|
||||
\\footer{{{srcUrl}}}{{{srcHost}}}
|
||||
|
||||
\\begin{{ingredients}}{{{img or ''}}}
|
||||
{ingredients}
|
||||
\\end{{ingredients}}
|
||||
|
||||
{instructions}
|
||||
'''
|
||||
|
||||
def process_ingredient(self, ing, alt):
|
||||
grp = ing.get('group')
|
||||
if grp:
|
||||
return f'\\ingGroup{{{grp}}}'
|
||||
|
||||
ret = ''
|
||||
val = ing['value']
|
||||
meas = ing['measure']
|
||||
note = ing['note']
|
||||
ret += '\\item'
|
||||
if val or meas:
|
||||
sep = '~' if val and meas else ''
|
||||
ret += '[{}{}{}]'.format(val or '', sep, meas or '')
|
||||
ret += f' \\ingName{{{ ing["name"] }}}' # keep space in front
|
||||
if note:
|
||||
ret += '\\ingDetail{'
|
||||
for prt in note.split():
|
||||
if prt.startswith('@../'):
|
||||
ret += f' \\pagelink{{{ prt[4:].rstrip("/") }}}'
|
||||
else:
|
||||
ret += ' ' + prt
|
||||
ret += '}'
|
||||
return ret
|
||||
|
||||
|
||||
class HtmlToTex(Plugin):
|
||||
name = u'HTML to TEX converter'
|
||||
description = u'Will convert html formatted text to (la)tex format.'
|
||||
|
||||
def on_before_build_all(self, builder, **extra):
|
||||
# export current build dir
|
||||
dest_file = my_dir
|
||||
def on_after_prune(self, builder, **extra):
|
||||
maketex = bool(builder.extra_flags.get('ENABLE_PDF_EXPORT'))
|
||||
print('PDF Export: ' + ('ENABLED' if maketex else 'DISABLED'))
|
||||
if not maketex:
|
||||
return
|
||||
|
||||
dest_dir = my_dir
|
||||
for x in range(3):
|
||||
dest_file = os.path.dirname(dest_file)
|
||||
dest_file = os.path.join(dest_file, 'extras', 'pdf-export',
|
||||
'setup-builddir.tex')
|
||||
with open(dest_file, 'w') as f:
|
||||
dest_dir = os.path.dirname(dest_dir)
|
||||
dest_dir = os.path.join(dest_dir, 'extras', 'pdf-export')
|
||||
|
||||
start_time = time.time()
|
||||
print('PDF Export: generate tex files')
|
||||
with open(os.path.join(dest_dir, 'dyn-builddir.tex'), 'w') as f:
|
||||
# Export current build dir (for image search)
|
||||
f.write('\\def\\builddir{' + builder.destination_path + '}')
|
||||
parser = RecipeToTex(self.env.jinja_env.filters['enumIngredients'])
|
||||
for alt in self.env.load_config().list_alternatives():
|
||||
tex = ''
|
||||
for recipe in builder.pad.get('/recipes', alt=alt).children:
|
||||
tex += parser.make(recipe, alt)
|
||||
fname = os.path.join(dest_dir, f'dyn-recipes-{alt}.tex')
|
||||
with open(fname, 'w') as f:
|
||||
f.write(tex)
|
||||
print('PDF Export: done in %.2f sec' % (time.time() - start_time))
|
||||
|
||||
def on_setup_env(self, **extra):
|
||||
self.env.jinja_env.filters['html_to_tex'] = html_to_tex
|
||||
self.env.jinja_env.filters['raw_text_to_tex'] = raw_text_to_tex
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{%- set recipe_label = localize(this.alt, 'ingredients.recipeLink') %}
|
||||
|
||||
{%- for recipe in site.get('/recipes', this.alt).children %}
|
||||
\newrecipe{ {{- recipe._slug }}}{ {{- recipe.name | raw_text_to_tex }}}
|
||||
\meta{ {{- recipe.time|duration(this.alt) if recipe.time else '' }}}{ {{- recipe.yield or '' }}}
|
||||
\footer{ {{- recipe.source | string | raw_text_to_tex or '' }}}{ {{- recipe.source.host | raw_text_to_tex if recipe.source.host else '' }}}
|
||||
{%- set img = recipe | title_image(small=True) %}
|
||||
|
||||
\begin{ingredients}{ {%- if img %}{{ img|url }}{% else %}{% endif %}}
|
||||
{%- for ing in recipe|enumIngredients(this.alt, mode='tex') %}
|
||||
{%- if ing['group'] %}
|
||||
\ingGroup{ {{- ing['group'] | raw_text_to_tex }}}
|
||||
{%- else %}
|
||||
\item
|
||||
{%- if ing['value'] or ing['measure'] -%}
|
||||
[{%- if ing['value'] %}{{ ing['value'] }}{% endif -%}
|
||||
{%- if ing['measure'] %}~{{ ing['measure'] }}{% endif -%}]
|
||||
{%- endif %} \ingName{ {{- ing['name'] -}}}
|
||||
{%- if ing['note'] %}\ingDetail{
|
||||
{%- for prt in ing['note'].split() -%}
|
||||
{%- if prt.startswith('@../') %} \pagelink{ {{- prt[4:-1] -}} }
|
||||
{%- else %} {{ prt -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
\end{ingredients}
|
||||
|
||||
{{ recipe.directions.html | html_to_tex }}
|
||||
{%- endfor %}
|
||||
Reference in New Issue
Block a user