[scribus-dev] Tables GSoC - Weekly Report #5

Elvis Stansvik elvstone at gmail.com
Tue Jul 5 11:43:53 UTC 2011


Hi Craig and everyone,

A full two days late, but here's my fifth report [1].

The reason it took such time is that I was scrambling to get my local
branch with the painting refactor work ready to be pushed. I pushed it
late last night (or early morning depending on how you see it :)

No screencast this time, just an attached screenshot that shows some
collapsed joined and non-joined borders in Scribus
(scribus_table_joins.png).

Cheers,
Elvis

[1] http://wiki.scribus.net/canvas/GSoC_2011_Tables_Weekly_Reports

= Tables GSoC - Weekly Report #5 =

== Work Report ==
* Fixed the API docs of Scribus to work with Doxygen 1.7+.
** Some HTML elements had changed, so the CSS was updated to match.
** Updated Doxyfile with `doxygen -u'.
** Added *.js and *.repository to `clean' target as those are
generated nowadays. No *.ttf seems to be generated anymore, but I kept
them in to stay compatible with older Doxygen.
** Enabled JAVADOC_AUTOBRIEF (OK:ed by avox on IRC).

The above is not directly related to my project, but as I try to
document all code I'm writing I wanted to see what it looks like by
generating it locally. Anyway, it was 15-20 min of work. Moving on, I
also

* Added class TableBorderLine which represents a single line in a
table/cell border. Perhaps it can be scrapped in favor of the
SingleLine class in scribusstructs.h as they are very similar, but I'm
keeping it for now.
* Added class TableBorder which represents a border along one side of
a table/cell. It is essentially a list of TableBorderLine with a
convenience function bool joinsWith(const TableBorder& other) const;
to check if it can be joined with another border. A TableBorder with
no TableBorderLines in it is considered a null border. These are used
for convenience during painting to represent the absence of a border.
* Updated the scripting API and table/cell styles to use the new
border system instead of individually set border color/width/et.c.
properties.
* Added abstract base class TablePainter representing a painter that
can paint a PageItem_Table. The class has a single pure virtual
function void paintTable(ScPainter*) in which subclasses should do
their work.
* Added class CollapsedTablePainter, an implementation of TablePainter
that can paint a table with borders collapsed and joined at
intersections.
* Modified PageItem_Table to hold a pointer to a TablePainter and
paint itself using this painter in the DrawObj_Item(...) function.
* Added three utility functions, kept in a new TableUtils namespace
for easy unit testing. The functions are used by CollapsedTablePainter
when collapsing and joining borders. They are
** collapseBorders(...) which given two borders returns them collapsed,
** joinHorizontal(...) which given a horizontal table border and its
adjoining borders will return the adjustments necessary for joining
the border,
** joinVertical(...) which given a vertical table border and its
adjoining borders will return the adjustments necessary for joining
the border,
* Added columnPosition(int) and rowPosition(int) API to PageItem_Table.
* Added an QString asString() const; to CellStyle for easy printing.

In addition to just listing the changes like this, I feel I need to
elaborate a bit on what has taken such time. I did explain it a little
bit in an earlier mid-week report. The problem has been a hard one for
mainly two reasons:

* Each cell may have N neighboring cells along one of its edges. The
objective of the painting algorithm is to iterate over all cell edges
in the table, and for each cell edge, iterate over all shared border
sections. It must then figure out all (possibly six) cells surrounding
the border section to be painted, along with the collapsed borders
between them (or between them and the table). When that is done, the
border section (or rather, the coordinates at which to paint the
border section) needs to be adjusted for joining. This is delegated to
the joining algorithm.

* There are 41 one possible join cases (that I've identified at
least). They can be seen in the attached picture, which I finally had
to draw to sort it out in my head. The objective of the joining
algorithm is then to, given a border section along with the other
border sections coming in to meet it at its start and end point,
return adjusted start and end points as well as offsets for the
individual border lines in order to make a proper join.

On top of this is the question of painting order. The joining
algorithm must be necessity make an assumption about painting order;
either horizontal borders should be painted on top for the algorithm
to work, or vertical should be painted on top.

The joining algorithm I've written assumes that all horizontal borders
in the table are painted on top of vertical ones, which means two
iterations across the cells. I've been unable to find an algorithm
which makes no assumption about the painting order. I don't think
there is any actually, and iteration across the table cells should be
fast enough. Perhaps in the future optimizations could be made, but
then I think they should rather be done to the internal structures of
the table to make cell accesses fast,
rather than convoluting the joining algorithm.

All in all, a tough nut to crack, and there are currently the
following two small problems with the painting/joining left:

1) The joining algorithm does not handle some cases properly.
2) The painting algoritm sometimes mistakenly identifies a neighboring
border as a null border.

For 1), I feel that I need a break from this problem to not go insane,
and will let it go for now. The code is completely isolated in
TableUtils::joinHorizontal(...) and TableUtils::joinVertical(...) and
can be improved on later. I welcome anyone who likes puzzles to look
at the picture with the 41 join cases and see if they can find an
optimal algorithm that covers all cases with a minimal amount of code.

For 2) I know the reason and how to fix it, it's just that I haven't
gotten around to it. In short, what happens is that instead of
collapsing with the table border, it's requesting a cell outside the
table, which will return an invalid cell with a null border, and
collapses with that.

As I've intentionally kept the collapsing and joining algorithms as
separate functions in the TableUtils namespace, what remains is to
write unit tests that cover all the 41 cases.

== Project Status ==
I'm still behind schedule by at least one week. The reason I decided
to put all my efforts into getting this painting working correctly is
I didn't want to leave it unfinished, or leave a design that would
later come back and bite me when trying to paint complex borders.

I had no idea it would be such much hard work. I think I've used up
all my sketchbooks on this problem, and lost some sleep over it. I
feel now that, to not lose my mind, I really need to get on with some
of the other problems, such as UI and integration with text frames.

I've looked ahead a bit on my schedule, and found that

Week 6:
* Wrapping up for mid-term evaluation.
* Fix bugs/finish up what I have so far.

Week 7:
* Row and column spanning.

Week 8:
* Table/row/column/cell styles.

Since row and column spanning is done, and the styles are in a pretty
good shape (they are there, and new properties can be added removed
easily), I feel that there's still a good opportunity for me to catch
up.

== Problems / Questions ==
My biggest question right now is:

* What should be the general approach for integration of text frames?
The table will hold a set of text frames for its cells, but then? The
user should be prohibited from doing certain things to the text
frames, such as selecting and resizing them. This should be handled by
the table. In a way they will be similar to locked text frames, until
the user decides he wants to edit their content. Should I go for an
approach where I add a bool isTableCellContent() or similar to
PageItem_TextFrame? Or should PageItem_TextFrame even be inherited,
creating a new item for cell content? I'm open to all ideas, so if you
happen to have given this problem some thought, please share, because
frankly I haven't (yet).

== Next Week ==
I'll yet again be working on catching up with my schedule, which means
working on integration of text frames and basic UI. Since mid-term
evaluation is next week, perhaps I should also write some kind of
mid-term report. What do you think Craig, do you think its worthwhile
or just a waste of coding time? Perhaps you feel up to date enough
with my status. I also really should get around to making another blog
post showing the new features, as I've only made one post so far.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: table_border_join_cases.png
Type: image/png
Size: 94208 bytes
Desc: not available
URL: <http://lists.scribus.net/pipermail/scribus-dev/attachments/20110705/c806cb35/attachment-0002.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: scribus_table_joins.png
Type: image/png
Size: 139141 bytes
Desc: not available
URL: <http://lists.scribus.net/pipermail/scribus-dev/attachments/20110705/c806cb35/attachment-0003.png>


More information about the scribus-dev mailing list