latex.c (22238B)
/* $Id$ */ /* * Copyright (c) 2020--2021 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #if HAVE_SYS_QUEUE # include <sys/queue.h> #endif #include <assert.h> #include <ctype.h> #include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "lowdown.h" #include "extern.h" struct latex { unsigned int oflags; /* same as in lowdown_opts */ struct hentryq headers_used; /* headers we've seen */ ssize_t headers_offs; /* header offset */ size_t footsz; /* current footnote */ }; /* * Return zero on failure, non-zero on success. */ static int rndr_escape_text(struct lowdown_buf *ob, const char *data, size_t sz) { size_t i; for (i = 0; i < sz; i++) switch (data[i]) { case '&': case '%': case '$': case '#': case '_': case '{': case '}': if (!hbuf_putc(ob, '\\')) return 0; if (!hbuf_putc(ob, data[i])) return 0; break; case '~': if (!HBUF_PUTSL(ob, "\\textasciitilde{}")) return 0; break; case '^': if (!HBUF_PUTSL(ob, "\\textasciicircum{}")) return 0; break; case '\\': if (!HBUF_PUTSL(ob, "\\textbackslash{}")) return 0; break; default: if (!hbuf_putc(ob, data[i])) return 0; break; } return 1; } /* * Return zero on failure, non-zero on success. */ static int rndr_escape(struct lowdown_buf *ob, const struct lowdown_buf *dat) { return rndr_escape_text(ob, dat->data, dat->size); } static int rndr_autolink(struct lowdown_buf *ob, const struct rndr_autolink *param) { if (param->link.size == 0) return 1; if (!HBUF_PUTSL(ob, "\\url{")) return 0; if (param->type == HALINK_EMAIL && !HBUF_PUTSL(ob, "mailto:")) return 0; if (!rndr_escape(ob, ¶m->link)) return 0; return HBUF_PUTSL(ob, "}"); } static int rndr_entity(struct lowdown_buf *ob, const struct rndr_entity *param) { const char *tex; unsigned char texflags; tex = entity_find_tex(¶m->text, &texflags); if (tex == NULL) return rndr_escape(ob, ¶m->text); if ((texflags & TEX_ENT_MATH) && (texflags & TEX_ENT_ASCII)) return hbuf_printf(ob, "$\\mathrm{%s}$", tex); if (texflags & TEX_ENT_ASCII) return hbuf_puts(ob, tex); if (texflags & TEX_ENT_MATH) return hbuf_printf(ob, "$\\mathrm{\\%s}$", tex); return hbuf_printf(ob, "\\%s", tex); } static int rndr_blockcode(struct lowdown_buf *ob, const struct rndr_blockcode *param) { if (ob->size && !HBUF_PUTSL(ob, "\n")) return 0; #if 0 HBUF_PUTSL(ob, "\\begin{lstlisting}"); if (lang->size) { HBUF_PUTSL(ob, "[language="); rndr_escape(ob, lang); HBUF_PUTSL(ob, "]\n\n"); } else HBUF_PUTSL(ob, "\n"); #else HBUF_PUTSL(ob, "\\begin{verbatim}\n"); #endif if (!hbuf_putb(ob, ¶m->text)) return 0; #if 0 HBUF_PUTSL(ob, "\\end{lstlisting}\n"); #else return HBUF_PUTSL(ob, "\\end{verbatim}\n"); #endif } static int rndr_definition_title(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\item [")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "] "); } static int rndr_definition(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\begin{description}\n")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "\\end{description}\n"); } static int rndr_blockquote(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (ob->size && !HBUF_PUTSL(ob, "\n")) return 0; if (!HBUF_PUTSL(ob, "\\begin{quotation}\n")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "\\end{quotation}\n"); } static int rndr_codespan(struct lowdown_buf *ob, const struct rndr_codespan *param) { #if 0 HBUF_PUTSL(ob, "\\lstinline{"); hbuf_putb(ob, text); #else if (!HBUF_PUTSL(ob, "\\texttt{")) return 0; if (!rndr_escape(ob, ¶m->text)) return 0; #endif return HBUF_PUTSL(ob, "}"); } static int rndr_triple_emphasis(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\textbf{\\emph{")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "}}"); } static int rndr_double_emphasis(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\textbf{")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "}"); } static int rndr_emphasis(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\emph{")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "}"); } static int rndr_highlight(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\underline{")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "}"); } static int rndr_linebreak(struct lowdown_buf *ob) { return HBUF_PUTSL(ob, "\\linebreak\n"); } static int rndr_header(struct lowdown_buf *ob, const struct lowdown_buf *content, const struct lowdown_node *n, struct latex *st) { const char *type; ssize_t level; struct lowdown_buf *buf = NULL; const struct lowdown_buf *id; int rc = 0; if (n->rndr_header.attr_id.size) { if ((buf = hbuf_new(32)) == NULL) goto out; if (!rndr_escape(buf, &n->rndr_header.attr_id)) goto out; id = buf; } else { id = hbuf_id(NULL, n, &st->headers_used); if (id == NULL) goto out; } if (ob->size && !HBUF_PUTSL(ob, "\n")) goto out; if (!HBUF_PUTSL(ob, "\\hypertarget{")) goto out; if (!hbuf_putb(ob, id)) goto out; if (!HBUF_PUTSL(ob, "}{%\n")) goto out; level = (ssize_t)n->rndr_header.level + st->headers_offs; if (level < 1) level = 1; switch (level) { case 1: type = "\\section"; break; case 2: type = "\\subsection"; break; case 3: type = "\\subsubsection"; break; case 4: type = "\\paragraph"; break; default: type = "\\subparagraph"; break; } if (!hbuf_puts(ob, type)) goto out; if (!(st->oflags & LOWDOWN_LATEX_NUMBERED) && !HBUF_PUTSL(ob, "*")) goto out; if (!HBUF_PUTSL(ob, "{")) goto out; if (!hbuf_putb(ob, content)) goto out; if (!HBUF_PUTSL(ob, "}\\label{")) goto out; if (!hbuf_putb(ob, id)) goto out; if (!HBUF_PUTSL(ob, "}}\n")) goto out; rc = 1; out: hbuf_free(buf); return rc; } static int rndr_link(struct lowdown_buf *ob, const struct lowdown_buf *content, const struct rndr_link *param) { int loc; loc = param->link.size > 0 && param->link.data[0] == '#'; if (param->attr_id.size > 0) { if (!HBUF_PUTSL(ob, "\\hypertarget{")) return 0; if (!hbuf_putb(ob, ¶m->attr_id)) return 0; if (!HBUF_PUTSL(ob, "}{%\n")) return 0; } if (loc && !HBUF_PUTSL(ob, "\\hyperlink{")) return 0; else if (!loc && !HBUF_PUTSL(ob, "\\href{")) return 0; if (loc && !rndr_escape_text (ob, ¶m->link.data[1], param->link.size - 1)) return 0; else if (!loc && !rndr_escape(ob, ¶m->link)) return 0; if (!HBUF_PUTSL(ob, "}{")) return 0; if (!hbuf_putb(ob, content)) return 0; if (param->attr_id.size > 0 && !HBUF_PUTSL(ob, "}")) return 0; return HBUF_PUTSL(ob, "}"); } static int rndr_list(struct lowdown_buf *ob, const struct lowdown_buf *content, const struct rndr_list *param) { const char *type; if (ob->size && !hbuf_putc(ob, '\n')) return 0; /* TODO: HLIST_FL_ORDERED and param->start */ type = (param->flags & HLIST_FL_ORDERED) ? "enumerate" : "itemize"; if (!hbuf_printf(ob, "\\begin{%s}\n", type)) return 0; if (!(param->flags & HLIST_FL_BLOCK) && !HBUF_PUTSL(ob, "\\itemsep -0.2em\n")) return 0; if (!hbuf_putb(ob, content)) return 0; return hbuf_printf(ob, "\\end{%s}\n", type); } static int rndr_listitem(struct lowdown_buf *ob, const struct lowdown_buf *content, const struct rndr_listitem *param) { size_t size; /* Only emit <li> if we're not a <dl> list. */ if (!(param->flags & HLIST_FL_DEF)) { if (!HBUF_PUTSL(ob, "\\item")) return 0; if ((param->flags & HLIST_FL_CHECKED) && !HBUF_PUTSL(ob, "[$\\rlap{$\\checkmark$}\\square$]")) return 0; if ((param->flags & HLIST_FL_UNCHECKED) && !HBUF_PUTSL(ob, "[$\\square$]")) return 0; if (!HBUF_PUTSL(ob, " ")) return 0; } /* Cut off any trailing space. */ if ((size = content->size) > 0) { while (size && content->data[size - 1] == '\n') size--; if (!hbuf_put(ob, content->data, size)) return 0; } return HBUF_PUTSL(ob, "\n"); } static int rndr_paragraph(struct lowdown_buf *ob, const struct lowdown_buf *content) { size_t i = 0; if (content->size == 0) return 1; while (i < content->size && isspace((unsigned char)content->data[i])) i++; if (i == content->size) return 1; if (!HBUF_PUTSL(ob, "\n")) return 0; if (!hbuf_put(ob, content->data + i, content->size - i)) return 0; return HBUF_PUTSL(ob, "\n"); } static int rndr_raw_block(struct lowdown_buf *ob, const struct rndr_blockhtml *param, const struct latex *st) { size_t org = 0, sz = param->text.size; if (st->oflags & LOWDOWN_LATEX_SKIP_HTML) return 1; while (sz > 0 && param->text.data[sz - 1] == '\n') sz--; while (org < sz && param->text.data[org] == '\n') org++; if (org >= sz) return 1; if (ob->size && !HBUF_PUTSL(ob, "\n")) return 0; if (!HBUF_PUTSL(ob, "\\begin{verbatim}\n")) return 0; if (!hbuf_put(ob, param->text.data + org, sz - org)) return 0; return HBUF_PUTSL(ob, "\\end{verbatim}\n"); } static int rndr_hrule(struct lowdown_buf *ob) { if (ob->size && !hbuf_putc(ob, '\n')) return 0; return HBUF_PUTSL(ob, "\\noindent\\hrulefill\n"); } static int rndr_image(struct lowdown_buf *ob, const struct rndr_image *param) { const char *cp; char dimbuf[32]; unsigned int x, y; float pct; int rc = 0; /* * Scan in our dimensions, if applicable. * It's unreasonable for them to be over 32 characters, so use * that as a cap to the size. */ if (param->dims.size && param->dims.size < sizeof(dimbuf) - 1) { memset(dimbuf, 0, sizeof(dimbuf)); memcpy(dimbuf, param->dims.data, param->dims.size); rc = sscanf(dimbuf, "%ux%u", &x, &y); } /* Extended attributes override dimensions. */ if (!HBUF_PUTSL(ob, "\\includegraphics[")) return 0; if (param->attr_width.size || param->attr_height.size) { if (param->attr_width.size && param->attr_width.size < sizeof(dimbuf) - 1) { memset(dimbuf, 0, sizeof(dimbuf)); memcpy(dimbuf, param->attr_width.data, param->attr_width.size); /* Try to parse as a percentage. */ if (sscanf(dimbuf, "%e%%", &pct) == 1) { if (!hbuf_printf(ob, "width=%.2f" "\\linewidth", pct / 100.0)) return 0; } else { if (!hbuf_printf(ob, "width=%.*s", (int)param->attr_width.size, param->attr_width.data)) return 0; } } if (param->attr_height.size && param->attr_height.size < sizeof(dimbuf) - 1) { if (param->attr_width.size && !HBUF_PUTSL(ob, ", ")) return 0; if (!hbuf_printf(ob, "height=%.*s", (int)param->attr_height.size, param->attr_height.data)) return 0; } } else if (rc > 0) { if (!hbuf_printf(ob, "width=%upx", x)) return 0; if (rc > 1 && !hbuf_printf(ob, ", height=%upx", y)) return 0; } if (!HBUF_PUTSL(ob, "]{")) return 0; cp = memrchr(param->link.data, '.', param->link.size); if (cp != NULL) { if (!HBUF_PUTSL(ob, "{")) return 0; if (!rndr_escape_text (ob, param->link.data, cp - param->link.data)) return 0; if (!HBUF_PUTSL(ob, "}")) return 0; if (!rndr_escape_text(ob, cp, param->link.size - (cp - param->link.data))) return 0; } else { if (!rndr_escape(ob, ¶m->link)) return 0; } return HBUF_PUTSL(ob, "}"); } static int rndr_raw_html(struct lowdown_buf *ob, const struct rndr_raw_html *param, const struct latex *st) { if (st->oflags & LOWDOWN_LATEX_SKIP_HTML) return 1; return rndr_escape(ob, ¶m->text); } static int rndr_table(struct lowdown_buf *ob, const struct lowdown_buf *content) { /* Open the table in rndr_table_header. */ if (ob->size && !hbuf_putc(ob, '\n')) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "\\end{longtable}\n"); } static int rndr_table_header(struct lowdown_buf *ob, const struct lowdown_buf *content, const struct rndr_table_header *param) { size_t i; char align; int fl; if (!HBUF_PUTSL(ob, "\\begin{longtable}[]{")) return 0; for (i = 0; i < param->columns; i++) { fl = param->flags[i] & HTBL_FL_ALIGNMASK; if (fl == HTBL_FL_ALIGN_CENTER) align = 'c'; else if (fl == HTBL_FL_ALIGN_RIGHT) align = 'r'; else align = 'l'; if (!hbuf_putc(ob, align)) return 0; } if (!HBUF_PUTSL(ob, "}\n")) return 0; return hbuf_putb(ob, content); } static int rndr_tablecell(struct lowdown_buf *ob, const struct lowdown_buf *content, const struct rndr_table_cell *param) { if (!hbuf_putb(ob, content)) return 0; return (param->col < param->columns - 1) ? HBUF_PUTSL(ob, " & ") : HBUF_PUTSL(ob, " \\\\\n"); } static int rndr_superscript(struct lowdown_buf *ob, const struct lowdown_buf *content) { if (!HBUF_PUTSL(ob, "\\textsuperscript{")) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "}"); } static int rndr_normal_text(struct lowdown_buf *ob, const struct rndr_normal_text *param) { return rndr_escape(ob, ¶m->text); } static int rndr_footnote_ref(struct lowdown_buf *ob, const struct lowdown_buf *content, struct latex *st) { if (!hbuf_printf(ob, "\\footnote[%zu]{", ++st->footsz)) return 0; if (!hbuf_putb(ob, content)) return 0; return HBUF_PUTSL(ob, "}\n"); } static int rndr_math(struct lowdown_buf *ob, const struct rndr_math *param) { if (param->blockmode && !HBUF_PUTSL(ob, "\\[")) return 0; else if (!param->blockmode && !HBUF_PUTSL(ob, "\\(")) return 0; if (!hbuf_putb(ob, ¶m->text)) return 0; if (param->blockmode && !HBUF_PUTSL(ob, "\\]")) return 0; else if (!param->blockmode && !HBUF_PUTSL(ob, "\\)")) return 0; return 1; } static int rndr_doc_footer(struct lowdown_buf *ob, const struct latex *st) { if (st->oflags & LOWDOWN_STANDALONE) return HBUF_PUTSL(ob, "\\end{document}\n"); return 1; } static int rndr_doc_header(struct lowdown_buf *ob, const struct lowdown_metaq *mq, const struct latex *st) { const struct lowdown_meta *m; const char *author = NULL, *title = NULL, *affil = NULL, *date = NULL, *rcsauthor = NULL, *rcsdate = NULL; if (!(st->oflags & LOWDOWN_STANDALONE)) return 1; if (!HBUF_PUTSL(ob, "% Options for packages loaded elsewhere\n" "\\PassOptionsToPackage{unicode}{hyperref}\n" "\\PassOptionsToPackage{hyphens}{url}\n" "%\n" "\\documentclass[11pt,a4paper]{article}\n" "\\usepackage{amsmath,amssymb}\n" "\\usepackage{lmodern}\n" "\\usepackage{iftex}\n" "\\ifPDFTeX\n" " \\usepackage[T1]{fontenc}\n" " \\usepackage[utf8]{inputenc}\n" " \\usepackage{textcomp} % provide euro and other symbols\n" "\\else % if luatex or xetex\n" " \\usepackage{unicode-math}\n" " \\defaultfontfeatures{Scale=MatchLowercase}\n" " \\defaultfontfeatures[\\rmfamily]{Ligatures=TeX,Scale=1}\n" "\\fi\n" "\\usepackage{xcolor}\n" "\\usepackage{graphicx}\n" "\\usepackage{longtable}\n" "\\usepackage{hyperref}\n" "\\begin{document}\n")) return 0; TAILQ_FOREACH(m, mq, entries) if (strcasecmp(m->key, "author") == 0) author = m->value; else if (strcasecmp(m->key, "affiliation") == 0) affil = m->value; else if (strcasecmp(m->key, "date") == 0) date = m->value; else if (strcasecmp(m->key, "rcsauthor") == 0) rcsauthor = rcsauthor2str(m->value); else if (strcasecmp(m->key, "rcsdate") == 0) rcsdate = rcsdate2str(m->value); else if (strcasecmp(m->key, "title") == 0) title = m->value; /* Overrides. */ if (title == NULL) title = "Untitled article"; if (rcsauthor != NULL) author = rcsauthor; if (rcsdate != NULL) date = rcsdate; if (!hbuf_printf(ob, "\\title{%s}\n", title)) return 0; if (author != NULL) { if (!hbuf_printf(ob, "\\author{%s", author)) return 0; if (affil != NULL && !hbuf_printf(ob, " \\\\ %s", affil)) return 0; if (!HBUF_PUTSL(ob, "}\n")) return 0; } if (date != NULL && !hbuf_printf(ob, "\\date{%s}\n", date)) return 0; return HBUF_PUTSL(ob, "\\maketitle\n"); } static int rndr_meta(struct lowdown_buf *ob, const struct lowdown_buf *content, struct lowdown_metaq *mq, const struct lowdown_node *n, struct latex *st) { struct lowdown_meta *m; ssize_t val; const char *ep; if ((m = calloc(1, sizeof(struct lowdown_meta))) == NULL) return 0; TAILQ_INSERT_TAIL(mq, m, entries); m->key = strndup(n->rndr_meta.key.data, n->rndr_meta.key.size); if (m->key == NULL) return 0; m->value = strndup(content->data, content->size); if (m->value == NULL) return 0; if (strcmp(m->key, "shiftheadinglevelby") == 0) { val = (ssize_t)strtonum (m->value, -100, 100, &ep); if (ep == NULL) st->headers_offs = val + 1; } else if (strcmp(m->key, "baseheaderlevel") == 0) { val = (ssize_t)strtonum (m->value, 1, 100, &ep); if (ep == NULL) st->headers_offs = val; } return 1; } static int rndr(struct lowdown_buf *ob, struct lowdown_metaq *mq, void *arg, const struct lowdown_node *n) { struct lowdown_buf *tmp; struct latex *st = arg; const struct lowdown_node *child; int ret = 0; if ((tmp = hbuf_new(64)) == NULL) return 0; TAILQ_FOREACH(child, &n->children, entries) if (!rndr(tmp, mq, st, child)) goto out; /* * These elements can be put in either a block or an inline * context, so we're safe to just use them and forget. */ if (n->chng == LOWDOWN_CHNG_INSERT && !HBUF_PUTSL(ob, "{\\color{blue} ")) goto out; if (n->chng == LOWDOWN_CHNG_DELETE && !HBUF_PUTSL(ob, "{\\color{red} ")) goto out; switch (n->type) { case LOWDOWN_BLOCKCODE: if (!rndr_blockcode(ob, &n->rndr_blockcode)) return 0; break; case LOWDOWN_BLOCKQUOTE: if (!rndr_blockquote(ob, tmp)) return 0; break; case LOWDOWN_DEFINITION: if (!rndr_definition(ob, tmp)) return 0; break; case LOWDOWN_DEFINITION_TITLE: if (!rndr_definition_title(ob, tmp)) return 0; break; case LOWDOWN_DOC_HEADER: if (!rndr_doc_header(ob, mq, st)) return 0; break; case LOWDOWN_META: if (n->chng != LOWDOWN_CHNG_DELETE && !rndr_meta(ob, tmp, mq, n, st)) return 0; break; case LOWDOWN_HEADER: if (!rndr_header(ob, tmp, n, st)) return 0; break; case LOWDOWN_HRULE: if (!rndr_hrule(ob)) return 0; break; case LOWDOWN_LIST: if (!rndr_list(ob, tmp, &n->rndr_list)) return 0; break; case LOWDOWN_LISTITEM: if (!rndr_listitem(ob, tmp, &n->rndr_listitem)) return 0; break; case LOWDOWN_PARAGRAPH: if (!rndr_paragraph(ob, tmp)) return 0; break; case LOWDOWN_TABLE_BLOCK: if (!rndr_table(ob, tmp)) return 0; break; case LOWDOWN_TABLE_HEADER: if (!rndr_table_header(ob, tmp, &n->rndr_table_header)) return 0; break; case LOWDOWN_TABLE_CELL: if (!rndr_tablecell(ob, tmp, &n->rndr_table_cell)) return 0; break; case LOWDOWN_BLOCKHTML: if (!rndr_raw_block(ob, &n->rndr_blockhtml, st)) return 0; break; case LOWDOWN_LINK_AUTO: if (!rndr_autolink(ob, &n->rndr_autolink)) return 0; break; case LOWDOWN_CODESPAN: if (!rndr_codespan(ob, &n->rndr_codespan)) return 0; break; case LOWDOWN_DOUBLE_EMPHASIS: if (!rndr_double_emphasis(ob, tmp)) return 0; break; case LOWDOWN_EMPHASIS: if (!rndr_emphasis(ob, tmp)) return 0; break; case LOWDOWN_HIGHLIGHT: if (!rndr_highlight(ob, tmp)) return 0; break; case LOWDOWN_IMAGE: if (!rndr_image(ob, &n->rndr_image)) return 0; break; case LOWDOWN_LINEBREAK: if (!rndr_linebreak(ob)) return 0; break; case LOWDOWN_LINK: if (!rndr_link(ob, tmp, &n->rndr_link)) return 0; break; case LOWDOWN_TRIPLE_EMPHASIS: if (!rndr_triple_emphasis(ob, tmp)) return 0; break; case LOWDOWN_SUPERSCRIPT: if (!rndr_superscript(ob, tmp)) return 0; break; case LOWDOWN_FOOTNOTE: if (!rndr_footnote_ref(ob, tmp, st)) return 0; break; case LOWDOWN_MATH_BLOCK: if (!rndr_math(ob, &n->rndr_math)) return 0; break; case LOWDOWN_RAW_HTML: if (!rndr_raw_html(ob, &n->rndr_raw_html, st)) return 0; break; case LOWDOWN_NORMAL_TEXT: if (!rndr_normal_text(ob, &n->rndr_normal_text)) return 0; break; case LOWDOWN_ENTITY: if (!rndr_entity(ob, &n->rndr_entity)) return 0; break; case LOWDOWN_ROOT: if (!hbuf_putb(ob, tmp)) return 0; if (!rndr_doc_footer(ob, st)) return 0; break; default: if (!hbuf_putb(ob, tmp)) return 0; break; } if ((n->chng == LOWDOWN_CHNG_INSERT || n->chng == LOWDOWN_CHNG_DELETE) && !HBUF_PUTSL(ob, "}")) goto out; ret = 1; out: hbuf_free(tmp); return ret; } int lowdown_latex_rndr(struct lowdown_buf *ob, void *arg, const struct lowdown_node *n) { struct latex *st = arg; struct lowdown_metaq metaq; int rc; TAILQ_INIT(&st->headers_used); TAILQ_INIT(&metaq); st->headers_offs = 1; st->footsz = 0; rc = rndr(ob, &metaq, st, n); lowdown_metaq_free(&metaq); hentryq_clear(&st->headers_used); return rc; } void * lowdown_latex_new(const struct lowdown_opts *opts) { struct latex *p; if ((p = calloc(1, sizeof(struct latex))) == NULL) return NULL; p->oflags = opts == NULL ? 0 : opts->oflags; return p; } void lowdown_latex_free(void *arg) { free(arg); }