-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhanced block rendering (floats, collapsing margins...) #299
Conversation
By keeping track of static buffer being used by a upper LVFormatter, and using dynamic buffers in re-entrant calls if it is.
Adds BLOCK_RENDERING_* flags and macros to allow enabling/disabling various block rendering features. Saved in a global gRenderBlockRenderingFlags variable, made part of the global hash so a re-rendering is triggered on change. Main available features/enhancements over legacy: - better/proper handling of forced and unforced page splits (no more break between a top border and the first text line) - collapsing vertical margins - "auto" and negative vertical and horizontal margins - block widths and heights (traditional or W3C box model) - support for floats and clear
Also add support for CSS3 "break-before" property names (synonym of the CSS2 "page-break-before") and friends. Also enable parsing negative margins.
initNodeRendMethod() and lvrend.cpp setNodeStyle(): handle floats, wrap them in a <floatBox> element, update the ->float_ and ->display property if needed, and decide the appropriate rendering method of nodes. getNodeListMarker(): handle possible new floatBox between UL > LI lvstsheet.cpp: skip floatBox nodes (like autoBoxing nodes are) in parent/ancestor rules checking.
It was storing the border box top left position (relative to parent container border box), and its width and height. Add slots to also store: - various useful flags. - for erm_final nodes, the inner content box top and left position (relative to this node border box) and width. - some extra slots to store outer floats footprint over an erm_final node. - top and bottom overflows (when inner floats overflow this node's box), for erm_final and erm_block nodes. These will help rendering, drawing, and elementFromPoint() and createXPointer(pt) alike functions. This increases RenderRectAccessor size by a factor of 4, but it seems to compress really well as most of these slots will stay zero in regular nodes when no floats are involved.
Renamed renderBlockElement() to renderBlockElementLegacy(). Added new renderBlockElementEnhanced() with the new block rendering codes, tunable with BLOCK_RENDERING_* flags. Added a wrapper renderBlockElement() to decide which of renderBlockElementLegacy() or renderBlockElementEnhanced() to use depending on gRenderBlockRenderingFlags. getRenderedWidths(): handle measurement rules depending on selected BLOCK_RENDERING_* flags. Added a FlowState object (block formatting context manager) to be used by renderBlockElementEnhanced() that deals with: - vertical margins collapsing. - block floats positionning and clearing, and float overflows of their container. - allow or not page splitting (in these margins and regular text lines) by being a proxy to lvpagesplitter's context.AddLine(). - transfer of block floats footprints to erm_final nodes formatted by lvtextfm. - transfer of overflowing embedded floats from lvtextm to main flow lvtextfm.cpp: accept BlockFloatFootprint (implementation is part of next commit). lvpagesplitter.cpp: - handle lines fully or partialy backward (can happen when negative collapsed margins are enabled). - handle new RN_SPLIT_DISCARD_AT_START flag for margins after an unforced break. ldomNode::renderFinalBlock(): store/restore BlockFloatFootprint in RenderRectAccessor, as this function is called on many occasions after rendering. ldomNode::getAbsRect(): adds inner= parameter to optionally get the abs rect of the content box instead of the border box. Tables: some tweaks when called from enhanced_rendering. Fix (in legacy and enhanced modes): when list-style-position=inside and marker propagated to first erm_final child, store the LI node index in RenderRectAccessor so it is cached (unlike previous hack, which wasn't cached, so markers weren't displayed on next book loads from cache).
Handle embedded floats (floats defined inside erm_final nodes, whose positionning interacts with surrounding text), and <BR clear=>. Handle BlockFloatFootprint (passed from RenderBlockElementEnhanced, and stored in RenderRectAccessor) when formatting or reformatting from cache. alignLine(): small rewrite because of floats.
DrawDocument(): draw blocks with overflows (overflowing their container), possibly switching to 2-steps drawing (background first, then content) if needed. LFormattedText::Draw(): handle drawing of embedded floats, with adjusted native selection marks (when text selection is in progress).
Update the various methods used to get a XPointer to the element at a screen/document point/page, or a screen Rect from a XPointer/XRange, to handle and navigate thru floats and boxes' overflows: LVDocView::getPageDocumentRange() LVDocView::getBookmark() ldomDocument::createXPointer(lvPoint) ldomXPointer::getRect() ldomXRange::getRectEx() ldomNode::elementFromPoint(lvPoint) Make them simpler in enhanced rendering mode by using the saved content box fields in RenderRectAccessor (fmt.getInnerWidth() and friends). For ldomNode::elementFromPoint(): alternate logic when in enhanced rendering mode because of collapsed margins, also checking in bottom overflow before checking next sibling.
New private CSS property "-cr-only-if:" to activate next properties depending on gRenderBlockRenderingFlags features or document type. Conditions can be AND'ed with: -cr-only-if: legacy epub-document; A condition can be negated by prefixing it with a hyphen: -cr-only-if: -epub-document; It applies until the end of the rule, or until a new "-cr-only-if:" is met. Update epub.css: - remove "text-align: center" from H1...H6, because it looks like publisher rarely specify/reset it to "text-align: left" even if they obviously want that (centering can be forced with a style tweak). - remove "text-align: justify" from P: this should just be inherited (eg. from a <CENTER> container), possibly from our BODY "text-align: justify". - remove "page-break-before: always" on headings (H1...H6) in EPUB documents (because publishers don't expect it and most reading software only break page on DocFragment). Keep it on H1 only on non-EPUB documents (and keep it on H2 and H3 in legacy rendering mode). - remove horizontal margins on TABLE as a table with "width: 100%" would otherwise overflow block/page. - add clear/float support for some tags/attributes like: <BR clear=all>, <IMG align=left>, <TABLE align=right>.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most impressive.
/* Don't break page on headings in EPUBs: publishers may not expect it, | ||
* as most EPUB renderers only break page on a new DocFragment. */ | ||
h1 { | ||
-cr-only-if: -epub-document; /* only if NOT EPUB document */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This faux-property applies to the entire selector?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From last commit's message :)
New private CSS property "-cr-only-if:" to activate next
properties depending on gRenderBlockRenderingFlags features
or document type.
Conditions can be AND'ed with:
-cr-only-if: legacy epub-document;
A condition can be negated by prefixing it with a hyphen:
-cr-only-if: -epub-document;
It applies until the end of the rule, or until
a new "-cr-only-if:" is met.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can combine them into a single selector.
For example, I'm experimenting with this for Wikipedia EPUBs:
body > div > div.thumb {
/* Allow main thumbnails to float (only the main
* ones, not the deeper ones in gallery, which is
* complicated to get right */
float: right !important;
/* Change some of their styles when floating */
-cr-only-if: float-floatboxes;
clear: right;
margin: 0em 0em 0.2em 0.5em !important;
font-size: 80% !important;
/* Floats' inner elements' widths would still be used to
* size this float - but if none, text length would be.
* Ensure we have a fixed width for thumbnails when not
* in "web" mode */
-cr-only-if: float-floatboxes -allow-style-w-h-absolute-units;
width: 33% !important; /* or 40% ? */
}
And only now I find this wonderful site with tons of small HTML files with sample CSS cases and documentation - that we can feed to KOReader to see how well it does! |
I'd imagine those are all part of the W3C test suites these days though. :-) |
Adds alternative block rendering code, which provides the following features/enhancements over legacy:
(Some previous discussions about this happened in #234 (comment) and #294.)
Available features are toggable with the following flags (lvrend.h):
We'll provide 4 sets of features from frontend code, currently set as:
Many comments about the specs and observations in the code.
A few more here:
There are 2 different sections of code for positionning floats, because there are two contexts for floats:
Given how crengine works:
Initially, I had two limitations that made things simple:
Killing these 2 limitations needed more complex code (that makes drawing and selection less straightforward), that can be enabled with DO_NOT_CLEAR_OWN_FLOATS and ALLOW_EXACT_FLOATS_FOOTPRINTS. There should be no reason after that to want floats without these :) but keeping them as toggable may help discriminating which feature code is at play when we'll be debugging.
The following screenshot illustrates these limitations, as the 2nd one is still there when there are more than 5 floats involved (1st part, with in red, the footprint rectangles) - but not when there are 5 or less (2nd part):
Everything was checked against Firefox (and Prince for page breaks), and we render mostly everything quite similarly - including floats within floats or table cells!:
There are still some little things we don't do according to the specs in some edge cases:
I'll update Wikipedia EPUB stylesheet to have a different set of styles depending on the block rendering mode, so we can decide to have thumbnails floating or not, with a fixed width or not, which looks pretty neat:
Thumbnails galleries can be made floating, but it can look a bit ugly (but that's how floats work :)
Would be better to use
display: inline-block
for these, which shouldn't be too hard to implement after all this.Should allow closing:
koreader/koreader#2652 Floating image with caption does not respect width/float
koreader/koreader#2843 CSS auto margins are ignored, cannot center element
koreader/koreader#2858 CSS: Margins do not collapse properly
koreader/koreader#2878 Negative margins are ignored
koreader/koreader#3432 Some CSS weaknesses
Sharing my huge test-float-misc.html with many test cases (as a HTML file + images, and not an EPUB, so we can easily load it in browsers for comparison): test-float-misc.zip
(The "EBR5/9 Implement enhanced renderBlockElement" commit diff is a bit ugly because it mixes DrawBorder (that I didn't touch) and my FlowState/RenderBlockElementEnhanced code - better to just read the no-diff added ccode to lvrend.cpp starting with FlowState).
Dumping a few URLs I kept for reference, or just had a look at for early inspiration:
Page breaks:
https://www.w3.org/TR/CSS2/page.html#allowed-page-breaks
https://www.w3.org/TR/CSS22/page.html#page-breaks
https://www.w3.org/TR/css-break-3/
Previous crengine work on page breaks: #33 #40 #49
Collapsing margins:
https://www.w3.org/TR/CSS21/box.html#collapsing-margins
https://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
https://www.w3.org/TR/css-break-3/#break-margins
https://www.w3.org/TR/css-break-4/#propdef-margin-break
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing
https://www.princexml.com/forum/topic/462/margin-collision-and-page-break-before not always appreciated
Floats:
https://pavpanchekha.com/blog/css-floats.html
https://webkit.org/blog/118/webcore-rendering-v-floats/
http://book.mixu.net/css/1-positioning.html
https://bugzilla.mozilla.org/show_bug.cgi?id=630181
Other float handling implementations:
https://github.com/philborlin/CSSBox/blob/master/src/main/java/org/fit/cssbox/layout/FloatList.java
https://github.com/silexlabs/Cocktail/blob/master/cocktail/core/floats/FloatsManager.hx
https://github.com/litehtml/litehtml html_tag.cpp types.h
http://source.netsurf-browser.org/netsurf.git/tree/content/handlers/html/layout.c