Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to the tex rendering pipeline #362

Merged
merged 10 commits into from
Jun 14, 2020
4 changes: 3 additions & 1 deletion src/Weave.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Weave

using Highlights, Mustache, Requires, Pkg
using Highlights, Mustache, Requires, Pkg, REPL


# directories
Expand Down Expand Up @@ -32,6 +32,8 @@ end
take2string!(io) = String(take!(io))
joinlines(lines) = join(lines, '\n')

get_format(doctype::AbstractString) = FORMATS[doctype]

"""
list_out_formats()

Expand Down
1 change: 0 additions & 1 deletion src/rendering/rendering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# - 4. Document Interface

using Mustache, Highlights, .WeaveMarkdown, Markdown, Dates, Printf
using REPL.REPLCompletions: latex_symbols


const FORMATS = Dict{String,WeaveFormat}()
Expand Down
119 changes: 64 additions & 55 deletions src/rendering/texformats.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

abstract type TexFormat <: WeaveFormat end

set_rendering_options!(docformat::TexFormat; keep_unicode = false, kwargs...) = docformat.keep_unicode |= keep_unicode
function set_rendering_options!(docformat::TexFormat; keep_unicode = false, template=nothing, kwargs...)
docformat.keep_unicode |= keep_unicode
docformat.template = get_tex_template(template)
end

function formatfigures(chunk, docformat::TexFormat)
fignames = chunk.figures
Expand Down Expand Up @@ -73,40 +76,17 @@ function md_length_to_latex(def, reference)
return def
end

# plain Tex
# ---------

Base.@kwdef mutable struct Tex <: TexFormat
description = "Latex with custom code environments"
extension = "tex"
codestart = "\\begin{juliacode}"
codeend = "\\end{juliacode}"
termstart = "\\begin{juliaterm}"
termend = "\\end{juliaterm}"
outputstart = "\\begin{juliaout}"
outputend = "\\end{juliaout}"
mimetypes = ["application/pdf", "image/png", "text/latex", "text/plain"]
fig_ext = ".pdf"
out_width = "\\linewidth"
out_height = nothing
fig_pos = "htpb"
fig_env = "figure"
# specials
keep_unicode = false
end
register_format!("tex", Tex())

# minted Tex
# ----------

Base.@kwdef mutable struct TexMinted <: TexFormat
description = "Latex using minted for highlighting"
extension = "tex"
codestart = "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}"
codestart = "\\begin{minted}[escapeinside=||, mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}"
codeend = "\\end{minted}"
termstart = "\\begin{minted}[fontsize=\\footnotesize, xleftmargin=0.5em, mathescape]{jlcon}"
termstart = "\\begin{minted}[escapeinside=||, mathescape, fontsize=\\footnotesize, xleftmargin=0.5em]{jlcon}"
termend = "\\end{minted}"
outputstart = "\\begin{minted}[fontsize=\\small, xleftmargin=0.5em, mathescape, frame = leftline]{text}"
outputstart = "\\begin{minted}[escapeinside=||, mathescape, fontsize=\\small, xleftmargin=0.5em, frame = leftline]{text}"
outputend = "\\end{minted}"
mimetypes = ["application/pdf", "image/png", "text/latex", "text/plain"]
fig_ext = ".pdf"
Expand All @@ -116,6 +96,11 @@ Base.@kwdef mutable struct TexMinted <: TexFormat
fig_env = "figure"
# specials
keep_unicode = false
template = nothing
tex_deps = "\\usepackage{minted}"
# how to escape latex in verbatim/code environment
escape_starter = "|\$"
escape_closer = "\$|"
end
register_format!("texminted", TexMinted())

Expand All @@ -142,6 +127,10 @@ Base.@kwdef mutable struct JMarkdown2tex <: TexFormat
highlight_theme = nothing
template = nothing
keep_unicode = false
tex_deps = ""
# how to escape latex in verbatim/code environment
escape_starter = "(*@"
escape_closer = "@*)"
end
register_format!("md2tex", JMarkdown2tex())
register_format!("md2pdf", JMarkdown2tex())
Expand All @@ -155,17 +144,28 @@ end
get_tex_template(::Nothing) = get_template(normpath(TEMPLATE_DIR, "md2pdf.tpl"))
get_tex_template(x) = get_template(x)

function render_doc(docformat::TexFormat, body, doc)
return Mustache.render(
docformat.template;
body = body,
highlight = "",
tex_deps = docformat.tex_deps,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end

function render_doc(docformat::JMarkdown2tex, body, doc)
return Mustache.render(
docformat.template;
body = body,
highlight = get_highlight_stylesheet(MIME("text/latex"), docformat.highlight_theme),
tex_deps = docformat.tex_deps,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end

# very similar to export to html
function format_chunk(chunk::DocChunk, docformat::JMarkdown2tex)
function format_chunk(chunk::DocChunk, docformat::TexFormat)
out = IOBuffer()
io = IOBuffer()
for inline in chunk.content
Expand All @@ -182,7 +182,11 @@ function format_chunk(chunk::DocChunk, docformat::JMarkdown2tex)
end
clear_buffer_and_format!(io, out, WeaveMarkdown.latex)
out = take2string!(out)
return docformat.keep_unicode ? out : uc2tex(out)
return unicode2latex(docformat, out)
end

function format_output(result, docformat::TexFormat)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will produce "invalid" output texminted format, right ?
Hightlights.jl will include its own characters around some special characters e.g. _, so that it will result in weird output for minted output.
How about defining new format_output for TexMinted ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted. I hadn't figured this out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed it.
However it did make my code have more duplicate lines again.

return unicode2latex(docformat, result, true)
end

function format_output(result, docformat::JMarkdown2tex)
Expand All @@ -192,43 +196,48 @@ function format_output(result, docformat::JMarkdown2tex)
Highlights.Format.escape(io, MIME("text/latex"), x, charescape = true),
result,
)
docformat.keep_unicode || return uc2tex(result_escaped, true)
return result_escaped
return unicode2latex(docformat, result_escaped, true)
end

function format_code(code, docformat::TexFormat)
return unicode2latex(docformat, code, true)
end
function format_code(code, docformat::JMarkdown2tex)
ret = highlight_code(MIME("text/latex"), code, docformat.highlight_theme)
docformat.keep_unicode || return uc2tex(ret)
return ret
unicode2latex(docformat, ret, false)
end

# Convert unicode to tex, escape listings if needed
function uc2tex(s, escape = false)
for key in keys(latex_symbols)
if escape
s = replace(s, latex_symbols[key] => "(*@\\ensuremath{$(texify(key))}@*)")
# from julia symbols (e.g. "\bfhoge") to valid latex
const UNICODE2LATEX = let
function texify(s)
return if occursin(r"^\\bf[A-Z]$", s)
replace(s, "\\bf" => "\\bm{\\mathrm{") * "}}"
elseif startswith(s, "\\bfrak")
replace(s, "\\bfrak" => "\\bm{\\mathfrak{") * "}}"
elseif startswith(s, "\\bf")
replace(s, "\\bf" => "\\bm{\\") * "}"
elseif startswith(s, "\\frak")
replace(s, "\\frak" => "\\mathfrak{") * "}"
else
s = replace(s, latex_symbols[key] => "\\ensuremath{$(texify(key))}")
s
end
end
Dict(unicode => texify(sym) for (sym, unicode) in REPL.REPLCompletions.latex_symbols)
end

function unicode2latex(docformat::TexFormat, s, escape = false)
# Check whether to convert at all and return input if not
docformat.keep_unicode && return s
for (unicode, latex) in UNICODE2LATEX
body = "\\ensuremath{$(latex)}"
target = escape ? string(docformat.escape_starter, body, docformat.escape_closer) : body
s = replace(s, unicode => target)
end
return s
end

# should_render(chunk) ? highlight_term(MIME("text/latex"), , docformat.highlight_theme) : ""
format_termchunk(chunk, docformat::TexFormat) =
string(docformat.termstart, chunk.output, docformat.termend, "\n")

format_termchunk(chunk, docformat::JMarkdown2tex) =
should_render(chunk) ? highlight_term(MIME("text/latex"), chunk.output, docformat.highlight_theme) : ""

# Make julia symbols (\bf* etc.) valid latex
function texify(s)
return if occursin(r"^\\bf[A-Z]$", s)
replace(s, "\\bf" => "\\bm{\\mathrm{") * "}}"
elseif startswith(s, "\\bfrak")
replace(s, "\\bfrak" => "\\bm{\\mathfrak{") * "}}"
elseif startswith(s, "\\bf")
replace(s, "\\bf" => "\\bm{\\") * "}"
elseif startswith(s, "\\frak")
replace(s, "\\frak" => "\\mathfrak{") * "}"
else
s
end
end
2 changes: 1 addition & 1 deletion src/run.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function run_doc(
# cache :all, :user, :off, :refresh

doc.doctype = isnothing(doctype) ? (doctype = detect_doctype(doc.source)) : doctype
doc.format = deepcopy(FORMATS[doctype])
doc.format = deepcopy(get_format(doctype))

cwd = doc.cwd = get_cwd(doc, out_path)
isdir(cwd) || mkdir(cwd)
Expand Down
4 changes: 3 additions & 1 deletion templates/md2pdf.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
\usepackage{graphicx}
\usepackage{microtype}
\usepackage{hyperref}

{{#:tex_deps}}
{{{ :tex_deps }}}
{{/:tex_deps}}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1.2ex}

Expand Down
22 changes: 1 addition & 21 deletions test/figureformatter_test.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
test_formatfigures(chunk, format) = Weave.formatfigures(chunk, Weave.FORMATS[format])
test_formatfigures(chunk, format) = Weave.formatfigures(chunk, get_format(format))


# Make a dummy codehunk with figure
Expand All @@ -9,7 +9,6 @@ chunk.figures = ["figs/figures_plot1.png"]


@test test_formatfigures(chunk, "md2tex") == "\\includegraphics{figs/figures_plot1.png}\n"
@test test_formatfigures(chunk, "tex") == "\\includegraphics{figs/figures_plot1.png}\n"
@test test_formatfigures(chunk, "texminted") == "\\includegraphics{figs/figures_plot1.png}\n"
@test test_formatfigures(chunk, "pandoc") == "![](figs/figures_plot1.png)\\ \n\n"
@test test_formatfigures(chunk, "github") == "![](figs/figures_plot1.png)\n"
Expand All @@ -24,7 +23,6 @@ chunk.options[:out_width] = "100%"


chunk.options[:fig_cap] = "Nice plot"
@test test_formatfigures(chunk, "tex") == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\end{figure}\n"
@test test_formatfigures(chunk, "pandoc") == "![Nice plot](figs/figures_plot1.png){width=100%}\n"
@test test_formatfigures(chunk, "md2tex") == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\end{figure}\n"
@test test_formatfigures(chunk, "md2html") == "<figure>\n<img src=\"figs/figures_plot1.png\" width=\"100%\" />\n<figcaption>Nice plot</figcaption>\n</figure>\n"
Expand All @@ -35,21 +33,3 @@ chunk.options[:fig_cap] = "Nice plot"

chunk.options[:label] = "somefig"
@test test_formatfigures(chunk, "pandoc") == "![Nice plot](figs/figures_plot1.png){width=100% #fig:somefig}\n"
@test test_formatfigures(chunk, "tex") == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\label{fig:somefig}\n\\end{figure}\n"


chunk.options[:label] = nothing
chunk.options[:fig_cap] = nothing
chunk.options[:fig_env] = "center"
chunk.options[:fig_pos] = ""
@test test_formatfigures(chunk, "tex") == "\\begin{center}\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\end{center}\n"


chunk.options[:out_width] = "50%"
chunk.options[:out_height] = "75 %"
@test test_formatfigures(chunk, "tex") == "\\begin{center}\n\\includegraphics[width=0.5\\linewidth,height=0.75\\paperheight]{figs/figures_plot1.png}\n\\end{center}\n"


chunk.options[:out_width] = "A%"
chunk.options[:out_height] = "0.5\\textwidth"
@test test_formatfigures(chunk, "tex") == "\\begin{center}\n\\includegraphics[width=A%,height=0.5\\textwidth]{figs/figures_plot1.png}\n\\end{center}\n"
36 changes: 0 additions & 36 deletions test/formatter_test.jl

This file was deleted.

30 changes: 30 additions & 0 deletions test/render/texformats.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@testset "unicode to latex conversion" begin
unicode2latex(args...) = Weave.unicode2latex(get_format("md2tex"), args...)

# unit test
let
s = unicode2latex("α = 10")
@test !occursin("α", s)
@test occursin("alpha", s)
end

# end2end
let
str = """
```julia
α = 10
```
"""
doc = mock_run(str; doctype = "md2tex")
Weave.set_rendering_options!(doc.format)
rendered = Weave.render_doc(doc)
@test occursin("alpha", rendered)
@test !occursin("α", rendered)

doc = mock_run(str; doctype = "md2tex")
Weave.set_rendering_options!(doc.format; keep_unicode = true)
rendered = Weave.render_doc(doc)
@test !occursin("alpha", rendered)
@test occursin("α", rendered)
end
end # @testset "rendering tex formats"
7 changes: 5 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# %%
using Weave, Test
using Weave: WeaveDoc, run_doc
using Weave: WeaveDoc, run_doc, get_format


function mock_doc(str, informat = "markdown")
Expand Down Expand Up @@ -49,6 +49,10 @@ end
include("run/test_error.jl")
end

@testset "render" begin
include("render/texformats.jl")
end

@testset "conversions" begin
include("test_converter.jl")
end
Expand All @@ -58,7 +62,6 @@ end
end

@testset "legacy" begin
include("formatter_test.jl")
include("markdown_test.jl")
include("figureformatter_test.jl")
include("cache_test.jl")
Expand Down