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

Page splitting: revamp, fix some issues #443

Merged
merged 11 commits into from
Jul 10, 2021
3 changes: 2 additions & 1 deletion crengine/include/cssdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ enum css_line_break_t {
css_lb_normal,
css_lb_loose,
css_lb_strict,
css_lb_anywhere
css_lb_anywhere,
css_lb_cr_loose // private value "line-break: -cr-loose" to ignore  
};

/// word-break property values
Expand Down
6 changes: 3 additions & 3 deletions crengine/include/lvarray.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,10 @@ class LVArray
_count++;
}

/// returns index of specified value, -1 if not found
int indexOf(int value) const {
/// returns index of specified item, -1 if not found
int indexOf(T item) const {
for ( int i=0; i<_count; i++ ) {
if ( _array[i] == value )
if ( _array[i] == item )
return i;
}
return -1;
Expand Down
3 changes: 3 additions & 0 deletions crengine/include/lvtextfm.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ typedef struct
lUInt32 height; /**< height of text fragment */
lUInt16 width; /**< width of text fragment */
lUInt16 page_height; /**< max page height */
LVHashTable<lUInt32, lString32Collection*> * inlineboxes_links;

// Each line box starts with a zero-width inline box (called "strut") with
// the element's font and line height properties:
Expand Down Expand Up @@ -482,6 +483,8 @@ class LFormattedText
return m_pbuffer->floats[index];
}

lString32Collection * GetInlineBoxLinks( ldomNode * node );

void Draw( LVDrawBuf * buf, int x, int y, ldomMarkedRangeList * marks = NULL, ldomMarkedRangeList *bookmarks = NULL );

bool isReusable() { return m_pbuffer->is_reusable; }
Expand Down
2 changes: 1 addition & 1 deletion crengine/src/lvdocview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ LVDocView::LVDocView(int bitsPerPixel, bool noDefaultDocument) :
m_pageMargins(DEFAULT_PAGE_MARGIN,
DEFAULT_PAGE_MARGIN / 2 /*+ INFO_FONT_SIZE + 4 */,
DEFAULT_PAGE_MARGIN, DEFAULT_PAGE_MARGIN / 2),
m_pagesVisible(2), m_pagesVisible_onlyIfSane(true),
m_pagesVisible(2), m_pagesVisible_onlyIfSane(true), m_twoVisiblePagesAsOnePageNumber(false),
m_pageHeaderInfo(PGHDR_PAGE_NUMBER
#ifndef LBOOK
| PGHDR_CLOCK
Expand Down
8 changes: 7 additions & 1 deletion crengine/src/lvopc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ static const lChar32 * const OPC_PropertiesContentType = U"application/vnd.openx

OpcPart::~OpcPart()
{
m_relations.clear();
LVHashTable<lString32, LVHashTable<lString32, lString32> *>::iterator it = m_relations.forwardIterator();
LVHashTable<lString32, LVHashTable<lString32, lString32> *>::pair* p;
while ((p = it.next()) != NULL) {
LVHashTable<lString32, lString32>* relationsTable = p->value;
if (relationsTable)
delete relationsTable;
}
}

LVStreamRef OpcPart::open()
Expand Down
464 changes: 461 additions & 3 deletions crengine/src/lvpagesplitter.cpp

Large diffs are not rendered by default.

54 changes: 41 additions & 13 deletions crengine/src/lvrend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5099,10 +5099,11 @@ class FlowState {
// allows knowing how much the main text glyphs and hanging punctuation
// can protrude inside this float (we limit that to the first level margin,
// not including any additional inner padding or margin)
lString32Collection links; // footnote links found in this float
bool is_right;
bool final_pos; // true if y0/y1 are the final absolute position and this
// float should not be moved when pushing vertical margins.
BlockFloat( int x0, int y0, int x1, int y1, bool r, int l, bool f, ldomNode * n=NULL) :
BlockFloat( int x0, int y0, int x1, int y1, bool r, int l, bool f, ldomNode * n=NULL, lString32Collection * linkids=NULL) :
lvRect(x0,y0,x1,y1),
level(l),
inward_margin(0),
Expand All @@ -5120,7 +5121,19 @@ class FlowState {
else
inward_margin = (x1 - x0) - (fmt.getX() + fmt.getWidth());
}
if (linkids && linkids->length() > 0) {
// This floats has footnotes, that we'll push to page context
// when the float is passed by
links.addAll(*linkids);
}
}
void addLinks(LVRendPageContext * context) {
// Associate footnote links with the last line added to context
for ( int n=0; n<links.length(); n++ ) {
context->addLink( links[n] );
}
links.clear(); // Be sure we don't add them again
}
};
int direction; // flow inline direction (LTR/RTL)
lInt32 lang_node_idx; // dataIndex of nearest upper node with a lang="" attribute (0 if none)
Expand Down Expand Up @@ -5215,6 +5228,7 @@ class FlowState {
// by leaveBlockLevel(). But let's ensure we clean up well.
for (int i=_floats.length()-1; i>=0; i--) {
BlockFloat * flt = _floats[i];
flt->addLinks(&context);
_floats.remove(i);
delete flt;
}
Expand Down Expand Up @@ -6123,6 +6137,7 @@ class FlowState {
for (int i=_floats.length()-1; i>=0; i--) {
BlockFloat * flt = _floats[i];
if (flt->level > level) {
flt->addLinks(&context);
_floats.remove(i);
delete flt;
}
Expand Down Expand Up @@ -6171,6 +6186,7 @@ class FlowState {
if (flt->bottom <= c_y) {
// This float is past, we shouldn't have to worry
// about it anymore
flt->addLinks(&context);
_floats.remove(i);
delete flt;
}
Expand Down Expand Up @@ -6322,7 +6338,7 @@ class FlowState {
}
return fit_top_y;
}
void addFloat( ldomNode * node, css_clear_t clear, bool is_right, int top_margin ) {
void addFloat( ldomNode * node, css_clear_t clear, bool is_right, int top_margin, lString32Collection * link_ids=NULL ) {
RenderRectAccessor fmt( node );
int width = fmt.getWidth();
int height = fmt.getHeight();
Expand All @@ -6339,7 +6355,7 @@ class FlowState {
fy = pos_y;
int fx = 0;
fy = getYWithAvailableWidth(fy, width, height, x_min, x_max, fx, is_right);
_floats.push( new BlockFloat( fx, fy, fx + width, fy + height, is_right, level, true, node) );
_floats.push( new BlockFloat( fx, fy, fx + width, fy + height, is_right, level, true, node, link_ids) );

// Get relative coordinates to current container top
shift_x = fx - x_min;
Expand Down Expand Up @@ -7830,14 +7846,9 @@ void renderBlockElementEnhanced( FlowState * flow, ldomNode * enode, int x, int
// no border (if borders, only the padding can be used).
renderBlockElement( alt_context, child, (is_rtl ? 0 : list_marker_padding) + padding_left,
0, width - list_marker_padding - padding_left - padding_right, 0, 0, direction );
flow->addFloat(child, child_clear, is_right, flt_vertical_margin);
// Gather footnotes links accumulated by alt_context
lString32Collection * link_ids = alt_context.getLinkIds();
if (link_ids->length() > 0) {
for ( int n=0; n<link_ids->length(); n++ ) {
flow->getPageContext()->addLink( link_ids->at(n) );
}
}
flow->addFloat(child, child_clear, is_right, flt_vertical_margin, alt_context.getLinkIds());
// We pass the footnote links accumulated by alt_context,
// so they can be forwarded onto the main context lines.
}
else {
css_clear_t child_clear = child_style->clear;
Expand Down Expand Up @@ -8278,6 +8289,17 @@ void renderBlockElementEnhanced( FlowState * flow, ldomNode * enode, int x, int
// check link start flag for every word
if ( line->words[w].flags & LTEXT_WORD_IS_LINK_START ) {
const src_text_fragment_t * src = txform->GetSrcInfo( line->words[w].src_text_index );
if ( line->words[w].flags & LTEXT_WORD_IS_INLINE_BOX ) {
// With an inline box, links were already parsed when it was rendered,
// and have been stored in the txform buffer
lString32Collection * links = txform->GetInlineBoxLinks( (ldomNode*)src->object );
if ( links ) {
for ( int n=0; n<links->length(); n++ ) {
flow->getPageContext()->addLink( links->at(n), link_insert_pos );
}
}
continue;
}
if ( src && src->object ) {
ldomNode * node = (ldomNode*)src->object;
ldomNode * parent = node->getParentNode();
Expand Down Expand Up @@ -10078,8 +10100,14 @@ void setNodeStyle( ldomNode * enode, css_style_ref_t parent_style, LVFontRef par
// it's better or not: we'll see.
// (Note that we can use * { font-variant: normal !important; } to
// stop any font-variant without !important from being applied.)
pstyle->font_features.value |= parent_style->font_features.value;
pstyle->font_features.type = css_val_unspecified;
// There is one case where we don't inherit: when styles had this
// node ending up being (css_val_unspecified, 0), which can only
// happen with "font-variant(-*): normal/none", that might be
// used to prevent some upper font-variant to be inherited.
if ( pstyle->font_features.type == css_val_inherited || pstyle->font_features.value != 0 ) {
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
Expand Down
6 changes: 3 additions & 3 deletions crengine/src/lvstring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ static int size_8 = 0;

/// get reference to atomic constant string for string literal e.g. cs8("abc") -- fast and memory effective
const lString8 & cs8(const char * str) {
int index = (int)(((ptrdiff_t)str * CONST_STRING_BUFFER_HASH_MULT) & CONST_STRING_BUFFER_MASK);
unsigned int index = (unsigned int)(((ptrdiff_t)str * CONST_STRING_BUFFER_HASH_MULT) & CONST_STRING_BUFFER_MASK);
for (;;) {
const void * p = const_ptrs_8[index];
if (p == str) {
Expand Down Expand Up @@ -93,7 +93,7 @@ static int size_32 = 0;

/// get reference to atomic constant wide string for string literal e.g. cs32("abc") -- fast and memory effective
const lString32 & cs32(const char * str) {
int index = (int)(((ptrdiff_t)str * CONST_STRING_BUFFER_HASH_MULT) & CONST_STRING_BUFFER_MASK);
unsigned int index = (unsigned int)(((ptrdiff_t)str * CONST_STRING_BUFFER_HASH_MULT) & CONST_STRING_BUFFER_MASK);
for (;;) {
const void * p = const_ptrs_32[index];
if (p == str) {
Expand All @@ -118,7 +118,7 @@ const lString32 & cs32(const char * str) {

/// get reference to atomic constant wide string for string literal e.g. cs32(U"abc") -- fast and memory effective
const lString32 & cs32(const lChar32 * str) {
int index = (((int)((ptrdiff_t)str)) * CONST_STRING_BUFFER_HASH_MULT) & CONST_STRING_BUFFER_MASK;
unsigned int index = (((unsigned int)((ptrdiff_t)str)) * CONST_STRING_BUFFER_HASH_MULT) & CONST_STRING_BUFFER_MASK;
for (;;) {
const void * p = const_ptrs_32[index];
if (p == str) {
Expand Down
12 changes: 11 additions & 1 deletion crengine/src/lvstsheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,7 @@ static const char * css_lb_names[] =
"loose",
"strict",
"anywhere",
"-cr-loose",
NULL
};

Expand Down Expand Up @@ -3185,7 +3186,16 @@ void LVCssDeclaration::apply( css_style_rec_t * style )
case cssd_font_features:
// We want to 'OR' the bitmap from any declaration that is to be applied to this node
// (while still ensuring !important).
style->ApplyAsBitmapOr( read_length(p), &style->font_features, imp_bit_font_features, is_important );
{
css_length_t font_features = read_length(p);
if ( font_features.value == 0 && font_features.type == css_val_unspecified ) {
// except if "font-variant: normal/none", which resets all previously set bits
style->Apply( font_features, &style->font_features, imp_bit_font_features, is_important );
}
else {
style->ApplyAsBitmapOr( font_features, &style->font_features, imp_bit_font_features, is_important );
}
}
break;
case cssd_text_indent:
style->Apply( read_length(p), &style->text_indent, imp_bit_text_indent, is_important );
Expand Down
74 changes: 58 additions & 16 deletions crengine/src/lvtextfm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,15 @@ void lvtextFreeFormatter( formatted_text_fragment_t * pbuffer )
}
free( pbuffer->floats );
}
if (pbuffer->inlineboxes_links)
{
LVHashTable<lUInt32, lString32Collection*>::iterator it = pbuffer->inlineboxes_links->forwardIterator();
LVHashTable<lUInt32, lString32Collection*>::pair* pair;
while ( (pair = it.next()) ) {
delete pair->value;
}
free( pbuffer->inlineboxes_links );
}
free(pbuffer);
}

Expand Down Expand Up @@ -2051,22 +2060,22 @@ class LVFormatter {
RENDER_RECT_SET_FLAG(fmt, BOX_IS_RENDERED);
// We'll have alignLine() do the fmt.setX/Y once it is fully positioned

// We'd like to gather footnote links accumulated by alt_context
// (we do that for floats), but it's quite more complicated:
// we have them here too early, and we would need to associate
// the links to this "char" index, so needing in LVFormatter
// something like:
// LVHashTable<lUInt32, lString32Collection> m_inlinebox_links
// When adding this inlineBox to a frmline, we could then get back
// the links, and associate them to the frmline (so, needing a
// new field holding a lString32Collection, which would hold
// all the links in all the inlineBoxs part of that line).
// Finally, in renderBlockElementEnhanced, when adding
// links for words, we'd also need to add the one found
// in the frmline's lString32Collection.
// A bit complicated, for a probably very rare case, so
// let's just forget it and not have footnotes from inlineBox
// among our in-page footnotes...
// Gather footnote links accumulated by alt_context
lString32Collection * link_ids = alt_context.getLinkIds();
if (link_ids->length() > 0) {
if ( m_pbuffer->inlineboxes_links == NULL ) {
m_pbuffer->inlineboxes_links = new LVHashTable<lUInt32, lString32Collection*>(16);
}
lString32Collection * links;
lUInt32 key = node->getDataIndex();
if ( !m_pbuffer->inlineboxes_links->get(key, links) ) {
links = new lString32Collection();
m_pbuffer->inlineboxes_links->set(key, links);
}
for ( int n=0; n<link_ids->length(); n++ ) {
links->add( link_ids->at(n) );
}
}
}
// (renderBlockElement() above may update our RenderRectAccessor(),
// so (re)get it only now)
Expand Down Expand Up @@ -2990,6 +2999,17 @@ class LVFormatter {
baseline_to_bottom = word->o.height - word->o.baseline;
// We can't really ensure strut_confined with inline-block boxes,
// or we could miss content (it would be overwritten by next lines)
if ( m_pbuffer->inlineboxes_links ) {
// The buffer has some inline boxes with footnote links.
// If this inline box has some, let lvrend.cpp know, so it can
// fetch them when adding this line to the page split context
lString32Collection * links;
lUInt32 key = ((ldomNode *) srcline->object)->getDataIndex();
if ( m_pbuffer->inlineboxes_links->get(key, links) ) {
word->flags |= LTEXT_WORD_IS_LINK_START;
// we re-use this flag already used by lvrend.cpp
}
}
}
else { // image
word->flags = LTEXT_WORD_IS_OBJECT;
Expand Down Expand Up @@ -4536,6 +4556,18 @@ static void freeFrmLines( formatted_text_fragment_t * m_pbuffer )
}
m_pbuffer->floats = NULL;
m_pbuffer->floatcount = 0;

// Also clear inlinebox links containers
if (m_pbuffer->inlineboxes_links)
{
LVHashTable<lUInt32, lString32Collection*>::iterator it = m_pbuffer->inlineboxes_links->forwardIterator();
LVHashTable<lUInt32, lString32Collection*>::pair* pair;
while ( (pair = it.next()) ) {
delete pair->value;
}
free( m_pbuffer->inlineboxes_links );
}
m_pbuffer->inlineboxes_links = NULL;
}

// experimental formatter
Expand Down Expand Up @@ -4617,6 +4649,16 @@ lUInt32 LFormattedText::Format(lUInt16 width, lUInt16 page_height, int para_dire
return h;
}

lString32Collection * LFormattedText::GetInlineBoxLinks( ldomNode * node ) {
if ( m_pbuffer->inlineboxes_links ) {
lString32Collection * links;
if ( m_pbuffer->inlineboxes_links->get(node->getDataIndex(), links) ) {
return links;
}
}
return NULL;
}

void LFormattedText::setImageScalingOptions( img_scaling_options_t * options )
{
m_pbuffer->img_zoom_in_mode_block = options->zoom_in_block.mode;
Expand Down
Loading