1377 Commits
Author SHA1 Message Date
John MacFarlaneandClaude Opus 5.5 1d49297127 Markdown, RST, Haddock writers: don't drop notes nested in notes.
Rendering a note can add further notes to the state. Previously
these were lost. Now we keep rendering notes until no new ones
appear, as the Org writer already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:30:28 -07:00
John MacFarlane 044472d4d9 Use yamlet for YAML parsing.
Pandoc parsed YAML with the yaml package, which depends on the C
library libyaml. It now uses yamlet, a pure Haskell YAML 1.2 parser.
Values still go through the existing aeson instances.

Behavior changes:

- YAML errors show the file, the line and the column, with the
  offending line. In metadata blocks the line is the line in the
  document.
- `yes`, `no`, `on`, `off`, `y` and `n` are strings,
  not booleans, as in YAML 1.2. This restores the behavior before
  the switch from HsYAML.
- Duplicate keys in a mapping are an error. They were a warning.
- .5, .inf and .nan are numbers.

Text.Pandoc.Translations: readTranslations takes the path of the
file, for error messages [API change].
2026-10-13 08:59:17 -07:00
John MacFarlane eafe417098 LaTeX writer: use braces around term containing =.
This is needed for the new LaTeX list code, which is triggered
by using `pdfstandard`.

See https://github.com/latex3/tagging-project/issues/1039.

Closes #11954.
2026-10-12 19:00:52 -07:00
Aslak Hellesøy 763c555a05 Markdown writer: write wide tables as grid tables.
A table was written as a grid table only if it had spans or a footer,
or if `writerColumns >= 8 * numcols`.  A wider table with block content
in a cell therefore fell back to raw HTML, even with grid_tables
enabled; with the default 72 columns that is any table of ten or more
columns.

The width check was added in ea619bfcb (2017, #2834) to fix grid
tables nested in grid tables.  The old grid writer gave each column
`floor(columns * share) - 3` characters and rendered cell contents with
`writerColumns` reduced to the column width, so nested tables got
narrower at each level.  Below about 8 characters per column those
widths reached zero or went negative and the layout broke.

The `gridTable` rewrite in 01257ef6e (pandoc 3.7) removed that failure
mode.  A column is never made narrower than its content (or, when
wrapping, than its longest unbreakable word), and cells are rendered
with unchanged `opts`, so `writerColumns` no longer shrinks for nested
content.  The RST and Muse writers already call the same `gridTable`
with no width check, and the Markdown writer already writes grid tables
wider than `columns` when a table has spans or a footer.

The check is therefore removed.  This follows the preference stated in
7c2dd0359 for markdown tables over raw HTML.  No existing test output
changes.

Closes #11945.
2026-10-10 10:46:25 -07:00
John MacFarlane 649aae2f4c SelfContained: fix regression in escaping of data URIs.
A change in 3.12 broke escaping of data URIs, so that
`--embed-resources` no longer worked properly with JavaScript
includes, breaking slide show formats.

Closes #11942.
2026-10-07 09:03:28 -07:00
John MacFarlane de45c51eb9 Fix test for #11939. 2026-10-07 08:35:33 -07:00
John MacFarlane eaf3404795 Markdown writer: Don't use a multiline table for a header-only table.
(It won't read back as a table.)

Closes #11939.
2026-10-06 23:30:10 -07:00
Erik Demaine c248f880e5 Math: respect TeX groups and comments. (#11887)
Prevent math delimiters inside TeX brace groups and percent comments
from prematurely closing an equation:

* Generalize the existing inline `\text` group handling to arbitrary
  brace groups, and support groups in display math too.
* Recognize unescaped `%` as starting a comment through the next newline.
  Ignore any braces or math delimiters within it.

Developed with OpenAI Codex.

PR revised by John MacFarlane.
2026-10-05 15:34:23 -07:00
John MacFarlane f98e0d21d7 Test the fodt --link-images reference in the Haskell test suite.
The command test extracted the xlink:href by piping pandoc's output
into grep, and the command tests run under cmd.exe on Windows, where
there is no grep and the single quotes around the pattern are not
stripped.  Nothing matched, and the test failed.

Tests.Writers.ODT makes the same assertion by parsing the output and
comparing the xlink:href of every draw:image.  Reading the output back
in would not do instead: fixRelativeLink strips a leading "../", so a
flat file that wrongly had one would still read back as "lalune.jpg".
2026-10-04 01:44:27 +00:00
John MacFarlane 7b400e6229 Don't extract particles from given and family names...
in structured CSL YAML references.  Thus,

    family: de Gaulle

should not create a non-dropping particle "de"; "de Gaulle"
should be considered the integral family name.

This is a behavior change that could affect some bibliography
processing.

The commit 4be49d0ce9 is
also reverted (adding a section explaining the former behavior
to the manual).

Closes #11911.
2026-10-03 18:03:12 -07:00
John MacFarlane 58ec1c976d Add tests for the fodt reader and writer.
The fixtures in test/odt/fodt are LibreOffice's own conversions of the
zipped fixtures in test/odt/odt, and they are compared against the very
same expectations, so that flat and zipped input are asserted to
converge on identical documents.

There are only four of them, because a flat file shares nearly all of
the reader with a zipped one: once readFODT has handed the root element
over, the converter is the very same code.  Two of the four are there
for the two paths that a flat file alone reaches: image, whose data has
to be decoded from office:binary-data and typed from draw:mime-type
rather than found in the archive, and formula, whose MathML is read
from the draw:object itself rather than from a sub-document.  The other
two guard against whitespace, which is peculiar to the format without
being a separate branch of the reader, since LibreOffice pretty-prints
a flat file and that indentation is in the XML the reader sees:
preformattedText is a CodeBlock, whose whitespace is significant and so
is compared exactly, and textMixedStyles is a run of nested emphasis,
where stray indentation would show up between the inlines.

`test/command/fodt-roundtrip.md` round-trips markdown through the flat
format, which also covers the writer, whose output has a timestamp in
it and so cannot be compared byte for byte.

Together with previous commits, closes #4010.
2026-10-03 14:47:02 -07:00
John MacFarlane 8f949dbb09 Revert "Typst writer: omit blank line at end of block."
This reverts commit 6accb0b9f7.
2026-10-02 21:43:46 -07:00
John MacFarlane 8d2e8435a5 Fix escaping bug in commonmark writer.
A non-alphanumeric after an escaped backslash would be omitted.

Closes #11927.
2026-10-02 10:10:36 -07:00
John MacFarlane 2d244c7180 LaTeX writer: allow alt text in figure to wrap.
Closes #11924.
2026-10-02 10:00:31 -07:00
John MacFarlaneandClaude 4580225a02 OpenDocument writer: sort automatic text styles numerically.
The text styles were kept in a map keyed on the set of style
attributes, with the generated style name as part of the value, and
that name was used to order the style definitions in
`office:automatic-styles`.  Sorting names as text puts `T10` between
`T1` and `T2`, so a document with ten or more distinct text styles
emitted them in a confusing order.

Store the style's number rather than its name, which makes the
existing sort numeric, and derive the name from it.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 05:27:32 +00:00
John MacFarlaneandClaude 008afcaba1 OpenDocument writer: reuse identical automatic paragraph styles.
Every blockquote, every explicitly aligned table cell, and every
direction-adjusted style got a fresh `Pn` automatic style, even when
an identical one already existed. A document with many of them paid
for a style definition each time, and the style list that gets walked
when emitting `office:automatic-styles` grew with it.

Key the styles on their attributes and paragraph properties and reuse
a match. The direction cache added for RTL support is subsumed by
this one and goes away.

The test output changes only by dropping duplicate definitions:
`writer.opendocument` had three identical `Quotations` styles and
6792.md two identical centred table-cell styles.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 05:18:49 +00:00
John MacFarlaneandClaude 8f46b9a6c9 OpenDocument writer: render the abstract as blocks.
The abstract was wrapped in a Div with `custom-style` set to
`Abstract`, but then handed to `metaToContext`, which renders metadata
fields with the inline writer. That dropped the Div, and with it the
style, and also the `text:p` wrapper, leaving bare character data as a
direct child of `office:text`, where it is not valid. A
multi-paragraph abstract was collapsed into one paragraph with a
`text:line-break`.

Render the abstract separately with the block writer and remove it
from the metadata that goes through the inline writer.

This fixes the regression from 013351f602, which assumed the abstract
was filtered out of `meta'`; it was in fact set there.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 04:36:19 +00:00
John MacFarlaneandClaude 8a58b75b62 OpenDocument writer: apply the requested style to Plain blocks.
`withParagraphStyle` only wrapped `Para` in a paragraph with the
requested style; a `Plain` fell through to `blockToOpenDocument` and
came out with the default style. So, for instance, the body of a
caption-less figure, which is a `Plain`, got `Text_20_body` rather
than `Figure`.

The figure writer worked around this for the captioned case by
rewriting `Plain` to `Para` itself; that is no longer needed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 04:34:53 +00:00
John MacFarlaneandClaude dcb1adde39 OpenDocument writer: use valid column style names past column 26.
Column style names came from `map chr [65..]`, so a table with more
than 26 columns named them `Table1.[`, `Table1.\`, and so on. A
`style:name` is an XML NCName, which those are not. Use spreadsheet
style names instead: A, B, ..., Z, AA, AB, ...

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 04:33:44 +00:00
John MacFarlaneandClaude 0cb6ff4fe6 OpenDocument writer: give nested notes distinct ids.
The note counter was read before the note's body was rendered and
only bumped afterwards, so a note nested inside another one was
numbered as if the outer note did not exist and the two shared a
`text:id` (ftn0, ftn0, ftn2). Bump the counter first.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-01 04:32:18 +00:00
John MacFarlane b7aa2db96a Docx reader: keep bookmark when stipping caption label.
This allows us to preserve internal links to figures.
Closes #11918.
2026-09-30 17:33:11 -07:00
Robert SzarkaandClaude 62bfc1feac Docx writer: put a heading's bookmark in its paragraph
The bookmark for a section's id surrounded the whole section, at body
level, so a screen reader met it at the section's last line rather than
at the heading. The docx reader looks for bookmarks only inside
paragraphs, so it never read that one back: the heading got an id from
its text, and links to it kept the bookmark's name, which no longer
matches since bookmark names start with an underscore. Now the bookmark
surrounds the heading's text inside the heading's paragraph, as Word
writes one, and links to headings survive a round trip.

Closes #11845. See also #8825.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-30 08:53:55 -07:00
John MacFarlane 31fed556f5 Markdown reader: put alert after tip, etc. in classes.
Some of the writers (gfm, docbook, asciidoc, rst) check for the
admonition name as the first class, so the change putting `alert`
first broke the output. (Regression from 3.12.)

Closes #11919.
2026-09-30 08:49:12 -07:00
John MacFarlane b4cf3db71f Docx reader/writer: use comment-id instead of id in AST for comments.
Using "id" causes problems in some cases, because of overlap with
the id part of pandoc attributes.

Note that the Docx writer will still interpret an "id" attribute for
legacy compatibility, so if you use markdown files that specify "id",
they should still work.
2026-09-28 09:54:47 -07:00
John MacFarlane ccf3d02429 Fix djot, RST reader bugs that led to an empty class.
RST and djot readers emitted an empty class in code blocks
with no specified language (`[ "" ]`).
2026-09-28 08:45:30 -07:00
John MacFarlane b0af6de2d7 Typst reader: collapse citations around soft break.
Closes #11897.
2026-09-26 22:07:44 -07:00
Samuel Huang 0bde362483 Typst reader: handle block content in inline element bodies (#11881)
Inline elements with a content body (emph, strong, underline, strike,
sub, super, smallcaps, highlight, link, lower, upper, ...) failed with
a parse error when the body contained a paragraph break or other block
content, since inline handlers parse their bodies with pInlines, which
cannot consume paragraph breaks. Example:
```
#emph[hi

there]
```
Generalize `fixNesting`, which already hoisted single block children out
of inline elements (#11017): the content of an inline element is now
split at paragraph breaks and at block children; inline runs are
wrapped back in the element, and block children are pulled out with
the styling applied to their own contents when they have a body. A
pandoc inline cannot span paragraphs, so this is the closest
structural rendering; the paragraph structure is preserved.

Remove the kludge we used for `#quote`, using `getInlineBody`; it
is no longer needed with this general fix.
2026-09-26 15:49:54 -07:00
Robert SzarkaandClaude c51b6a6a0a Docx writer: write link titles as ScreenTips.
A link with a non-empty title now gets a `w:tooltip` attribute on its
`w:hyperlink`, which Word shows as a ScreenTip and the docx reader
reads back as the title.

Closes #11869.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-24 23:43:02 -07:00
John MacFarlane c8c68ec0ae Markdown reader: reset sourcepos in parseWithString'...
...when parsing a block quote or list item. Otherwise
it can happen that by the time parseWithString' is called,
the position has already been set to the next file on the
command line.

Closes #11888.

Closes #11888.
2026-09-23 20:16:34 -07:00
John MacFarlane da7a07a54d ODT reader: handle textProperties on paragraph styles.
Previously these just got ignored, not applied to the paragraph's
text.

Cloess #2623.
2026-09-23 18:14:44 -07:00
John MacFarlane 57272eab32 LaTeX writer: don't use footnotehyper for notes in longtable.
Instead, generate them manually as we do for floating tables.

This removes our dependency on footnotehyper, and solves a
compatibility problem with `endfloat`.

Closes #11857.
2026-09-20 11:33:39 -07:00
John MacFarlaneandClaude a83dbaf989 HTML writer: use truncate consistently for table width percentages.
The overall table width used `round` while individual column widths
(in colSpecListToHtml) used `truncate`, so the table width could
exceed the sum of the column widths.  Update test goldens.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-20 10:28:30 -07:00
Samuel Huang 63bc7174ca Typst reader: handle highlight as a mark span (#11879) 2026-09-19 09:49:20 -07:00
John MacFarlane c7365a39f0 Depend on dev citeproc. 2026-09-18 22:34:26 -07:00
John MacFarlane 7cf4d41fe5 Use compactifyTable for tables produced by all readers.
Remove old ad hoc `paraToPlain` at the table cell level.

This should ensure that we don't get tables that mix
Plain and Para. Such tables tend to look funny when
rendered in docx and other formats.

Closes #11864.
2026-09-12 20:02:41 -07:00
John MacFarlane 0e1930aced rebase_relative_paths: recognize URLs with unknown schemes.
Closes #11858.
2026-09-10 22:14:26 -07:00
John MacFarlaneandClaude 0e399cc799 Update test for raw_html-aware inline <style> handling.
With raw_html disabled (the default for the HTML reader), an inline
<style> element is now ignored rather than passed through as a
RawInline; the raw_html-enabled behavior is still tested.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:12:53 +00:00
John MacFarlaneandClaude cd949780c6 LaTeX reader: don't discard state changes made in optional arguments.
The `opt` parsers in LaTeX.hs and Citation.hs parsed the bracketed
tokens in a separate runParserT call, discarding any state changes
(macro definitions, labels, notes) made while parsing them.  Since
an optional argument is not a TeX group, such changes should persist.
Use parseFromToks, which runs the sub-parse in the same parser run.
This also removes a per-optional-argument state copy.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 63a1710cd4 LaTeX reader: keep ASCII quotes when ligatures are disabled.
Commit 47610fe3d (#10781) disabled the `--`, `---`, and `"` ligatures
inside `\texttt`, but single `` ` `` and `'` characters were still
converted to curly quotes, so `\texttt{it's}` produced code containing
a Unicode right single quotation mark.  Gate those conversions on the
ligatures flag too, so quote characters inside `\texttt` stay as
typed.  Update test/command/3958.md accordingly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 113568e6e2 LaTeX reader: fix doubled source positions in retokenizeComment.
`tokenize pos` already produces tokens positioned relative to pos,
but the result was then shifted again by pos's line and column,
roughly doubling the reported positions of tokens retokenized from
comments in URL arguments.  Tokenize from the position just after
the '%' instead, and drop the extra shift.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 1cffc95ceb LaTeX reader: fix position off-by-one after ## in tokenizer.
When tokenizing `##` not followed by digits, the continuation
restarted at column pos+1 instead of pos+2, shifting the source
positions of all subsequent tokens on the line one column left.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlane c54ad9ef75 Typst writer: emit label for bare table if present.
Previously a label was only emitted if the table was figurized
(and not `.typst:no-figure`).

Closes #11849.
2026-09-08 08:34:30 -07:00
John MacFarlaneandClaude a93f2fdc7f SelfContained: only add role and aria-label when inlining SVGs.
role="img" and an aria-label copied from alt were being added to
every element with a src-like attribute, not just inlined SVGs.
That is redundant on img elements, whose alt already provides the
accessible name, and actively wrong on elements like video, audio,
iframe, and embed, where role="img" misrepresents interactive
content as a static image to assistive technology.

The attributes are only needed when an img element is replaced by
an inline svg element, since the alt text loses its meaning there;
restrict them to that path.  Inline SVG output is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-07 15:41:18 +00:00
John MacFarlaneandClaude c585c71118 LaTeX reader: support LaTeX3 (xparse) document commands.
Support the features described in the LaTeX usrguide
(https://www.latex-project.org/help/documentation/usrguide.pdf):

- `\New/Renew/Provide/DeclareDocumentCommand` and the corresponding
  `...DocumentEnvironment` commands, with argument specifiers
  `m o O d D s t r R v e E b` and argument processors (`>{...}`:
  `\TrimSpaces`, `\ReverseBoolean`, `\SplitArgument`, `\SplitList`).
  Environment end code can refer to the environment's arguments.
- `\ProcessList`, `\UseName`, and `\ExpandArgs` (c, n, N letters).
- The conditionals `\IfNoValueTF`, `\IfValueTF`, `\IfBooleanTF`,
  `IfBlankTF` (and variants with just `T` or `F`).
- `\New/Renew/DeclareCommandCopy` and `...EnvironmentCopy`.
- Case changing: `\MakeUppercase`, `\MakeLowercase`, and
  `\MakeTitlecase`, `\NoCaseChange`,
  `\Declare{Upper,Lower,Title}caseExclusions`; `\CaseSwitch` and
  `\Declare*caseMapping`.
- The expandable evaluators `\inteval` and `\fpeval` (a practical
  arithmetic subset); `\dimeval` and `\skipeval` substitute their
  expression unevaluated.
- `\expandableinput`.
- `\ExplSyntaxOn/Off` regions are tokenized with `:` and `_` as
  letters, so expl3 code can be skipped cleanly; `\ShowCommand` and
  the ltkeys commands (`\DeclareKeys`, `\SetKeys`,
  `\ProcessKeyOptions`, ...) are parsed and ignored.
- Some constants, such as `c_ampersand_str`, are supported in
  expl3 regions.

Adds new `ArgSpec` constructors to non-exported module Text.Pandoc.TeX.

New tests in `test/command/latex3-commands.md` and
`test/command/latex3-usrguide.md`. (The latter correspond to the
examples in the user guide and were confirmed against LaTeX output
when possible.)

Closes #7540.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-06 13:28:54 -07:00
John MacFarlane 6accb0b9f7 Typst writer: omit blank line at end of block.
This is just a cosmetic change; it is not semantically
significant.

Closes #11844.
2026-09-04 09:37:08 -07:00
John MacFarlane 3b91fcdd2c Fix Text.Pandoc.Data.getDataFileNames with -embed_data_files.
Previously it was not looking in the right directory
and not recursing.

The function now alphabetizes its results so that test output
is the same with or without `embed_data_files`.
2026-09-01 22:45:21 -07:00
Hendrik Erz fa0dfdf0e1 Markdown reader: case-insensitive alert keywords (#11836)
The keywords (e.g. `TIP`) for GFM-style alerts need not be all caps.
This commit fixes that. It also adds the class `alert` to the produced
Divs to allow for namespacing.

This commit changes the emitted classes from the alert extension
of the basic Markdown reader to add the class `alert` to the
produced DIVs to allow for namespacing.
2026-08-29 17:25:41 -07:00
John MacFarlane 6708521943 Rename some command tests. 2026-08-28 09:30:20 -07:00
John MacFarlane 9dfad4005d MediaWiki writer: put nowiki around possible URLs.
Otherwise they get linkified automatically.
Closes #11834.
2026-08-27 19:55:16 -07:00
John MacFarlane 2f103b3c76 Docx reader: handle case where non-header rows come before header rows.
In this case we'll treat them all as the header in order to
preserve the order.

Added test case for #11833.
2026-08-27 14:51:13 -07:00