From 0b94f39d9a8390cfb5c85934bb80ae620dbdce84 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Apr 2021 21:58:41 +0530 Subject: [PATCH] E-book viewer: Fix a regression in 5.15 that caused the viewer to not respect page-break properties --- src/calibre/srv/fast_css_transform.cpp | 31 +++++++++++++++++++-- src/calibre/srv/tests/fast_css_transform.py | 11 +++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/calibre/srv/fast_css_transform.cpp b/src/calibre/srv/fast_css_transform.cpp index 4b6afb0a80..1022c74dc3 100644 --- a/src/calibre/srv/fast_css_transform.cpp +++ b/src/calibre/srv/fast_css_transform.cpp @@ -313,7 +313,7 @@ class Token { text.reserve(16); } - Token(const TokenType type, const char32_t ch, size_t out_pos) : + Token(const TokenType type, const char32_t ch, size_t out_pos = 0) : type(type), text(), unit_at(0), out_pos(out_pos) { text.reserve(16); if (ch) text.push_back(ch); @@ -391,6 +391,17 @@ class Token { } } + bool is_property_terminator() const { + switch(type) { + case TokenType::whitespace: + return text.size() > 0 && text.find_first_of('\n') != std::string::npos; + case TokenType::delimiter: + return text.size() == 1 && (text[0] == ';' || text[0] == '}'); + default: + return false; + } + } + PyObject* get_text_as_python() const { PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, text.data(), text.size()); if (ans == NULL) throw python_error("Failed to convert token value to python unicode object"); @@ -414,6 +425,10 @@ class Token { text.replace(pos, len, (size_t)0u, 0); } + void prepend(const char32_t *src) { + text.insert(0, src); + } + void set_text(const PyObject* src) { if (PyUnicode_READY(src) != 0) throw python_error("Failed to set token value from unicode object as readying the unicode obect failed"); int kind = PyUnicode_KIND(src); void *data = PyUnicode_DATA(src); @@ -602,9 +617,21 @@ class TokenQueue { case PropertyType::font_size: process_values = std::bind(&TokenQueue::process_font_sizes, this, std::placeholders::_1); break; - case PropertyType::page_break: + case PropertyType::page_break: { it->erase_text_substring(0, 5); + size_t pos = std::distance(queue.begin(), it); + std::vector copies; + copies.reserve(queue.size()); + while (it < queue.end() && !it->is_property_terminator()) { copies.push_back(*(it++)); } + if (copies.size()) { + queue.insert(queue.begin() + pos, std::make_move_iterator(copies.begin()), std::make_move_iterator(copies.end())); + size_t idx = pos + copies.size(); + queue[idx].prepend(U"-webkit-column-"); + queue.emplace(queue.begin() + idx, TokenType::whitespace, ' '); + queue.emplace(queue.begin() + idx, TokenType::delimiter, ';'); + } changed = true; keep_going = false; + } break; case PropertyType::non_standard_writing_mode: it->set_text(U"writing-mode"); diff --git a/src/calibre/srv/tests/fast_css_transform.py b/src/calibre/srv/tests/fast_css_transform.py index 68bea6ec5f..890df2cd34 100644 --- a/src/calibre/srv/tests/fast_css_transform.py +++ b/src/calibre/srv/tests/fast_css_transform.py @@ -31,6 +31,13 @@ class TestTransform(SimpleTest): def s(src, expected, url_callback=upper_case): return d(src, expected, url_callback=url_callback, is_declaration=False) + s('.c { page-break-after: 1 always }', '.c { break-after: 1 always ; -webkit-column-break-after: 1 always }') + s('.c { page-break-after: always\ncolor:red }', '.c { break-after: always; -webkit-column-break-after: always\ncolor:red }') + s('.c { page-break-after: always\n}', '.c { break-after: always; -webkit-column-break-after: always\n}') + s('.c { page-break-after: always;color:red }', '.c { break-after: always; -webkit-column-break-after: always;color:red }') + s('.c { page-break-after: /**/always }', '.c { break-after: always ; -webkit-column-break-after: always }') + s('.c { page-break-after: always !important }', '.c { break-after: always !important ; -webkit-column-break-after: always !important }') + s('.c { page-break-after: always;}', '.c { break-after: always; -webkit-column-break-after: always;}') s('.c{x:url(y)}', '.c{x:url("Y")}') s('@im/* c */port "x.y";', '@import "X.Y";') s('@import url("narrow.css") supports(display: flex) handheld and (max-width: 400px);', @@ -59,6 +66,7 @@ class TestTransform(SimpleTest): d('-epub-writing-mode: a; -web/* */kit-writing-mode: b; writing-mode: c', 'writing-mode: a; writing-mode: b; writing-mode: c') d('xxx:yyy', 'xxx:yyy') + d('page-break-before: always', 'break-before: always; -webkit-column-break-before: always') sheet = ''' @import "b/loc.test"; @@ -78,9 +86,10 @@ class TestTransform(SimpleTest): @zoo { not(.woo) and why { font: 16px "something something" 16; + page-break-before: avoid } } } .why { font: 16px} ''' - s(sheet, sheet.replace('16px', '1rem').replace('b/loc.test', 'B/LOC.TEST')) + s(sheet, sheet.replace('16px', '1rem').replace('b/loc.test', 'B/LOC.TEST').replace('page-', 'break-before: avoid; -webkit-column-'))