diff --git a/cr3gui/data/epub.css b/cr3gui/data/epub.css index 196516c6e..77865f9cc 100644 --- a/cr3gui/data/epub.css +++ b/cr3gui/data/epub.css @@ -137,7 +137,7 @@ table caption { * approximation than no pre */ pre, xmp { white-space: pre; /* has to be set in fb2def.h to work */ - font-family: "Droid Sans Mono", "Liberation Mono", "DeJaVu Sans Mono", monospace; + font-family: monospace; text-align: left; margin-top: 0.5em; margin-bottom: 0.5em; @@ -145,7 +145,7 @@ pre, xmp { } /* Inline elements (all unknown elements default now to display: inline) */ -code, kbd, samp, tt { font-family: "Droid Sans Mono", "Liberation Mono", "DeJaVu Sans Mono", monospace; } +code, kbd, samp, tt { font-family: monospace; } sup { font-size: 70%; vertical-align: super; } sub { font-size: 70%; vertical-align: sub; } small { font-size: 80%; } diff --git a/cr3gui/data/fb2.css b/cr3gui/data/fb2.css index 3da176a2d..8447d1c1f 100644 --- a/cr3gui/data/fb2.css +++ b/cr3gui/data/fb2.css @@ -83,11 +83,11 @@ th { font-weight: bold; text-align: center; background-color: #DDD } table caption { text-indent: 0px; padding: 4px; background-color: #EEE } table, th, td { border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse; } -tt, samp, kbd { font-family: "Droid Sans Mono", "Liberation Mono", "DeJaVu Sans Mono", "Courier New", "Courier", monospace; } +tt, samp, kbd { font-family: monospace; } code, pre { white-space: pre; text-align: left; - font-family: "Droid Sans Mono", "Liberation Mono", "DeJaVu Sans Mono", "Courier New", "Courier", monospace; + font-family: monospace; } code { display: inline; diff --git a/crengine/include/lvdocviewprops.h b/crengine/include/lvdocviewprops.h index 12a3b179a..86df91e33 100644 --- a/crengine/include/lvdocviewprops.h +++ b/crengine/include/lvdocviewprops.h @@ -10,6 +10,7 @@ #define PROP_FONT_COLOR "font.color.default" #define PROP_FONT_FACE "font.face.default" #define PROP_FONT_BASE_WEIGHT "font.face.base.weight" // replaces PROP_FONT_WEIGHT_EMBOLDEN ("font.face.weight.embolden") +#define PROP_FONT_MONOSPACE_SCALE "font.monospace.size.scale.percent" #define PROP_BACKGROUND_COLOR "background.color.default" #define PROP_BACKGROUND_IMAGE "background.image.filename" #define PROP_TXT_OPTION_PREFORMATTED "crengine.file.txt.preformatted" @@ -18,6 +19,7 @@ #define PROP_LOG_AUTOFLUSH "crengine.log.autoflush" #define PROP_FONT_SIZE "crengine.font.size" #define PROP_FALLBACK_FONT_FACES "crengine.font.fallback.faces" +#define PROP_FALLBACK_FONT_SIZES_ADJUSTED "crengine.font.fallback.sizes.adjusted" // multiple fallback font faces are to be separated by '|' #define PROP_STATUS_FONT_COLOR "crengine.page.header.font.color" #define PROP_STATUS_FONT_FACE "crengine.page.header.font.face" diff --git a/crengine/include/lvfntman.h b/crengine/include/lvfntman.h index 497d22ecd..bf3bf2372 100644 --- a/crengine/include/lvfntman.h +++ b/crengine/include/lvfntman.h @@ -595,6 +595,8 @@ class LVFontManager int _antialiasMode; kerning_mode_t _kerningMode; hinting_mode_t _hintingMode; + int _monospaceSizeScale; + bool _fallbackFontSizesAdjusted; public: /// garbage collector frees unused fonts virtual void gc() = 0; @@ -643,8 +645,19 @@ class LVFontManager /// get kerning mode: true==ON, false=OFF virtual void SetKerningMode( kerning_mode_t mode ) { _kerningMode = mode; gc(); clearGlyphCache(); } + /// get monospace size scale percent + virtual int GetMonospaceSizeScale() { return _monospaceSizeScale; } + /// set monospace size scale percent + virtual void SetMonospaceSizeScale( int scale ) { _monospaceSizeScale = scale; gc(); clearGlyphCache(); } + + /// get adjusted fallback font sizes state + virtual int GetFallbackFontSizesAdjusted() { return _fallbackFontSizesAdjusted; } + /// set adjusted fallback font sizes state + virtual void SetFallbackFontSizesAdjusted( bool adjusted ) { _fallbackFontSizesAdjusted = adjusted; gc(); } + /// constructor - LVFontManager() : _antialiasMode(font_aa_all), _kerningMode(KERNING_MODE_DISABLED), _hintingMode(HINTING_MODE_AUTOHINT) { } + LVFontManager() : _antialiasMode(font_aa_all), _kerningMode(KERNING_MODE_DISABLED), _hintingMode(HINTING_MODE_AUTOHINT) + , _monospaceSizeScale(100), _fallbackFontSizesAdjusted(false) { } /// destructor virtual ~LVFontManager() { } /// returns available typefaces @@ -652,7 +665,7 @@ class LVFontManager /// returns available font files virtual void getFontFileNameList( lString32Collection & ) { } /// returns font filename and face index for font name - virtual bool getFontFileNameAndFaceIndex( lString32 name, bool bold, bool italic, lString8 & filename, int & index ) { return false; } + virtual bool getFontFileNameAndFaceIndex( lString32 name, bool bold, bool italic, lString8 & filename, int & index, int & family_type ) { return false; } /// returns registered or instantiated document embedded font list virtual void getRegisteredDocumentFontList( int document_id, lString32Collection & list ) { } virtual void getInstantiatedDocumentFontList( int document_id, lString32Collection & list ) { } diff --git a/crengine/src/lvdocview.cpp b/crengine/src/lvdocview.cpp index 20aac5e78..cf9703fa4 100644 --- a/crengine/src/lvdocview.cpp +++ b/crengine/src/lvdocview.cpp @@ -6765,6 +6765,12 @@ CRPropRef LVDocView::propsApply(CRPropRef props) { if ( UnicodeToUtf8(value) != oldFaces ) { REQUEST_RENDER("propsApply fallback font faces") } + } else if (name == PROP_FALLBACK_FONT_SIZES_ADJUSTED) { + bool adjusted = props->getIntDef(PROP_FALLBACK_FONT_SIZES_ADJUSTED, false); + if (fontMan->GetFallbackFontSizesAdjusted() != adjusted) { + fontMan->SetFallbackFontSizesAdjusted(adjusted); + REQUEST_RENDER("propsApply - adjusted fallback font sizes") + } } else if (name == PROP_STATUS_FONT_FACE) { setStatusFontFace(UnicodeToUtf8(value)); } else if (name == PROP_STATUS_LINE || name == PROP_SHOW_TIME @@ -6800,6 +6806,12 @@ CRPropRef LVDocView::propsApply(CRPropRef props) { fontSize = MAX_STATUS_FONT_SIZE; setStatusFontSize(fontSize);//cr_font_sizes value = lString32::itoa(fontSize); + } else if (name == PROP_FONT_MONOSPACE_SCALE) { + int scale = props->getIntDef(PROP_FONT_MONOSPACE_SCALE, 100); + if (fontMan->GetMonospaceSizeScale() != scale) { + fontMan->SetMonospaceSizeScale(scale); + REQUEST_RENDER("propsApply - font monospace size scale") + } } else if (name == PROP_HYPHENATION_DICT) { // hyphenation dictionary lString32 id = props->getStringDef(PROP_HYPHENATION_DICT, diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index ec90e008f..8ef90a0f7 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -504,7 +504,15 @@ class LVFontDef } lUInt32 getHash() { - return (((((_size * 31) + _weight)*31 + _italic)*31 + _features)*31+ _family)*31 + _name.getHash(); + lUInt32 h = (((((_size * 31) + _weight)*31 + _italic)*31 + _features)*31+ _family)*31 + _name.getHash(); + // _bias is not a static property, and can be set/unset on these definitions. + // If a same _bias value is moved from one font to another, *adding* _bias + // to this hash may result in a final identical GetFontListHash() value + // (as GetFontListHash() does add all these hashes). + // We need to use it a multiplicator to be sure it changes GetFontListHash(). + if ( _bias > 0 ) + h *= _bias + 1; + return h; } /// returns font file name @@ -594,7 +602,7 @@ class LVFontCache if (doc == -1 || doc == documentId) // skip document fonts hash = hash + _registered_list[i]->getDef()->getHash(); } - return 0; + return hash; } virtual void getFaceList( lString32Collection & list ) { @@ -620,7 +628,7 @@ class LVFontCache } list.sort(); } - virtual bool getFontFileNameAndFaceIndex( lString32 name, bool bold, bool italic, lString8 & filename, int & index ) + virtual bool getFontFileNameAndFaceIndex( lString32 name, bool bold, bool italic, lString8 & filename, int & index, int & family_type ) { int base_weight = bold ? 700 : 400; LVFontDef * best_def = NULL; @@ -646,6 +654,7 @@ class LVFontCache if ( best_def ) { filename = best_def->getName(); index = best_def->getIndex(); + family_type = best_def->getFamily(); return true; } return false; @@ -1292,7 +1301,8 @@ class LVFreeTypeFace : public LVFont FT_Face _face; FT_GlyphSlot _slot; FT_Matrix _matrix; // helper matrix for fake italic metrics - int _size; // caracter height in pixels + int _face_size; // font size in pixels (requested from face, internal) + int _size; // font size (~ visual char height) in pixels (public) int _height; // full line height in pixels int _hyphen_width; int _baseline; @@ -1344,6 +1354,9 @@ class LVFreeTypeFace : public LVFont if ( _fallbackFontIsSet ) return _fallbackFont.get(); _fallbackFont = fontMan->GetFallbackFont(_size, getWeight(), _italic!=0); + if ( fontMan->GetFallbackFontSizesAdjusted() ) { + _fallbackFont = getVisuallyAdjustedOtherFont( _fallbackFont ); + } _fallbackFontIsSet = true; return _fallbackFont.get(); } @@ -1360,10 +1373,49 @@ class LVFreeTypeFace : public LVFont if ( _nextFallbackFontIsSet ) return _nextFallbackFont.get(); _nextFallbackFont = fontMan->GetFallbackFont(_size, getWeight(), _italic!=0, _faceName); + if ( fontMan->GetFallbackFontSizesAdjusted() ) { + _nextFallbackFont = getVisuallyAdjustedOtherFont( _nextFallbackFont ); + } _nextFallbackFontIsSet = true; return _nextFallbackFont.get(); } + LVFontRef getVisuallyAdjustedOtherFont( LVFontRef other_font ) { + if ( other_font.isNull() ) + return other_font; + // We use the x-height (the height of the lowercase 'x') + // as the visual cue we want to make similar. + int h = getXHeight(); + int other_h = ((LVFreeTypeFace*)(other_font.get()))->getXHeight(); + int other_adjusted_size = (_size * h + _size/2) / other_h; // round it + if ( other_font->getSize() == other_adjusted_size ) + return other_font; + return fontMan->GetFont(other_adjusted_size, other_font->getWeight(), other_font->getItalic()!=0, + other_font->getFontFamily(), other_font->getTypeFace(), other_font->getFeatures(), -1); + } + + int getXHeight() { + int x_height = 0; + int glyph_index = getCharIndex( 'x', 0 ); + if ( glyph_index ) { + int error = FT_Load_Glyph( _face, glyph_index, FT_LOAD_DEFAULT ); + if ( !error ) { + x_height = _slot->metrics.horiBearingY; + } + } + if ( x_height <= 0 ) { + TT_OS2 * os2 = (TT_OS2 *)FT_Get_Sfnt_Table(_face, FT_SFNT_OS2); + if ( os2 && os2->sxHeight > 0 ) { + // The os2 table values are each a constant, that we need to scale + x_height = FT_MulFix(os2->sxHeight, _face->size->metrics.y_scale); + } + } + if ( x_height <= 0 ) { + x_height = _size*64 / 2; // 0.5em + } + return x_height; + } + virtual LVFont * getDecimalListItemFont() { if ( _DecimalListItemFontIsSet ) return _DecimalListItemFont.get(); @@ -1436,7 +1488,7 @@ class LVFreeTypeFace : public LVFont FT_Library getLibrary() { return _library; } LVFreeTypeFace( LVMutex &mutex, FT_Library library, LVFontGlobalGlyphCache * globalCache ) - : _mutex(mutex), _fontFamily(css_ff_sans_serif), _library(library), _face(NULL) + : _mutex(mutex), _fontFamily(css_ff_sans_serif), _library(library), _face(NULL), _face_size(0) , _size(0), _hyphen_width(0), _baseline(0), _underline_offset(0), _underline_thickness(0) , _weight(400), _italic(0), _features(0), _extra_metric(NULL) , _glyph_cache(globalCache), _drawMonochrome(false) @@ -1779,7 +1831,7 @@ class LVFreeTypeFace : public LVFont // Used when an embedded font (registered by RegisterDocumentFont()) is intantiated bool loadFromBuffer(LVByteArrayRef buf, int index, int size, css_font_family_t fontFamily, - bool monochrome, bool italicize, int weight=-1 ) { + bool monochrome, bool italicize, int weight=-1, int face_size=-1 ) { FONT_GUARD _hintingMode = fontMan->GetHintingMode(); _drawMonochrome = monochrome; @@ -1806,6 +1858,7 @@ class LVFreeTypeFace : public LVFont //FT_Face_SetUnpatentedHinting( _face, 1 ); _slot = _face->glyph; _faceName = familyName(_face); + _face_size = face_size > 0 ? face_size : size; CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str() ); //if ( !FT_IS_SCALABLE( _face ) ) { // Clear(); @@ -1814,7 +1867,7 @@ class LVFreeTypeFace : public LVFont error = FT_Set_Pixel_Sizes( _face, /* handle to face object */ 0, /* pixel_width */ - size ); /* pixel_height */ + _face_size ); /* pixel_height */ #if USE_HARFBUZZ==1 if (FT_Err_Ok == error) { @@ -1892,7 +1945,7 @@ class LVFreeTypeFace : public LVFont // Load font from file path bool loadFromFile( const char * fname, int index, int size, css_font_family_t fontFamily, - bool monochrome, bool italicize, int weight=-1 ) { + bool monochrome, bool italicize, int weight=-1, int face_size=-1 ) { FONT_GUARD _hintingMode = fontMan->GetHintingMode(); _drawMonochrome = monochrome; @@ -1923,6 +1976,7 @@ class LVFreeTypeFace : public LVFont //FT_Face_SetUnpatentedHinting( _face, 1 ); _slot = _face->glyph; _faceName = familyName(_face); + _face_size = face_size > 0 ? face_size : size; CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str() ); //if ( !FT_IS_SCALABLE( _face ) ) { // Clear(); @@ -1931,7 +1985,7 @@ class LVFreeTypeFace : public LVFont error = FT_Set_Pixel_Sizes( _face, /* handle to face object */ 0, /* pixel_width */ - size ); /* pixel_height */ + _face_size ); /* pixel_height */ #if USE_HARFBUZZ==1 if (FT_Err_Ok == error) { @@ -4427,7 +4481,7 @@ class LVFreeTypeFace : public LVFont error = FT_Set_Pixel_Sizes( _face, /* handle to face object */ 0, /* pixel_width */ - _size ); /* pixel_height */ + _face_size ); /* pixel_height */ return; } @@ -5009,6 +5063,7 @@ class LVFreeTypeFontManager : public LVFontManager FONT_MAN_GUARD if ( _fallbackFontFaces.length() == 0 ) return LVFontRef(); + /* No real need to limit the number of instances (we prefer accuracy) // reduce number of possible distinct sizes for fallback font if ( size>40 ) size &= 0xFFF8; @@ -5016,6 +5071,7 @@ class LVFreeTypeFontManager : public LVFontManager size &= 0xFFFC; else if ( size>16 ) size &= 0xFFFE; + */ // If forFaceName not provided, returns first font among _fallbackFontFaces. // If forFaceName provided, returns the one just after it, if forFaceName is // among _fallbackFontFaces. If it is not, return the first one. @@ -5101,6 +5157,32 @@ class LVFreeTypeFontManager : public LVFontManager } } + /// set monospace size scale percent + virtual void SetMonospaceSizeScale( int scale ) + { + FONT_MAN_GUARD + _monospaceSizeScale = scale; + gc(); + clearGlyphCache(); + // We would need to loop thru each instance and somehow invalidate + // the monospace instances, as we need to load another face size + // from the font file. This seems quite tedious to implement, as + // nodes at this moment keep each instance's refCount alive. + // But, when having only a single document loaded, _monospaceSizeScale + // being part of the document hash, changing it will cause a rerendering, + // which will cause all font instances to have no references and + // being destroyed before the re-rendering, which will instantiate + // them again with the adjusted size. + } + + virtual void SetFallbackFontSizesAdjusted( bool adjusted ) + { + FONT_MAN_GUARD + _fallbackFontSizesAdjusted = adjusted; + _cache.clearFallbackFonts(); + gc(); + } + /// clear glyph cache virtual void clearGlyphCache() { @@ -5416,10 +5498,10 @@ class LVFreeTypeFontManager : public LVFontManager _cache.regularizeRegisteredFontsWeights(print_updates); } - virtual bool getFontFileNameAndFaceIndex( lString32 name, bool bold, bool italic, lString8 & filename, int & index ) + virtual bool getFontFileNameAndFaceIndex( lString32 name, bool bold, bool italic, lString8 & filename, int & index, int & family_type ) { FONT_MAN_GUARD - return _cache.getFontFileNameAndFaceIndex(name, bold, italic, filename, index); + return _cache.getFontFileNameAndFaceIndex(name, bold, italic, filename, index, family_type); } virtual void getRegisteredDocumentFontList( int document_id, lString32Collection & list ) @@ -5652,10 +5734,14 @@ class LVFreeTypeFontManager : public LVFontManager //printf("going to load font file %s\n", fname.c_str()); bool loaded = false; + int face_size = size; + if ( family == css_ff_monospace && GetMonospaceSizeScale() != 100 ) { + face_size = size * GetMonospaceSizeScale() / 100; + } if (item->getDef()->getBuf().isNull()) - loaded = font->loadFromFile( pathname.c_str(), item->getDef()->getIndex(), size, family, isBitmapModeForSize(size), italicize, item->getDef()->getWeight() ); + loaded = font->loadFromFile( pathname.c_str(), item->getDef()->getIndex(), size, family, isBitmapModeForSize(size), italicize, item->getDef()->getWeight(), face_size ); else - loaded = font->loadFromBuffer(item->getDef()->getBuf(), item->getDef()->getIndex(), size, family, isBitmapModeForSize(size), italicize, item->getDef()->getWeight() ); + loaded = font->loadFromBuffer(item->getDef()->getBuf(), item->getDef()->getIndex(), size, family, isBitmapModeForSize(size), italicize, item->getDef()->getWeight(), face_size ); if (loaded) { //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); diff --git a/crengine/src/lvtextfm.cpp b/crengine/src/lvtextfm.cpp index 5ce72a403..9b5807315 100644 --- a/crengine/src/lvtextfm.cpp +++ b/crengine/src/lvtextfm.cpp @@ -4845,7 +4845,7 @@ class LVFormatter { if ( !grabbedExceedingSpace && m_pbuffer->min_space_condensing_percent != 100 && i < m_length-1 && - ( m_flags[i] & LCHAR_IS_SPACE ) && + ( m_flags[i] & LCHAR_IS_SPACE ) && !( m_flags[i] & LCHAR_LOCKED_SPACING ) && !(m_flags[i+1] & LCHAR_IS_SPACE) ) { // Each space not followed by a space is candidate for space condensing int dw = getMaxCondensedSpaceTruncation(i); diff --git a/crengine/src/lvtinydom.cpp b/crengine/src/lvtinydom.cpp index 847e59108..d0dba9719 100644 --- a/crengine/src/lvtinydom.cpp +++ b/crengine/src/lvtinydom.cpp @@ -388,6 +388,8 @@ lUInt32 calcGlobalSettingsHash(int documentId, bool already_rendered) // if ( fontMan->getKerning() ) // hash += 127365; hash = hash * 31 + (int)fontMan->GetKerningMode(); + hash = hash * 31 + fontMan->GetMonospaceSizeScale(); + hash = hash * 31 + (int)fontMan->GetFallbackFontSizesAdjusted(); hash = hash * 31 + fontMan->GetFontListHash(documentId); // Hinting mode change does not need to trigger a re-render, as since // we use FT_LOAD_TARGET_LIGHT, hinting has no effect on the x-axis diff --git a/crengine/src/lvxml.cpp b/crengine/src/lvxml.cpp index 542e52744..6ce056851 100644 --- a/crengine/src/lvxml.cpp +++ b/crengine/src/lvxml.cpp @@ -5986,11 +5986,32 @@ bool LVHTMLParser::CheckFormat() if ( html_ext && (s.pos("