Skip to content

Commit

Permalink
CSS: switch -cr-hint from enum to bitmap
Browse files Browse the repository at this point in the history
This allows setting multiple hints on a same node
(i.e. "-cr-hint: footnote-inpage strut-confined").
  • Loading branch information
poire-z committed Jul 19, 2020
1 parent c9faa83 commit d159a66
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 83 deletions.
70 changes: 48 additions & 22 deletions crengine/include/cssdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,28 +336,54 @@ enum css_generic_value_t {
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
// (see src/lvstsheet.cpp css_cr_hint_names[]= for explanations)
enum css_cr_hint_t {
css_cr_hint_inherit,
css_cr_hint_none,
css_cr_hint_noteref,
css_cr_hint_noteref_ignore,
css_cr_hint_footnote,
css_cr_hint_footnote_ignore,
css_cr_hint_footnote_inpage,
css_cr_hint_toc_level1,
css_cr_hint_toc_level2,
css_cr_hint_toc_level3,
css_cr_hint_toc_level4,
css_cr_hint_toc_level5,
css_cr_hint_toc_level6,
css_cr_hint_toc_ignore,
css_cr_hint_strut_confined,
css_cr_hint_text_selection_inline,
css_cr_hint_text_selection_block,
css_cr_hint_text_selection_skip
};
// -cr-hint is a non standard property for providing hints to crengine via style tweaks
// Handled as a bitmap, with a flag for each hint, as we might set multiple on a same node (max 31 bits)
#define CSS_CR_HINT_NONE 0x00000000 // default value

// Reset any hint previously set and don't inherit any from parent
#define CSS_CR_HINT_NONE_NO_INHERIT 0x00000001 // -cr-hint: none

// Text and images should not overflow/modify their paragraph strut baseline and height
// (it could have been a non-standard named value for line-height:, but we want to be
// able to not override existing line-height: values)
#define CSS_CR_HINT_STRUT_CONFINED 0x00000002 // -cr-hint: strut-confined (inheritable)

// A node with these should be considered as TOC item of level N when building alternate TOC
#define CSS_CR_HINT_TOC_LEVEL1 0x00000100 // -cr-hint: toc-level1
#define CSS_CR_HINT_TOC_LEVEL2 0x00000200 // -cr-hint: toc-level2
#define CSS_CR_HINT_TOC_LEVEL3 0x00000400 // -cr-hint: toc-level3
#define CSS_CR_HINT_TOC_LEVEL4 0x00000800 // -cr-hint: toc-level4
#define CSS_CR_HINT_TOC_LEVEL5 0x00001000 // -cr-hint: toc-level5
#define CSS_CR_HINT_TOC_LEVEL6 0x00002000 // -cr-hint: toc-level6
#define CSS_CR_HINT_TOC_LEVELS_MASK 0x00003F00
// Ignore H1...H6 that have this when building alternate TOC
#define CSS_CR_HINT_TOC_IGNORE 0x00004000 // -cr-hint: toc-ignore

// Tweak text selection behaviour when traversing a node with these hints
#define CSS_CR_HINT_TEXT_SELECTION_INLINE 0x00010000 // -cr-hint: text-selection-inline don't add a '\n' before inner text
// (even if the node happens to be block)
#define CSS_CR_HINT_TEXT_SELECTION_BLOCK 0x00020000 // -cr-hint: text-selection-block add a '\n' before inner text (even
// if the node happens to be inline)
#define CSS_CR_HINT_TEXT_SELECTION_SKIP 0x00040000 // -cr-hint: text-selection-skip don't include inner text in selection

// To be set on a block element: it is a footnote (must be a full footnote block container),
// and to be displayed at the bottom of all pages that contain a link to it.
#define CSS_CR_HINT_FOOTNOTE_INPAGE 0x00080000 // -cr-hint: footnote-inpage

// For footnote popup detection by koreader-base/cre.cpp
#define CSS_CR_HINT_NOTEREF 0x01000000 // -cr-hint: noteref link is to a footnote
#define CSS_CR_HINT_NOTEREF_IGNORE 0x02000000 // -cr-hint: noteref-ignore link is not to a footnote (even if
// everything else indicates it is)
#define CSS_CR_HINT_FOOTNOTE 0x04000000 // -cr-hint: footnote block is a footnote (must be a full
// footnote block container)
#define CSS_CR_HINT_FOOTNOTE_IGNORE 0x08000000 // -cr-hint: footnote-ignore block is not a footnote (even if
// everything else indicates it is)

// A few of them are inheritable, most are not.
#define CSS_CR_HINT_INHERITABLE_MASK 0x00000002

// Macro for easier checking
#define STYLE_HAS_CR_HINT(s, h) ( (bool)(s->cr_hint.value & CSS_CR_HINT_##h) )

/// css length value
typedef struct css_length_tag {
Expand Down
6 changes: 3 additions & 3 deletions crengine/include/lvstyles.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ struct css_style_rec_tag {
css_clear_t clear;
css_direction_t direction;
lString16 content;
css_cr_hint_t cr_hint;
css_length_t cr_hint;
// The following should only be used when applying stylesheets while in lvend.cpp setNodeStyle(),
// and cleaned up there, before the style is cached and shared. They are not serialized.
lInt8 flags; // bitmap of STYLE_REC_FLAG_*
Expand Down Expand Up @@ -202,7 +202,7 @@ struct css_style_rec_tag {
, float_(css_f_none)
, clear(css_c_none)
, direction(css_dir_inherit)
, cr_hint(css_cr_hint_none)
, cr_hint(css_val_inherited, 0)
, flags(0)
, pseudo_elem_before_style(NULL)
, pseudo_elem_after_style(NULL)
Expand Down Expand Up @@ -240,7 +240,7 @@ struct css_style_rec_tag {
if (is_important == 0x3) importance |= bit; // update importance flag (!important comes from higher_importance CSS)
}
}
// Similar to previous one, but logical-OR'ing values, for bitmaps (currently, only style->font_features)
// Similar to previous one, but logical-OR'ing values, for bitmaps (currently, only style->font_features and style->cr_hint)
inline void ApplyAsBitmapOr( css_length_t value, css_length_t *field, css_style_rec_important_bit bit, lUInt8 is_important ) {
if ( !(important & bit)
|| (is_important == 0x3)
Expand Down
23 changes: 18 additions & 5 deletions crengine/src/lvrend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2380,7 +2380,7 @@ lString16 renderListItemMarker( ldomNode * enode, int & marker_width, LFormatted
// Scale it according to gInterlineScaleFactor
if (style->line_height.type != css_val_screen_px && gInterlineScaleFactor != INTERLINE_SCALE_FACTOR_NO_SCALE)
line_h = (line_h * gInterlineScaleFactor) >> INTERLINE_SCALE_FACTOR_SHIFT;
if ( style->cr_hint == css_cr_hint_strut_confined )
if ( STYLE_HAS_CR_HINT(style, STRUT_CONFINED) )
flags |= LTEXT_STRUT_CONFINED;
}
marker += "\t";
Expand Down Expand Up @@ -2657,7 +2657,7 @@ void renderFinalBlock( ldomNode * enode, LFormattedText * txform, RenderRectAcce
int f_half_leading = (line_h - fh) / 2;
txform->setStrut(line_h, fb + f_half_leading);
}
else if ( style->cr_hint == css_cr_hint_strut_confined ) {
else if ( STYLE_HAS_CR_HINT(style, STRUT_CONFINED) ) {
// Previous branch for the top final node has set the strut.
// Inline nodes having "-cr-hint: strut-confined" will be confined
// inside that strut.
Expand Down Expand Up @@ -3533,7 +3533,8 @@ void copystyle( css_style_ref_t source, css_style_ref_t dest )
dest->clear = source->clear;
dest->direction = source->direction;
dest->content = source->content ;
dest->cr_hint = source->cr_hint;
dest->cr_hint.type = source->cr_hint.type ;
dest->cr_hint.value = source->cr_hint.value ;
}

// Only used by renderBlockElementLegacy()
Expand Down Expand Up @@ -3771,7 +3772,7 @@ int renderBlockElementLegacy( LVRendPageContext & context, ldomNode * enode, int
lString16 footnoteId;
// Allow displaying footnote content at the bottom of all pages that contain a link
// to it, when -cr-hint: footnote-inpage is set on the footnote block container.
if ( style->cr_hint == css_cr_hint_footnote_inpage &&
if ( STYLE_HAS_CR_HINT(style, FOOTNOTE_INPAGE) &&
enode->getDocument()->getDocFlag(DOC_FLAG_ENABLE_FOOTNOTES)) {
footnoteId = enode->getFirstInnerAttributeValue(attr_id);
if ( !footnoteId.empty() )
Expand Down Expand Up @@ -6080,7 +6081,7 @@ void renderBlockElementEnhanced( FlowState * flow, ldomNode * enode, int x, int
lString16 footnoteId;
// Allow displaying footnote content at the bottom of all pages that contain a link
// to it, when -cr-hint: footnote-inpage is set on the footnote block container.
if ( style->cr_hint == css_cr_hint_footnote_inpage &&
if ( STYLE_HAS_CR_HINT(style, FOOTNOTE_INPAGE) &&
enode->getDocument()->getDocFlag(DOC_FLAG_ENABLE_FOOTNOTES)) {
footnoteId = enode->getFirstInnerAttributeValue(attr_id);
if ( !footnoteId.empty() )
Expand Down Expand Up @@ -9036,6 +9037,18 @@ void setNodeStyle( ldomNode * enode, css_style_ref_t parent_style, LVFontRef par
pstyle->font_features.value |= parent_style->font_features.value;
pstyle->font_features.type = css_val_unspecified;

// cr_hint is also a bitmap, and only some bits are inherited.
// A node starts with (css_val_inherited, 0), but if some
// stylesheet has applied some -cr-hint to it, we meet it
// here with (css_val_unspecified, bitmap) and we report the
// inheritable bits from the parent.
// Unless "-cr-hint: none" has been applied to the node, which
// prevents inheritance
if ( !STYLE_HAS_CR_HINT(pstyle, NONE_NO_INHERIT) ) {
pstyle->cr_hint.value |= (parent_style->cr_hint.value & CSS_CR_HINT_INHERITABLE_MASK);
pstyle->cr_hint.type = css_val_unspecified;
}

//UPDATE_LEN_FIELD( text_indent );
spreadParent( pstyle->text_indent, parent_style->text_indent );
switch( pstyle->font_weight )
Expand Down
96 changes: 60 additions & 36 deletions crengine/src/lvstsheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1789,40 +1789,6 @@ static const char * css_dir_names[] =
NULL
};

// -cr-hint names (non standard property for providing hints to crengine via style tweaks)
static const char * css_cr_hint_names[]={
"inherit",
"none",
// For footnote popup detection:
"noteref", // link is to a footnote
"noteref-ignore", // link is not to a footnote (even if everything else indicates it is)
"footnote", // block is a footnote (must be a full footnote block container)
"footnote-ignore", // block is not a footnote (even if everything else indicates it is)
"footnote-inpage", // block is a footnote (must be a full footnote block container), and to be
// displayed at the bottom of all pages that contain a link to it.
"toc-level1", // to be considered as TOC item of level N when building alternate TOC
"toc-level2",
"toc-level3",
"toc-level4",
"toc-level5",
"toc-level6",
"toc-ignore", // ignore these H1...H6 when building alternate TOC

// Next one is not really a hint, but might have some active effect on rendering/layout.
// It has effect on inline nodes only, while the ones above mostly apply to block
// nodes. So, provide it with a lower specificity if those above also need to be used.
"strut-confined", // text and images should not overflow/modify their paragraph strut
// baseline and height (it could have been a non-standard named
// value for line-height:, but we want to be able to not override
// existing line-height: values)

// Tweak text selection when traversing a node with these hints
"text-selection-inline", // don't add a '\n' before inner text, even if the node happens to be block
"text-selection-block", // add a '\n' before inner text even if the node happens to be inline
"text-selection-skip", // don't include inner text in text selection
NULL
};

static const char * css_cr_only_if_names[]={
"any",
"always",
Expand Down Expand Up @@ -1973,7 +1939,54 @@ bool LVCssDeclaration::parse( const char * &decl, bool higher_importance, lxmlDo
break;
// non standard property for providing hints via style tweaks
case cssd_cr_hint:
n = parse_name( decl, css_cr_hint_names, -1 );
{
// All values are mapped into a single style->cr_hint 31 bits bitmap
int hints = 0; // "none" = no hint
int nb_parsed = 0;
int nb_invalid = 0;
while ( *decl && *decl !=';' && *decl!='}') {
// Details in crengine/include/cssdef.h (checks ordered by most likely to be seen)
if ( substr_icompare("none", decl) ) {
// Forget everything parsed previously, and prevent inheritance
hints = CSS_CR_HINT_NONE_NO_INHERIT;
}
else if ( substr_icompare("footnote-inpage", decl) ) hints |= CSS_CR_HINT_FOOTNOTE_INPAGE;
else if ( substr_icompare("strut-confined", decl) ) hints |= CSS_CR_HINT_STRUT_CONFINED;
else if ( substr_icompare("text-selection-skip", decl) ) hints |= CSS_CR_HINT_TEXT_SELECTION_SKIP;
else if ( substr_icompare("text-selection-inline", decl) ) hints |= CSS_CR_HINT_TEXT_SELECTION_INLINE;
else if ( substr_icompare("text-selection-block", decl) ) hints |= CSS_CR_HINT_TEXT_SELECTION_BLOCK;
else if ( substr_icompare("toc-level1", decl) ) hints |= CSS_CR_HINT_TOC_LEVEL1;
else if ( substr_icompare("toc-level2", decl) ) hints |= CSS_CR_HINT_TOC_LEVEL2;
else if ( substr_icompare("toc-level3", decl) ) hints |= CSS_CR_HINT_TOC_LEVEL3;
else if ( substr_icompare("toc-level4", decl) ) hints |= CSS_CR_HINT_TOC_LEVEL4;
else if ( substr_icompare("toc-level5", decl) ) hints |= CSS_CR_HINT_TOC_LEVEL5;
else if ( substr_icompare("toc-level6", decl) ) hints |= CSS_CR_HINT_TOC_LEVEL6;
else if ( substr_icompare("toc-ignore", decl) ) hints |= CSS_CR_HINT_TOC_IGNORE;
else if ( substr_icompare("noteref", decl) ) hints |= CSS_CR_HINT_NOTEREF;
else if ( substr_icompare("noteref-ignore", decl) ) hints |= CSS_CR_HINT_NOTEREF_IGNORE;
else if ( substr_icompare("footnote", decl) ) hints |= CSS_CR_HINT_FOOTNOTE;
else if ( substr_icompare("footnote-ignore", decl) ) hints |= CSS_CR_HINT_FOOTNOTE_IGNORE;
//
else if ( parse_important(decl) ) {
parsed_important = IMPORTANT_DECL_SET;
break; // stop looking for more
}
else { // unsupported or invalid named value
nb_invalid++;
// Walk over unparsed value, and continue checking
while (*decl && *decl !=' ' && *decl !=';' && *decl!='}')
decl++;
}
nb_parsed++;
skip_spaces( decl );
}
if ( nb_parsed - nb_invalid > 0 ) { // at least one valid named value seen
buf<<(lUInt32) (prop_code | importance | parsed_important);
buf<<(lUInt32) css_val_unspecified; // len.type
buf<<(lUInt32) hints; // len.value
// (css_val_unspecified just says this value has no unit)
}
}
break;
case cssd_display:
n = parse_name( decl, css_d_names, -1 );
Expand Down Expand Up @@ -3158,7 +3171,18 @@ void LVCssDeclaration::apply( css_style_rec_t * style )
style->Apply( (css_direction_t) *p++, &style->direction, imp_bit_direction, is_important );
break;
case cssd_cr_hint:
style->Apply( (css_cr_hint_t) *p++, &style->cr_hint, imp_bit_cr_hint, is_important );
{
// We want to 'OR' the bitmap from any declaration that is to be applied to this node
// (while still ensuring !important) - unless this declaration had "-cr-hint: none"
// in which case we should reset previously set bits
css_length_t cr_hint = read_length(p);
if ( cr_hint.value & CSS_CR_HINT_NONE_NO_INHERIT ) {
style->Apply( cr_hint, &style->cr_hint, imp_bit_cr_hint, is_important );
}
else {
style->ApplyAsBitmapOr( cr_hint, &style->cr_hint, imp_bit_cr_hint, is_important );
}
}
break;
case cssd_content:
{
Expand Down
6 changes: 3 additions & 3 deletions crengine/src/lvstyles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ lUInt32 calcHash(css_style_rec_t & rec)
+ (lUInt32)rec.float_) * 31
+ (lUInt32)rec.clear) * 31
+ (lUInt32)rec.direction) * 31
+ (lUInt32)rec.cr_hint) * 31
+ (lUInt32)rec.cr_hint.pack()) * 31
+ (lUInt32)rec.font_name.getHash()
+ (lUInt32)rec.background_image.getHash()
+ (lUInt32)rec.content.getHash());
Expand Down Expand Up @@ -363,7 +363,7 @@ bool css_style_rec_t::serialize( SerialBuf & buf )
ST_PUT_ENUM(clear);
ST_PUT_ENUM(direction);
buf << content;
ST_PUT_ENUM(cr_hint);
ST_PUT_LEN(cr_hint);
lUInt32 hash = calcHash(*this);
buf << hash;
return !buf.error();
Expand Down Expand Up @@ -424,7 +424,7 @@ bool css_style_rec_t::deserialize( SerialBuf & buf )
ST_GET_ENUM(css_clear_t, clear);
ST_GET_ENUM(css_direction_t, direction);
buf>>content;
ST_GET_ENUM(css_cr_hint_t, cr_hint);
ST_GET_LEN(cr_hint);
lUInt32 hash = 0;
buf >> hash;
// printf("imp: %llx oldhash: %lx ", important, hash);
Expand Down
Loading

0 comments on commit d159a66

Please sign in to comment.