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

Enhanced block rendering: handle RTL direction #312

Merged
merged 7 commits into from
Sep 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cr3gui/data/epub.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ blockquote {
margin-left: 2em;
margin-right: 1em;
}
blockquote:dir(rtl) {
margin-left: 1em;
margin-right: 2em;
}
hr {
height: 2px;
background-color: #808080;
Expand All @@ -69,6 +73,10 @@ ol {
list-style-type: decimal;
margin-left: 1em;
}
ul:dir(rtl), ol:dir(rtl) {
margin-left: 0;
margin-right: 1em;
}
li {
display: list-item;
text-indent: 0;
Expand All @@ -86,6 +94,10 @@ dt {
dd {
margin-left: 1.3em;
}
dd:dir(rtl) {
margin-left: 0;
margin-right: 1.3em;
}

/* Tables */
table {
Expand Down
12 changes: 11 additions & 1 deletion crengine/include/cssdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ enum css_text_align_t {
css_ta_left,
css_ta_right,
css_ta_center,
css_ta_justify
css_ta_justify,
css_ta_start, // = left if LTR, right if RTL
css_ta_end // = right if LTR, left if LTR
};

/// vertical-align property values
Expand Down Expand Up @@ -274,6 +276,14 @@ enum css_clear_t {
css_c_both
};

/// direction property values
enum css_direction_t {
css_dir_inherit,
css_dir_unset,
css_dir_ltr,
css_dir_rtl
};

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"
Expand Down
18 changes: 10 additions & 8 deletions crengine/include/lvpagesplitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@

#define RN_SPLIT_DISCARD_AT_START 0x400

#define RN_LINE_IS_RTL 0x1000

#define RN_GET_SPLIT_BEFORE(flags) (flags & 0x7)
#define RN_GET_SPLIT_AFTER(flags) (flags >> 3)

enum page_type_t {
PAGE_TYPE_NORMAL = 0,
PAGE_TYPE_COVER = 1
};
#define RN_PAGE_TYPE_NORMAL 0x01
#define RN_PAGE_TYPE_COVER 0x02
#define RN_PAGE_MOSTLY_RTL 0x10
#define RN_PAGE_FOOTNOTES_MOSTLY_RTL 0x20

/// footnote fragment inside page
class LVPageFootNoteInfo {
Expand Down Expand Up @@ -215,14 +217,14 @@ class LVRendPageInfo {
int start; /// start of page
int index; /// index of page
lInt16 height; /// height of page, does not include footnotes
lInt16 type; /// type: PAGE_TYPE_NORMAL, PAGE_TYPE_COVER
lInt8 flags; /// RN_PAGE_*
CompactArray<LVPageFootNoteInfo, 1, 4> footnotes; /// footnote fragment list for page
LVRendPageInfo(int pageStart, lUInt16 pageHeight, int pageIndex)
: start(pageStart), index(pageIndex), height(pageHeight), type(PAGE_TYPE_NORMAL) {}
: start(pageStart), index(pageIndex), height(pageHeight), flags(RN_PAGE_TYPE_NORMAL) {}
LVRendPageInfo(lUInt16 coverHeight)
: start(0), index(0), height(coverHeight), type(PAGE_TYPE_COVER) {}
: start(0), index(0), height(coverHeight), flags(RN_PAGE_TYPE_COVER) {}
LVRendPageInfo()
: start(0), index(0), height(0), type(PAGE_TYPE_NORMAL) { }
: start(0), index(0), height(0), flags(RN_PAGE_TYPE_NORMAL) { }
bool serialize( SerialBuf & buf );
bool deserialize( SerialBuf & buf );
};
Expand Down
11 changes: 11 additions & 0 deletions crengine/include/lvptrvec.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ class LVPtrVector
}
_list[ indexTo ] = p;
}
/// reverse items
void reverse()
{
if ( empty() )
return;
for ( int i=0; i < _count/2; i++ ) {
T * tmp = _list[i];
_list[i] = _list[_count-1 - i];
_list[_count-1 - i] = tmp;
}
}
/// copy constructor
LVPtrVector( const LVPtrVector & v )
: _list(NULL), _size(0), _count(0)
Expand Down
48 changes: 35 additions & 13 deletions crengine/include/lvrend.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,37 @@

#include "lvtinydom.h"

// Current direction, from dir="ltr" or dir="rtl" element attribute
// Should map directly to the RENDER_RECT_FLAG_DIRECTION_* below
// (bit 0: 0=unset 1=set - bit 1: 0=normal 1=inverted - bit 2: 0=horizontal 1=vertical)
#define REND_DIRECTION_UNSET 0X00
#define REND_DIRECTION_LTR 0X01 // 0b001
#define REND_DIRECTION_RTL 0X03 // 0b011
// Not supported:
// #define REND_DIRECTION_TTB 0X05 // 0b101
// #define REND_DIRECTION_BTT 0X07 // 0b111

// Flags for RenderRectAccessor
#define RENDER_RECT_FLAG_INNER_FIELDS_SET 0x0001
#define RENDER_RECT_FLAG_NO_CLEAR_OWN_FLOATS 0x0002
#define RENDER_RECT_FLAG_FINAL_FOOTPRINT_AS_SAVED_FLOAT_IDS 0x0004
#define RENDER_RECT_FLAG_FLOATBOX_IS_RIGHT 0x0008
#define RENDER_RECT_FLAG_FLOATBOX_IS_RENDERED 0x0010
#define RENDER_RECT_FLAG_DIRECTION_SET 0x0001
#define RENDER_RECT_FLAG_DIRECTION_INVERTED 0x0002
#define RENDER_RECT_FLAG_DIRECTION_VERTICAL 0x0004 // only horizontal supported
#define RENDER_RECT_FLAG_INNER_FIELDS_SET 0x0008
#define RENDER_RECT_FLAG_NO_CLEAR_OWN_FLOATS 0x0010
#define RENDER_RECT_FLAG_FINAL_FOOTPRINT_AS_SAVED_FLOAT_IDS 0x0020
#define RENDER_RECT_FLAG_FLOATBOX_IS_RIGHT 0x0040
#define RENDER_RECT_FLAG_FLOATBOX_IS_RENDERED 0x0080

#define RENDER_RECT_SET_FLAG(r, f) ( r.setFlags( r.getFlags() | RENDER_RECT_FLAG_##f ) )
#define RENDER_RECT_UNSET_FLAG(r, f) ( r.setFlags( r.getFlags() & ~RENDER_RECT_FLAG_##f ) )
#define RENDER_RECT_HAS_FLAG(v, f) ( (bool)(v.getFlags() & RENDER_RECT_FLAG_##f) )
#define RENDER_RECT_HAS_FLAG(r, f) ( (bool)(r.getFlags() & RENDER_RECT_FLAG_##f) )

#define RENDER_RECT_FLAG_DIRECTION_MASK 0x0007
#define RENDER_RECT_SET_DIRECTION(r, d) ( r.setFlags( r.getFlags() | d ) )
#define RENDER_RECT_GET_DIRECTION(r) ( r.getFlags() & RENDER_RECT_FLAG_DIRECTION_MASK )
#define RENDER_RECT_PTR_GET_DIRECTION(r) ( r->getFlags() & RENDER_RECT_FLAG_DIRECTION_MASK )
#define RENDER_RECT_HAS_DIRECTION(r) ( (bool)(r.getFlags() & RENDER_RECT_FLAG_DIRECTION_SET) )
#define RENDER_RECT_HAS_DIRECTION_RTL(r) ( (bool)(r.getFlags() & RENDER_RECT_FLAG_DIRECTION_MASK == REND_DIRECTION_RTL) )
#define RENDER_RECT_PTR_HAS_DIRECTION_RTL(r) ( (bool)(r->getFlags() & RENDER_RECT_FLAG_DIRECTION_MASK == REND_DIRECTION_RTL) )

class FlowState;

Expand Down Expand Up @@ -90,17 +111,18 @@ void initFormatData( ldomNode * node );
/// initializes rendering method for node
int initRendMethod( ldomNode * node, bool recurseChildren, bool allowAutoboxing );
/// converts style to text formatting API flags
int styleToTextFmtFlags( const css_style_ref_t & style, int oldflags );
int styleToTextFmtFlags( const css_style_ref_t & style, int oldflags, int direction=REND_DIRECTION_UNSET );
/// renders block as single text formatter object
void renderFinalBlock( ldomNode * node, LFormattedText * txform, RenderRectAccessor * fmt, int & flags,
int ident, int line_h, int valign_dy=0, bool * is_link_start=NULL );
/// renders block which contains subblocks (with gRenderBlockRenderingFlags as flags)
int renderBlockElement( LVRendPageContext & context, ldomNode * node, int x, int y, int width );
int renderBlockElement( LVRendPageContext & context, ldomNode * enode, int x, int y, int width, int direction=REND_DIRECTION_UNSET );
/// renders block which contains subblocks
int renderBlockElement( LVRendPageContext & context, ldomNode * node, int x, int y, int width, int rend_flags );
int renderBlockElement( LVRendPageContext & context, ldomNode * enode, int x, int y, int width, int direction, int rend_flags );
/// renders table element
int renderTable( LVRendPageContext & context, ldomNode * element, int x, int y, int width, bool shrink_to_fit,
int & fitted_width, bool pb_inside_avoid=false, bool enhanced_rendering=false );
int renderTable( LVRendPageContext & context, ldomNode * element, int x, int y, int width,
bool shrink_to_fit, int & fitted_width, int direction=REND_DIRECTION_UNSET,
bool pb_inside_avoid=false, bool enhanced_rendering=false );
/// sets node style
void setNodeStyle( ldomNode * node, css_style_ref_t parent_style, LVFontRef parent_font );
/// copy style
Expand All @@ -115,10 +137,10 @@ void DrawDocument( LVDrawBuf & drawbuf, ldomNode * node, int x0, int y0, int dx,
// maxWidth: width if it would be rendered on an infinite width area
// minWidth: width with a wrap on all spaces (no hyphenation), so width taken by the longest word
// full function for recursive use:
void getRenderedWidths(ldomNode * node, int &maxWidth, int &minWidth, bool ignorePadding, int rendFlags,
void getRenderedWidths(ldomNode * node, int &maxWidth, int &minWidth, int direction, bool ignorePadding, int rendFlags,
int &curMaxWidth, int &curWordWidth, bool &collapseNextSpace, int &lastSpaceWidth, int indent);
// simpler function for first call:
void getRenderedWidths(ldomNode * node, int &maxWidth, int &minWidth, bool ignorePadding=false, int rendFlags=0);
void getRenderedWidths(ldomNode * node, int &maxWidth, int &minWidth, int direction=REND_DIRECTION_UNSET, bool ignorePadding=false, int rendFlags=0);

#define STYLE_FONT_EMBOLD_MODE_NORMAL 0
#define STYLE_FONT_EMBOLD_MODE_EMBOLD 300
Expand Down
6 changes: 6 additions & 0 deletions crengine/include/lvstsheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ typedef LVRef<LVCssDeclaration> LVCssDeclRef;
// See https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
enum LVCssSelectorPseudoClass
{
csspc_root, // :root
csspc_dir, // :dir(rtl), :dir(ltr)
csspc_first_child, // :first-child
csspc_first_of_type, // :first-of-type
csspc_nth_child, // :nth-child(even), :nth-child(3n+4)
Expand All @@ -107,10 +109,13 @@ enum LVCssSelectorPseudoClass
csspc_nth_last_of_type, // :nth-last-of-type()
csspc_only_child, // :only-child
csspc_only_of_type, // :only-of-type
csspc_empty, // :empty
};

static const char * css_pseudo_classes[] =
{
"root",
"dir",
"first-child",
"first-of-type",
"nth-child",
Expand All @@ -121,6 +126,7 @@ static const char * css_pseudo_classes[] =
"nth-last-of-type",
"only-child",
"only-of-type",
"empty",
NULL
};

Expand Down
9 changes: 6 additions & 3 deletions crengine/include/lvstyles.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ enum css_style_rec_important_bit {
imp_bit_widows = 1ULL << 53,
imp_bit_float = 1ULL << 54,
imp_bit_clear = 1ULL << 55,
imp_bit_cr_hint = 1ULL << 56
imp_bit_direction = 1ULL << 56,
imp_bit_cr_hint = 1ULL << 57
};

/**
Expand All @@ -92,8 +93,8 @@ typedef 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 57 css properties
// lvstsheet knows about 72, which are mapped to these 57
// we have currently below 58 css properties
// lvstsheet knows about 73, which are mapped to these 58
// 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)
Expand Down Expand Up @@ -140,6 +141,7 @@ typedef struct css_style_rec_tag {
css_orphans_widows_value_t widows;
css_float_t float_; // "float" is a C++ keyword...
css_clear_t clear;
css_direction_t direction;
css_cr_hint_t cr_hint;
css_style_rec_tag()
: refCount(0)
Expand Down Expand Up @@ -182,6 +184,7 @@ typedef struct css_style_rec_tag {
, widows(css_orphans_widows_inherit)
, float_(css_f_none)
, clear(css_c_none)
, direction(css_dir_inherit)
, cr_hint(css_cr_hint_none)
{
// css_length_t fields are initialized by css_length_tag()
Expand Down
6 changes: 4 additions & 2 deletions crengine/include/lvtextfm.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,9 @@ class LFormattedText
flags, interval, valign_dy, margin, object, (lUInt16)offset, letter_spacing );
}

lUInt32 Format(lUInt16 width, lUInt16 page_height, BlockFloatFootprint * float_footprint = NULL);
lUInt32 Format(lUInt16 width, lUInt16 page_height,
int para_direction=0, // = REND_DIRECTION_UNSET in lvrend.h
BlockFloatFootprint * float_footprint = NULL );

int GetSrcCount()
{
Expand Down Expand Up @@ -393,7 +395,7 @@ class LFormattedText
return m_pbuffer->floats[index];
}

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

LFormattedText() { m_pbuffer = lvtextAllocFormatter( 0 ); }

Expand Down
27 changes: 18 additions & 9 deletions crengine/src/lvdocview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1865,10 +1865,10 @@ void LVDocView::drawPageTo(LVDrawBuf * drawbuf, LVRendPageInfo & page,
// end of line with some fonts) to not be cut by this clipping.
clip.left = pageRect->left;
clip.right = pageRect->left + pageRect->width();
if (page.type == PAGE_TYPE_COVER)
if (page.flags & RN_PAGE_TYPE_COVER)
clip.top = pageRect->top + m_pageMargins.top;
if (((m_pageHeaderInfo || !m_pageHeaderOverride.empty()) && page.type
!= PAGE_TYPE_COVER) && getViewMode() == DVM_PAGES) {
if ( ( (m_pageHeaderInfo || !m_pageHeaderOverride.empty()) && (page.flags & RN_PAGE_TYPE_NORMAL) )
&& getViewMode() == DVM_PAGES ) {
int phi = m_pageHeaderInfo;
if (getVisiblePageCount() == 2) {
if (page.index & 1) {
Expand All @@ -1892,7 +1892,7 @@ void LVDocView::drawPageTo(LVDrawBuf * drawbuf, LVRendPageInfo & page,
}
drawbuf->SetClipRect(&clip);
if (m_doc) {
if (page.type == PAGE_TYPE_COVER) {
if (page.flags & RN_PAGE_TYPE_COVER) {
lvRect rc = *pageRect;
drawbuf->SetClipRect(&rc);
//if ( m_pageMargins.bottom > m_pageMargins.top )
Expand Down Expand Up @@ -1962,8 +1962,17 @@ void LVDocView::drawPageTo(LVDrawBuf * drawbuf, LVRendPageInfo & page,
// The line separator was using the full page width:
// int x1 = pageRect->right - m_pageMargins.right;
// but 1/7 of page width looks like what we can see in some books
int x1 = pageRect->left + m_pageMargins.left + (pageRect->right-pageRect->left)/7;
drawbuf->FillRect(pageRect->left + m_pageMargins.left, fny, x1, fny+1, cl);
int sep_width = (pageRect->right - pageRect->left) / 7;
int x0, x1;
if ( page.flags & RN_PAGE_FOOTNOTES_MOSTLY_RTL ) { // draw separator on the right
x1 = pageRect->right - m_pageMargins.right;
x0 = x1 - sep_width;
}
else {
x0 = pageRect->left + m_pageMargins.left;
x1 = x0 + sep_width;
}
drawbuf->FillRect(x0, fny, x1, fny+1, cl);
}
}
}
Expand Down Expand Up @@ -2254,7 +2263,7 @@ void LVDocView::Draw(LVDrawBuf & drawbuf, int position, int page, bool rotate, b
drawbuf.SetClipRect(NULL);
drawPageBackground(drawbuf, 0, position);
int cover_height = 0;
if (m_pages.length() > 0 && m_pages[0]->type == PAGE_TYPE_COVER)
if (m_pages.length() > 0 && (m_pages[0]->flags & RN_PAGE_TYPE_COVER))
cover_height = m_pages[0]->height;
if (position < cover_height) {
lvRect rc;
Expand Down Expand Up @@ -2536,7 +2545,7 @@ LVRef<ldomXRange> LVDocView::getPageDocumentRange(int pageIndex) {
pageIndex = getCurPage();
if (pageIndex >= 0 && pageIndex < m_pages.length()) {
LVRendPageInfo * page = m_pages[pageIndex];
if (page->type != PAGE_TYPE_NORMAL)
if (page->flags & RN_PAGE_TYPE_COVER)
return res;
start_y = page->start;
end_y = page->start + page->height;
Expand Down Expand Up @@ -4664,7 +4673,7 @@ ldomXPointer LVDocView::getCurrentPageMiddleParagraph() {
pageIndex = getCurPage();
if (pageIndex >= 0 && pageIndex < m_pages.length()) {
LVRendPageInfo *page = m_pages[pageIndex];
if (page->type == PAGE_TYPE_NORMAL)
if (page->flags & RN_PAGE_TYPE_NORMAL)
ptr = m_doc->createXPointer(lvPoint(0, page->start + page->height / 2));
}
}
Expand Down
Loading