From f64d682cc6c86d75aa015042ba6bb8dfa465aff8 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sun, 5 Jul 2020 17:56:17 +0200 Subject: [PATCH 1/8] (Upstream) HyphMan cleanup Includes some small refactoring and changes needed for CoolReader Android build. --- crengine/include/hyphman.h | 26 +++++---- crengine/src/hyphman.cpp | 105 ++++++++++++++++++------------------- crengine/src/lvdocview.cpp | 4 -- 3 files changed, 67 insertions(+), 68 deletions(-) diff --git a/crengine/include/hyphman.h b/crengine/include/hyphman.h index 318e46e15..e542d471e 100644 --- a/crengine/include/hyphman.h +++ b/crengine/include/hyphman.h @@ -74,12 +74,12 @@ class HyphDictionary public: HyphDictionary( HyphDictType type, lString16 title, lString16 id, lString16 filename ) : _type(type), _title(title), _id( id ), _filename( filename ) { } - HyphDictType getType() { return _type; } - lString16 getTitle() { return _title; } - lString16 getId() { return _id; } - lString16 getFilename() { return _filename; } + HyphDictType getType() const { return _type; } + lString16 getTitle() const { return _title; } + lString16 getId() const { return _id; } + lString16 getFilename() const { return _filename; } bool activate(); - virtual lUInt32 getHash() { return getTitle().getHash(); } + virtual lUInt32 getHash() const { return getTitle().getHash(); } virtual ~HyphDictionary() { } }; @@ -98,7 +98,7 @@ class HyphDictionaryList HyphDictionary * get( int index ) { return (index>=0 && index<+_list.length()) ? _list[index] : NULL; } HyphDictionaryList() { addDefault(); } bool open(lString16 hyphDirectory, bool clear = true); - HyphDictionary * find( lString16 id ); + HyphDictionary * find( const lString16& id ); bool activate( lString16 id ); }; @@ -109,12 +109,18 @@ class HyphDictionaryList // the document if the book does not contain any language tag, and // we end up going with it anyway. -class HyphDictionary; -class HyphDictionaryList; class TexHyph; class AlgoHyph; class SoftHyphensHyph; +class HyphDataLoader +{ +public: + HyphDataLoader() {} + virtual ~HyphDataLoader() {} + virtual LVStreamRef loadData(lString16 id) = 0; +}; + /// hyphenation manager class HyphMan { @@ -127,6 +133,7 @@ class HyphMan // static HyphDictionary * _selectedDictionary; static HyphDictionaryList * _dictList; // available hyph dict files (+ none/algo/softhyphens) static LVHashTable _loaded_hyph_methods; // methods with loaded dictionaries + static HyphDataLoader* _dataLoader; static int _LeftHyphenMin; static int _RightHyphenMin; static int _TrustSoftHyphens; @@ -134,8 +141,9 @@ class HyphMan static void uninit(); static bool initDictionaries(lString16 dir, bool clear = true); static HyphDictionaryList * getDictList() { return _dictList; } + static bool addDictionaryItem(HyphDictionary* dict); + static void setDataLoader(HyphDataLoader* loader); static bool activateDictionary( lString16 id ) { return _dictList->activate(id); } - static bool activateDictionaryFromStream( LVStreamRef stream ); // used by CoolReader on Android static HyphDictionary * getSelectedDictionary(); // was: { return _selectedDictionary; } static int getLeftHyphenMin() { return _LeftHyphenMin; } static int getRightHyphenMin() { return _RightHyphenMin; } diff --git a/crengine/src/hyphman.cpp b/crengine/src/hyphman.cpp index e456c81b0..8c9e089f8 100644 --- a/crengine/src/hyphman.cpp +++ b/crengine/src/hyphman.cpp @@ -55,6 +55,8 @@ int HyphMan::_LeftHyphenMin = HYPH_DEFAULT_HYPHEN_MIN; int HyphMan::_RightHyphenMin = HYPH_DEFAULT_HYPHEN_MIN; int HyphMan::_TrustSoftHyphens = HYPH_DEFAULT_TRUST_SOFT_HYPHENS; LVHashTable HyphMan::_loaded_hyph_methods(16); +HyphDataLoader* HyphMan::_dataLoader = NULL; + // Obsolete: now fetched from TextLangMan main lang TextLangCfg // HyphDictionary * HyphMan::_selectedDictionary = NULL; @@ -140,6 +142,28 @@ typedef struct { } hyph_index_item_t; #pragma pack(pop) +class HyphDataLoaderFromFile: public HyphDataLoader +{ +public: + HyphDataLoaderFromFile() : HyphDataLoader() {} + virtual ~HyphDataLoaderFromFile() {} + virtual LVStreamRef loadData(lString16 id) { + HyphDictionaryList* dictList = HyphMan::getDictList(); + HyphDictionary * p = dictList->find(id); + if ( !p ) + return LVStreamRef(); + if ( p->getType() == HDT_NONE || + p->getType() == HDT_ALGORITHM || + p->getType() == HDT_SOFTHYPHENS || + ( p->getType() != HDT_DICT_ALAN && p->getType() != HDT_DICT_TEX) ) + return LVStreamRef(); + lString16 filename = p->getFilename(); + return LVOpenFileStream( filename.c_str(), LVOM_READ ); + } +}; + + + void HyphMan::uninit() { // Avoid existing frontend code to have to call it: @@ -154,6 +178,9 @@ void HyphMan::uninit() if ( _dictList ) delete _dictList; _dictList = NULL; + if ( _dataLoader ) + delete _dataLoader; + _dataLoader = NULL; /* Obsolete: _selectedDictionary = NULL; if ( HyphMan::_method != &ALGO_HYPH && HyphMan::_method != &NO_HYPH && HyphMan::_method != &SOFTHYPHENS_HYPH ) @@ -162,60 +189,14 @@ void HyphMan::uninit() */ } -bool HyphMan::activateDictionaryFromStream( LVStreamRef stream ) -{ - if ( stream.isNull() ) - return false; - /* Obsolete: - CRLog::trace("remove old hyphenation method"); - if ( HyphMan::_method != &NO_HYPH && HyphMan::_method != &ALGO_HYPH && HyphMan::_method != &SOFTHYPHENS_HYPH && HyphMan::_method ) { - delete HyphMan::_method; - HyphMan::_method = &NO_HYPH; - } - */ - CRLog::trace("creating new TexHyph method"); - TexHyph * method = new TexHyph(HYPH_DICT_ID_DICTIONARY); - CRLog::trace("loading from file"); - if ( !method->load( stream ) ) { - CRLog::error("HyphMan::activateDictionaryFromStream: Cannot open hyphenation dictionary from stream" ); - delete method; - return false; - } - if (method->largest_overflowed_word) - printf("CRE WARNING: hyph dict from stream: some hyphenation patterns were too long and have been ignored: increase MAX_PATTERN_SIZE from %d to %d\n", MAX_PATTERN_SIZE, method->largest_overflowed_word); - CRLog::debug("Dictionary is loaded successfully. Activating."); - - // Replace any previously dict loaded from stream - HyphMethod * prev_method; - if ( _loaded_hyph_methods.get(HYPH_DICT_ID_DICTIONARY, prev_method) ) { - delete prev_method; - _loaded_hyph_methods.remove(HYPH_DICT_ID_DICTIONARY); - } - _loaded_hyph_methods.set(HYPH_DICT_ID_DICTIONARY, method); - - if (!_dictList) - _dictList = new HyphDictionaryList(); - /* Obsolete: - HyphMan::_method = method; - */ - if ( HyphMan::_dictList->find(lString16(HYPH_DICT_ID_DICTIONARY))==NULL ) { - HyphDictionary * dict = new HyphDictionary( HDT_DICT_ALAN, cs16("Dictionary"), lString16(HYPH_DICT_ID_DICTIONARY), lString16::empty_str ); - HyphMan::_dictList->add(dict); - /* Obsolete: - HyphMan::_selectedDictionary = dict; - */ - } - TextLangMan::setMainLangFromHyphDict( HYPH_DICT_ID_DICTIONARY ); - CRLog::trace("Activation is done"); - return true; -} - bool HyphMan::initDictionaries(lString16 dir, bool clear) { if (clear && _dictList) delete _dictList; if (clear || !_dictList) _dictList = new HyphDictionaryList(); + if (NULL == _dataLoader) + _dataLoader = new HyphDataLoaderFromFile; if (_dictList->open(dir, clear)) { if ( !_dictList->activate( lString16(DEF_HYPHENATION_DICT) ) ) _dictList->activate( lString16(HYPH_DICT_ID_ALGORITHM) ); @@ -226,6 +207,21 @@ bool HyphMan::initDictionaries(lString16 dir, bool clear) } } +// for android +bool HyphMan::addDictionaryItem(HyphDictionary* dict) +{ + if (_dictList->find(dict->getId())) + return false; + _dictList->add(dict); + return true; +} + +void HyphMan::setDataLoader(HyphDataLoader* loader) { + if (_dataLoader) + delete _dataLoader; + _dataLoader = loader; +} + bool HyphMan::setLeftHyphenMin( int left_hyphen_min ) { if (left_hyphen_min >= HYPH_MIN_HYPHEN_MIN && left_hyphen_min <= HYPH_MAX_HYPHEN_MIN) { HyphMan::_LeftHyphenMin = left_hyphen_min; @@ -269,7 +265,7 @@ HyphDictionary * HyphMan::getSelectedDictionary() { } HyphMethod * HyphMan::getHyphMethodForDictionary( lString16 id, int leftHyphenMin, int rightHyphenMin ) { - if ( id.empty() ) + if ( id.empty() || NULL == _dataLoader) return &NO_HYPH; HyphDictionary * p = _dictList->find(id); if ( !p || p->getType() == HDT_NONE ) @@ -285,21 +281,20 @@ HyphMethod * HyphMan::getHyphMethodForDictionary( lString16 id, int leftHyphenMi // printf("getHyphMethodForDictionary reusing cached %s\n", UnicodeToUtf8(p->getFilename()).c_str()); return method; } - lString16 filename = p->getFilename(); - LVStreamRef stream = LVOpenFileStream( filename.c_str(), LVOM_READ ); + LVStreamRef stream = _dataLoader->loadData(id); if ( stream.isNull() ) { - CRLog::error("Cannot open hyphenation dictionary %s", UnicodeToUtf8(filename).c_str() ); + CRLog::error("Cannot open hyphenation dictionary %s", UnicodeToUtf8(id).c_str() ); return &NO_HYPH; } TexHyph * newmethod = new TexHyph(id, leftHyphenMin, rightHyphenMin); if ( !newmethod->load( stream ) ) { - CRLog::error("Cannot open hyphenation dictionary %s", UnicodeToUtf8(filename).c_str() ); + CRLog::error("Cannot open hyphenation dictionary %s", UnicodeToUtf8(id).c_str() ); delete newmethod; return &NO_HYPH; } // printf("CRE: loaded hyphenation dict %s\n", UnicodeToUtf8(id).c_str()); if ( newmethod->largest_overflowed_word ) - printf("CRE WARNING: %s: some hyphenation patterns were too long and have been ignored: increase MAX_PATTERN_SIZE from %d to %d\n", UnicodeToUtf8(filename).c_str(), MAX_PATTERN_SIZE, newmethod->largest_overflowed_word); + printf("CRE WARNING: %s: some hyphenation patterns were too long and have been ignored: increase MAX_PATTERN_SIZE from %d to %d\n", UnicodeToUtf8(id).c_str(), MAX_PATTERN_SIZE, newmethod->largest_overflowed_word); _loaded_hyph_methods.set(id, newmethod); return newmethod; } @@ -382,7 +377,7 @@ void HyphDictionaryList::addDefault() } -HyphDictionary * HyphDictionaryList::find( lString16 id ) +HyphDictionary * HyphDictionaryList::find( const lString16& id ) { for ( int i=0; i<_list.length(); i++ ) { if ( _list[i]->getId() == id ) diff --git a/crengine/src/lvdocview.cpp b/crengine/src/lvdocview.cpp index 885eb9b5c..3f3701bf0 100644 --- a/crengine/src/lvdocview.cpp +++ b/crengine/src/lvdocview.cpp @@ -6100,7 +6100,6 @@ void LVDocView::propsUpdateDefaults(CRPropRef props) { props->setIntDef(PROP_STATUS_FONT_SIZE, fs); lString16 hyph = props->getStringDef(PROP_HYPHENATION_DICT, DEF_HYPHENATION_DICT); -#if !defined(ANDROID) HyphDictionaryList * dictlist = HyphMan::getDictList(); if (dictlist) { if (dictlist->find(hyph)) @@ -6109,7 +6108,6 @@ void LVDocView::propsUpdateDefaults(CRPropRef props) { props->setStringDef(PROP_HYPHENATION_DICT, lString16( HYPH_DICT_ID_ALGORITHM)); } -#endif props->setIntDef(PROP_STATUS_LINE, 0); props->setIntDef(PROP_SHOW_TITLE, 1); props->setIntDef(PROP_SHOW_TIME, 1); @@ -6373,7 +6371,6 @@ CRPropRef LVDocView::propsApply(CRPropRef props) { fontSize = MAX_STATUS_FONT_SIZE; setStatusFontSize(fontSize);//cr_font_sizes value = lString16::itoa(fontSize); -#if !defined(ANDROID) } else if (name == PROP_HYPHENATION_DICT) { // hyphenation dictionary lString16 id = props->getStringDef(PROP_HYPHENATION_DICT, @@ -6408,7 +6405,6 @@ CRPropRef LVDocView::propsApply(CRPropRef props) { HyphMan::setTrustSoftHyphens(trustSoftHyphens); REQUEST_RENDER("propsApply hyphenation trust_soft_hyphens") } -#endif } else if (name == PROP_TEXTLANG_MAIN_LANG) { lString16 lang = props->getStringDef(PROP_TEXTLANG_MAIN_LANG, TEXTLANG_DEFAULT_MAIN_LANG); if ( lang != TextLangMan::getMainLang() ) { From e68bfeed22f3a3604a2fb39091c91bd5c9b66a12 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 5 Jul 2020 17:56:19 +0200 Subject: [PATCH 2/8] Rendering methods: remove erm_runin Not really needed, and avoid some checks. --- crengine/include/lvstyles.h | 1 - crengine/src/lvrend.cpp | 1 - crengine/src/lvtinydom.cpp | 18 ++++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crengine/include/lvstyles.h b/crengine/include/lvstyles.h index 8b1a155b2..ced9cc85c 100644 --- a/crengine/include/lvstyles.h +++ b/crengine/include/lvstyles.h @@ -278,7 +278,6 @@ enum lvdom_element_render_method erm_block, ///< render as block element (render as containing other elements) erm_final, ///< final element: render the whole it's content as single render block erm_inline, ///< inline element - erm_runin, ///< run-in (used as a solution to inline FB2 footnotes) erm_table, ///< table element: render as table erm_table_row_group, ///< table row group erm_table_header_group, ///< table header group diff --git a/crengine/src/lvrend.cpp b/crengine/src/lvrend.cpp index 52b0249a9..d883dda8f 100644 --- a/crengine/src/lvrend.cpp +++ b/crengine/src/lvrend.cpp @@ -580,7 +580,6 @@ class CCRTable { } break; case erm_inline: - case erm_runin: // do nothing break; } diff --git a/crengine/src/lvtinydom.cpp b/crengine/src/lvtinydom.cpp index 404a1d81a..ee6ab6da7 100644 --- a/crengine/src/lvtinydom.cpp +++ b/crengine/src/lvtinydom.cpp @@ -3975,7 +3975,7 @@ static void writeNodeEx( LVStream * stream, ldomNode * node, lString16Collection rm = erm_final; } } - if ( (rm != erm_inline && rm != erm_runin) || node->isBoxingInlineBox()) { + if ( rm != erm_inline || node->isBoxingInlineBox()) { doNewLineBeforeStartTag = true; doNewLineAfterStartTag = true; // doNewLineBeforeEndTag = false; // done by child elements @@ -4113,7 +4113,6 @@ static void writeNodeEx( LVStream * stream, ldomNode * node, lString16Collection case erm_block: *stream << "B"; break; case erm_final: *stream << "F"; break; case erm_inline: *stream << "i"; break; - case erm_runin: *stream << "r"; break; case erm_table: *stream << "T"; break; case erm_table_row_group: *stream << "TRG"; break; case erm_table_header_group: *stream << "THG"; break; @@ -4969,7 +4968,7 @@ static bool isInlineNode( ldomNode * node ) //int d = node->getStyle()->display; //return ( d==css_d_inline || d==css_d_run_in ); int m = node->getRendMethod(); - return m==erm_inline || m==erm_runin; + return m == erm_inline; } static bool isFloatingNode( ldomNode * node ) @@ -5429,7 +5428,7 @@ static void detectChildTypes( ldomNode * parent, bool & hasBlockItems, bool & ha int m = node->getRendMethod(); if ( d==css_d_none || m==erm_invisible ) continue; - if ( m==erm_inline || m==erm_runin) { //d==css_d_inline || d==css_d_run_in + if ( m==erm_inline ) { //d==css_d_inline || d==css_d_run_in hasInline = true; } else { hasBlockItems = true; @@ -5801,7 +5800,7 @@ bool ldomNode::isEmbeddedBlockBoxingInlineBox(bool inline_box_checks_done) const if ( hasAttribute( attr_T ) ) { // T="EmbeddedBlock" // (no other possible value yet, no need to compare strings) int cm = getChildNode(0)->getRendMethod(); - if ( cm == erm_inline || cm == erm_runin || cm == erm_invisible || cm == erm_killed ) + if ( cm == erm_inline || cm == erm_invisible || cm == erm_killed ) return false; // child has been reset to inline return true; } @@ -5901,7 +5900,7 @@ void ldomNode::initNodeRendMethod() if ( !child->isElement() ) // text node continue; int cm = child->getRendMethod(); - if ( cm == erm_inline || cm == erm_runin ) { + if ( cm == erm_inline ) { has_inline_nodes = true; // We won't be able to make it erm_block continue; } @@ -5945,7 +5944,7 @@ void ldomNode::initNodeRendMethod() if ( !child->isElement() ) // text node continue; int cm = child->getRendMethod(); - if ( cm == erm_inline || cm == erm_runin || cm == erm_invisible || cm == erm_killed ) + if ( cm == erm_inline || cm == erm_invisible || cm == erm_killed ) continue; if ( !isNotBoxWrappingNode( child ) ) continue; @@ -6007,7 +6006,7 @@ void ldomNode::initNodeRendMethod() // runin //CRLog::trace("switch all children elements of <%s> to inline", LCSTR(getNodeName())); recurseElements( resetRendMethodToInline ); - setRendMethod(erm_runin); + setRendMethod(erm_inline); } else if ( d==css_d_list_item_legacy ) { // list item (no more used, obsolete rendering method) setRendMethod(erm_final); @@ -6214,7 +6213,7 @@ void ldomNode::initNodeRendMethod() inBetweenTextNode = prev; prev = getChildNode(i-2); } - if ( prev->isElement() && prev->getRendMethod()==erm_runin ) { + if ( prev->isElement() && prev->getStyle()->display == css_d_run_in ) { bool do_autoboxing = true; int run_in_idx = inBetweenTextNode ? i-2 : i-1; int block_idx = i; @@ -11516,7 +11515,6 @@ ldomNode * ldomXPointerEx::getThisBlockNode() return NULL; lvdom_element_render_method rm = node->getRendMethod(); switch ( rm ) { - case erm_runin: // treat as separate block case erm_block: case erm_final: case erm_table: From 373309d3d57ce3bb3b5592a1c7b9f9555af1d6b0 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 5 Jul 2020 17:56:21 +0200 Subject: [PATCH 3/8] Text formatting: simplify 'display: run-in' handling Get rid of LTEXT_RUNIN_FLAG, and instead fetch the alignment flags from the next node early. Mostly only used with FB2 footnotes to bring two block nodes together. This fixes FB2 footnotes lost alignment since 49f82501 and 28ae22c8. --- crengine/include/lvtextfm.h | 2 +- crengine/src/lvrend.cpp | 63 +++++++++++++++++++++++-------------- crengine/src/lvtextfm.cpp | 26 +++------------ 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/crengine/include/lvtextfm.h b/crengine/include/lvtextfm.h index b8a528ea4..ea82cb1e9 100644 --- a/crengine/include/lvtextfm.h +++ b/crengine/include/lvtextfm.h @@ -62,7 +62,7 @@ extern "C" { // (Don't waste the 4th bit not used in the 4-bits sets above) #define LTEXT_FLAG_OWNTEXT 0x0008 // store local copy of text instead of pointer #define LTEXT_IS_LINK 0x0080 // source text is a link (to gather in-page footnotes) -#define LTEXT_RUNIN_FLAG 0x8000 // element display mode is runin (used with FB2 footnotes) +#define LTEXT__AVAILABLE_BIT_16__ 0x8000 // Text white-space and hyphenation handling #define LTEXT_FLAG_PREFORMATTED 0x00010000 // text is preformatted (white-space: pre, pre-wrap, break-spaces) diff --git a/crengine/src/lvrend.cpp b/crengine/src/lvrend.cpp index d883dda8f..c597f540b 100644 --- a/crengine/src/lvrend.cpp +++ b/crengine/src/lvrend.cpp @@ -2140,9 +2140,8 @@ lUInt32 styleToTextFmtFlags( bool is_block, const css_style_ref_t & style, lUInt if ( is_block ) { // text alignment flags flg = oldflags & ~LTEXT_FLAG_NEWLINE; - if ( !(oldflags & LTEXT_RUNIN_FLAG) ) { - switch (style->text_align) - { + switch (style->text_align) + { case css_ta_left: flg |= LTEXT_ALIGN_LEFT; break; @@ -2164,9 +2163,9 @@ lUInt32 styleToTextFmtFlags( bool is_block, const css_style_ref_t & style, lUInt case css_ta_auto: // shouldn't happen (only accepted with text-align-last) case css_ta_inherit: break; - } - switch (style->text_align_last) - { + } + switch (style->text_align_last) + { case css_ta_left: flg |= LTEXT_LAST_LINE_ALIGN_LEFT; break; @@ -2188,12 +2187,8 @@ lUInt32 styleToTextFmtFlags( bool is_block, const css_style_ref_t & style, lUInt case css_ta_auto: // let flg have none of the above set, which will mean "auto" case css_ta_inherit: break; - } } } - else if ( style->display == css_d_run_in ) { - flg |= LTEXT_RUNIN_FLAG; - } // We should clean these flags that we got from the parent node via baseFlags: // CSS white-space inheritance is correctly handled via styles (so, no need // for this alternative way to ensure inheritance with flags), but might have @@ -2461,6 +2456,7 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce return; } + css_style_ref_t style = enode->getStyle(); bool is_object = false; const css_elem_def_props_t * ntype = enode->getElementTypePtr(); if ( ntype && ntype->is_object ) @@ -2478,7 +2474,7 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce // when recursing its children which are inline), it will also set // horitontal alignment flags. bool is_block = rm == erm_final; - lUInt32 flags = styleToTextFmtFlags( is_block, enode->getStyle(), baseflags, direction ); + lUInt32 flags = styleToTextFmtFlags( is_block, style, baseflags, direction ); // Note: // - baseflags (passed by reference) is shared and re-used by this node's siblings // (all inline); it should carry newline/horizontal aligment flag, which should @@ -2489,11 +2485,41 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce int width = fmt->getWidth(); int em = enode->getFont()->getSize(); - css_style_ref_t style = enode->getStyle(); ldomNode * parent = enode->getParentNode(); // Needed for various checks below if (parent && parent->isNull()) parent = NULL; + // Nodes with "display: run-in" are inline nodes brought at start of the final node + bool isRunIn = style->display == css_d_run_in; + if ( isRunIn ) { + // The text alignment of the paragraph should come from the following + // sibling node. The one set from the parent final node has probably + // not yet been consumed, so update it. + if ( baseflags & LTEXT_FLAG_NEWLINE ) { + if ( enode->getNodeIndex() == 0 && parent && parent->getChildCount() > 1 ) { + ldomNode * next_sibling = parent->getChildNode(1); + if ( next_sibling && !next_sibling->isNull() ) { + // next_sibling is an original block node that should have + // been erm_final, but has been made erm_inline so it can + // be prepended with the run-in node content. + lUInt32 next_sibling_flags = styleToTextFmtFlags( true, next_sibling->getStyle(), baseflags, direction ); + // Grab only the alignment flags + lUInt32 align_flags_mask = LTEXT_FLAG_NEWLINE | (LTEXT_FLAG_NEWLINE<display==css_d_run_in; - if ( thisIsRunIn ) - flags |= LTEXT_RUNIN_FLAG; // Some elements add some generated content lUInt16 nodeElementId = enode->getNodeId(); @@ -3149,10 +3172,7 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce } } - // Note: CSS "display: run-in" is no longer used with our epub.css (it is - // used with older css files for "body[name="notes"] section title", either - // for crengine internal footnotes displaying, or some FB2 features) - if ( thisIsRunIn ) { + if ( isRunIn ) { // append space to run-in object LVFontRef font = enode->getFont(); css_style_ref_t style = enode->getStyle(); @@ -3160,9 +3180,7 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce lUInt32 bgcl = style->background_color.type!=css_val_color ? 0xFFFFFFFF : style->background_color.value; lChar16 delimiter[] = {UNICODE_NO_BREAK_SPACE, UNICODE_NO_BREAK_SPACE}; //160 txform->AddSourceLine( delimiter, sizeof(delimiter)/sizeof(lChar16), cl, bgcl, font.get(), lang_cfg, - LTEXT_RUNIN_FLAG | LTEXT_FLAG_PREFORMATTED | LTEXT_FLAG_OWNTEXT, - line_h, valign_dy, 0, NULL ); - flags &= ~LTEXT_RUNIN_FLAG; + LTEXT_FLAG_PREFORMATTED | LTEXT_FLAG_OWNTEXT, line_h, valign_dy, 0, NULL ); } } @@ -3249,7 +3267,6 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce break; } } - //baseflags &= ~LTEXT_RUNIN_FLAG; if ( rm == erm_final && (baseflags & LTEXT_SRC_IS_CLEAR_BOTH) ) { // We're leaving the top final node with a clear: not consumed // (set by a last or single
), with no follow-up diff --git a/crengine/src/lvtextfm.cpp b/crengine/src/lvtextfm.cpp index 41ef269cf..aa297a07e 100644 --- a/crengine/src/lvtextfm.cpp +++ b/crengine/src/lvtextfm.cpp @@ -3400,19 +3400,13 @@ class LVFormatter { // measure paragraph text measureText(); - // run-in detection (mostly only used for FB2 footnotes) + // We keep as 'para' the first source text, as it carries + // the text alignment to use with all added lines. src_text_fragment_t * para = &m_pbuffer->srctext[start]; - int i; - for ( i=start; isrctext[i].flags & LTEXT_RUNIN_FLAG) ) { - para = &m_pbuffer->srctext[i]; - break; - } - } // detect case with inline preformatted text inside block with line feeds -- override align=left for this case bool preFormattedOnly = true; - for ( i=start; isrctext[i].flags & LTEXT_FLAG_PREFORMATTED) ) { preFormattedOnly = false; break; @@ -3420,7 +3414,7 @@ class LVFormatter { } if ( preFormattedOnly ) { bool lfFound = false; - for ( i=0; isrctextlen; i++ ) { -// int flg = m_pbuffer->srctext[i].flags; -// if ( (flg & LTEXT_RUNIN_FLAG) ) -// TR("run-in found"); -// TR(" %d: flg=%04x al=%d ri=%d '%s'", i, flg, (flg & LTEXT_FLAG_NEWLINE), (flg & LTEXT_RUNIN_FLAG)?1:0, (flg<EXT_SRC_IS_OBJECT ? "" : LCSTR(lString16(m_pbuffer->srctext[i].t.text, m_pbuffer->srctext[i].t.len)) ) ); -// } -// TR("============================"); int srctextlen = m_pbuffer->srctextlen; int clear_after_last_flag = 0; @@ -4077,14 +4063,13 @@ class LVFormatter { srctextlen -= 1; // Don't process this last srctext } - bool prevRunIn = srctextlen>0 && (m_pbuffer->srctext[0].flags & LTEXT_RUNIN_FLAG); for ( i=1; i<=srctextlen; i++ ) { // Split on LTEXT_FLAG_NEWLINE, mostly set when
met // (we check m_pbuffer->srctext[i], the next srctext that we are not // adding to the current paragraph, as
and its clear= are carried // by the following text.) bool isLastPara = (i == srctextlen); - if ( isLastPara || ((m_pbuffer->srctext[i].flags & LTEXT_FLAG_NEWLINE) && !prevRunIn) ) { + if ( isLastPara || (m_pbuffer->srctext[i].flags & LTEXT_FLAG_NEWLINE) ) { if ( m_pbuffer->srctext[start].flags & LTEXT_SRC_IS_CLEAR_BOTH ) { // (LTEXT_SRC_IS_CLEAR_BOTH is a mask, will match _LEFT and _RIGHT too) floatClearText( m_pbuffer->srctext[start].flags & LTEXT_SRC_IS_CLEAR_BOTH ); @@ -4113,7 +4098,6 @@ class LVFormatter { processParagraph( start, i, isLastPara ); start = i; } - prevRunIn = (isrctext[i].flags & LTEXT_RUNIN_FLAG); } if ( !m_no_clear_own_floats ) { // Clear our own floats so they are fully contained in this final block. From 10ac3434eb873833e364509fb6d98682a060f4dc Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 5 Jul 2020 17:56:23 +0200 Subject: [PATCH 4/8] Simplify background image drawing For better support of the image original colors and transparency. --- crengine/src/lvrend.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/crengine/src/lvrend.cpp b/crengine/src/lvrend.cpp index c597f540b..bc0146893 100644 --- a/crengine/src/lvrend.cpp +++ b/crengine/src/lvrend.cpp @@ -7937,13 +7937,21 @@ void DrawBackgroundImage(ldomNode *enode,LVDrawBuf & drawbuf,int x0,int y0,int d draw_y = 0; } // Ready to have crengine do all the work. - // (Inspired from LVDocView::drawPageBackground(), we have to do it that complex - // way to avoid memory leaks; and we have to use a 16bpp LVColorDrawBuf, - // 32bpp would mess colors up). - LVRef buf = LVRef( new LVColorDrawBuf(img_w, img_h, 16) ); - buf->Draw(img, 0, 0, img_w, img_h, false); // (dither=false doesn't matter with a color buffer) - LVImageSourceRef src = LVCreateDrawBufImageSource(buf.get(), false); - LVImageSourceRef transformed = LVCreateStretchFilledTransform(src, transform_w, transform_h, + /* Looks like we don't need that: + + // (Inspired from LVDocView::drawPageBackground(), we have to do it that complex + // way to avoid memory leaks; and we have to use a 16bpp LVColorDrawBuf, + // 32bpp would mess colors up). + LVRef buf = LVRef( new LVColorDrawBuf(img_w, img_h, 16) ); + buf->Draw(img, 0, 0, img_w, img_h, false); // (dither=false doesn't matter with a color buffer) + LVImageSourceRef src = LVCreateDrawBufImageSource(buf.get(), false); + LVImageSourceRef transformed = LVCreateStretchFilledTransform(src, transform_w, transform_h, + + We can just transform the original image, which will work in its original + colorspace/depth, ensure alpha/transparency, and will be converted only + at the end to the final drawbuf bit depth. + */ + LVImageSourceRef transformed = LVCreateStretchFilledTransform(img, transform_w, transform_h, hori_transform, vert_transform, transform_x, transform_y); // We use the DrawBuf clip facility to ensure we don't draw outside this node fmt lvRect orig_clip; From 53cd73c8f935cd916f9c2f0c9a84d1d0a6986072 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 5 Jul 2020 17:56:26 +0200 Subject: [PATCH 5/8] CSS: adds support for "background-size" property Including keywords "contain" and "cover". Remove parsed but unimplemented "background-attachment", which would anyway not make much sense to support: we behave as "background-attachment: scroll". --- crengine/include/cssdef.h | 16 +++----- crengine/include/lvstyles.h | 34 +++++++++-------- crengine/src/lvrend.cpp | 74 ++++++++++++++++++++++++++++++++++++- crengine/src/lvstsheet.cpp | 57 +++++++++++++++++++++++----- crengine/src/lvstyles.cpp | 14 ++++--- 5 files changed, 153 insertions(+), 42 deletions(-) diff --git a/crengine/include/cssdef.h b/crengine/include/cssdef.h index 71c740a25..719f7cb43 100644 --- a/crengine/include/cssdef.h +++ b/crengine/include/cssdef.h @@ -260,14 +260,6 @@ enum css_background_repeat_value_t { css_background_r_inherit, css_background_r_none }; -enum css_background_attachment_value_t { - css_background_scroll, - css_background_fixed, - css_background_local, - css_background_a_initial, - css_background_a_inherit, - css_background_a_none -}; enum css_background_position_value_t { css_background_left_top, css_background_left_center, @@ -330,9 +322,11 @@ enum css_direction_t { }; enum css_generic_value_t { - css_generic_auto = -1, // (css_val_unspecified, css_generic_auto), for "margin: auto" - css_generic_normal = -2, // (css_val_unspecified, css_generic_normal), for "line-height: normal" - css_generic_transparent = -3 // (css_val_unspecified, css_generic_transparent), for "color: transparent" + css_generic_auto = -1, // (css_val_unspecified, css_generic_auto), for "margin: auto" + css_generic_normal = -2, // (css_val_unspecified, css_generic_normal), for "line-height: normal" + css_generic_transparent = -3, // (css_val_unspecified, css_generic_transparent), for "color: transparent" + css_generic_contain = -4, // (css_val_unspecified, css_generic_contain), for "background-size: contain" + css_generic_cover = -5 // (css_val_unspecified, css_generic_cover), for "background-size: cover" }; // Non standard property for providing hints to crengine via style tweaks diff --git a/crengine/include/lvstyles.h b/crengine/include/lvstyles.h index ced9cc85c..ae8798cbc 100644 --- a/crengine/include/lvstyles.h +++ b/crengine/include/lvstyles.h @@ -72,18 +72,19 @@ enum css_style_rec_important_bit { imp_bit_border_color_left = 1ULL << 45, imp_bit_background_image = 1ULL << 46, imp_bit_background_repeat = 1ULL << 47, - imp_bit_background_attachment = 1ULL << 48, - imp_bit_background_position = 1ULL << 49, - imp_bit_border_collapse = 1ULL << 50, - imp_bit_border_spacing_h = 1ULL << 51, - imp_bit_border_spacing_v = 1ULL << 52, - imp_bit_orphans = 1ULL << 53, - imp_bit_widows = 1ULL << 54, - imp_bit_float = 1ULL << 55, - imp_bit_clear = 1ULL << 56, - imp_bit_direction = 1ULL << 57, - imp_bit_content = 1ULL << 58, - imp_bit_cr_hint = 1ULL << 59 + imp_bit_background_position = 1ULL << 48, + imp_bit_background_size_h = 1ULL << 49, + imp_bit_background_size_v = 1ULL << 50, + imp_bit_border_collapse = 1ULL << 51, + imp_bit_border_spacing_h = 1ULL << 52, + imp_bit_border_spacing_v = 1ULL << 53, + imp_bit_orphans = 1ULL << 54, + imp_bit_widows = 1ULL << 55, + imp_bit_float = 1ULL << 56, + imp_bit_clear = 1ULL << 57, + imp_bit_direction = 1ULL << 58, + imp_bit_content = 1ULL << 59, + imp_bit_cr_hint = 1ULL << 60 }; // Style handling flags @@ -101,8 +102,8 @@ struct css_style_rec_tag { int refCount; // for reference counting lUInt32 hash; // cache calculated hash value here lUInt64 important; // bitmap for !important (used only by LVCssDeclaration) - // we have currently below 60 css properties - // lvstsheet knows about 82, which are mapped to these 60 + // we have currently below 61 css properties + // lvstsheet knows about 83, which are mapped to these 61 // update bits above if you add new properties below lUInt64 importance; // bitmap for important bit's importance/origin // (allows for 2 level of !important importance) @@ -142,8 +143,8 @@ struct css_style_rec_tag { css_length_t border_color[4]; ///< border-top-color, -right-, -bottom-, -left- lString8 background_image; css_background_repeat_value_t background_repeat; - css_background_attachment_value_t background_attachment; css_background_position_value_t background_position; + css_length_t background_size[2];//first width and second height css_border_collapse_value_t border_collapse; css_length_t border_spacing[2];//first horizontal and the second vertical spacing css_orphans_widows_value_t orphans; @@ -194,7 +195,6 @@ struct css_style_rec_tag { , border_style_right(css_border_none) , border_style_left(css_border_none) , background_repeat(css_background_r_none) - , background_attachment(css_background_a_none) , background_position(css_background_p_none) , border_collapse(css_border_seperate) , orphans(css_orphans_widows_inherit) @@ -215,6 +215,8 @@ struct css_style_rec_tag { border_width[1] = css_length_t(css_val_unspecified, 0); border_width[2] = css_length_t(css_val_unspecified, 0); border_width[3] = css_length_t(css_val_unspecified, 0); + background_size[0] = css_length_t(css_val_unspecified, 0); + background_size[1] = css_length_t(css_val_unspecified, 0); } void AddRef() { refCount++; } int Release() { return --refCount; } diff --git a/crengine/src/lvrend.cpp b/crengine/src/lvrend.cpp index bc0146893..e12a05b48 100644 --- a/crengine/src/lvrend.cpp +++ b/crengine/src/lvrend.cpp @@ -3480,8 +3480,9 @@ void copystyle( css_style_ref_t source, css_style_ref_t dest ) dest->border_color[3]=source->border_color[3]; dest->background_image=source->background_image; dest->background_repeat=source->background_repeat; - dest->background_attachment=source->background_attachment; dest->background_position=source->background_position; + dest->background_size[0]=source->background_size[0]; + dest->background_size[1]=source->background_size[1]; dest->border_collapse=source->border_collapse; dest->border_spacing[0]=source->border_spacing[0]; dest->border_spacing[1]=source->border_spacing[1]; @@ -7840,8 +7841,79 @@ void DrawBackgroundImage(ldomNode *enode,LVDrawBuf & drawbuf,int x0,int y0,int d lString16 filepath = lString16(style->background_image.c_str()); LVImageSourceRef img = enode->getParentNode()->getDocument()->getObjectImageSource(filepath); if (!img.isNull()) { + // Native image size int img_w =img->GetWidth(); int img_h =img->GetHeight(); + + // See if background-size specified and we need to adjust image native size + css_length_t bg_w = style->background_size[0]; + css_length_t bg_h = style->background_size[1]; + if ( bg_w.type != css_val_unspecified || bg_w.value != 0 || bg_h.type != css_val_unspecified || bg_h.value != 0 ) { + int new_w = 0; + int new_h = 0; + RenderRectAccessor fmt( enode ); + int container_w = fmt.getWidth(); + int container_h = fmt.getHeight(); + bool check_lengths = true; + if ( bg_w.type == css_val_unspecified && bg_h.type == css_val_unspecified ) { + if ( bg_w.value == css_generic_contain && bg_h.value == css_generic_contain ) { + // Image should be fully contained in container (no crop) + int scale_w = 1024 * container_w / img_w; + int scale_h = 1024 * container_h / img_h; + if ( scale_w < scale_h ) { + new_w = container_w; + new_h = img_h * scale_w / 1024; + } + else { + new_h = container_h; + new_w = img_w * scale_h / 1024; + } + check_lengths = false; + } + else if ( bg_w.value == css_generic_cover && bg_h.value == css_generic_cover ) { + // Image should fully cover container (crop allowed) + int scale_w = 1024 * container_w / img_w; + int scale_h = 1024 * container_h / img_h; + if ( scale_w > scale_h ) { + new_w = container_w; + new_h = img_h * scale_w / 1024; + } + else { + new_h = container_h; + new_w = img_w * scale_h / 1024; + } + check_lengths = false; + } + } + if ( check_lengths ) { + int em = enode->getFont()->getSize(); + // These will compute to 0 if (css_val_unspecified, 0) when really not specified + new_w = lengthToPx(style->background_size[0], container_w, em); + new_h = lengthToPx(style->background_size[1], container_h, em); + if ( new_w == 0 ) { + if ( new_h == 0 ) { // keep image native size + new_h = img_h; + new_w = img_w; + } + else { // use style height, keep aspect ratio + new_w = img_w * new_h / img_h; + } + } + else if ( new_h == 0 ) { // use style width, keep aspect ratio + new_h = new_w * img_h / img_w; + } + } + if ( new_w == 0 || new_h == 0 ) { + // width or height computed to 0: nothing to draw + return; + } + if ( new_w != img_w || new_h != img_h ) { + img = LVCreateStretchFilledTransform(img, new_w, new_h, IMG_TRANSFORM_STRETCH, IMG_TRANSFORM_STRETCH, 0, 0); + img_w = new_w; + img_h = new_h; + } + } + // We can use some crengine facilities for background repetition and position, // which has the advantage that img will be decoded once even if tiling it many // times and if the target is many screen-heights long (like could be). diff --git a/crengine/src/lvstsheet.cpp b/crengine/src/lvstsheet.cpp index eb2692185..a04bf6149 100644 --- a/crengine/src/lvstsheet.cpp +++ b/crengine/src/lvstsheet.cpp @@ -104,8 +104,8 @@ enum css_decl_code { cssd_background, cssd_background_image, cssd_background_repeat, - cssd_background_attachment, cssd_background_position, + cssd_background_size, cssd_border_collapse, cssd_border_spacing, cssd_orphans, @@ -197,8 +197,8 @@ static const char * css_decl_name[] = { "background", "background-image", "background-repeat", - "background-attachment", "background-position", + "background-size", "border-collapse", "border-spacing", "orphans", @@ -393,6 +393,7 @@ static bool parse_number_value( const char * & str, css_length_t & value, bool accept_negative=false, bool accept_auto=false, bool accept_normal=false, + bool accept_contain_cover=false, bool is_font_size=false ) { const char * orig_pos = str; @@ -414,6 +415,18 @@ static bool parse_number_value( const char * & str, css_length_t & value, value.value = css_generic_normal; return true; } + if ( accept_contain_cover ) { + if ( substr_icompare( "contain", str ) ) { + value.type = css_val_unspecified; + value.value = css_generic_contain; + return true; + } + if ( substr_icompare( "cover", str ) ) { + value.type = css_val_unspecified; + value.value = css_generic_cover; + return true; + } + } if ( is_font_size ) { // Absolute-size keywords, based on the default font size (which is medium) // Factors as suggested in https://drafts.csswg.org/css-fonts-3/#absolute-size-value @@ -2253,7 +2266,7 @@ bool LVCssDeclaration::parse( const char * &decl, bool higher_importance, lxmlDo if ( prop_code==cssd_font_size ) is_font_size = true; css_length_t len; - if ( parse_number_value( decl, len, accept_percent, accept_negative, accept_auto, accept_normal, is_font_size) ) { + if ( parse_number_value( decl, len, accept_percent, accept_negative, accept_auto, accept_normal, false, is_font_size) ) { buf<<(lUInt32) (prop_code | importance | parse_important(decl)); buf<<(lUInt32) len.type; buf<<(lUInt32) len.value; @@ -2598,9 +2611,6 @@ bool LVCssDeclaration::parse( const char * &decl, bool higher_importance, lxmlDo else if ( n==24 ) n=0; // "inherit" = "left top" } break; - case cssd_background_attachment: - n = parse_name( decl, css_bg_attachment_names, -1 ); - break; case cssd_background: { // Limited parsing of this possibly complex property @@ -2681,6 +2691,34 @@ bool LVCssDeclaration::parse( const char * &decl, bool higher_importance, lxmlDo } } break; + case cssd_background_size: + { + // https://developer.mozilla.org/en-US/docs/Web/CSS/background-size + css_length_t len[2]; + int i; + for (i = 0; i < 2; i++) { + if ( !parse_number_value( decl, len[i], true, false, true, false, true ) ) + break; + } + if (i) { + if (i == 1) { // Only 1 value parsed + if ( len[0].type == css_val_unspecified ) { // "auto", "contain" or "cover" + len[1].type = css_val_unspecified; + len[1].value = len[0].value; + } + else { // first value is a length: second value should be "auto" + len[1].type = css_val_unspecified; + len[1].value = css_generic_auto; + } + } + buf<<(lUInt32) (prop_code | importance | parse_important(decl)); + for (i = 0; i < 2; i++) { + buf<<(lUInt32) len[i].type; + buf<<(lUInt32) len[i].value; + } + } + } + break; case cssd_border_spacing: { css_length_t len[2]; @@ -2992,12 +3030,13 @@ void LVCssDeclaration::apply( css_style_rec_t * style ) case cssd_background_repeat: style->Apply( (css_background_repeat_value_t) *p++, &style->background_repeat, imp_bit_background_repeat, is_important ); break; - case cssd_background_attachment: - style->Apply( (css_background_attachment_value_t) *p++, &style->background_attachment, imp_bit_background_attachment, is_important ); - break; case cssd_background_position: style->Apply( (css_background_position_value_t) *p++, &style->background_position, imp_bit_background_position, is_important ); break; + case cssd_background_size: + style->Apply( read_length(p), &style->background_size[0], imp_bit_background_size_h, is_important ); + style->Apply( read_length(p), &style->background_size[1], imp_bit_background_size_v, is_important ); + break; case cssd_border_spacing: style->Apply( read_length(p), &style->border_spacing[0], imp_bit_border_spacing_h, is_important ); style->Apply( read_length(p), &style->border_spacing[1], imp_bit_border_spacing_v, is_important ); diff --git a/crengine/src/lvstyles.cpp b/crengine/src/lvstyles.cpp index a070e652b..a16ec0f6f 100644 --- a/crengine/src/lvstyles.cpp +++ b/crengine/src/lvstyles.cpp @@ -44,7 +44,7 @@ lUInt32 calcHash(font_ref_t & f) lUInt32 calcHash(css_style_rec_t & rec) { if ( !rec.hash ) - rec.hash = ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + rec.hash = (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( (lUInt32)(rec.important >> 32)) * 31 + (lUInt32)(rec.important & 0xFFFFFFFFULL)) * 31 + (lUInt32)(rec.importance >> 32)) * 31 @@ -93,8 +93,9 @@ lUInt32 calcHash(css_style_rec_t & rec) + (lUInt32)rec.border_color[2].pack()) * 31 + (lUInt32)rec.border_color[3].pack()) * 31 + (lUInt32)rec.background_repeat)*31 - + (lUInt32)rec.background_attachment)*31 + (lUInt32)rec.background_position)*31 + + (lUInt32)rec.background_size[0].pack())*31 + + (lUInt32)rec.background_size[1].pack())*31 + (lUInt32)rec.font_family) * 31 + (lUInt32)rec.border_collapse)*31 + (lUInt32)rec.border_spacing[0].pack())*31 @@ -161,8 +162,9 @@ bool operator == (const css_style_rec_t & r1, const css_style_rec_t & r2) r1.border_color[3]==r2.border_color[3]&& r1.background_image==r2.background_image&& r1.background_repeat==r2.background_repeat&& - r1.background_attachment==r2.background_attachment&& r1.background_position==r2.background_position&& + r1.background_size[0]==r2.background_size[0]&& + r1.background_size[1]==r2.background_size[1]&& r1.border_collapse==r2.border_collapse&& r1.border_spacing[0]==r2.border_spacing[0]&& r1.border_spacing[1]==r2.border_spacing[1]&& @@ -349,8 +351,9 @@ bool css_style_rec_t::serialize( SerialBuf & buf ) ST_PUT_LEN4(border_color); buf<>background_image; ST_GET_ENUM(css_background_repeat_value_t ,background_repeat); - ST_GET_ENUM(css_background_attachment_value_t ,background_attachment); ST_GET_ENUM(css_background_position_value_t ,background_position); + ST_GET_LEN(background_size[0]); + ST_GET_LEN(background_size[1]); ST_GET_ENUM(css_border_collapse_value_t ,border_collapse); ST_GET_LEN(border_spacing[0]); ST_GET_LEN(border_spacing[1]); From 56b5ec0e06c5864856410359f6bf02f37b998788 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 5 Jul 2020 17:56:28 +0200 Subject: [PATCH 6/8] CSS: support background:url("data:image/png;base64,...) Just a matter of accepting this when parsing "background:" and "background-image:". What's been added to support %s\n", UnicodeToLocal(codeBase).c_str(), str.c_str(), UnicodeToUtf8(path).c_str()); str = UnicodeToUtf8(path); @@ -2582,7 +2587,17 @@ bool LVCssDeclaration::parse( const char * &decl, bool higher_importance, lxmlDo const char *tmp = decl; int len=0; while (*tmp && *tmp!=';' && *tmp!='}' && *tmp!='!') { - tmp++; len++; + if ( *tmp == '(' && *(tmp-3) == 'u' && *(tmp-2) == 'r' && *(tmp-1) == 'l') { + // Accepts everything until ')' after 'url(', including ';' + // needed when parsing: url("data:image/png;base64,abcd...") + tmp++; len++; + while ( *tmp && *tmp!=')' ) { + tmp++; len++; + } + } + else { + tmp++; len++; + } } str.append(decl,len); decl += len; @@ -2626,14 +2641,24 @@ bool LVCssDeclaration::parse( const char * &decl, bool higher_importance, lxmlDo const char *tmp = decl; int len = 0; while (*tmp && *tmp!=';' && *tmp!='}' && *tmp!='!') { - tmp++; len++; + if ( *tmp == '(' && *(tmp-3) == 'u' && *(tmp-2) == 'r' && *(tmp-1) == 'l') { + // Accepts everything until ')' after 'url(', including ';' + // needed when parsing: url("data:image/png;base64,abcd...") + tmp++; len++; + while ( *tmp && *tmp!=')' ) { + tmp++; len++; + } + } + else { + tmp++; len++; + } } lString8 str; str.append(decl,len); - if ( Utf8ToUnicode(str).lowercase().startsWith("url") ) { + if ( Utf8ToUnicode(str).lowercase().startsWith("url(") ) { tmp = str.c_str(); len = 0; - while (*tmp && *tmp!=';' && *tmp!='}' && *tmp!=')') { + while (*tmp && *tmp!=')') { tmp++; len++; } len = len + 1; From fd0523bbf6012e624902d22dcdd574877de9bee4 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 5 Jul 2020 17:56:30 +0200 Subject: [PATCH 7/8] Fix elements cancelling inherited "white-space: pre" All elements (except PRE-like ones) should start with "white-space: inherit" and not "white-space: normal", so they don't cancel "white-space: pre" when they are children of a
.
Only the root node is set to "white-space: normal",
thal all children will inherit from (except when
they get a speficied white-space: property value).
---
 crengine/include/dtddef.h  | 16 +++++++--------
 crengine/include/fb2def.h  | 40 +++++++++++++++++++-------------------
 crengine/src/lvtinydom.cpp |  4 ++--
 3 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/crengine/include/dtddef.h b/crengine/include/dtddef.h
index 2e6856e20..c9ee348fc 100644
--- a/crengine/include/dtddef.h
+++ b/crengine/include/dtddef.h
@@ -125,25 +125,25 @@ struct ns_def_t {
 #define XS_BEGIN_TAGS \
         static elem_def_t fb2_elem_table [] =  {
 #define XS_TAG1(itm) \
-        { el_ ## itm, #itm, {false, false, css_d_block, css_ws_normal} },
+        { el_ ## itm, #itm, {false, false, css_d_block, css_ws_inherit} },
 #define XS_TAG2(itm, name) \
-        { el_ ## itm, name, {false, false, css_d_block, css_ws_normal} },
+        { el_ ## itm, name, {false, false, css_d_block, css_ws_inherit} },
 #define XS_TAG1T(itm) \
-        { el_ ## itm, #itm, {true, false, css_d_block, css_ws_normal} },
+        { el_ ## itm, #itm, {true, false, css_d_block, css_ws_inherit} },
 #define XS_TAG1OBJ(itm) \
-        { el_ ## itm, #itm, {false, true, css_d_inline, css_ws_normal} },
+        { el_ ## itm, #itm, {false, true, css_d_inline, css_ws_inherit} },
 #define XS_TAG2T(itm, name) \
-        { el_ ## itm, name, {true, false, css_d_block, css_ws_normal} },
+        { el_ ## itm, name, {true, false, css_d_block, css_ws_inherit} },
 #define XS_TAG1I(itm) \
-        { el_ ## itm, #itm, {true, false, css_d_inline, css_ws_normal} },
+        { el_ ## itm, #itm, {true, false, css_d_inline, css_ws_inherit} },
 #define XS_TAG2I(itm, name) \
-        { el_ ## itm, name, {true, false, css_d_inline, css_ws_normal} },
+        { el_ ## itm, name, {true, false, css_d_inline, css_ws_inherit} },
 #define XS_TAG1D(itm, txt, disp, ws) \
         { el_ ## itm, #itm, {txt, false, disp, ws} },
 #define XS_TAG2D(itm, name, txt, false, disp, ws) \
         { el_ ## itm, name, {txt, false, disp, ws} },
 #define XS_END_TAGS \
-        { 0, NULL, {false, false, css_d_block, css_ws_normal} } \
+        { 0, NULL, {false, false, css_d_block, css_ws_inherit} } \
         };
 
 #undef  XS_BEGIN_ATTRS
diff --git a/crengine/include/fb2def.h b/crengine/include/fb2def.h
index f17ba5629..db5741035 100644
--- a/crengine/include/fb2def.h
+++ b/crengine/include/fb2def.h
@@ -48,7 +48,7 @@ XS_TAG1I( inlineBox )
 //  - it doesn't have a text node child, the content will be fetched from
 //    its style->content when rendering and drawing text.
 // It does not box anything and has no child, so it's not considered a boxing node.
-XS_TAG1D( pseudoElem, false, css_d_none, css_ws_normal )
+XS_TAG1D( pseudoElem, false, css_d_none, css_ws_inherit )
 
 // Internal element for EPUB, containing each individual HTML file
 XS_TAG1( DocFragment )
@@ -59,10 +59,10 @@ XS_TAG2( xml_stylesheet, "?xml-stylesheet" )
 // Classic HTML / EPUB elements
 XS_TAG1( html )
 XS_TAG1( head )
-XS_TAG1D( title, true, css_d_block, css_ws_normal )
-XS_TAG1D( style, true, css_d_none, css_ws_normal )
-XS_TAG1D( script, true, css_d_none, css_ws_normal )
-XS_TAG1D( base, false, css_d_none, css_ws_normal ) // among crengine autoclose elements
+XS_TAG1D( title, true, css_d_block, css_ws_inherit )
+XS_TAG1D( style, true, css_d_none, css_ws_inherit )
+XS_TAG1D( script, true, css_d_none, css_ws_inherit )
+XS_TAG1D( base, false, css_d_none, css_ws_inherit ) // among crengine autoclose elements
 XS_TAG1T( body )
 XS_TAG1( param ) /* quite obsolete, child of ... was there, let's keep it */
 
@@ -123,16 +123,16 @@ XS_TAG1T( dt )
 XS_TAG1T( dd )
 
 // Tables
-XS_TAG1D( table, false, css_d_table, css_ws_normal )
-XS_TAG1D( caption, true, css_d_table_caption, css_ws_normal )
-XS_TAG1D( col, false, css_d_table_column, css_ws_normal )
-XS_TAG1D( colgroup, false, css_d_table_column_group, css_ws_normal )
-XS_TAG1D( tr, false, css_d_table_row, css_ws_normal )
-XS_TAG1D( tbody, false, css_d_table_row_group, css_ws_normal )
-XS_TAG1D( thead, false, css_d_table_header_group, css_ws_normal )
-XS_TAG1D( tfoot, false, css_d_table_footer_group, css_ws_normal )
-XS_TAG1D( th, true, css_d_table_cell, css_ws_normal )
-XS_TAG1D( td, true, css_d_table_cell, css_ws_normal )
+XS_TAG1D( table, false, css_d_table, css_ws_inherit )
+XS_TAG1D( caption, true, css_d_table_caption, css_ws_inherit )
+XS_TAG1D( col, false, css_d_table_column, css_ws_inherit )
+XS_TAG1D( colgroup, false, css_d_table_column_group, css_ws_inherit )
+XS_TAG1D( tr, false, css_d_table_row, css_ws_inherit )
+XS_TAG1D( tbody, false, css_d_table_row_group, css_ws_inherit )
+XS_TAG1D( thead, false, css_d_table_header_group, css_ws_inherit )
+XS_TAG1D( tfoot, false, css_d_table_footer_group, css_ws_inherit )
+XS_TAG1D( th, true, css_d_table_cell, css_ws_inherit )
+XS_TAG1D( td, true, css_d_table_cell, css_ws_inherit )
 
 // Inline elements
 XS_TAG1OBJ( img ) /* inline and specific handling as 'object' */
@@ -168,7 +168,7 @@ XS_TAG1I( u )
 XS_TAG1I( var )
 
 // Ruby elements (defaults to inline)
-XS_TAG1D( ruby, true, css_d_ruby, css_ws_normal )
+XS_TAG1D( ruby, true, css_d_ruby, css_ws_inherit )
 XS_TAG1I( rbc ) // no more in HTML5, but in 2001's https://www.w3.org/TR/ruby/
 XS_TAG1I( rtc )
 XS_TAG1I( rb )
@@ -189,10 +189,10 @@ XS_TAG1( epigraph )
 XS_TAG1( part )
 XS_TAG1( poem )
 XS_TAG1( stanza )
-XS_TAG1D( binary, true, css_d_none, css_ws_normal )
-XS_TAG1D( description, false, css_d_none, css_ws_normal )
-XS_TAG1D( genre, true, css_d_none, css_ws_normal )
-XS_TAG1D( stylesheet, true, css_d_none, css_ws_normal )
+XS_TAG1D( binary, true, css_d_none, css_ws_inherit )
+XS_TAG1D( description, false, css_d_none, css_ws_inherit )
+XS_TAG1D( genre, true, css_d_none, css_ws_inherit )
+XS_TAG1D( stylesheet, true, css_d_none, css_ws_inherit )
 XS_TAG1I( spacing )
 XS_TAG1I( strikethrough )
 XS_TAG1I( underline )
diff --git a/crengine/src/lvtinydom.cpp b/crengine/src/lvtinydom.cpp
index ee6ab6da7..31d2d5327 100644
--- a/crengine/src/lvtinydom.cpp
+++ b/crengine/src/lvtinydom.cpp
@@ -84,7 +84,7 @@ int gDOMVersionRequested     = DOM_VERSION_CURRENT;
 
 /// change in case of incompatible changes in swap/cache file format to avoid using incompatible swap file
 // increment to force complete reload/reparsing of old file
-#define CACHE_FILE_FORMAT_VERSION "3.05.42k"
+#define CACHE_FILE_FORMAT_VERSION "3.05.43k"
 /// increment following value to force re-formatting of old book after load
 #define FORMATTING_VERSION_ID 0x0024
 
@@ -7428,7 +7428,7 @@ void ldomDocumentWriter::OnTagBody()
         _flags = _currNode->getFlags(); // _flags may have been updated (if white-space: pre)
         // And only after this we can add the  as a first child
         // element of this BODY node. It will not be displayed thanks to fb2def.h:
-        //   XS_TAG1D( stylesheet, true, css_d_none, css_ws_normal )
+        //   XS_TAG1D( stylesheet, true, css_d_none, css_ws_inherit )
         OnTagOpen(L"", L"stylesheet");
         OnTagBody();
         OnText(styleText.c_str(), styleText.length(), 0);

From 546b96dcc865ce36766790ff4fd4ecce5726afaa Mon Sep 17 00:00:00 2001
From: poire-z 
Date: Sun, 5 Jul 2020 18:16:41 +0200
Subject: [PATCH 8/8] initNodeStyle(): skip some possibly costly validation

---
 crengine/src/lvtinydom.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/crengine/src/lvtinydom.cpp b/crengine/src/lvtinydom.cpp
index 31d2d5327..5d4a46605 100644
--- a/crengine/src/lvtinydom.cpp
+++ b/crengine/src/lvtinydom.cpp
@@ -16124,11 +16124,16 @@ void ldomNode::initNodeStyle()
         {
             ldomNode * parent = getParentNode();
 
+            /* This has never triggered over the years, so trust we don't need it.
+             * This might also improve quite a bit TXT documents handling (where
+             * the main node may have tens of thousands of 
 children, one
+             * for each line of the text file.
+             *
             // DEBUG TEST
             if ( parent->getChildIndex( getDataIndex() )<0 ) {
                 CRLog::error("Invalid parent->child relation for nodes %d->%d", parent->getDataIndex(), getDataIndex() );
             }
-
+            */
 
             //lvdomElementFormatRec * parent_fmt = node->getParentNode()->getRenderData();
             css_style_ref_t style = parent->getStyle();