Skip to content
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

Add support for display: inline-block/inline-table. And other fixes #325

Merged
merged 8 commits into from
Jan 12, 2020

Conversation

poire-z
Copy link
Contributor

@poire-z poire-z commented Jan 10, 2020

See individual commit messages for details.
Some additional notes:

I did not find much specs and docs about inline-block / inline-table, here are the ones I read:
https://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align only the last 2 paragraphs :)
https://blog.mozilla.org/webdev/2009/02/20/cross-browser-inline-block/
http://www.gtalbot.org/BrowserBugsSection/Safari3Bugs/baseline-inline-table-vertical-align.html
https://stackoverflow.com/questions/19352072/what-is-the-difference-between-inline-block-and-inline-table
https://www.brunildo.org/test/inline-block.html
https://www.brunildo.org/test/inline-block2.html

Some screenshots:
KOReader showing https://www.brunildo.org/test/inline-block.html:
image

Mix of floats and inline-block:
image

Inline-block in a float in an inline-block in a float:
image

Snippet from https://stackoverflow.com/questions/19352072/what-is-the-difference-between-inline-block-and-inline-table/56305302#56305302, Firefox on the left, KOReader on the right:
image


This change is Reviewable

virxkane and others added 3 commits January 10, 2020 14:48
- Delay computation of alignment when vertical-align: top or bottom,
  as we need the full line to be laid out to know the final line
  height and baseline.
- Also make the image vertical alignment code not assuming that
  baseline_to_bottom=0 (even if it's true for images), so it's
  ready to be re-used when implementing "display: inline-block"
  (where the baseline is not the bottom of the box).
- Fix half_leading by setting half_leading_bottom, as otherwise
  1px could be lost, causing possible alignment issues (but
  actually not anymore with the added delayed computation).
@poire-z poire-z changed the title Add support for display: inline-block/inline-table, and others Add support for display: inline-block/inline-table. And other fixes Jan 10, 2020
@Frenzie
Copy link
Member

Frenzie commented Jan 10, 2020

Wow, I waited many years for browsers other than Opera implement inline-block. Advanced stuff! ;-)

Copy link
Member

@Frenzie Frenzie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 10 of 10 files at r2.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @poire-z)


crengine/include/cssdef.h, line 32 at r2 (raw file):

    css_d_list_item_block,  // display: list-item
    css_d_inline_block,
    css_d_inline_table, // (needs to be before css_d_table, as we use tests like if: (style->display > css_d_table))

Also makes some kind of logical sense. :-)

@poire-z
Copy link
Contributor Author

poire-z commented Jan 11, 2020

Added a commit, that will allow hyphenation in footnote popups, see koreader/koreader#5699 (comment).

@NiLuJe : is static lUInt16 widths[MAX_WORD_SIZE] = { 0 }; (that I picked from your zero-init commit) ok and the right way to do it (for a 64 items array that won't be modified, and that I want all items to be set to 0 only once, avoiding a memset() or the need to define it outside that function)?

@NiLuJe
Copy link
Member

NiLuJe commented Jan 11, 2020

@poire-z: Yep, allocated once on the stack (in the data/static section), zero-initalized, then re-used as-is w/ function scope ;).

EDIT: Oops, not technically on the stack, which is why access might be slower ;).

@NiLuJe
Copy link
Member

NiLuJe commented Jan 11, 2020

I'm not familiar with the call stack, but does this actually need to be static?

(Re-allocating/initializing on the stack for each call is likely to be faster than accessing static storage).

@poire-z
Copy link
Contributor Author

poire-z commented Jan 11, 2020

I'm not familiar with the call stack, but does this actually need to be static?
(Re-allocating/initializing on the stack for each call is likely to be faster than accessing static storage).

Dunno if it "needs" to be.
But this function is dumping a section of the DOM tree, and is recursive as we walk child nodes.
So, for <p>a<span>b<b>c<i>d</i>e</b>f</span>g</p>, there will be dozen calls, 3-4 recursives, and that bit of new code executed 7 times.
So, that will be 4 x 64b occupied on the stack, and 7 times the need to init it to 64x0.
(Note that the section using it is not recursive, so a static can be used.)

That array is passed further to HyphMan::hyphenate( ... lUInt16 * widths ...) as a pointer, so I guess, static or on the stack, it's still just a pointer :)
The fact that it's further than on the stack is really an issue?
And is it the same stack when we call a method on an object (HyphMan is also a static object/singleton)?

Still think it needs to be on the stack?

Bonus question: is it ok to #define some stuff (for readability) between a if and a else? (it compiles)

        if ( WNEFLAG(TEXT_SHOW_UNICODE_CODEPOINT) ) {
               [...]
        }
        #define MIN_WORD_LEN_TO_HYPHENATE 4
        #define MAX_WORD_SIZE 64
        else if ( WNEFLAG(TEXT_HYPHENATE) && HyphMan::isEnabled() && txt.length() >= 4 ) {

@NiLuJe
Copy link
Member

NiLuJe commented Jan 11, 2020

I edited my first answer, because it was actually inaccurate (and conflicting ^^).

Where static storage gets allocated is devised by the compiler, and everything's set up at process startup time. So it's not technically on the stack, and, by the time it gets used, that usually ends up being "far" from the stack. That, plus other shenanigans explain the potential performance penalty vs. everything being on the stack.

Unlike dynamic memory (i.e., malloc/new -> heap), I'd never worry about the cost of automatic memory allocation (-> stack).

That said, in this case, the fact that the gist of it is done in another function which only gets a pointer adds another potential layer of fun ^^. I wouldn't overly worry about it, though.


Yes, you can define stuff in function scope, and it's properly scoped by the compiler. In this case, I'd probably move it at the top of the if ladder instead of in-between rungs, but that's mostly stylistic (f.g., I hate that break between the closing brace and the else with a fiery passion ^^).

@NiLuJe
Copy link
Member

NiLuJe commented Jan 11, 2020

Also, fun fact: unlike automatic storage, static storage is, by contract (i.e., C99 says so) zero-initialized.

@NiLuJe
Copy link
Member

NiLuJe commented Jan 11, 2020

TL;DR: My gut feeling would be, yeah, keep it on the stack, the cost of zeroing 128 bytes should be negligible.

TL;DR²: I'd only use static when I actually need the re-use the same data in subsequent calls for some reason (usually, bad ones ^^), not for any other concerns.
In practice, I think my (own) only valid uses of (function scope) statics are for increasing function-specific counters.

Not that it's an issue here, but it's also process wide, meaning it's not thread-safe (which explains why TLS (thread local storage) exists).

@poire-z
Copy link
Contributor Author

poire-z commented Jan 11, 2020

So, that would be just removing the static?
And how large can become the stack? If I were to need an array of a fixed size of 1MB or 100MB, would you still suggest the stack? If not, what would the threshold be?

Or I might just make the hyphenate() code accepts *width=NULL and just skip all the width stuff when we give it NULL - so we don't have to decide for or against static here :)
(But ok, that would add a test in the regular book hyphenation case, so probably not good to make it do more work, even if just a NULL check - and I don't mind if that new occasionally used code is slow.)

probably move it at the top of the if ladder instead of in-between rungs

even when they are just used by one late branch - and are a bit out of context that far away?
But... I guess I can just switch the order of the branches then :) no, logic lost

@NiLuJe
Copy link
Member

NiLuJe commented Jan 12, 2020

IIRC, on Linux, we get 8MB of stack per-thread, and that's it (unless someone does wonky pthread shit ;p).

(FWIW, that's virtual memory on Linux, it'll only consume as much physical memory as needed at the time).

Blowing it up will crash (stack overflow). This is (partly) why alloca() has been deprecated in favor of VLAs, which are considered harder to get wrong (and/or use for stupidly large arrays), for some reason (spoiler alert: I prefer alloca ;p).

So, yeah, anything relatively large ought to go on the heap ;).

Here, we're talking about 128 bytes, so, yep, fairly insignificant ;).

@NiLuJe
Copy link
Member

NiLuJe commented Jan 12, 2020

As for the define stuff, like I said, my comment was mostly stylistic. If you want it closer to the code, and you don't actually need it in the branch's conditional itself, I'd move it inside the branch, which should work just as well ;).

Floats among floats should be cleared and shouldn't overflow
the top float box (they were overflowing its margin bottom).
Also, when they are cleared, the space cleared should not
be removed from the bottom margin, so it's fully shown.
Also fix uninitialized value (reported by clang-tidy).
Generic support for baseline of blocks. Will be needed to
support "display: inline-block" (boxes whose baseline is
the baseline of the last line box it contains) and
"inline-table" (boxes whose baseline is the baseline of
the first line box, or the bottom of the first table row).
- FlowState::addContentLine() takes an additional parameter,
  that we set when adding a line (ignored when adding paddings
  and margins).
- renderBlockElement() takes an optional *baseline so callers
  can get it additionally to the returned height.
- Also allows storing a baseline in RenderRectAccessor.
- Enhanced Block Rendering: add a new toggable feature to allow
  rendering correctly an element with display: inline-block as
  an inline box (like an image, part of a line).
- initNodeRendMethod() and lvrend.cpp setNodeStyle(): wrap
  elements with display: inline-block or inline-table in an
  internal inlineBox element (mostly just like we wrap an
  element with float: in a floatBox element).
  An inlineBox is erm_inline, and only needs to get the
  vertical-align property of its child.
- renderBlockElementEnhanced(): deal with inlineBox specifically,
  but mostly the same as we handle a floatBox element.
- getRenderedWidths(): recursive call to measure an inlineBox
  while measuring its containing final block.
- renderFinalBlock(): add an inlineBox to the LFormattedText
  object the same way we add images.
- lvtextfm: handle inlineBox mostly as we handle images (same
  vertical alignment code), and render them when measuring
  text to get their width, height, and baseline. Store their
  size and position in their RenderRectAccessor (once the words
  are aligned, and their position on the line known).
  Move some code from the float drawing section in an added
  getAbsMarksFromMarks() function, as we need it too with
  inlineBoxes when drawing selected text.
- ldomDocument::createXPointer(pt): allow recursive call,
  used when pt is inside an inlineBox or a floatBox (former
  code for floatBox replaced by this cleaner way).
crengine native text selection (and internal bookmarks)
highlighting was expecting single-column full-width
paragraphs with nothing else on the line, and may be ugly
when floats, table cells, or RTL/BiDi text are involved.
This adds an alternative method for drawing them by using
getSegmentRects(), and getting multiple small rects that
will each compose the final highlighting.
It can be toggled by using ldomXRange.setFlags(2) on the
range to highlight (the original method will still be
used when using ldomXRange.setFlags(1)).
Also avoid division by zero (reported by clang-tidy).
Pass text nodes content to HyphMan, and add soft-hyphens
where it says hyphenation is allowed (with the user or
language current hyphenation settings).
(This will allow popup footnotes to be hyphenated,
as MuPDF does hyphenate on soft-hyphens.)
Also re-order some flags in a more logical order.
@poire-z
Copy link
Contributor Author

poire-z commented Jan 12, 2020

(Rebased with minor changes in appropriate commits, to fix/avoid a few clang-tidy warnings.)

@poire-z
Copy link
Contributor Author

poire-z commented Jan 30, 2020

Looks like I didn't test with images having themselves <IMG style="display: inline-block"...>. They are currently rendereded as... invisible, so simply not shown :(
(and all of the images are like that in my next read - thought inline-block was rare...)

I guess I just forgot to add the new css_d_inline_block and css_d_inline_table with the others there:

if (ntype && ntype->is_object) { // image
switch ( d )
{
case css_d_block:
case css_d_list_item_block:
case css_d_inline:
case css_d_run_in:
setRendMethod( erm_final );
break;
default:
//setRendMethod( erm_invisible );
recurseElements( resetRendMethodToInvisible );
break;
}

@poire-z
Copy link
Contributor Author

poire-z commented Jan 30, 2020

And that stupid book has that kind of stuff:

ol {
    list-style: none;
}
ol > li {
    list-style-position: inside;
    margin-left: 0;
    display: table;
}
ol li > .elemliste_puce,
ul li > .elemliste_puce {
    padding-right: 0.5em;
    display: table-cell;
}
ol li > .wrapper_listeord_elemliste,
ul li > .wrapper_listenonord_elemliste {
    display: table-cell;
}
 <ol class="table_root">
  <li class="table_article">
      <a href="PL0.xhtml" class="table_titre">Avant-propos</a>
    </li><li class="table_sect_som">
      <a href="PL2.xhtml" class="table_titre">Première partie. Avant l'écriture</a>
     <ol>
      <li class="table_article">
      <a href="PL1.xhtml" class="table_titre">Avant l'écriture</a>
     <ol>
      <li class="table_chapitre">
      <a href="PL1.xhtml#s1n2" class="table_titre">Les linguistes </a>
    </li><li class="table_chapitre">
      <a href="PL1.xhtml#s1n3" class="table_titre">La grande famille </a>
    </li><li class="table_chapitre">
      <a href="PL1.xhtml#s1n4" class="table_titre">L'Extrême-Orient</a>

So, display:table with only some explicit display:table-cell (not present in that one) - and all stuff not display: table-something are discarded by the table rendering code...
I don't even know how that is supposed to work. Implicit table-cells created? Or just consider what's not display: table-* like just normal flow block or inline stuff?

No real idea how we should handle/workaround that in crengine...

With some added borders, it looks like this:
image

image
The one with ~F / final is shown, not the one with ~i / inline.
Might be a just matter of resetting these ~i to ~F... Tricky stuff this rendering method business in crengine :|

@Frenzie
Copy link
Member

Frenzie commented Jan 30, 2020

I think that without a table/table-row a table-cell is basically just a (inline?) block. Not 100 % sure though.

In your case the display: table may be supposed to generate an implicit table-row and/or table-cell; at least I seem to recall having seen something like that in the spec. (Sorry, don't quite feel like double checking atm but maybe it helps?)

@poire-z
Copy link
Contributor Author

poire-z commented Jan 30, 2020

https://www.w3.org/TR/CSS2/tables.html#anonymous-boxes might be that spec and explain how it is expected to be done.
Will require some reading :) and thinking :|

@Frenzie
Copy link
Member

Frenzie commented Jan 30, 2020

We'd better look at https://www.w3.org/TR/CSS22/tables.html

Even though it's probably 99 % the same, the updates are generally about removing ambiguities and adding more clarifications. (On the flipside, older specs may be easier to understand due to being less verbose.)

Here it says that all missing table elements must effectively be generated if they're not present:
https://www.w3.org/TR/CSS22/tables.html#anonymous-boxes
That's pretty complex stuff. :-/

@poire-z
Copy link
Contributor Author

poire-z commented Feb 7, 2020

Here it says that all missing table elements must effectively be generated if they're not present[...] That's pretty complex stuff. :-/

Indeed :| But I had a go at it, and we'll be using even more Boxes!
To the point of getting ridiculous like this:
image
for that small snippet (1st part of 1st snippet in https://stackoverflow.com/questions/19352072/what-is-the-difference-between-inline-block-and-inline-table/19352149#19352149):

<style>
.itable { display: inline-table; }
.iblock { display: inline-block; }
.cell { display: table-cell; }
.wrapper > span { border: 1px solid #000; padding: 5px;}
</style>
<fieldset>
  <legend>inline-table</legend>
  <div class="itable wrapper">
    <span class="cell">table-cell</span>
    <span class="iblock">inline-block</span>
    <span class="cell">table-cell</span>
  </div>
</fieldset>

But I get quite per-specs / per-Firefox results, and better results than before for inline-tables (which must really be wrapped by an anonymous table element), so that must be the right way to do it,

Anyway, by looking at a dozen of fancy publishers EPUBs, I found 2 that had some stuff display: table-cell not wrapped in any real table nor stuff with display: table. The one described above, and another one where all the footnotes where laid out with that stuff.
As a result, such stuff in incomplete tables were rendered invisible, and we and users might have some books with some content that KOReader has never shown to them...

So, this fix, because it adds many new boxes which might not be as rare as floats, and may break past highlights, I'll need to bump gDOMVersionCurrent / DOM_VERSION_CURRENT so only new books will benefit from it.
Which might be the occasion to bring in "consistent/normalized" xpointers from the upstream work @pkb did when merging our stuff - that would be used only from this new DOM_VERSION (to avoid the need for migrating old books xpointers, which we might still want to do at a later date).

I'll need a few more days to work out all that, and it will need some testing.
@Frenzie : I've seen you already moved a few issues from 2020.02 to 2020.03 - any feeling of when you'll be releasing 2020.02? Middle of month or end of month? Depending on this, I'll either get lazy or hurry on that work :)

(Although I'm a bit concerned I introduced inline-block/inline-table in 2020.01 without a new DOM_VERSION - I'd rather have make them enabled only for a new DOMVersion, because some books are ridiculously abusing inline-blocks for large text, so I may have broken past highlights with that - I may make them depending on the coming up DOM_VERSION, so only books opened/highlighted with 2020.01 may have some issues - but not those highlighted with any 2019/2018... this compatibility stuff is quite a malediction...).

@poire-z
Copy link
Contributor Author

poire-z commented Feb 7, 2020

Another nearly unrelated specs reading/english gramar question:
In https://www.w3.org/TR/2017/WD-css-tables-3-20170307/#breaking-rules :

When fragmenting a table, user agents must attempt to preserve the table rows unfragmented if the cells spanning the row do not span any subsequent row, and their height is at least twice smaller than both the fragmentainer height and width. Other rows are said freely fragmentable.

A fragmentainer is just the page/screen rectangle in my context.
How do you understand "than both"?
If I have a page of height=10cm and width=5cm, and a table row of height=4cm, does this row meet the condition or not? Should I use a && or a || in:

if ( row->height < context.getPageHeight()/2 && row->height < provided_available_width/2 ) {
    content_line_flags = RN_SPLIT_BEFORE_AVOID | RN_SPLIT_AFTER_AVOID;
}

Re-reading that spec text, it looks like it should be &&, but I would more logically have gone with || - any strong opinion when reading this specs snippet?

(I'll be doing that for single-columns tables only, where it's mostly just used as a wrapper - but small rows with only 2-3 lines of text better be kept together, just need to decide on the threshold, and that spec is giving one.)

@Frenzie
Copy link
Member

Frenzie commented Feb 7, 2020

I've seen you already moved a few issues from 2020.02 to 2020.03 - any feeling of when you'll be releasing 2020.02? Middle of month or end of month?

We've had some pretty big changes already, so I'm planning for in a week, maybe two. End of the month isn't really a thing I want to turn into a habit. In December I didn't feel it was quite ready around the 12th or so and I didn't really have time after that. :-)

this compatibility stuff is quite a malediction...

Yeah, I'd say that ideally the DOM wouldn't change (except on HTML-parsing bug fixes) with CSS changes. Actually browsers implement something like that too. There's the DOM, which arguably is what we don't quite have. The DOM seldom changes once HTML parsing has reached a certain level of maturity (which isn't too hard with HTML5), and there's the shadow DOM. What we have may be a bit like the browser's shadow DOM.

See https://developer.mozilla.org/en-US/docs/Glossary/Shadow_tree and https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM although those don't look as informative as I'd have expected. I don't really know where you'd get better info, just that I've seen it.

A fragmentainer is just the page/screen rectangle in my context.
How do you understand "than both"?

I think it's saying that the whole row should be transferred to the next page if it's smaller than half the fragmentainer width & height. I'm not sure I understand why they're linking width to height though.

Re-reading that spec text, it looks like it should be &&, but I would more logically have gone with || - any strong opinion when reading this specs snippet?

Doesn't that "and" translate into logical OR?

@NiLuJe
Copy link
Member

NiLuJe commented Feb 7, 2020 via email

@poire-z
Copy link
Contributor Author

poire-z commented Feb 7, 2020

I think it's saying that the whole row should be transferred to the next page if it's smaller than half the fragmentainer width & height

Yep, you've translated "twice as small" into "half" and removed "both", but you're as unclear with "width & height" :)
If I rewrite that again, the question is whether:
if it's smaller than both the fragmentainer half width & height
should read as:
if it's smaller than half the fragmentainer width and/or if it's smaller than half the fragmentainer width

Doesn't that "and" translate into logical OR?

That's my question :) Would english grammar give in that easily that a "and" translate into a logical OR ?! :)

Actually, after a few more tests with my current test case where the table is just a wrapper (the book mentionned above), I'd be tempted to go with && and do as if I hadn't asked the question :)

In these screenshots (in dual page mode), each red bordered thingie is a table with a single cell... which is ridiculous (I'm finally getting authors would use display:table-cell to get a non-taking-full-available-width block element, unlike a simple DIV/LI which would take all available width - but this has no use there...)

With || in my code (so, triggered more easily, with a row height < page_width/2):
image
"Les langues de l'Europe médiévale" is smaller, so not fragmented and put all on the 2nd page.

With && in my code (so triggered less easily with a row height < page_height/2):
image
and next page (that starts with a small one):
image

These pages actually look really nice without my borders:
image
But there, not very welcome cut aftter "Les mondes arabe et turco-iranien"...
(May be publishers also use tables to avoid that kind of cut? But page-break-before/after/inside: avoid would work as well and be clearer...)

@NiLuJe
Copy link
Member

NiLuJe commented Feb 7, 2020

Put it another way:

John, Jim & Jack are brothers.
John is smaller than both Jim and Jack.
=> John is smaller than both his brothers.

Means John is the smallest of the brothers, not that he's smaller than two of 'em standing on each other's shoulders ;).

@poire-z
Copy link
Contributor Author

poire-z commented Feb 7, 2020

Explained that way, it looks so obvious... :), thanks!
But ok, whether I rigthfully stumbled on that ambiguous wording, or I'm just being tricked by that simple translation :) I'm going with && - because I prefer the result now. (I may revisit that when I find a test-case where || would give better results :)

@poire-z
Copy link
Contributor Author

poire-z commented Feb 7, 2020

What we have may be a bit like the browser's shadow DOM.

Yep, a DOM tailored for rendering, which is its main usage in crengine.
The only 2 things for which we'd need the real DOM I can think ok right now:

  • consistent xpointers (for highlights, last_xpointer) at the crengine API boundaries (in/out)
  • CSS parent/sibling selectors

@pkb 's work was to make the code dealing with the first one always walk parents and siblings to look if there's any of our "shadow element autoBoxing/floatBox/inlineBox/tabularBox" so to skip/walk/count them, so the output/input can be made like if they weren't there.
Which might be costly, I dunno yet how much. But with the cre call cache we added recently, that cost might be softened.

@Frenzie
Copy link
Member

Frenzie commented Feb 7, 2020

That's my question :) Would english grammar give in that easily that a "and" translate into a logical OR ?! :)

English grammar isn't formal logic, sorry. ;-) I don't think French is really any different than Dutch, German or English in that regard, is it? Basically we just have a sentence that a good editor would probably suggest to disambiguate. But anyway, on my end that was more of a rhetorical question. I think it means logical OR within the context of your piece of code.

Btw, I think the specs have their own GH pages; there might already be an issue or a PR about them. Not suggesting you go wasting a lot of of time looking for that, but it could be worth trying for a few minutes.

@poire-z
Copy link
Contributor Author

poire-z commented Feb 7, 2020

Btw, I think the specs have their own GH pages; there might already be an issue or a PR about them

Yep, I met a few of the discussions there while looking for other stuff - but I didn't think of making an explicite search for this topic :|
But not much luck with this and variations:
https://github.com/w3c/csswg-drafts/search?q=fragmentainer+table+row&type=Issues

@shermp
Copy link

shermp commented Feb 8, 2020

When fragmenting a table, user agents must attempt to preserve the table rows unfragmented if the cells spanning the row do not span any subsequent row, and their height is at least twice smaller than both the fragmentainer height and width. Other rows are said freely fragmentable.

FWIW as a native English speaker, I find myself scratching my head over that particular phrasing. It's... not great.

@Frenzie
Copy link
Member

Frenzie commented Feb 8, 2020

It's Speclish, not English. ;-) Speclish has more in common with Euro English than with native English, but it's less easily comprehensible than either one.

@NiLuJe
Copy link
Member

NiLuJe commented Feb 9, 2020

Looks like I didn't test with images having themselves <IMG style="display: inline-block"...>. They are currently rendereded as... invisible, so simply not shown :(
(and all of the images are like that in my next read - thought inline-block was rare...)

Yup, managed to randomly hit that on my current book, too :D.

(On full-page images in the front matter, which made for a few puzzling "how many times do I have to page next until I see some text?!" :D)

@poire-z
Copy link
Contributor Author

poire-z commented Feb 9, 2020

In the meantime, a small styletweak will help :) img { display: block; }
(or the existing one Images> Full-width images)

@NiLuJe
Copy link
Member

NiLuJe commented Feb 9, 2020

I went the heavy handed way and switched the rendering mode to legacy, but, good to know, thanks ;).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Problem in select RTL text in PDF (EPUB has been fixed) Memory error 'Use-after-free'
5 participants