standardebooks / tools

The Standard Ebooks toolset for producing our ebook files.
Other
1.42k stars 125 forks source link

page-break-after: avoid is broken in iBooks #101

Closed robinwhittleton closed 4 years ago

robinwhittleton commented 6 years ago

https://github.com/standardebooks/tools/blob/4c2ff3163bff98ebf16ba241915eafb360c07fd9/build#L718

build adds a -webkit-column-break-after: avoid after the specified page-break-after: avoid. Removing this for iBooks fixes the lack of breaking (indicating that it’s overriding the page-break functionality, but not actually applying as it’s a page, not a column. Moving the -webkit-column-break-after declaration to before the page-break-after declaration fixes the issue, but breaks Readium. It looks like Readium hasn’t got support for page-break-* yet: https://github.com/readium/readium-shared-js/issues/127

I’m not sure what the fix is here. Potentially we could use a iBooks filter like we do for theming to add in a specific fix, but it feels like this is more a Readium problem?

acabal commented 6 years ago

Can you clarify what exactly is fixed in iBooks by removing this compatibility CSS?

How exactly does moving it break Readium?

robinwhittleton commented 6 years ago

The same problem in reverse. At the moment iBooks (latest version at least) has no page breaks after the element it’s applied to, but Readium does. Removing it means that iBooks gains page breaks, but Readium loses them.

This came up during the Wilfred Owen production - that has article { page-break-after: always; }.

acabal commented 6 years ago

Hmmm... if you look in tools/compatibility.css we specifically target iBooks a few times with the :root[__ibooks_internal_theme] selector. So it's possible to target specific CSS for iBooks only. But can we target Readium only? I'm not sure. @danielweck any idea if that's possible?

Maybe we can do something fancy where if we detect page-break-*, we get that selector and duplicate it into an iBooks-specific target?

For example if we find:

article{
    page-break-after: avoid;
}

Then we generate:

article{
    page-break-after: avoid;
    -webkit-column-break-after: avoid; /* for Readium */
}

:root[__ibooks_internal_theme] article{
    page-break-after: avoid; /* Override Readium fix to work on iBooks again */
}

In theory, that applies the -webkit-column-break-after property for Readium, and then once again overrides it back to page-break-after only for iBooks. Nasty but possibly workable.

Can you drop that CSS into an ebook and see if that does the trick?

danielweck commented 6 years ago

@acabal I don't think it is possible to "sniff" Readium by matching a CSS selector on :root / html or even body, like with iBook's :root[__ibooks_internal_theme].

danielweck commented 6 years ago

By the way, there are several versions and flavors of Readium, which one are you referring to? The latest web/cloud reader? https://readium.firebaseapp.com

acabal commented 6 years ago

Thanks for clarifying Daniel!

I assume Robin's referring to the web reader.

(Side note, allowing a special selector or media query to target Readium specifically might be a good feature to consider. Being able to target specific readers with CSS is very helpful for ebook producers. Could be as simple as adding a "readium" class to the root element, or something like that.)

robinwhittleton commented 6 years ago

Sorry, should have explained more. I hadn’t heard of Readium until I saw that source comment, and just installed it from the Chrome app store to test. I’m using Readium for Chrome 2.30.0.

danielweck commented 6 years ago

@JayPanoz I thought this discussion would be "right up your street" :) (not only the "page-break" stuff, but also the comment made by @acabal about making Readium detectable by content creators via CSS selectors, e.g. <html class="readium"> ... perhaps something for Readium2?)

JayPanoz commented 6 years ago

Yeah iBooks supports page-break-* through the setPagination API they designed at the time they created iBooks. There’s a paginationMode prop available for the now deprecated UIWebView and app developers can choose whether the -webkit-column-break-* or page-break-* CSS properties should be used through paginationBreakingMode.

This may be a private API on MacOS because you can’t find an equivalent in the Apple dev doc and we know it already is private for WKWebView, which replaces the UIWebView.

That being said, they’re still using hacks to respect page-breaks, by appending invisible hrs whenever needed in the DOM, especially on reflow e.g. font-size change, window resize, etc.; for instance, to trick a figure into page-break-inside: avoid they will prepend an hr element with page-break-before: always.

Webkit per se only uses page-break-* properties for print media, and we must wait for CSS3 break-* properties so that values can be applied to both print and columns – note the mapping for aliasing page-break-* where always is mapped to page so… we’re toast because we would need column.

Support is currently a huge mess.

That being said, it’s a bug I reported to iBooks more than 2 years ago through their bugreport service and that should have been fixed. As far as I can tell, it should work if the page-break-* prop comes last, and maybe -webkit-column-break-* overrides the value which, given their API, would then be ignored at the paginationBreakingMode level.

making Readium detectable by content creators via CSS selectors

That’s something we quickly put aside at the ReadiumCSS level because we can’t guarantee every app using the project would behave the same e.g. have the same colors for reading modes, have the same exact user settings, “page” config, etc. And as we’ve seen with the navigator.epubReadingSystem.name, few apps bother customizing such hooks and you end up targeting a lot of apps in which things can differ greatly.

So if you add native APIs on top of that, it wouldn’t solve the issue. Let’s imagine we append a hook in the DOM so that authors can deal with page-break-*.

We’ll have something like:

<html class="r2-reader">

And then, in the author’s stylesheet:

element {
  page-break-inside: avoid;
}

.r2-reader element {
  -webkit-column-break-inside: avoid;
}

What if the app is using the setPagination API, which may well become public for the WKWebView we use, as Apple is now deprecating the UIWebView?

The .r2-reader element declaration will override page-break-inside: avoid, having the opposite effect of the expected outcome; it will in practice reset the previous one to auto.

Mind you, I’m not implying this couldn’t help for other use cases, but it will sure backfire for some common cases as we can’t really control how people will implement on top of the R2 SDK.

R2 Desktop is different though, as this is a public-facing app, and not a test app like on iOS and Android.

Now, at the macro level, I won’t even dare imagine targeting specific apps; I must already do that occasionally for a significant codebase in JavaScript, and it’s a nightmare to maintain (“if app X and Z do that, if app Y do something else, etc.”)

Some frameworks are already doing that for device-specific styles e.g. ASCIIDoctor EPUB3 and as you can see, they implemented a really small subset of Reading Apps – I know a lot more implemented the navigator.epubReadingSystem object, a handful are using a custom navigator.userAgent to identify themselves and you can occasionally find app-specific objects in the global scope (window).

JayPanoz commented 6 years ago

Albeit hack-ish, this may work as a pure CSS solution though:

element {
  page-break-inside: avoid;
  break-inside: avoid;
}

@supports not ((page-break-inside: avoid) and (break-inside: avoid)) {
  element {
    -webkit-column-break-inside: avoid;
  }
}

As far as I can remember, it worked at the time I reported the issue to iBooks for some reason.

robinwhittleton commented 6 years ago

Thanks for the comments @JayPanoz, that’s really helpful in explaining the complicated background behind this. Your suggested @supports workaround looks hopeful, or at least the start of a potential solution for SE, so I’ll have a play around with that when I get a spare hour.

robinwhittleton commented 4 years ago

Thought I’d test Readium out again to see if there had been any progress, only to be met with a “Chrome is removing app support, please try another reader” message, something backed up by this press release. There seems to be a Readium Desktop application but I’m not sure of the current state or technology basis.

So do we want to continue adding compatibility fixes for a discontinued project?

acabal commented 4 years ago

IIRC Readium is the back-end rendering project, and the Chrome app was kind of a shell or proof of concept for the back-end. The idea is that the Readium back-end could be used by anyone putting together an ereader. So, we should still try to support it if possible, even if the Chrome app is deprecated.

robinwhittleton commented 4 years ago

I’ve been working on a patch to implement @JayPanoz’s suggestion in https://github.com/standardebooks/tools/issues/101#issuecomment-396538742, by changing the existing solution from a regex to a tinycss2 serilaisation that lets me insert a supports block after every qualified rule that needs it. I’ve been testing in Vivlio’s macOS application which seems to use Readium in some way, but it doesn’t seem to be working (and anyway seems to be an Electron app so I’d guess its CSS support mirrors Chromium’s?).

Can anyone suggest a macOS (or potentially Linux) app that uses Readium that I can use to test this patch compatibility with?

acabal commented 4 years ago

Any progress here Robin?

robinwhittleton commented 4 years ago

I’ve got an experimental branch (now rebased) that might fix this, but as I couldn’t work out how to test Readium I couldn’t proceed.

acabal commented 4 years ago

What about this https://github.com/readium/readium-desktop

acabal commented 4 years ago

Sorry, I see you suggested that earlier. But did it not work out?

robinwhittleton commented 4 years ago

Right, got back to where I was. The root problem is that I can’t find a CSS combination that satisfies both Readium and Books. Readium is ultra-picky with getting just the right setup, and unfortunately doesn’t seem to reflow with break rule changes in developer tools (which more than once has lead to me to believe that I’d found the magical combination).

The branch I pushed above doesn’t have a working combo, although the AST approach should be easier to manipulate if a working combination is found.

acabal commented 4 years ago

OK. Let's close this for now due to inactivity. If you find time to explore this further then we can reopen then.