Estimated read time: 5 minutes
Writer now has the early steps to handle tables that are both floating and span over multiple pages.
This work is primarily for Collabora Online, but is useful on the desktop as well.
Motivation¶
As requested in tdf#61594 10 year ago, the use-case is that you can already have floating tables:
And multi-page tables:
And what we want is a combination of them, like this:
This is a quite complicated feature, since both floating objects and tables are complex, and this combines them to create even more complexity.
However, such constructs are used in existing DOCX files and we're expected to correctly display them.
Results so far¶
The feature is enabled by default, but the DOCX import only makes use of it if you set the
SW_FORCE_FLY_SPLIT=1
environment variable. This allows playing with the feature even if there are
lots of known problems still.
On the positive side, core.git sw/qa/core/layout/data/
has 4 files now which are rendered exactly
the way Word does.
A bit of terminology: once a frame is split, the first element of the chain is called master, the remaining frames are called follows.
Here are some screenshots from the journey so far:
This is a fly frame with enough content that it doesn't fit the body frame. It should split, but fly frames could not be split.
First try, just limit the height of the (master) fly frame, so at least it stays inside the body frame. But now some content is not rendered.
Next try. Now have have 2 flys, but the second has zero height and the content of the second fly leaks into the body of the second page.
This one is better, but the position of the follow fly frame is bad, no actual wrapping happens. Also, we assume that there are multiple paragraphs after the table, which will cause problems for floating tables at the end of the document. So I reworked the anchoring code to split the anchor to as many pages as necessary...
Which sounds good, but now the text around the anchor point is duplicated.
Better, now the anchor text is gone in the master anchor, but still there is a misleading paragraph marker.
And now this looks reasonable. Fine, we have some minimal split flys, let's try it with tables instead of just two paragraphs:
With a bit of work, the table's two rows can split, but again the text in the anchor is duplicated.
Next try, now the anchor text is correct, but the horizontal position of the table is still bad, it bleeds out towards the left margin area.
And with more work, now this looks correct.
Let's add some vertical offset! That should be only applied on the first page, and now the follow fly doesn't have that unwanted offset.
Now we have 2 documents that lay out correctly on 2 pages. Let's try 3 pages:
This falls apart, the 2nd and the 3rd row are both on page 2.
After partitioning the fly frames to 3 categories (master, non-last follows, last follow), more than 2 pages also work.
This is a sample where the table has a single cell, so we need to split the (only) row, not just split the table's rows. The first is harder. Currently we don't even try to split it.
Next try, now we split it, but the position of the follow fly is wrong.
Finally split of a single row inside multi-page floating tables also work. That's where we are. Don't try to do anything too custom (like inserting a header or footer), those cases are still known-broken.
How is this implemented?¶
If you would like to know a bit more about how this works, continue reading... :-)
As usual, the high-level problem was addressed by a series of small changes:
- sw: add document model for multi-page fly frames
- sw: add UNO API for multi-page fly frames
- sw: inherit SwFlyAtContentFrame from SwFlowFrame
- sw: add an SwFlyAtContentFrame ctor that creates a follow fly frame
- sw: add an initial SwFrame::GetNextFlyLeaf()
- sw: if the fly is to be split, then limit its growth in SwFlyFrame::Format()
- sw: introduce SwFlyFrame::IsFlySplitAllowed()
- sw: call GetNextFlyLeaf() in SwFrame::GetLeaf()
- sw: avoid unwanted initial content in split/follow fly frames
- sw: implement SwFrame::GetPrevFlyLeaf()
- sw: handle split flys in SwTextFormatter::FormatLine()
- sw: fix anchoring in SwFrame::GetNextFlyLeaf()
- sw floattable: fix cid#1520800
- sw floattable: fix cid#1520804
- sw: call FormatEmpty() in SwTextFrame::Format() for split fly masters
- sw floattable: handle table-in-fly in SwFrame::GetNextFlyLeaf()
- sw floattable: improve handling of split flys in SwTextFrame::FormatEmpty()
- DOCX filter: fix horizontal pos of floattables with compat mode >= 15
- sw floattable: limit vertical position for the follows of split flys
- sw floattable: add ImportFloatingTableAsSplitFly expert setting
- sw floattable: it's fine to recalc the table in SwTabFrame::MakeAll()
- sw floattable: fix handling of multiple splits, i.e. table over 3 or more pages
- sw floattable: fix locking in SwTextFrame::Prepare()
- sw floatable: teach the DOCX filter about SwFormatFlySplit
- DOCX export: move the bulk of the table export code to a new file
- sw floattable: teach the ODT filter about SwFormatFlySplit
- sw floattable: don't promise infinite growth in SwFlyFrame::Grow_()
- sw floattable: allow extra space on top of child content in SwFlyFrame::Grow_()
- sw floattable: fix fly pos invalidation in follow anchor frames
The design of the layout representation is documented in the SwFormatFlySplit constructor.
Want to start using this?¶
You can get a snapshot / demo of Collabora Office 23.05 and try it out yourself right now: try the unstable snapshot. Collabora intends to continue supporting and contributing to LibreOffice, the code is merged so we expect all of this work will be available in TDF's next release too (7.6).