Commit Graph
1387 Commits
Author SHA1 Message Date
Patrick CreightonandCopilot a5af15c2d5 Docx reader: fix namespaces in notes and comments. (#11996)
The contents of footnotes, endnotes and comments were read with the
namespace prefixes declared in word/document.xml rather than in their
own parts. If word/footnotes.xml, word/endnotes.xml or word/comments.xml
declared a prefix that document.xml did not, such as `r` or `m`, the
elements using it were dropped: a hyperlink in a footnote lost its text,
and an equation in an endnote or comment disappeared. They are now read
with the namespace maps that archiveToNotes and archiveToComments
already compute.

Closes #11991.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-24 17:33:07 -07:00
John MacFarlane 2512e1b6e7 Markdown writer: fix regression with code block language.
We try to identify which class is a language name and emit
that as the gfm style info string.

If none of the classes match a recognized language, before
3.10.1 we emitted the first class; fater 3.10.1, we emit
nothing. This commit reverts to the pre-3.10.1 behavior.

Closes #11987.
2026-10-24 16:28:23 -07:00
Patrick CreightonandCopilot b7019a9fb0 Docx reader: keep grid columns that have no width. (#11997)
`w:w` is optional on `w:gridCol`, but elemToTblGrid skipped the grid
columns that had none. The table then had fewer columns than its rows
had cells, and the extra cells were dropped. Every grid column is now
kept. If any of them has no (readable) width, all columns get the
default width, as for a table without `w:tblGrid`, since writers don't
handle a mix of specified and default widths well.

Closes #11989.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-24 11:04:52 -07:00
Aslak HellesøyandClaude Opus 5.5 f4d4af66db Docx reader: read w:fldSimple fields. (#11946)
A `w:fldSimple` element had no case in `elemToParPart'`, so `mapD` dropped it
together with its result runs: "Figure <fldSimple>1</fldSimple> Caption"
read as "Figure Caption", and a simple REF field lost its text. It is now
read as a Field with its runs as children, like a complex field.

`stripCaptionLabel` relied on that drop to remove the number after an
English Figure/Table label; it now drops the number field explicitly, so
those captions read as before.

Closes #11944.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:00:13 -07:00
John MacFarlane c08b112ffa Fix command test 9197.
We need an explicit `-o -` to get docx to go to stdout.
2026-10-22 19:45:47 -07:00
John MacFarlaneandClaude Opus 5.5 979c7a9779 Docx writer/reader: fix multi-paragraph list items.
Previously the docx writer gave the second and subsequent paragraphs
of a list item numbering from a separate list with an invisible
bullet.  This looked right, but semantically these paragraphs were
items of another list, which is what Word, screen readers, and
pandoc's own docx reader saw; the list was also split in three.

The writer now gives these paragraphs no numbering, just an
indentation that lines them up with the text of the list item
(as Word's "List Continue" styles do).

The docx reader now records the indentation of each list level
and tracks the open list levels; an unnumbered paragraph whose
left indentation matches the text indentation of an open level
is treated as a continuation of that level's current item,
rather than as a block quote.  So multi-paragraph list items
now round-trip through docx.

Closes #9197.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 23:11:04 -07:00
John MacFarlaneandClaude Opus 5.5 42626eef1d LaTeX reader: detect macro expansion loops across doMacros calls.
A macro whose expansion contains itself after an unknown command,
e.g. `\def\small{\@setfontsize\small\@ixpt{10}}`, was expanded
again and again at the same position, each time in a fresh call
to doMacros, so the existing depth check never triggered and
pandoc hung.  We now track consecutive expansions of the same
macro at the same position and raise PandocMacroLoop after 1000.

Closes #11088.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:12:31 -07:00
John MacFarlane d8d35b59ce RTF reader: fix tokenization bug.
We were consuming certain symbols after `\uNNNN` during tokenization.
This led to an *extra* character being consumed as the "fallback,"
so letters went missing after encoded unicode characters.

Closes #11981.
2026-10-21 12:04:08 -07:00
John MacFarlane c91ba8bc0a Typst writer: escape ... and -? ligatures.
Closes #11979.
2026-10-21 00:01:43 -07:00
John MacFarlane 0c2e12283f Use dev version of citeproc 2026-10-18 09:38:41 -07:00
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