ISO 32000-2 Table 369 makes the order normative: a Ruby shall contain
one RB followed by an RT, or by RP, RT and RP. A ruby's two sub-lines
paint in visual order, though, and each opened its structure element as
it painted, so an exported ruby held its RT before its RB. PAC 2026
reports that as two errors per ruby; veraPDF does not report it.
An element is ordered among its siblings where its type is set, not
where it is painted, so both kids are created and typed before either
sub-line paints, and each sub-line then enters the kid already created
for it.
Which sub-line carries the annotation follows OnTop, while bRubyTop only
reflects where a squared grid puts the annotation and is true everywhere
else. Keying the role off bRubyTop tagged the base of a ruby positioned
below outside a squared grid, or to the right, as the RT and its
annotation as the RB, so both the role and the element follow OnTop now.
A squared grid with the ruby text below was already right, bRubyTop and
OnTop agreeing there.
The kids are created only where BeginInlineStructureElements would enter
them: a ruby in the repeated headline row of a follow table would
otherwise leave an empty RB and RT on the table, once per following
page.
testLinkWithRuby asserted only that both kids are present, the order
being wrong when it was written; it compares them in order now.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I8e38a3544008f79939d7d0795c4808a1a72caad7
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13402
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 65c62bc31aff793465f31ab6172c118921fe9d51)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211690
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Object streams and a cross-reference stream shrink a tagged PDF, but
I1e27dc3a8f43aa8b6c75ae930615c8d4b4645627 ("tdf#157028 vcl: write the
structure tree into object streams") left an encrypted export out of
both, so a password kept the whole structure tree uncompressed. The
spreadsheet reported in tdf#166233, attachment 171182, exported tagged
at 2368642 bytes, and at 13465120 with a password. The protected export
is 2389620 bytes now, and what it has over the plain tagged one is what
encryption itself costs.
This writer encrypts a string as it builds the object. ISO 32000-2 7.5.7
forbids encrypting a string in an object stream separately: the stream
that carries it is encrypted instead. So the strings of a stream-bound
object go in plain now, and the object stream is encrypted as a whole,
with the cipher set up for the stream's own object number rather than
for the objects inside. The stream's /Length counts what the cipher
adds. Only the structure elements and the ID tree put strings in a
stream, so only those two builders ask getStringEncryptor for one, and
it returns nothing while the file uses object streams.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: Ia6a063244baf456d7c7368a6bba7a8cbb96e31b0
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13370
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 59193295323917bd065ea3a235f7653ad7f9b04b)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211655
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
With Tagged PDF on, a paragraph in a language other than the document's
gets /Lang on its structure element. What is anchored in the paragraph
is tagged inside that same element and inherits the attribute, so a
frame's text and its description would be declared the paragraph's
language.
0d2d9e656d87647f904f1a463ed9b879c2bfb10c (tdf#163272 sw: give a
paragraph its own /Lang, 2026-09-21) avoided that by putting no /Lang on
such a paragraph, on a check of one node of a text frame that can cover
several. Its text still carried the language, on a Span, while what was
anchored in it specified none and inherited the document's. In a German
document that declared a shape's English text German. See
https://gerrit.collaboraoffice.com/c/online/+/13117/comment/89a7866d_907a2957/
The paragraph specifies its own language now, and a frame anchored in it
no longer inherits it. A Figure, a Formula and a frame's Division
specify the document's where the paragraph's differs, which is what the
frame's own paragraphs are measured against. A drawing shape's text is
tagged by drawinglayer, which takes the locale of the drawn portions and
specifies it where they agree; such a shape is nested in its anchor
paragraph in Writer and in no such paragraph elsewhere, so there is
nothing to compare against.
A drawing shape's own element specifies the document's language too. svx
anchors it under the anchor paragraph, drawinglayer opens it there, and
it carries the shape's title and description. Without the attribute, a
described shape in a paragraph that specifies one would have its
description declared the paragraph's language, and its text likewise
where the portions disagree.
testParagraphLanguage compared a structure type against a language and
quoted no text, so nothing identified the words affected. It lists every
element holding text or a description now, against the language it
specifies, an entry reading "P de-DE: Ein deutscher Absatz mit einem
Bild."; one specifying none is listed without a language, so correct
inheritance is asserted too. PDFiumPageObject gained getMarkedContentID
to join a page object's text to its element.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I1eecf0b56f09fc6d144894910a9037d89884dd3b
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13239
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 898a2e1b3a6fc07d7b43315b2f4781b32f1a20d3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211609
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Turning Tagged PDF on added 2.4 MB to the reporter's document, 2.0 MB of that
its structure tree: 12562 elements, each its own top-level object,
uncompressed, and each costing a further 20 bytes in the cross-reference
table.
The elements go into /Type/ObjStm streams of 200 now, with the parent and ID
trees. The cross-reference has to become a /Type/XRef stream with them,
because a table cannot address an object inside another object. PDF 1.5
introduced both, so a PDF/A-1b file keeps the plain form. So does an encrypted
file, since a string in an object stream shall not be separately encrypted,
and this writer encrypts strings as it builds the object. So does a hybrid
file, because sdext reads the embedded original document out of a trailer.
The reporter's document exported 3157118 bytes tagged before the change and
1086230 after, against 769448 untagged either way. An 8300-row Writer table
exported 4017140 bytes tagged before and 500967 after, against 73183 untagged.
testTdf159066 and testTdf173162 grep the exported bytes for something the
structure tree carries, /Alt< and the /Namespaces array. They inflate the
object streams first now. testListLabelPDFExport counted its List element in
the token stream, and asks GetObjects() for it.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I1e27dc3a8f43aa8b6c75ae930615c8d4b4645627
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13209
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit ad4bb8ae080aba98080dfaabd688dfdb48cf7ade)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211556
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Patches:
- external/pdfium/extractpatterns-fill.patch.1 is now upstream
- external/pdfium/extractpatterns-stroke.patch.1 as well
- external/pdfium/fix-windows-build.patch.1 as well
- external/pdfium/system-icu.patch.1 as well
- external/pdfium/system-dragonbox.diff is a new dependency, patch the
code to use our bundled dragonbox
- update external/pdfium/system-abseil.diff to apply again
Adapted tests:
- CppunitTest_vcl_pdfexport goes back to its previous expected values
after pdfium.git commit 17cf94b52e01507108da6460ebca6b48995a698e
(Revert "Fix mixed-direction line extraction", 2026-09-17)
- CppunitTest_vcl_pdfexport2 as well
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I09bf5fc9f54b697eed2e144c3f57c3b72f3bbc37
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13508
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
(cherry picked from commit 0272de3a9b98d0c3ef4479e0dc12abe8cc040447)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211551
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
GetElements() hands out the tokens the file itself carries, so a caller
hunting structure elements finds none of those a /Type/ObjStm holds - every
one of them, in a file from a producer that compresses its objects. They are
there already: Tokenize unpacks each object stream it passes and registers
what it finds, so only an accessor was missing.
The 64 places that scanned the token stream for an object ask GetObjects()
instead.
testTdf57423 and testStyleNamedStructureTypes compared the order as well. The
map is keyed by object number, which for a structure tree is document order,
where the token stream carried the emission order that writes a child before
its parent - the type sequence reads "Document Table TR TD" now instead of
ending "TR Table". Both hold the same 22 elements; testTdf57423 stopped
indexing its figures and compares one string per kind.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I3e46e600e0aed3802f5ee2f8c6dc25a50002763d
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13207
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 50bf4f7acb344520ce2055bddfaf844c23239a8e)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211450
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
An Impress master slide with a bitmap background could turn plain
blue after saving and reopening the document. The saved styles.xml
still named the fill (for example draw:fill-image-name="Bitmap_20_2")
but held no draw:fill-image for it, so the reload fell back to the
default fill colour. The document had to stay idle long enough for its
images to be swapped out, which made the problem look intermittent.
Switching slides prefetches the page graphics with
GraphicFilter::MakeGraphicsAvailableThreaded, added in commit
8062e88e73 ("try to make available all slide images using threads")
with Change-Id Ifc39a2757834a9fb0dbafa61f13f5454e69af330. A text object
without a fill reports the page background there, so the master
background image is part of that prefetch. The loader hands the decoded
result to ImpGraphic::updateFromLoadedGraphic directly, which resets
mnSizeBytes to 0 and leaves the recalculation to swapIn, but swapIn
never runs on this path. The next memory trim then swapped the graphic
out with a size of 0. A swapped out graphic that is not prepared
answers GetSizeBytes from that value, so SvxUnoBitmapTable treated the
fill as empty and left it out of the export.
updateFromLoadedGraphic now measures the loaded content while it is in
memory, so the graphic keeps its size through the next swap out
whichever path loaded it, and the memory manager counts the real size.
Signed-off-by: Jaume Pujantell <jaume.pujantell@collabora.com>
Change-Id: Id472f542a531a1b9578322f3fdd12a8dd39843df
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13475
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 6a19f87f2430feacd16715947d385cee7e7c5a65)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211533
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
PDFDocument sized every row of a /Type/XRef stream as one predictor byte plus
the /W fields and read the fields from offset one, and the no-prediction arm
of the filter never copied the row it had read. /Predictor defaults to 1, so
a stream written without /DecodeParms decoded as nothing but free entries. It
gave up entirely when /Filter was absent, which is as legal.
An indirect /Length is resolved through those offsets. With none of them the
tokenizer cannot skip a stream body, so it reads the compressed bytes as
tokens and Read() fails on the file.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I4929e879e02ba91dce8280421f129f91dba9074d
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13205
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 3adab88af78016314f210f2be66668190b1f3493)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211444
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Write a paragraph in a language other than the document's, then File -
Export As - Export as PDF with Tagged PDF on. The paragraph's element
said nothing about the language, and the Span holding its text said it
instead:
/StructElem /S/Standard /K[6 0 R]
/StructElem /S/Span /Lang (en-GB) /K[1 2 3 4 5 6 7 8]
A run of text was compared against the language of the document, so
every run of a paragraph that declares one of its own differed from it
and opened a Span. The comparison is against the paragraph now, and a
paragraph unlike the document names its language itself. A run unlike
its paragraph still opens a Span and names its own, which it did not
where it was the document's language the paragraph had departed from.
A Span that carries anything else - a strikeout, a script, a character
style - still stands, without the language it was repeating, and so
does a Link. A paragraph with something anchored in it says nothing,
as before: what hangs there is tagged underneath it and would take the
paragraph's language for its own.
testSpans reads four list items, three of them en-GB in a de-DE
document. It asserted the shape the old rule made - for one of them it
looked for a single Span and took the language off that, and for the
rest it counted the Spans that rule opened. It reads the language off
the paragraph now, asserts that the item at the document's own
language names none, and where an item holds a hyperlink and a
footnote it finds the two Links alone.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I4082891666598793f59d1bf62c33e883ebdebb8f
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13117
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 0d2d9e656d87647f904f1a463ed9b879c2bfb10c)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211375
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Insert - Table of Contents and Index, then export with File - Export
as PDF, Universal Accessibility (PDF/UA) on and PDF version 2.0. The
file fails ISO 14289-2 clause 8.2.5.8 once per entry, which veraPDF
reports as:
Each TOCI in the table of contents shall identify the target of the
reference using the Ref entry, either directly on the TOCI structure
element itself or on one of its child structure elements
An item named no target at all:
<</Type/StructElem/NS 2 0 R/S/TOCI /P 8 0 R /Pg 3 0 R /K[10 0 R ]>>
It names the heading now, in the /Ref entry PDF 2.0 added:
<</Type/StructElem/NS 2 0 R/S/TOCI /P 8 0 R /Pg 3 0 R /Ref[ 15 0 R ] /K[10 0 R ]>>
Object 15 is the /S/H1 the entry links to, found from the mark its
URL names. An item that links to a table, a frame or an object, or
that has no link at all, still names nothing.
The document of testTdf173162 linked its entries to
bookmark alone now, as Writer writes them, and the document passes
the profile, so the test asks for PDF/UA instead of plain tagging
and the harness validates it.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I76e73684d2ab09f187f56cab093d13980150d737
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12750
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit ee49bf58e7d3eb3ff94677e9a51c73b76eb51a99)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211338
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Put a footnote in a document and export with File - Export as PDF,
Tagged PDF on and PDF version 2.0. The file fails ISO 14289-2 clause
8.2.5.14 once per footnote. veraPDF, on the PDF/UA-2 profile, reports:
The Note standard structure type shall not be present in conforming
documents unless role mapped to a structure element in the PDF 2.0
namespace
The element carried the type PDF 2.0 replaced, in the 1.7 namespace
ISO 14289-2 8.2.4 accepts alongside the 2.0 one:
<</Type/StructElem/NS 2 0 R/S/Note /ID (id29) /P 28 0 R /Pg 3 0 R ...>>
Writer asks for FENote now, so the element lands in the 2.0 namespace
and the check stops failing:
<</Type/StructElem/NS 1 0 R/S/FENote /ID (id29) /P 28 0 R /Pg 3 0 R ...>>
Below PDF 2.0 the file still says Note, the only footnote type
ISO 32000-1 has.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I4674f4f902621ee8765121782799f42cc59c5e6e
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12749
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 42433cf89b23328991e7a64daea4f771b60f728a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211337
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Write a paragraph in Writer's built-in Index paragraph style and
export with Tagged PDF switched on. The element comes out as
/S/Index, over a role map that reads
/Index/P
so the standard Index type means a paragraph, document-wide. veraPDF,
on the PDF/UA-1 profile:
Standard tags defined in ISO 32000-1:2008, 14.8.4, shall not be
remapped
A style called Artifact went further. Its paragraph reached the
content stream beside the marker for content that is not part of the
structure at all, the two being one name:
/Artifact BMC
/Artifact<</MCID 10>>BDC
Writer names a structure element after the style that produced it,
and the role map records what that name means, one entry per name.
Styles collide there with the standard types, with each other, being
separate families for paragraphs and characters, and with the names
Writer asks for itself, Source Text writing Code literally.
A name a standard type has, or another alias asked for, is now passed
over for the first indexed name beside it that is free, so the Index
style writes /S/Index-1 over /Index-1/P. PDF 2.0 has standard names
1.7 lacks: a style called H7 gives way there, while a heading at
outline level 7, which Writer asks for by that same name, keeps it
and needs no entry at all. The synthetic Div containers ask for no
name now, and carry the Div tag as before.
The test document remapped four standard tags at PDF 1.7 and six at
2.0; now none at either.
Found while reproducing tdf#165454.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I63e9116e5db17bdb554b508a864e313a77f40bc2
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12361
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 54e0207ff6bcf937187a395912f1ed57b60d90e3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211314
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
A slide with a background, its own or its master's, exported to
tagged PDF with that background as untagged content. ISO 14289-1 7.1
leaves no third state, and veraPDF says so once per background:
Content is neither marked as Artifact nor tagged as real content
Page decoration is wrapped as an artifact in
ViewObjectContact::getPrimitive2DSequence, which the background never
reached: the master page descriptor builds its own hierarchy and
emitted the fill straight into the visitor. It now takes the same
wrapper as the page decoration beside it - the machinery of commit
81ef846485 ("(related: tdf#135192) svx:
PDF/UA export: tag background as Artifact", 2022-12-01), whose author
hoped there that "anything that's not SdrObject can be tagged as
Artifact".
The wrapper has to lie to be heard: VclMetafileProcessor2D drops a
tag that is a NonStructElement, and drops a background tag that is
not an image, so page decoration goes out as a Division claiming to
be an image. That second refusal is also why a shape on a master
slide, unless it is a graphic, is untagged to this day. LATER: let the
processor emit an artifact for a plain background and stop dropping
one, and both go.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I9431c460cade3d432cb7abf76930e23f8106a7f1
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12311
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit b11283cd97e1170688a75f4229fd43bd8298e276)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211166
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Exporting a document to PDF/UA-2 produced a file that fails
ISO 14289-2 clause 8.8, once per outline item and internal link.
veraPDF reports
Destination in Outline item, OpenAction or Link annotation is not
a structure destination
The destination named the page and an area on it, and that is all:
what was linked to could only be worked out by looking at what the
layout had put at that position.
A destination for a link or an outline item is now written as a GoTo
action carrying a structure destination beside the page one
(ISO 32000-2 12.3.2.3):
/A<</Type/Action/S/GoTo/D[<page> 0 R/XYZ ...]/SD[<elem> 0 R/XYZ ...]>>
sw creates a destination before its target is tagged, so the node is
remembered while the cursor is on it, and matched against the tags in
the pass that already runs after the last page. Creating one and
remembering its node is a single call now, which every destination a
link or an outline item points at goes through: bibliography entries
and links in the text of a shape are named as well.
The cursor is not always on the target: a jump that selects a frame
or a drawing object, and one that finds nothing, move it nowhere, and
where it was left standing on the link, that link's own element must
not become the target. The pass over a shape's links can instead
start with the cursor on the target already, where a jump that did
reach it shows no move either, so there the mark names its own node.
Those keep writing /Dest, as do a target that was never tagged and
one whose element was never emitted.
The element carries its role and its content, not a name of its own:
a heading becomes /S/H1 with no /Alt or /T. veraPDF does not check
what a structure destination points at, so the test does.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: Ic76d80d46f130d549ebaacf175393a3d6ec77998
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12300
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit a6502c6e96d13425bcb4a3bf5731a998982d5957)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211150
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
The commit "tdf#163240 vcl,sw: keep one Link tag for a wrapped link"
made language, baseline shift and decoration come from the link's first
portion for the whole link, and noted subdividing by Spans as the fix:
those three are attributes of the Link tag, and there is now one tag per
hyperlink, so a change inside it had nowhere to go.
Open a Span in the Link instead. A span waits inside its link, so two
tags can be open at once, and an annotation goes to the link's own tag -
vcl takes it on a Link only, and a styled run crossing a line break
would otherwise leave that line's annotation without a StructParent.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I302d32f66fcb4e3b1eaf63b136bdcf96aaea17b7
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/11543
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 8f52d63f897aaf710dfe4cd9771f39689471abfd)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211101
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
The commit "tdf#163240 vcl,sw: keep one Link tag for a wrapped link"
kept the tag across a line break. A ruby still split it, and a character
style on a ruby base pulled the following text into the Ruby element.
Tags were closed by a count, which callers decremented to leave one open
for the next portion; a count does not record which tag. Keep two stacks
instead: the tags this helper opened, and the tags left open for a
following portion. Close a deferred tag only while it is the current
element, so neither a ruby nor a fly closes a tag of another.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: Icdf793f149076db2b5dfac2b7668d0c414467427
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/11542
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 1936ac2268b9e603c64a03eff6eb8b7ecb8b56de)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211090
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Regression after commit a71da3b7a8
(tdf#156565 sw: PDF/UA export: split Link SE at line break,
2023-11-01), which closed the tag at each line break so that every
annotation had a tag to nest in.
A structure element could reference one annotation only, the
LinkAnnotation attribute being a value in a std::map; give it a vector
and emit an OBJR kid and a parent tree entry per annotation, so each
StructParent resolves to that tag. A page or column break still ends
the tag.
Language, baseline shift and decoration now come from the link's first
portion, at a line break as much as within a line - splitting the tag
at a break would only make the result depend on where the line ends.
LATER: subdivide the Link by Spans, which covers both.
testTdf157816Link and testTdf142806 expected a tag per annotation.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I493439a33b2cbc0eecf1af6b300859cd78ee87ba
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/11541
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit ade287e6a4edf7476e667f48082261deeddd96d5)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211077
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Exporting a document with a table of contents to PDF/UA-2 produced a
file that fails ISO 14289-2 clause 8.2.4, once for the TOC element
and once for each entry in it. veraPDF reports
Non-standard structure type http://iso.org/pdf2/ssn:/TOC is not
mapped to a standard type
Non-standard structure type http://iso.org/pdf2/ssn:/TOCI is not
mapped to a standard type
Commit d23c5560c1 (tdf#160196 add
support for PDF/UA-2 and NS for struct. elem., 2025-01-22) declared
one namespace, PDF 2.0, and pointed every element at it. ISO 32000-2
14.8.6 leaves TOC and TOCI out of that namespace, and also Art,
BlockQuote, Index, Quote, Note, Reference, BibEntry and Code. Writer
emits all but Art and Reference, so one footnote is enough to reach
this.
The bug asks for L and LI with Lbl and LBody instead, or for a
RoleMapNS entry to a standard type. Role mapping is the smaller of
the two and does clear 8.2.4 for the TOC, but the element is then
validated as an L, so veraPDF reports ISO 32005 Table 5 per entry
<LI> contains <P>
Neither remedy reaches Note, Index, Quote, Code or BibEntry.
ISO 14289-2 8.2.4 offers a third way out: it accepts the PDF 1.7
namespace alongside the 2.0 one, and TOC and TOCI are standard types
there, so nothing needs remapping and the tree keeps its shape.
Declare both namespaces, list both on the structure tree root, and
point all ten types at the 1.7 one. The root's RoleMap is the
fallback for elements there, so RoleMapNS stays on the 2.0 namespace.
Every PDF 2.0 export now declares both, PDF/A-4 included.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: Ibdf0f27afe2c7b5b5801dbe1b813afb6de95bb0f
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12200
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 92a2bfff6f47e055dd49d767a5013cbfcdafcf11)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211067
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
A document with a table of more than 8191 rows, exported to tagged
PDF, failed PAC with 1393 of
Structure element refers to a different parent than the actual parent
The kid list is split into generated Div containers. But m_aChildren
and m_aKids aren't parallel: a non-structure element gets a child
entry and no kid, and marked content adds a kid with no child.
Advancing both by the same count moved the wrong children. Cut the
child list where the moved kids end instead.
addInternalStructureContainer also held a reference into m_aStructure
across an emplace_back on it, so a split that reallocated left it
dangling. It takes an index now.
The test needs a document past that threshold. It takes about two
minutes locally, most of it in the PDF parser it reads the result back
with. Disable the test if that costs CI too much.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I37cb4f3e187b8a836b26e7c3da802bd12147904e
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12187
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 901e5afec874f0b16959038425365198dfbac7e7)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211066
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
A table cell whose first paragraph uses a style inherited from Table
Heading was tagged TD, so a screen reader announces it as data rather
than as a header; only the built-in style itself became TH.
lcl_IsHeadlineCell compared the paragraph's own style name and stopped
there. Walk the DerivedFrom() chain, the way the HTML table writer
already does.
Scope stays Column on every TH, so a row header still claims its
column; that is a defect of its own.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I9ccbd1b3837d3edc5087162a143560e8412a846d
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12168
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 270d087cad2c8327775ce204371830c34eecc0d7)
(cherry picked from commit d86b837d439355e62afcdd6b771603c260bc5949)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211049
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
A hyperlink whose text an alphabetical index entry covers exported a
link annotation with no tag around it, so PDF/UA checkers reject the
file:
Link annotation is not nested inside a Link structure element
that is ISO 14289-1 7.18.5. The same happens under a reference mark or
an RDF metadata field.
Each of the three lays its run out as its own portion type, a
SwTextPortion whose only addition is a shading that never reaches an
export. BeginInlineStructureElements listed Text, Lay and Para, so the
run matched no case and got no element at all - not even the Span
carrying its language.
Handle the three with the other text portions.
Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: Ie38193f64923cd6a0a3afa509691fb29a007f272
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12104
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 409e0cc615e191a0392ee080717bb84eb61e4724)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211027
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Exporting several cross-reference links placed right next to each
other, separated only by punctuation, kept only the last one
clickable. The export checks every link rectangle for the text it
covers, by hit-testing the middle of the rectangle. For a link only
one digit wide that point falls exactly where the hit test advances
to the next character, which belongs to the neighbouring link, so
the rectangle was dropped.
Ask which character contains that point, so a one-glyph link is
matched against its own position.
Signed-off-by: Samuel Mehrbrodt <samuel.mehrbrodt@collabora.com>
Change-Id: I722aa2c83af50e99b8c06e051ff0bff9b0f76e0a
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/10251
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 1284d1a3e3b93f02bd31068c9898743fa65e6e64)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210922
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
- fix build against our bundled ICU
- update external/pdfium/extractpatterns.patch to work after the
upstream commit fc790866bf26d977b2ea3a0c3938bb52895309ea (Consolidate
CFX_RenderDevice and remove CPDF_WindowsRenderDevice, 2026-06-29), which
folded CFX_DefaultRenderDevice into CFX_RenderDevice.
- update CppunitTest_vcl_pdfexport2: mostly likely needed after the
upstream commit 2f7a54a926a1b8e51aff9ac2aca5cccb29920aae (Partially
fix RTL character and line reading order in CPDF_TextPage, 2026-07-24)
- CppunitTest_vcl_pdfexport: same
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I25612e6696a24588b9bf045e666047e6c2955d7c
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/8557
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
(cherry picked from commit a7149002917e82ef07250d2e964537118294993a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210417
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
The end condition `E.back() > (nRound - 32)` is using the wrong value of
`nRound` and so the loop can end too early.
This has caused a few CI builds to fail after pushing the padding fix,
as the input to `computeHashR6` includes a random IV it happened rarely.
Fix it by counting the iterations of the loop that have been done.
Signed-off-by: Michael Stahl <michael.stahl@collabora.com>
Change-Id: I8bbdc85439789db3753942b1292ba48d39b76541
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/8625
Reviewed-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
(cherry picked from commit 082e5a49669e2d9058c2b3675c829a495b2044f3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/209277
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
vcl/qa/cppunit/pdfexport/pdfexport2.cxx(5438): error C2672: 'CppUnit::assertEquals': no matching overloaded function found
workdir/UnpackedTarball/cppunit/include\cppunit/TestAssert.h(161): note: could be 'void CppUnit::assertEquals(const T &,const T &,CppUnit::SourceLine,const std::string &)'
vcl/qa/cppunit/pdfexport/pdfexport2.cxx(5438): note: 'void CppUnit::assertEquals(const T &,const T &,CppUnit::SourceLine,const std::string &)': template parameter 'T' is ambiguous
vcl/qa/cppunit/pdfexport/pdfexport2.cxx(5438): note: could be 'sal_uInt64'
vcl/qa/cppunit/pdfexport/pdfexport2.cxx(5438): note: or 'size_t'
vcl/qa/cppunit/pdfexport/pdfexport2.cxx(5438): note: 'void CppUnit::assertEquals(const T &,const T &,CppUnit::SourceLine,const std::string &)': could not deduce template argument for 'const T &' from 'sal_uInt64'
the aCIDSet.GetSize is a sal_uInt64, but size_t is only 32bit on Windows x86
Change-Id: I9d1f6a99f8badbf1c191abf7ebe3235a6b24917c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/209079
Reviewed-by: Christian Lohmaier <lohmaier+LibreOffice@googlemail.com>
Tested-by: Jenkins
Their subsets were embedded as simple TrueType fonts. Simple TrueType
fonts select glyphs through the cmap table of the subset font, and we
had to give the subset font a fake cmap table that mapped codes to glyph
ID sequentially. This was a gross hack.
Embedding them as CIDFontType2 instead lets the CMap and the CIDToGIDMap
do the mapping, as they now already for fonts with a CFF table. The cmap
table is not needed as all now.
PDFium grows the top of the glyph bounding box by a 64th for composite
fonts but not for simple ones, which makes many tests to now fail. We
now patch that out to keep both kinds of embedding measuring glyphs the
same.
Change-Id: I4d136b0e5d2c63afd56e25a81aec16364e570cca
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208839
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
Tested-by: Jenkins
PDF/A-1 requires the font descriptor of a subsetted CIDFont to have a
CIDSet listing the CIDs of its font program, and fonts with a CFF table
are now embedded as CIDFont subsets.
PDF/A-2 and PDF/A-3 do not require one, and PDF 2.0 deprecates it.
Change-Id: I34fc883ec5b10b404b48b3b9a5dd816fabb28940
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208835
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
Tested-by: Jenkins
Instead of converting CFF table to a Type 1 font and embedding that, we
now embed the CFF table directly without modification.
This allows us to get rid of the CFF parsing code with its security
issues.
The CFF table is embedded as FontFile3 (/Subtype/CIDFontType0C) stream
of a CIDFontType0.
This is a rework of previous attempt in:
commit d9a6a3f7f0
Author: Khaled Hosny <khaled@aliftype.com>
Date: Sun Apr 12 18:46:43 2026 +0200
vcl: embed fonts with CFF table as OpenType fonts
That tried to embed them as full OpenType fonts, which was broken for
CID-keyed fonts and got reverted in:
commit 4c3c87e79d
Author: Khaled Hosny <khaled@aliftype.com>
Date: Thu Apr 30 02:56:06 2026 +0300
tdf#171869 vcl: restore Type1 PFB conversion for CFF fonts
Next version of harfbuzz-subset library will have an option to write an
identity charset (GID == CID) for CID-keyed fonts, so embedded as CFF
table will now work equally for both name-keyed and CID-keyed fonts.
As in interim solution (until we can require that version of HarfBuzz),
we now have a small module that parses only the CFF charset to get CID
mapping.
Change-Id: I57b0f722174613b91373c972e0c8eba3efeea5f4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208804
Tested-by: Jenkins
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
Since BitmapEx was unified into a single premultiplied 32-bit BGRA
Bitmap, converting such a bitmap runs a per-pixel loop that builds a
BitmapColor and dispatches through the `GetPixelFromData` /
`SetPixelOnData`, (un)premultiplying one channel at a time.
Add a fast scanline path for the common case, a premultiplied 32-bit
truecolor bitmap (ABGR/ARGB/BGRA/RGBA). It resolves each channel's
byte offset once and then moves the scanline bytes directly, using the
premultiply / unpremultiply lookup tables.
Signed-off-by: Karthik <karthik.godha@collabora.com>
Change-Id: Ie8ee47055bdffca9e06d48955fdc0b61b34e08ac
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/6752
Reviewed-by: Michael Stahl <michael.stahl@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 54c68922eaa2d2c4e20e10091ad4911e8d32aed2)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208641
Tested-by: Jenkins
reproduce by exporting to tagged PDF/UA a document where an outer table
spans more than one page and its repeated header row carries a nested
table.
#4 SwTaggedPDFHelper::BeginTag (...) at engine/sw/source/core/text/EnhancedPDFExportHelper.cxx:627
#5 SwTaggedPDFHelper::BeginBlockStructureElements (...) at engine/sw/source/core/text/EnhancedPDFExportHelper.cxx:1827
#6 SwTaggedPDFHelper::SwTaggedPDFHelper (...) at engine/sw/source/core/text/EnhancedPDFExportHelper.cxx:460
#7 SwLayoutFrame::PaintSwFrame (...) at engine/sw/source/core/layout/paintfrm.cxx:3659
#8 SwTabFrame::PaintSwFrame (...) at engine/sw/source/core/layout/paintfrm.cxx:4649
the header row is laid out again on each page, so the nested table gets
a master frame per page. the follow-headline check only looked at the
innermost enclosing table, so a nested table inside an outer table's
headline was not recognised as a repeat and was tagged once per page.
tagging the same frame a second time aborts on the assert that each
frame is tagged only once, so walk every enclosing table and treat the
frame as an artifact when any of them is a repeated follow headline.
Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Change-Id: I173c229fc46b8fbf7575c6a19293f54431dbe0e9
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5967
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Michael Stahl <michael.stahl@collabora.com>
(cherry picked from commit 9c31ded970a549fac846ad4f45349c70fde114d4)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208731
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Opening a password-protected PDF that is newer than version 1.6
failed with "loadComponentFromURL returned an empty reference":
the correct password was rejected. Online exports encrypted PDFs
as 1.7 by default, so this hit every password-protected PDF it
produced.
On import a too-new PDF is downgraded to 1.6 by
convertToHighestSupported, which for an encrypted stream has to
decrypt it (saveWithVersion with security removed). That needs
the password, but the Draw import path (SdPdfFilter::Import ->
ImportPDFUnloaded -> createBinaryDataContainer) passed neither
a password nor an interaction handler, so the downgrade bailed
out ("no Int handler for pass"), returned an empty container and
the load failed.
Thread the password already collected during type detection
(medium SID_PASSWORD), plus the interaction handler, from
SdPdfFilter down to convertToHighestSupported and pre-seed its
password loop with it. Passing the known password matters: the
kit treats a second password callback as a wrong-password retry
and aborts, so re-prompting via the handler alone would turn the
correct password into a spurious rejection.
Add a unit test covering ImportPDFUnloaded on an encrypted 1.7 PDF:
it yields no pages without a password and one page with the right one.
Fixes https://github.com/CollaboraOnline/online/issues/16022
Signed-off-by: Andras Timar <andras.timar@collabora.com>
Change-Id: I6d605c3495a6af2f1763fda2b8f57a058f70c402
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/7711
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 1f96e3d85a55bc1d6cf437fc96f729b44dbf8ce2)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208625
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>