From d558b201bda73ff641a5809777955f277f07fb61 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Fri, 25 Jan 2019 14:45:27 +0400 Subject: [PATCH 01/80] Fixed java.lang.NullPointerException at org.coolreader.crengine.FileInfo.isOnSDCard (FileInfo.java:442) --- android/src/org/coolreader/crengine/FileInfo.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/src/org/coolreader/crengine/FileInfo.java b/android/src/org/coolreader/crengine/FileInfo.java index 9d421ac093..c21b2c73c8 100644 --- a/android/src/org/coolreader/crengine/FileInfo.java +++ b/android/src/org/coolreader/crengine/FileInfo.java @@ -437,7 +437,9 @@ public boolean isBooksBySeriesDir() } public boolean isOnSDCard() { - if (null == parent) + if (null == parent || null == filename || null == title) + return false; + if (null == parent.pathname) return false; if ( ( (filename.compareTo("SD") == 0 && title.compareTo("SD") == 0) || (filename.compareTo("EXT SD") == 0 && title.compareTo("EXT SD") == 0) ) && From 9e8d9137eddb4bfc59fc2ca5542bb4287ade5a35 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Fri, 25 Jan 2019 15:37:06 +0400 Subject: [PATCH 02/80] Fix regression in previous commit: d558b201bda73ff641a5809777955f277f07fb61 --- android/src/org/coolreader/crengine/FileInfo.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/org/coolreader/crengine/FileInfo.java b/android/src/org/coolreader/crengine/FileInfo.java index c21b2c73c8..15ba42926a 100644 --- a/android/src/org/coolreader/crengine/FileInfo.java +++ b/android/src/org/coolreader/crengine/FileInfo.java @@ -437,12 +437,12 @@ public boolean isBooksBySeriesDir() } public boolean isOnSDCard() { - if (null == parent || null == filename || null == title) + if (null == parent || null == filename) return false; if (null == parent.pathname) return false; - if ( ( (filename.compareTo("SD") == 0 && title.compareTo("SD") == 0) || - (filename.compareTo("EXT SD") == 0 && title.compareTo("EXT SD") == 0) ) && + if ( ( (filename.compareTo("SD") == 0 && null != title && title.compareTo("SD") == 0) || + (filename.compareTo("EXT SD") == 0 && null != title && title.compareTo("EXT SD") == 0) ) && isDirectory && !isArchive && 0 == size && 0 == arcsize && parent.pathname.compareTo("@root") == 0) return true; From 9ac33051505ebda4adee2a5b5b7adf0750f3c968 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Fri, 25 Jan 2019 17:19:38 +0300 Subject: [PATCH 03/80] remove executable attribute from source files --- crengine/src/hyphman.cpp | 0 crengine/src/lvrend.cpp | 0 crengine/src/lvtextfm.cpp | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 crengine/src/hyphman.cpp mode change 100755 => 100644 crengine/src/lvrend.cpp mode change 100755 => 100644 crengine/src/lvtextfm.cpp diff --git a/crengine/src/hyphman.cpp b/crengine/src/hyphman.cpp old mode 100755 new mode 100644 diff --git a/crengine/src/lvrend.cpp b/crengine/src/lvrend.cpp old mode 100755 new mode 100644 diff --git a/crengine/src/lvtextfm.cpp b/crengine/src/lvtextfm.cpp old mode 100755 new mode 100644 From 823f6bf4882b15ca0e4dbe8db786d094b270cd38 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Fri, 25 Jan 2019 17:31:16 +0300 Subject: [PATCH 04/80] fix issue #68 - support for energy sistem eink --- android/src/org/coolreader/crengine/DeviceInfo.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android/src/org/coolreader/crengine/DeviceInfo.java b/android/src/org/coolreader/crengine/DeviceInfo.java index 3f5c0a3c06..c2067f0daf 100644 --- a/android/src/org/coolreader/crengine/DeviceInfo.java +++ b/android/src/org/coolreader/crengine/DeviceInfo.java @@ -25,6 +25,7 @@ public class DeviceInfo { public final static boolean EINK_TOLINO; public final static boolean FORCE_HC_THEME; public final static boolean EINK_SONY; + public final static boolean EINK_ENERGYSYSTEM; public final static boolean SONY_NAVIGATION_KEYS; public final static boolean USE_CUSTOM_TOAST; public final static boolean AMOLED_SCREEN; @@ -115,6 +116,8 @@ public static boolean supportsActionBar() { EINK_ONYX = (MANUFACTURER.toLowerCase().contentEquals("onyx") || MANUFACTURER.toLowerCase().contentEquals("onyx-intl")) && (BRAND.toLowerCase().contentEquals("onyx") || BRAND.toLowerCase().contentEquals("maccentre") || BRAND.toLowerCase().contentEquals("maccenter")) && MODEL.length() > 0; + EINK_ENERGYSYSTEM = ( + (BRAND.toLowerCase().contentEquals("energysistem")||BRAND.toLowerCase().contentEquals("energysystem")) && MODEL.toLowerCase().startsWith("ereader")); //MANUFACTURER -DNS, DEVICE -BK6004C, MODEL - DNS Airbook EGH602, PRODUCT - BK6004C EINK_DNS = MANUFACTURER.toLowerCase().contentEquals("dns") && MODEL.startsWith("DNS Airbook EGH"); @@ -122,7 +125,7 @@ public static boolean supportsActionBar() { (MODEL.toLowerCase().contentEquals("tolino") && DEVICE.toLowerCase().contentEquals("tolino_vision2")); //Tolino Vision HD4 doesn't show any Brand, only Model=tolino and DEVICE=tolino_vision2) - EINK_SCREEN = EINK_SONY || EINK_NOOK || EINK_ONYX || EINK_DNS || EINK_TOLINO; // TODO: set to true for eink devices like Nook Touch + EINK_SCREEN = EINK_SONY || EINK_NOOK || EINK_ONYX || EINK_ENERGYSYSTEM || EINK_DNS || EINK_TOLINO; // TODO: set to true for eink devices like Nook Touch // On Onyx Boox Monte Cristo 3 (and possible Monte Cristo, Monte Cristo 2) long press action on buttons are catch by system and not available for application // TODO: check this on other ONYX BOOX Readers From a03a7949505fac1aef489f08347082eb3b361d1d Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Fri, 25 Jan 2019 21:05:23 +0400 Subject: [PATCH 05/80] Minor fixes for previous commit... --- android/src/org/coolreader/crengine/FileInfo.java | 10 ++++------ android/src/org/coolreader/crengine/Scanner.java | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/android/src/org/coolreader/crengine/FileInfo.java b/android/src/org/coolreader/crengine/FileInfo.java index 15ba42926a..03c3b777b8 100644 --- a/android/src/org/coolreader/crengine/FileInfo.java +++ b/android/src/org/coolreader/crengine/FileInfo.java @@ -437,14 +437,12 @@ public boolean isBooksBySeriesDir() } public boolean isOnSDCard() { - if (null == parent || null == filename) + if (null == parent) return false; - if (null == parent.pathname) - return false; - if ( ( (filename.compareTo("SD") == 0 && null != title && title.compareTo("SD") == 0) || - (filename.compareTo("EXT SD") == 0 && null != title && title.compareTo("EXT SD") == 0) ) && + if ( ( ( "SD".equals(filename) && "SD".equals(title)) || + ("EXT SD".equals(filename) && "EXT SD".equals(title)) ) && isDirectory && !isArchive && 0 == size && 0 == arcsize && - parent.pathname.compareTo("@root") == 0) + ROOT_DIR_TAG.equals(parent.pathname) ) return true; return parent.isOnSDCard(); } diff --git a/android/src/org/coolreader/crengine/Scanner.java b/android/src/org/coolreader/crengine/Scanner.java index 26261b4215..b99b2b8c59 100644 --- a/android/src/org/coolreader/crengine/Scanner.java +++ b/android/src/org/coolreader/crengine/Scanner.java @@ -643,7 +643,7 @@ private FileInfo findParentInternal(FileInfo file, FileInfo root) { } for ( int i=0; i Date: Mon, 28 Jan 2019 08:46:49 +0300 Subject: [PATCH 06/80] apply patches from issues #72 #73 --- thirdparty/chmlib/src/chm_lib.c | 40 ++++++++++++-------------- thirdparty/chmlib/src/extract_chmLib.c | 7 ++++- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/thirdparty/chmlib/src/chm_lib.c b/thirdparty/chmlib/src/chm_lib.c index ed56b4615f..afa7dc3d76 100644 --- a/thirdparty/chmlib/src/chm_lib.c +++ b/thirdparty/chmlib/src/chm_lib.c @@ -56,6 +56,7 @@ #include "lzx.h" +#include #include #include #ifdef CHM_DEBUG @@ -154,34 +155,31 @@ typedef unsigned __int32 UInt32; typedef __int64 Int64; typedef unsigned __int64 UInt64; -/* I386, 32-bit, non-Windows */ -/* Sparc */ -/* MIPS */ -/* PPC */ -#elif __i386__ || __sun || __sgi || __ppc__ || __arm__ || __mips__ -typedef unsigned char UChar; -typedef short Int16; -typedef unsigned short UInt16; -typedef long Int32; -typedef unsigned long UInt32; -typedef long long Int64; -typedef unsigned long long UInt64; /* x86-64 */ /* Note that these may be appropriate for other 64-bit machines. */ -#elif __x86_64__ || __ia64__ || __aarch64__ +#elif defined(__LP64__) typedef unsigned char UChar; -typedef short Int16; -typedef unsigned short UInt16; -typedef int Int32; -typedef unsigned int UInt32; -typedef long Int64; -typedef unsigned long UInt64; +typedef int16_t Int16; +typedef uint16_t UInt16; +typedef int32_t Int32; +typedef uint32_t UInt32; +typedef int64_t Int64; +typedef uint64_t UInt64; +/* I386, 32-bit, non-Windows */ +/* Sparc */ +/* MIPS */ +/* PPC */ #else -/* yielding an error is preferable to yielding incorrect behavior */ -#error "Please define the sized types for your platform in chm_lib.c" +typedef unsigned char UChar; +typedef int16_t Int16; +typedef uint16_t UInt16; +typedef int32_t Int32; +typedef uint32_t UInt32; +typedef int64_t Int64; +typedef uint64_t UInt64; #endif /* GCC */ diff --git a/thirdparty/chmlib/src/extract_chmLib.c b/thirdparty/chmlib/src/extract_chmLib.c index 078cc35ded..478c892ade 100644 --- a/thirdparty/chmlib/src/extract_chmLib.c +++ b/thirdparty/chmlib/src/extract_chmLib.c @@ -102,6 +102,7 @@ int _extract_callback(struct chmFile *h, struct chmUnitInfo *ui, void *context) { + LONGUINT64 ui_path_len; char buffer[32768]; struct extract_context *ctx = (struct extract_context *)context; char *i; @@ -119,7 +120,11 @@ int _extract_callback(struct chmFile *h, if (snprintf(buffer, sizeof(buffer), "%s%s", ctx->base_path, ui->path) > 1024) return CHM_ENUMERATOR_FAILURE; - if (ui->length != 0) + /* Get the length of the path */ + ui_path_len = strlen(ui->path)-1; + + /* Distinguish between files and dirs */ + if (ui->path[ui_path_len] != '/' ) { FILE *fout; LONGINT64 len, remain=ui->length; From 4c51b27aa77dc7d962528a96c16e71934c32469b Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Thu, 31 Jan 2019 00:09:35 +0400 Subject: [PATCH 07/80] Fixed false triggering of application data directory removing. Fix PR #64. --- android/src/org/coolreader/CoolReader.java | 10 +++++----- android/src/org/coolreader/crengine/Engine.java | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/android/src/org/coolreader/CoolReader.java b/android/src/org/coolreader/CoolReader.java index c5013160bf..04abf28fc1 100644 --- a/android/src/org/coolreader/CoolReader.java +++ b/android/src/org/coolreader/CoolReader.java @@ -467,16 +467,16 @@ public void run() { if (!processIntent(getIntent())) showLastLocation(); } - if (Engine.getExternalSettingsDirName() != null) { - setExtDataDirCreateTime(new Date()); - } else { - setExtDataDirCreateTime(null); - } if (dataDirIsRemoved) { // show message ErrorDialog dlg = new ErrorDialog(this, getString(R.string.error), getString(R.string.datadir_is_removed, Engine.getExternalSettingsDirName())); dlg.show(); } + if (Engine.getExternalSettingsDirName() != null) { + setExtDataDirCreateTime(new Date()); + } else { + setExtDataDirCreateTime(null); + } stopped = false; log.i("CoolReader.onStart() exiting"); diff --git a/android/src/org/coolreader/crengine/Engine.java b/android/src/org/coolreader/crengine/Engine.java index 955cb8030a..5f029c08e4 100644 --- a/android/src/org/coolreader/crengine/Engine.java +++ b/android/src/org/coolreader/crengine/Engine.java @@ -553,6 +553,13 @@ private Engine(BaseActivity activity) { public void initAgain() { initMountRoots(); + File[] dataDirs = Engine.getDataDirectories(null, false, true); + if (dataDirs != null && dataDirs.length > 0) { + log.i("Engine.initAgain() : DataDir exist at start."); + DATADIR_IS_EXIST_AT_START = true; + } else { + log.i("Engine.initAgain() : DataDir NOT exist at start."); + } mFonts = findFonts(); findExternalHyphDictionaries(); if (!initInternal(mFonts)) { @@ -1429,6 +1436,11 @@ public void uninit() { // initialized = false; // } instance = null; + // to suppress further messages about data directory removed + // if activity destroyed but process is not unloaded from memory + // and if application data directory already exist at this point + if (null != CR3_SETTINGS_DIR_NAME) + DATADIR_IS_EXIST_AT_START = true; } protected void finalize() throws Throwable { From 228b6f605e6ec15d78cb3a10bfcb5d3796b6c36a Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Thu, 31 Jan 2019 13:46:42 +0300 Subject: [PATCH 08/80] update version --- android/AndroidManifest.xml | 2 +- android/app/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 939a9bdcf9..19f5c15ec7 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -4,7 +4,7 @@ + android:versionName="3.2.29-1" android:versionCode="32290"> Date: Sun, 3 Feb 2019 16:09:22 +0400 Subject: [PATCH 09/80] Added support of unicode file names in zip archives firstly introduced in InfoZip 6.3. --- crengine/src/lvstream.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/crengine/src/lvstream.cpp b/crengine/src/lvstream.cpp index c161cea725..43d2d23ff7 100644 --- a/crengine/src/lvstream.cpp +++ b/crengine/src/lvstream.cpp @@ -2563,6 +2563,7 @@ class LVZipArc : public LVArcContainerBase CurPos = 0; else CurPos -= sizeof(ReadBuf)-18; + // Find End of central directory record (EOCD) for ( Buf=0; Buf<64 && !found; Buf++ ) { //SetFilePointer(ArcHandle,CurPos,NULL,FILE_BEGIN); @@ -2686,13 +2687,29 @@ class LVZipArc : public LVArcContainerBase NextPosition += SeekLen; m_stream->Seek(NextPosition, LVSEEK_SET, NULL); - // {"DOS","Amiga","VAX/VMS","Unix","VM/CMS","Atari ST", - // "OS/2","Mac-OS","Z-System","CP/M","TOPS-20", - // "Win32","SMS/QDOS","Acorn RISC OS","Win32 VFAT","MVS", - // "BeOS","Tandem"}; - const lChar16 * enc_name = (ZipHeader.PackOS==0) ? L"cp866" : L"cp1251"; - const lChar16 * table = GetCharsetByte2UnicodeTable( enc_name ); - lString16 fName = ByteToUnicode( lString8(fnbuf), table ); + lString16 fName; + if (ZipHeader.PackVer >= 63 && (ZipHeader.Flags & 0x0800) == 0x0800) { + // Language encoding flag (EFS). If this bit is set, + // the filename and comment fields for this file + // MUST be encoded using UTF-8. (InfoZip APPNOTE-6.3.0) + //CRLog::trace("ZIP 6.3: Language encoding flag (EFS) enabled, using UTF-8 encoding."); + fName = Utf8ToUnicode(fnbuf); + } else { + if (isValidUtf8Data((const unsigned char *)fnbuf, SizeToRead)) { + //CRLog::trace("autodetected UTF-8 encoding."); + fName = Utf8ToUnicode(fnbuf); + } else { + // {"DOS","Amiga","VAX/VMS","Unix","VM/CMS","Atari ST", + // "OS/2","Mac-OS","Z-System","CP/M","TOPS-20", + // "Win32","SMS/QDOS","Acorn RISC OS","Win32 VFAT","MVS", + // "BeOS","Tandem"}; + // TODO: try to detect proper charset using 0x0008 Extra Field (InfoZip APPNOTE-6.3.5, Appendix D.4). + const lChar16 * enc_name = (ZipHeader.PackOS==0) ? L"cp866" : L"cp1251"; + //CRLog::trace("detected encoding %s", LCSTR(enc_name)); + const lChar16 * table = GetCharsetByte2UnicodeTable( enc_name ); + fName = ByteToUnicode( lString8(fnbuf), table ); + } + } item->SetItemInfo(fName.c_str(), ZipHeader.UnpSize, (ZipHeader.getAttr() & 0x3f)); item->SetSrc( ZipHeader.getOffset(), ZipHeader.PackSize, ZipHeader.Method ); From d0976a693b70a6a8b2c74b79b5f3fefd1627c425 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sun, 3 Feb 2019 16:13:15 +0400 Subject: [PATCH 10/80] Convert the book language code to lower case for proper comparision with the font language. --- android/jni/cr3engine.cpp | 2 +- android/src/org/coolreader/db/MainDB.java | 2 +- crengine/src/chmfmt.cpp | 2 +- crengine/src/epubfmt.cpp | 2 +- crengine/src/lvdocview.cpp | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/jni/cr3engine.cpp b/android/jni/cr3engine.cpp index e4b4aac6e8..8141e4c028 100644 --- a/android/jni/cr3engine.cpp +++ b/android/jni/cr3engine.cpp @@ -231,7 +231,7 @@ static bool GetBookProperties(const char *name, BookProperties * pBookProps) #endif lString16 authors = extractDocAuthors( &doc, lString16("|"), false ); lString16 title = extractDocTitle( &doc ); - lString16 language = extractDocLanguage( &doc ); + lString16 language = extractDocLanguage( &doc ).lowercase(); lString16 series = extractDocSeries( &doc, &pBookProps->seriesNumber ); #if SERIES_IN_AUTHORS==1 if ( !series.empty() ) diff --git a/android/src/org/coolreader/db/MainDB.java b/android/src/org/coolreader/db/MainDB.java index 768462d902..6b890af104 100644 --- a/android/src/org/coolreader/db/MainDB.java +++ b/android/src/org/coolreader/db/MainDB.java @@ -1400,7 +1400,7 @@ private void readFileInfoFromCursor( FileInfo fileInfo, Cursor rs ) fileInfo.createTime = rs.getInt(i++); fileInfo.lastAccessTime = rs.getInt(i++); fileInfo.flags = rs.getInt(i++); - fileInfo.language = rs.getString(i++); + fileInfo.language = rs.getString(i++).toLowerCase(); fileInfo.isArchive = fileInfo.arcname!=null; } diff --git a/crengine/src/chmfmt.cpp b/crengine/src/chmfmt.cpp index 9ef341165a..13f04cf873 100644 --- a/crengine/src/chmfmt.cpp +++ b/crengine/src/chmfmt.cpp @@ -1216,7 +1216,7 @@ bool ImportCHMDocument( LVStreamRef stream, ldomDocument * doc, LVDocViewCallbac if ( !title.empty() ) doc->getProps()->setString(DOC_PROP_TITLE, title); if ( !language.empty() ) - doc->getProps()->setString(DOC_PROP_LANGUAGE, language); + doc->getProps()->setString(DOC_PROP_LANGUAGE, language.lowercase()); fragmentCount = tocReader.appendFragments( progressCallback ); writer.OnTagClose(L"", L"body"); diff --git a/crengine/src/epubfmt.cpp b/crengine/src/epubfmt.cpp index a0679f5aa6..74e2759e92 100644 --- a/crengine/src/epubfmt.cpp +++ b/crengine/src/epubfmt.cpp @@ -724,7 +724,7 @@ bool ImportEpubDocument( LVStreamRef stream, ldomDocument * m_doc, LVDocViewCall lString16 title = doc->textFromXPath( cs16("package/metadata/title")); lString16 language = doc->textFromXPath( cs16("package/metadata/language")); m_doc_props->setString(DOC_PROP_TITLE, title); - m_doc_props->setString(DOC_PROP_LANGUAGE, language); + m_doc_props->setString(DOC_PROP_LANGUAGE, language.lowercase()); m_doc_props->setString(DOC_PROP_AUTHORS, author ); for ( int i=1; i<50; i++ ) { diff --git a/crengine/src/lvdocview.cpp b/crengine/src/lvdocview.cpp index 412e1f68b9..433e6e0e52 100644 --- a/crengine/src/lvdocview.cpp +++ b/crengine/src/lvdocview.cpp @@ -4382,9 +4382,9 @@ bool LVDocView::ParseDocument() { m_doc_props->setString(DOC_PROP_AUTHORS, extractDocAuthors(m_doc)); m_doc_props->setString(DOC_PROP_TITLE, extractDocTitle(m_doc)); if (txt_autodet_lang.length() > 0) - m_doc_props->setString(DOC_PROP_LANGUAGE, txt_autodet_lang); + m_doc_props->setString(DOC_PROP_LANGUAGE, txt_autodet_lang); // already in lowercase else - m_doc_props->setString(DOC_PROP_LANGUAGE, extractDocLanguage(m_doc)); + m_doc_props->setString(DOC_PROP_LANGUAGE, extractDocLanguage(m_doc).lowercase()); int seriesNumber = -1; lString16 seriesName = extractDocSeries(m_doc, &seriesNumber); m_doc_props->setString(DOC_PROP_SERIES_NAME, seriesName); From bd11a0c3bf00df03fd4c6b5d03612ef9f876413a Mon Sep 17 00:00:00 2001 From: Eugene Dvoretsky Date: Sun, 3 Feb 2019 23:46:38 +0300 Subject: [PATCH 11/80] Remove opacity in hc_more icon --- icons_src/cr3_button_more_hc-48x48-src.svg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/icons_src/cr3_button_more_hc-48x48-src.svg b/icons_src/cr3_button_more_hc-48x48-src.svg index 9d1215fe07..6c526c2845 100644 --- a/icons_src/cr3_button_more_hc-48x48-src.svg +++ b/icons_src/cr3_button_more_hc-48x48-src.svg @@ -58,13 +58,13 @@ id="layer1" transform="translate(0,-284.29999)"> + style="fill:#000000;fill-opacity:1"> Date: Mon, 4 Feb 2019 09:56:04 +0300 Subject: [PATCH 12/80] regenerate all cr3_button_more_hc.png --- .../res/drawable-hdpi/cr3_button_more_hc.png | Bin 1973 -> 1890 bytes .../res/drawable-ldpi/cr3_button_more_hc.png | Bin 897 -> 861 bytes .../res/drawable-mdpi/cr3_button_more_hc.png | Bin 1307 -> 1258 bytes .../res/drawable-xhdpi/cr3_button_more_hc.png | Bin 2634 -> 2535 bytes .../drawable-xxhdpi/cr3_button_more_hc.png | Bin 4141 -> 3812 bytes .../drawable-xxxhdpi/cr3_button_more_hc.png | Bin 5684 -> 5166 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/android/res/drawable-hdpi/cr3_button_more_hc.png b/android/res/drawable-hdpi/cr3_button_more_hc.png index c4931475a97620ba88256cc6f5c67a6a79e5b6b2..c19c1b19aec7e81a0b05d1ea3fa0eff4c9dde36b 100644 GIT binary patch delta 1802 zcmV+l2le>158@7xZGQ&eNklPRM3eVz4W_6GqcQ6d%As z#VO!A6`|iglw1*B=b=bOft1{$& z0Q^A+@z->PB!5eM6UndT5Qah_Ub=LNHk*wAQZk#(#)}s(G876apUPJxE8Z9RUy%%x z5~7A9Qm~*FRi(`nTv~yQpC|z$!{V#Pm&!S9cxZr+Ue7$rGQzI zA130OaDI{e8bJMx8#mx^I6yLK@!Pj=M^jT1Dl02dQc?mT1cJdJMn^}{-`|hn;o+oX z2_ewa(|?2J=4Jr+yAYy5(~0D-NirM`(`+^;HBwnwSv-35C`U&}wZ6U_8yln3>10+` zR#LIeW;4U#uyl85kryPtA<0vxP9>F~tgMXp?%mTQz7j)2Lo6#RODac4M~8I#H`S^V zV{01*gF)uy~>U28QoEtZ8 zWHcHj$yX#lNF?B(45l?2t*xyYBQMd~+NzmXF=+ACh_ST?uUxsJS#ie4$1^VA>C>m0 zm4E5-<;$}BCCNKWp`i)@hK7a`PBDqv+FESbutBv?I@Rp+j-kcJ0~)Aw){Hd-v|RIEN1(R?Y4A`(@LXNI($)OiWCu2C1y9jJsA` zT&(Jo2qh&YNyS!Ta&l5Oi{fsQd`*(MxqrE;^YG4{J8|LgMF;_p$AkL%`m|!N;1-IzNwSY59S(=; zJha>Gnl-(vt1Hg0ySrP{&u+J?=5{z7vg_Xx3HX#GFI>2w8YDYAJK+ncL^K+u%jJ6O z)6&u+mjSC{dU~4K+1aYO&!0apyMJFyB%p~T@7}$uSw-60+cigXVPS#w_4VAdXOD7! zyDHk-+conJ4i3uh2NDS=Ao=~`;v!2+OEt@x-|yEXH8V5A=g*&O_VfGw)a&)CIZI1R zZJC< zD={`U#%&6_tlGBU!EkrCdyb&J=oUE{%n z2Wc=EQi|#IdL{W=Ns(U`P{G(KF^?TPmQun@+VSJZCHXMPpC&C}SwssNI~Q$eXjpUd zcDr5q{YdY)o#X{FR_gZe-@m5h6(#yyTI81n2*!@M8Hq&bbUNQVd4H$VsT}z~rcHi@ zkl)FO9*>7cqcP*ejYcCq9*?qMeU=gO3K1WZ{Ej3iCMM`|xirsmk};i5N0-aRiHQkG ze2(OAGfG||;zN=*Wu`zNz_zwF78Vw!OuVqLkZo;k3No(iXQe`ft9fZqf7 z82~ISEa3L-+ZY%afPc^DgWvDRMiAv?fH1rN==E zSdCh2v1uJ7;3E%+4nC>a9xs1D)4oVFKAe-&CTfD5)W#Z2gG4FT_Ms*)czFQ^M(A)k z7Lc`meVAEhml^gRM)ah~`I0Z$*=wzD{eIuNee2sB_+GwS5r3;;gk%bU9|8Crz-Iso z05}1pgsRK}7y~c_;9mg#3ZO>_F}oW5$0Q^L$^9h%K=Lg~xkCMxQD& zIe>EjO7y$u&!3~ayIVYc`V<2L17du99DzUp0FaiJ2B*`BqM{;}mX@NTqJmquZZ(?x zCx9=65O-oF5`Qi5LX!8j7yW*}yn6MjDlRUj&1PGX78e)u%9Sh1@An%=<_Uu`enwq*|;7c_%HI;2`ZE9+2N|OkX{N-u_7bM%Ym%6&TR8CIL2LoTK zIXOA(>grO4(YX2{Yr_cs_avp1QhGcd6&Dw`>fp7;#ec=oTpVn*Q-z4)RVU0MyNZtpqud}mL96fqekc^uA&Ye3^Q&Yn&Tee{R`t=Y(z~}Q} zaBvX!?tk4A&z?Pt+LjOkw{PF(;lqas;4eam8p}o`e@0RU0s)zol@*nz;^X6a{P=O@ z^?EH&;Gv-*>2|wSe0+RVwzIOb7zhM(`=45Y7bO2dQl2_>DypfIo14qWj~}lzjF5eO zeKI#UH!2dXt*v@0{oQm|3dJ_k=kv+rWt^NJ|(r&k#_PKfUW-6s5$yt&~kqG=m8?*98V`HOQE$~_!8yhW0-Qct; zjepx_;P5#BJbd`j^ys$PY-n$9kA7c`QZ8P+NSn=OTBp0aTVr#*$73k}3;>=yd17j$ ztgH;Vxw$ccG?R@RH=?Yp%(PBlU!Q(_f0-~6CFEhufPTMU78Dc^KvcSE(()KoS!Z8LInM;|?1} ztGFl9Ld(d=Kxb#CTDNYUX5R&@QIt~QZ4A$|0FaWB5?0A5qkp5L z5!Xm@aWU@PxuYBo$Fgnhb~|p}y2WkVwnZ!-85uEMe{0sP(T_9w!AQUl02?-JFtdS| zFJCTOws-Gd+_-UrLWJWGLg3o9YpkrSTy}`fY%js)4=W_uNmAC<)|wtDE|<&l5IcA7 zoWhcMZwLjJRhP?U+IMYjt+o-Ket%);`gr;BWm6*w2?-Gwpiz}l((QJ?Cs9{dr$S-C zvN}FKE)x1kO~QeqiSpE+|zkNTI(u5tzwt@8Ej z*V5r|SQ@RWs#4R_(=mgenVFF_H8qybayT4(_3BkvBpQi;Ao(|vvZ<*lsw?I8?b{j6 zU3jSu4Gqa1J9b#wx7lnwd4KYx_Wn~^CAvt~lELd(9H@`zm@h+EE`3AuLn>TNYrlzK-Q+p1F11^_~hYlS=ettf(v$F{V zPuLePUf}NCyXfxj7W4D-(VE2d>(|-P&_Dq9g%JCrYDBVt!7Q;naev~(cb3>{XlT$= zXOiURs9A?d)RDowXmxe z%elEZ>2|w6ItjPiZS?$aV+Oy3$XUIMbaZs6w6wHU2cDLe#*U7T|M|eND&P$yYDrFz zl*7ZrvcA4vS>gzrP<|uUCwWjNsk7 zcL0Jnv8=2t6c!e;w6qkJm6a$jFK0qRf;RaKz^{c6cVod5lU9Tlcs0qd7)# zbJ8S~o+bGu$tuh6z(iK-SA!$~KSVHNwiUq~(|UyeYT!liSA&NDdW8^gm)iPX{@>)k Xp!Bx83T3@Y00000NkvXXu0mjfQ8%p~ diff --git a/android/res/drawable-ldpi/cr3_button_more_hc.png b/android/res/drawable-ldpi/cr3_button_more_hc.png index d737c5598133a644d8f91665cc68bf036ac59af5..e2280d2f1de20958c6ebef812715f70920055dff 100644 GIT binary patch delta 763 zcmVfsmuPphtFeU+%wgY|d7k;V z?`mb1lmw!OE)9Kt4i$&7uG|_03 zv9U2mM@NZ9qomVm3WWllPNxlIfS1n&9|ykay3X|UG$Qh(PEAdr>pH-9;Po)@$zHG5 z%4V}DiZT@ZUKK?llgZHQ^{#-4r`mF!$z+}lJgC`h7N8Dz-3Y#vN+s9KW3d>TrqO6L zXfzsVnno-ZbAPSm`1ly$^B{P@G|ihxBw`Ej`FxbiWge4qxs1=}v&BRr5lqu;1AZ&P zM@`cl*;Fc(N5Su}9jr1mOJq9Iw~w z7^v6l<>KN3fM&C4vyF_50Njd*sIIA5WOjB|c6WD0RsXmuiXuBZJ2Epf^YHB=zpNGf z7~G)#-}xY0TU!sd?d@%wP1kkDCIOdLfCWHRZKhvy|9shPZf*jc;da^eNhZ48?$y7LtaPuh)9rSD0^_dW7Lsw`ET7L43b{!ycT0c6~MsqrJbsPdpwc6bj+-c<^{UghCXd)(dKT?3y0j~mvo tN8c|ZvA()J`Xv!5DT>m5R_6ahegjinHOfiX?&|;m002ovPDHLkV1kK9d?f$? delta 800 zcmV+*1K<4J27w2VZh!tsL_t(YiM3TtPa0tmp8ZG)7pWi7L`@UmQ1C#aiGQO+vx$UI z#hyHAJQySNtbf9j(S#t|3NGjgj@DSbk@mo$He8GKqGaKnr-vflg;lJHlgwfFoq3*_ zd1u}sdSQ_sI|ML9L}4O&M?`OlXtI4&Bcks_lp~_7BuUr*#eWLG4It5MHm|eUEXU(< z4uwJ(8ymyu=qN&=5aRJT9~~WWv)QZz*aF~wD)=~nFNR_8)YKGYS$MIxm?aM z@^CoJx~_AjQhyPZN=4|p&f##lClEzZaD05s05-e9hfLGF4g>-=2am^td_KfbHlIKs0Mj(<00ylDA9P*k?q~{y!h_)VS|}8HvetE-0W4VwKF4A)8^Qeid|&W; zotvAp`Nm=~2A~rWscnv_a=C09y2ZssA|krDxS&iX<9|4inxFLh5gRnJ&Z`~c#p3Y94x3{v^eAKY#?+-^5~J|8qqMG)f%ir41RMVVsjyzmADwp*v(d8Cq&BuqGfG;$u%BHt ziD}O{H7$YxBn_h5PsK)7;#=Wq2i< zo100cQULz}2i^y71v2B~;~xZG$?e;>^XSndfEOFi3E1# zH@p&&NW`|+;NT!Ymep0YYTz4yP$*<6@OV5Fi^WYFC>D!&JRVEGP$*Q9glY}^Fh4&} zc@1BaeSdv@n}%27;>C-WeuiN%KR*x9FG;Slhpt>MC$R1zM~@z@USGR*O@?7?nAfjg zuj-E-J7zc6l+9)(sjJpN>-6-rMJ!4C_wTP>pEz+syLRoe)k#uION&mPJXyW(?CiAF zm07K|2Hx1c{rWg~@Sswulo}fwEn{xCTX*l?)qjBl?-o0k%Vp6?(#p!pJ59yZ-o1Nm z#ZyyLYwmk{dv)v9EsHLhOzOyyBWs?Sru}a0^Z6vHSbYGTpPikx{cNJqsO<#1di82m z6OYGjHPL9)w&(2ZEI?nafkOb@-Q5Y$BP- zWN2t;u>E$2hlg!Jb}>9WY`dno-EK0O48U)ei2?Y5C8y2AFbv}HIHgj_5nL*jh{a;I za7mmycMhC3qq2#cFJHc-qoZTPZ8aW`^MCN+LnbCBn3$Mgbaa$>JdV%jbFAy==wN1M z2H+=Kc-cS;@M3Ii%yC!TO1j-{Mn^{ho&(M+S=mGoSh#cN4sN&mgTQa!z70?SzSt06 zW8yHdFg7;E?%lh$4EK0EJb3V+0{^Zqc#VnAfOIaGZ2Zg4|G182^hVSjph z8X&`F|CZO7XaQ~j%jtBQSS&^`7<2>&gF#}k80mBxUDdaXcmwZ)#}mglUyz* z(=?UO=LN{?^(qhu$nW<{k|`;r)uY+e25sv%P-Dc9^tDzGr(KfT%0mm1{*m;DVHkgH ersqeG{{iJhOJl89LCXLD002ovP6b4+LSTX+FGR=y delta 1213 zcmV;u1Va1j37ZO#Zhx3bL_t(oh0T{uPa9VhhTpNl=1Z71SmwhvQE3}lQ6$=iE@-7R z3zRG?vBGN-84FcV3nm7!LTcrxQCmmK4?tm+MGQXxdEpHZVAGHkRk$@apJrl-7?j1m zZx94o^vnoA79FOoJlGGZUXogz<(zIJ_hg(KpMa{fH?q* zilXpR1(0k55CZT$fYy9Ik90bX?d@#Ue}7-@?Cc1Vf0Fc_7ycbdZFzZFT)ld=Z18DwyWO0hpBE(eNZu+JevRbb z%*>3etE;;({MhR1>Nq_;ElIv1dHqB129mEA7Z=54z>lq_riOEKbAsfnGrtLvkFwdU z@OV6?$r>9Q83+VqEEbas3k%}Kix=Yg^XD=U2*@i}u78-uc|0CwvspnhVg_#`c`!IQ zD9w@tgF&fjnjmRtnx=_BAYd9lG&Cd&tHWy~Aeu}jMRj$xVesR}kEI#-v_+#)LlM>0 z)l4Rng5+tIvnROW)6n~y}iAIH*O#ni_vDY>7G?q zR^r*SXS{y>eX+AxEIK~`kW#+)Dg16UH8mM3NT<^$X6E+o+kEomiOx3~jk3GD`^5eA z^>xEpZEkKRfL!qg$>8qpt}t3Qp-{+h3970p0T2KmJa}N}6AFb4m*Vd3t|0kCseqd# zMSn*}ht9dBrG@+Z`?};(O5ykWWlv9!6hi2p@9*!6mX;P>zK)KL<7N0osQ`uK-{a%s zhKff}ohU zIVr7Ht1ge-Zs+Risv!BR?!zG2O7g8)Gk>vKtqh05GM~?zg7f*jP*v3sZn0R{-`{WE zj0z$iZf|wzekX@wfA*8p*w>sVQl<+b;$F^yyPcGE4IFGvOs7Zhw*7 zOC%Dasj2D0aHrGB+1c4+czK)B5)q%0{3Daeh`zo)Sy@?GHrQsf@!q|Aa${pdkbKSa zZp%wVI7miF=2uo$gsQ62=ku9@eLf#mRh26%D}v-(l0SdwotJww^8)x0M@KVHCX+!b zmBP->jzSVHmy2Gn7p<+W0FVMOjW(mB+4_faU*vH5C5|=|?EpS1JbDA*F8~QeQS`f- b|MT@Poyr(X`a%kY00000NkvXXu0mjf#9>GY diff --git a/android/res/drawable-xhdpi/cr3_button_more_hc.png b/android/res/drawable-xhdpi/cr3_button_more_hc.png index 0f0bf92a1e58b1371ec76e3dea29432240759e40..6f357447d4a689c82a7ae58449bbb113013563c2 100644 GIT binary patch delta 2451 zcmV;E32gSt6z3C=ZhvG+L_t(|ob6j*OjGS0eolb`>j$L;g$@A&+A$TFUbsX+S20oJ zg;~_Zkhx(*P4WoI zzmWXI;Nc|scapy(8KZW*s`4BPQlf@Kf~PI90mpkFfcHHk&zKhPEKNWb`~U| z)9GL|8j+fs3V*ZNjLghTL`O&aax)E}0l;}d5WZ0>_MkvAl4K=GhxeoF>gr-;WhHZS za~Tm4K>)H!L_`F0b8}f)S;?-hE?=!;n&fXtYFCRl0Dz>0S|_XWyvNS zsBGD?g%>Ygq{FdD<9?F6R+=|RhLJo&a$byNc6OFkRex1XNJvnPxOYiNNMKb}6=!E> zr6Mel{3FS*l_IYv*)EOX`t|EfPfrgSaqp6zp3dvnuY0$Kzmkj%HTfixk3^4ib8|d( z>ePx6_b$hd9plW*j8uewkxUL1c@xPeqNm~EVPFJL9`hWVkZr!?7Coh(yq$ECi^hhc~56L*S$cIVa`JSF08jZ$PC+%Gf1_QghyS@7a zjT!>{UbHweGQyOUlvO9|udH3WmV<+zeU*O~EO`rEsVE%|2RCinwCW@Sl}(#AN%Mty z2FMfqrowb(Sv_zKU~v5SaWpnI$`Z~W^m;w=^MCV^laqrD8#X|va}5S(W@a!nG=!d> z9^AWk4^vZ9LHQ{vDndg;gZTFefGj}}{;S9c$*)8iZ8lp_PodL2!}aUeFDr=6n>X|7)vEzX`$KDME4OXi zwtp<&DJdzOo}Ly>Ip`X!$rj)bV$F(*ie&{*T3X8a`FUTw6k&dTo~5Ow%Zj0*qC#rw zj9l_xkbFy$@7}$mUawc=RIAmpzP>(a;@)UzXkcVyq@q~#dOhF1eJh%nB&kCnko<*M zaN!A=&X|XJ<>R{%I)z9+Kqi*RQG3XkWv1`1a!D z;VKotvt;%D{d?JlGcq!KUf1b#ICA8OY&-r?SXk(9k;JcWU+@}XdRCY;zAjErPs=tO z7w3ECNe~1yH8o+!jvcaXc_S|`5B2r+5Cq?&c7MKo!QpU7|9WPReSS3%SC+MIBkkesiWMsf*vn}~FMQF8JG&eWHY&NUnTSRQ^XKN-N zu2KQU#2=}tsj?0C^z_KOFMmHjA6KqiS@M+;1Ob;XUxvkEQPnp9q@|@v|1REy;ZB6! zldhQ<$V@QO($W+qFlW!6^=s?gxpRtaDVeFdb?cVTM95o&ha@>RHl}E_b^rc-*+DNX zEU>7kXpxPhM~_N}tpV}i!2?AxzI^%8Q*|i;{w&^QHk)M|KX&Yx?0>*X*TkoE(sSp| zo&50OgCZNn#l^DY+qiL~v}(;#0vr+VDk&+E?Lec^@bTlviU>?iO))Dgi7 zPb+d39v&|3ZB<8WYb(RU!xhC;US2LWQ7A_M9bHq%93LNNbab>LhjDRnA(Oe}jpz~RG(IXXJJY~rJ%qda{0@UnaxjYjD-*c8b{dlv(V@LREVU0vO> zf`Gdjo;Y!Wy}iA%iTC#Q(rUFbDk^H(e(UP$q$bP#@eH&){usayN#ekP1F+izccM#S z^XAPcC@AnOkAI7UuIcG%3=IvTySp3h?d|C6>kG=y{{8!LUej+PXl9Q9AO@E$bo?_x&BAgMeb$53w?ul2j#Kgq#;lqd0re6^h zdA9&zbSFn7+1A#kc$Ib~idL&-Yip~Nyq#)nl5&d>OMmj8qUHAXcE-lWt~zP4=yW>X zym?bfzME=olaq*$M9D_U&dyGnOr}*QZ!($K+1V*2KS(lBRq_%M5-Hg*`Rdgx?%TI- z)yP{c7HOtOx;999=KMn?LM&aI5J|e0sA_6z6z?Pnrs(y0*3{H^FUi>%f)geFL=Z^+ znv(7Et|09j&#fuj_dGaI; z27_wE4F&_PRx6)Be@-v-G1%{Xupp@=`6bpI{-`p7!w3xe5GUh4&P?{4}Y5$sRCD$ R01N;C002ovPDHLkV1h*~tvmn# delta 2550 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 M07*qoM6N<$f>mbyIRF3v diff --git a/android/res/drawable-xxhdpi/cr3_button_more_hc.png b/android/res/drawable-xxhdpi/cr3_button_more_hc.png index e754e2fe1dcbf67991d648afb5ac2666cd3c2ef6..61fb397de58c6e8121b43a3f2445de104b1d551f 100644 GIT binary patch delta 3739 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 diff --git a/android/res/drawable-xxxhdpi/cr3_button_more_hc.png b/android/res/drawable-xxxhdpi/cr3_button_more_hc.png index 44dfd14dbb7bc8e8d1ca8bafe7b22c247500d834..47913a906eea7a0c2120c4248cb37879d4ddc127 100644 GIT binary patch delta 5103 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 From a6ee16941dd9bf550785f820ed564fa76d5ccd23 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sat, 9 Mar 2019 14:38:26 +0000 Subject: [PATCH 13/80] Fixed regression in commit fe9b4ff91c98dd88ccc5d27396597fa49aeb9d85: when opened epub file option "Enable document internal styles" ignored (PROP_EMBEDDED_STYLES). Also fixes issue #81. --- android/jni/docview.cpp | 2 -- crengine/src/lvdocview.cpp | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/android/jni/docview.cpp b/android/jni/docview.cpp index 18ded5b4a5..cfb46567d8 100644 --- a/android/jni/docview.cpp +++ b/android/jni/docview.cpp @@ -1469,8 +1469,6 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_DocView_doCommandInterna if ( cmd>=READERVIEW_DCMD_START && cmd<=READERVIEW_DCMD_END) { return p->doCommand(cmd, param)?JNI_TRUE:JNI_FALSE; } - if (!p->_docview->isDocumentOpened()) - return JNI_FALSE; //CRLog::trace("doCommandInternal(%d, %d) -- passing to LVDocView", cmd, param); return p->_docview->doCommand((LVDocCmd)cmd, param) ? JNI_TRUE : JNI_FALSE; } diff --git a/crengine/src/lvdocview.cpp b/crengine/src/lvdocview.cpp index 433e6e0e52..4dfa7b6c51 100644 --- a/crengine/src/lvdocview.cpp +++ b/crengine/src/lvdocview.cpp @@ -5211,6 +5211,10 @@ CRBookmark * LVDocView::findBookmarkByPoint(lvPoint pt) { // execute command int LVDocView::doCommand(LVDocCmd cmd, int param) { CRLog::trace("doCommand(%d, %d)", (int)cmd, param); + if (NULL == m_doc) { + CRLog::warn("doCommand(): m_doc is NULL!"); + return 0; + } switch (cmd) { case DCMD_SET_DOC_FONTS: CRLog::trace("DCMD_SET_DOC_FONTS(%d)", param); From 684c456526a42c0e63a0e33658f5890c63d282e5 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sun, 10 Mar 2019 14:36:49 +0400 Subject: [PATCH 14/80] Updated intent filters in AndroidManifest.xml file: more chances to open in CoolReader from external applications, such as file managers, internet browsers, etc. Also added some tweaks when processing received intent that fix path to the file. --- android/AndroidManifest.xml | 33 ++++++++---- android/app/src/main/AndroidManifest.xml | 60 +++++----------------- android/src/org/coolreader/CoolReader.java | 27 +++++++++- 3 files changed, 64 insertions(+), 56 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 19f5c15ec7..88849088bb 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -50,7 +50,9 @@ - + + + @@ -61,6 +63,14 @@ + + + + + + + + @@ -92,23 +102,30 @@ - + + + + + + + - - - - + + + + + - + @@ -118,8 +135,6 @@ - - diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d43c5b3e1d..008f7e1fe7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -50,7 +50,9 @@ - + + + @@ -61,6 +63,14 @@ + + + + + + + + @@ -92,49 +102,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -165,8 +135,6 @@ - - diff --git a/android/src/org/coolreader/CoolReader.java b/android/src/org/coolreader/CoolReader.java index 04abf28fc1..c69456c231 100644 --- a/android/src/org/coolreader/CoolReader.java +++ b/android/src/org/coolreader/CoolReader.java @@ -1,6 +1,7 @@ // Main Class package org.coolreader; +import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Date; @@ -306,7 +307,10 @@ private boolean processIntent(Intent intent) { Uri uri = intent.getData(); intent.setData(null); if (uri != null) { - fileToOpen = uri.getPath(); + if (uri.getEncodedPath().contains("%00")) + fileToOpen = uri.getEncodedPath(); + else + fileToOpen = uri.getPath(); // if (fileToOpen.startsWith("file://")) // fileToOpen = fileToOpen.substring("file://".length()); } @@ -315,6 +319,27 @@ private boolean processIntent(Intent intent) { log.d("extras=" + intent.getExtras()); fileToOpen = intent.getExtras().getString(OPEN_FILE_PARAM); } + if (fileToOpen != null) { + // parse uri from system filemanager + if (fileToOpen.contains("%00")) { + // splitter between archive file name and inner file. + fileToOpen = fileToOpen.replace("%00", "@/"); + fileToOpen = Uri.decode(fileToOpen); + } + if (fileToOpen.startsWith("/document/primary:")) { + // scheme="content", host="com.android.externalstorage.documents" + // decode special uri form: /document/primary: + File[] dataDirs = Engine.getStorageDirectories(false); + if (dataDirs != null && dataDirs.length > 0) { + fileToOpen = fileToOpen.replace("/document/primary:", dataDirs[0].getAbsolutePath() + "/"); + } + } else if (fileToOpen.startsWith("/1////")) { + // scheme="content", host="com.google.android.apps.nbu.files.provider" + // caused by "Google Files", package="com.google.android.apps.nbu.files" + // skip "/1///" + fileToOpen = fileToOpen.substring(5); + } + } if (fileToOpen != null) { // patch for opening of books from ReLaunch (under Nook Simple Touch) while (fileToOpen.indexOf("%2F") >= 0) { From 3e75c45fbb202761eabc0849a2e5e20c4f077417 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 17 Mar 2019 15:20:41 +0100 Subject: [PATCH 15/80] Harfbuzz light: disable additional HB features (#271) Disalbing "liga" was not enough, there are other such features that need to be disabled to keep one glyph by codepoint, which is a requirement for the HARFBUZZ_LIGHT algorithm. Glitches could be espacially witnessed with latest FreeSerif on french texts. Also clear glyph caches when changing fallback font to prevent previous fallback font glyphs from leaking (reason why that happened not found). Also fix clang-tidy complaining about LBitmapFont. --- crengine/src/lvfntman.cpp | 42 +++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index fe1dba1d52..99e69ec363 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -864,7 +864,7 @@ class LVFreeTypeFace : public LVFont LVHashTable _glyph_cache2; LVHashTable _width_cache2; hb_buffer_t* _hb_opt_kern_buffer; - hb_feature_t _hb_opt_kern_features[2]; + hb_feature_t _hb_opt_kern_features[22]; #endif public: @@ -916,9 +916,38 @@ class LVFreeTypeFace : public LVFont // HarfBuzz features for full text shaping hb_feature_from_string("-kern", -1, &_hb_features[0]); // font kerning hb_feature_from_string("-liga", -1, &_hb_features[1]); // ligatures + // HarfBuzz features for lighweight characters width calculating with caching - hb_feature_from_string("-kern", -1, &_hb_opt_kern_features[0]); // font kerning - hb_feature_from_string("-liga", -1, &_hb_opt_kern_features[1]); // ligatures + hb_feature_from_string("-kern", -1, &_hb_opt_kern_features[0]); // Kerning: Fine horizontal positioning of one glyph to the next, based on the shapes of the glyphs + // We can enable these ones: + hb_feature_from_string("+mark", -1, &_hb_opt_kern_features[1]); // Mark Positioning: Fine positioning of a mark glyph to a base character + hb_feature_from_string("+mkmk", -1, &_hb_opt_kern_features[2]); // Mark-to-mark Positioning: Fine positioning of a mark glyph to another mark character + hb_feature_from_string("+curs", -1, &_hb_opt_kern_features[3]); // Cursive Positioning: Precise positioning of a letter's connection to an adjacent one + hb_feature_from_string("+locl", -1, &_hb_opt_kern_features[4]); // Substitutes character with the preferred form based on script language + + // We should disable these ones: + hb_feature_from_string("-liga", -1, &_hb_opt_kern_features[5]); // Standard Ligatures: replaces (by default) sequence of characters with a single ligature glyph + hb_feature_from_string("-rlig", -1, &_hb_opt_kern_features[6]); // Ligatures required for correct text display (any script, but in cursive) - Arabic, semitic + hb_feature_from_string("-clig", -1, &_hb_opt_kern_features[7]); // Applies a second ligature feature based on a match of a character pattern within a context of surrounding patterns + hb_feature_from_string("-ccmp", -1, &_hb_opt_kern_features[8]); // Glyph composition/decomposition: either calls a ligature replacement on a sequence of characters or replaces a character with a sequence of glyphs + // Provides logic that can for example effectively alter the order of input characters + hb_feature_from_string("-calt", -1, &_hb_opt_kern_features[9]); // Contextual Alternates: Applies a second substitution feature based on a match of a character pattern within a context of surrounding patterns + hb_feature_from_string("-rclt", -1, &_hb_opt_kern_features[10]); // Required Contextual Alternates: Contextual alternates required for correct text display which differs from the default join for other letters, required especially important by Arabic + hb_feature_from_string("-rvrn", -1, &_hb_opt_kern_features[11]); // Required Variation Alternates: Special variants of a single character, which need apply to specific font variation, required by variable fonts + hb_feature_from_string("-ltra", -1, &_hb_opt_kern_features[12]); // Left-to-right glyph alternates: Replaces characters with forms befitting left-to-right presentation + hb_feature_from_string("-ltrm", -1, &_hb_opt_kern_features[13]); // Left-to-right mirrored forms: Replaces characters with possibly mirrored forms befitting left-to-right presentation + hb_feature_from_string("-rtla", -1, &_hb_opt_kern_features[14]); // Right-to-left glyph alternates: Replaces characters with forms befitting right-to-left presentation + hb_feature_from_string("-rtlm", -1, &_hb_opt_kern_features[15]); // Right-to-left mirrored forms: Replaces characters with possibly mirrored forms befitting right-to-left presentation + hb_feature_from_string("-frac", -1, &_hb_opt_kern_features[16]); // Fractions: Converts figures separated by slash with diagonal fraction + hb_feature_from_string("-numr", -1, &_hb_opt_kern_features[17]); // Numerator: Converts to appropriate fraction numerator form, invoked by frac + hb_feature_from_string("-dnom", -1, &_hb_opt_kern_features[18]); // Denominator: Converts to appropriate fraction denominator form, invoked by frac + hb_feature_from_string("-rand", -1, &_hb_opt_kern_features[19]); // Replaces character with random forms (meant to simulate handwriting) + hb_feature_from_string("-trak", -1, &_hb_opt_kern_features[20]); // Tracking (?) + hb_feature_from_string("-vert", -1, &_hb_opt_kern_features[21]); // Vertical (?) + // Especially needed with FreeSerif and french texts: -ccmp + // Especially needed with Fedra Serif and "The", "Thuringe": -calt + // These tweaks seem fragile (adding here +smcp to experiment with small caps would break FreeSerif again). + // So, when tuning these, please check it still behave well with FreeSerif. #endif } @@ -1203,7 +1232,7 @@ class LVFreeTypeFace : public LVFont } hb_buffer_set_content_type(_hb_opt_kern_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); hb_buffer_guess_segment_properties(_hb_opt_kern_buffer); - hb_shape(_hb_font, _hb_opt_kern_buffer, _hb_opt_kern_features, 2); + hb_shape(_hb_font, _hb_opt_kern_buffer, _hb_opt_kern_features, 22); unsigned int glyph_count = hb_buffer_get_length(_hb_opt_kern_buffer); if (segLen == glyph_count) { hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(_hb_opt_kern_buffer, &glyph_count); @@ -2482,6 +2511,11 @@ class LVFreeTypeFontManager : public LVFontManager if ( !item ) face.clear(); _fallbackFontFace = face; + // Somehow, with Fedra Serif (only!), changing the fallback font does + // not prevent glyphs from previous fallback font to be re-used... + // So let's clear glyphs caches too. + gc(); + clearGlyphCache(); } return !_fallbackFontFace.empty(); } From d16055242cc54b5abe4335113f69a7003d2cc173 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Mon, 18 Mar 2019 10:39:09 +0400 Subject: [PATCH 16/80] Fixed freezes on text rendering with some fonts, when ligatures disabled and some characters divided into few glyphs. --- crengine/src/lvfntman.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index 99e69ec363..b5b815deae 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -1555,6 +1555,7 @@ class LVFreeTypeFace : public LVFont else { posInfo.offset = 0; posInfo.width = prev_width; + lastFitChar = i + 1; continue; /* ignore errors */ } } From c55e2b5df7de26f099195158116ce993571678b5 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Mon, 18 Mar 2019 13:06:09 +0400 Subject: [PATCH 17/80] Small fixes on non HarfBuzz code (if HarfBuzz disabled by any reason): * Don't allow set 'ligature' feature in cr3qt, * fixed potentially freezing. --- cr3qt/src/settings.cpp | 4 ++++ crengine/src/lvfntman.cpp | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cr3qt/src/settings.cpp b/cr3qt/src/settings.cpp index 9f182387fe..9e18cdfc1d 100644 --- a/cr3qt/src/settings.cpp +++ b/cr3qt/src/settings.cpp @@ -139,7 +139,11 @@ SettingsDlg::SettingsDlg(QWidget *parent, CR3View * docView ) : m_ui->cbEnableEmbeddedFonts->setEnabled(m_props->getBoolDef(PROP_EMBEDDED_STYLES, true)); optionToUi( PROP_TXT_OPTION_PREFORMATTED, m_ui->cbTxtPreFormatted ); optionToUi( PROP_FONT_KERNING_ENABLED, m_ui->cbFontKerning ); +#if USE_HARFBUZZ==1 optionToUi( PROP_FONT_LIGATURES_ENABLED, m_ui->cbLigatures ); +#else + m_ui->cbLigatures->setEnabled(false); +#endif optionToUi( PROP_FLOATING_PUNCTUATION, m_ui->cbFloatingPunctuation ); optionToUiIndex( PROP_IMG_SCALING_ZOOMIN_INLINE_MODE, m_ui->cbImageInlineZoominMode ); optionToUiIndex( PROP_IMG_SCALING_ZOOMIN_INLINE_SCALE, m_ui->cbImageInlineZoominScale ); diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index b5b815deae..c6cd773e51 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -14,6 +14,7 @@ #include #include +#include @@ -824,6 +825,7 @@ struct LVCharPosInfo int width; }; +#if USE_HARFBUZZ==1 inline lUInt32 getHash( const struct LVCharTriplet& triplet ) { //return (triplet.prevChar * 1975317 + 164521) ^ (triplet.Char * 1975317 + 164521) ^ (triplet.nextChar * 1975317 + 164521); @@ -831,6 +833,7 @@ inline lUInt32 getHash( const struct LVCharTriplet& triplet ) + (((lUInt64)triplet.prevChar) << 16) + (((lUInt64)triplet.nextChar) << 32) ); } +#endif class LVFreeTypeFace : public LVFont { @@ -1609,6 +1612,7 @@ class LVFreeTypeFace : public LVFont _wcache.put(ch, w); } else { widths[i] = prev_width; + lastFitChar = i + 1; continue; /* ignore errors */ } if ( ch_glyph_index==(FT_UInt)-1 ) @@ -1626,7 +1630,7 @@ class LVFreeTypeFace : public LVFont if ( !isHyphen ) // avoid soft hyphens inside text string prev_width = widths[i]; if ( prev_width > max_width ) { - if ( lastFitChar < i + 7) + if ( lastFitChar < (uint32_t)(i + 7)) break; } else { lastFitChar = i + 1; @@ -1857,13 +1861,13 @@ class LVFreeTypeFace : public LVFont if ( y + _height < clip.top || y >= clip.bottom ) return; - unsigned int i; //lUInt16 prev_width = 0; lChar16 ch; // measure character widths bool isHyphen = false; int x0 = x; #if USE_HARFBUZZ==1 + unsigned int i; hb_glyph_info_t *glyph_info = 0; hb_glyph_position_t *glyph_pos = 0; unsigned int glyph_count; @@ -1978,6 +1982,7 @@ class LVFreeTypeFace : public LVFont } #else FT_UInt previous = 0; + int i; int error; #if (ALLOW_KERNING==1) int use_kerning = _allowKerning && FT_HAS_KERNING( _face ); From 35a0811244b69484ca7ac62a6315b93ef3bf835e Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Mon, 18 Mar 2019 13:20:43 +0400 Subject: [PATCH 18/80] Disabled unused structures when HarfBuzz is disabled... --- crengine/src/lvfntman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index c6cd773e51..afc8da815c 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -809,6 +809,7 @@ static lUInt16 char_flags[] = { (ch==UNICODE_NO_BREAK_SPACE?LCHAR_DEPRECATED_WRAP_AFTER|LCHAR_IS_SPACE: \ (ch==UNICODE_HYPHEN?LCHAR_DEPRECATED_WRAP_AFTER:0)))) +#if USE_HARFBUZZ==1 struct LVCharTriplet { lChar16 prevChar; @@ -825,7 +826,6 @@ struct LVCharPosInfo int width; }; -#if USE_HARFBUZZ==1 inline lUInt32 getHash( const struct LVCharTriplet& triplet ) { //return (triplet.prevChar * 1975317 + 164521) ^ (triplet.Char * 1975317 + 164521) ^ (triplet.nextChar * 1975317 + 164521); From 32a3950ae42805c080992219d937ca293085d7dc Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sun, 24 Mar 2019 23:11:24 +0400 Subject: [PATCH 19/80] Android: fixed bug: always removed only first bookmark. --- .../org/coolreader/crengine/BookmarksDlg.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/android/src/org/coolreader/crengine/BookmarksDlg.java b/android/src/org/coolreader/crengine/BookmarksDlg.java index 67643401cd..33d9c196e8 100644 --- a/android/src/org/coolreader/crengine/BookmarksDlg.java +++ b/android/src/org/coolreader/crengine/BookmarksDlg.java @@ -373,11 +373,19 @@ public void onCreateContextMenu(ContextMenu menu, View v, MenuInflater inflater = mCoolReader.getMenuInflater(); menu.clear(); inflater.inflate(mList.isShortcutMode() ? R.menu.cr3_bookmark_shortcut_context_menu : R.menu.cr3_bookmark_context_menu, menu); - AdapterContextMenuInfo mi = (AdapterContextMenuInfo)menuInfo; - if ( mi!=null ) - selectedItem = mi.position; + //AdapterContextMenuInfo mi = (AdapterContextMenuInfo)menuInfo; + //if ( mi!=null ) + // selectedItem = mi.position; + // in this function menuInfo is always first item, + // but selectedItem is already set in BookmarkList's OnItemLongClickListener Bookmark bm = mList.getSelectedBookmark(); - menu.setHeaderTitle(getContext().getString(R.string.context_menu_title_bookmark)); + String bookmarkText = bm.getPosText(); + if (bookmarkText == null || bookmarkText.length() == 0) + bookmarkText = bm.getTitleText(); + if (bookmarkText != null && bookmarkText.length() > 0) + menu.setHeaderTitle(getContext().getString(R.string.context_menu_title_bookmark) + ": " + bookmarkText); + else + menu.setHeaderTitle(getContext().getString(R.string.context_menu_title_bookmark)); for ( int i=0; i Date: Sun, 24 Mar 2019 23:16:34 +0400 Subject: [PATCH 20/80] gradle plugin updated to 3.3.0, gradle to 4.10.1. --- android/.idea/gradle.xml | 3 +++ android/build.gradle | 2 +- android/gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml index 7ac24c777f..f43d428461 100644 --- a/android/.idea/gradle.xml +++ b/android/.idea/gradle.xml @@ -3,6 +3,9 @@ \ No newline at end of file diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 6a35da3f98..6c146c248d 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -31,6 +31,7 @@ android:icon="@mipmap/cr3_logo" android:allowClearUserData="true" android:process="org.coolreader" + android:usesCleartextTraffic="true" > diff --git a/android/app/src/test/java/org/coolreader/plugins/litres/UrlEncodedFormEntityReplacementTest.java b/android/app/src/test/java/org/coolreader/plugins/litres/UrlEncodedFormEntityReplacementTest.java new file mode 100644 index 0000000000..77416464fe --- /dev/null +++ b/android/app/src/test/java/org/coolreader/plugins/litres/UrlEncodedFormEntityReplacementTest.java @@ -0,0 +1,95 @@ +package org.coolreader.plugins.litres; + +//import org.apache.http.NameValuePair; +//import org.apache.http.client.entity.UrlEncodedFormEntity; +//import org.apache.http.message.BasicNameValuePair; + +import org.junit.Test; + +//import java.io.ByteArrayOutputStream; +//import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +//import java.util.LinkedList; +//import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +public class UrlEncodedFormEntityReplacementTest { + /* + @Test + public void testUrlEncodedFormEntity() { + int offset = 0; + int maxCount = 50; + + String result1 = null; + final String result_ref = "checkpoint=2000-01-01+00%3A00%3A00&rating=hot&limit=0%2C50&search_types=0"; + + Map params = new HashMap(); + params.put("rating", "hot"); + params.put("limit", "" + offset + "," + maxCount); + params.put("search_types", "0"); + params.put("checkpoint", "2000-01-01 00:00:00"); + + List list = new LinkedList(); + for (Map.Entry entry : params.entrySet()) { + list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + } + try { + UrlEncodedFormEntity postParams = new UrlEncodedFormEntity(list, "utf-8"); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + postParams.writeTo(outputStream); + outputStream.flush(); + outputStream.close(); + result1 = outputStream.toString("UTF-8"); + } catch (UnsupportedEncodingException e) { + fail(e.toString()); + } catch (IOException e) { + fail(e.toString()); + } + assertEquals(result1, result_ref); + } + */ + + @Test + public void testUrlEncodedFormEntityReplacement() { + int offset = 0; + int maxCount = 50; + + String result1 = null; + final String result_ref = "checkpoint=2000-01-01+00%3A00%3A00&rating=hot&limit=0%2C50&search_types=0"; + + Map params = new HashMap(); + params.put("rating", "hot"); + params.put("limit", "" + offset + "," + maxCount); + params.put("search_types", "0"); + params.put("checkpoint", "2000-01-01 00:00:00"); + + Iterator> iterator = params.entrySet().iterator(); + String value; + String entry_str; + result1 = ""; + while (true) { + Map.Entry entry = iterator.next(); + if (null != entry.getValue()) { + try { + value = URLEncoder.encode(entry.getValue(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + value = ""; + } + } else { + value = ""; + } + entry_str = entry.getKey() + "=" + value; + result1 += entry_str; + if (iterator.hasNext()) + result1 += "&"; + else + break; + } + assertEquals(result1, result_ref); + } +} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 606827d07c..43fd8bfdc0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' + classpath 'com.android.tools.build:gradle:3.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 07bc4bac5e..27ee6fd152 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jun 05 12:50:49 GMT+04:00 2019 +#Tue Sep 17 21:53:58 GMT+04:00 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/android/src/org/coolreader/plugins/litres/LitresConnection.java b/android/src/org/coolreader/plugins/litres/LitresConnection.java index 13d8ff4bd5..0aeeefb5ee 100644 --- a/android/src/org/coolreader/plugins/litres/LitresConnection.java +++ b/android/src/org/coolreader/plugins/litres/LitresConnection.java @@ -6,13 +6,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.Iterator; import java.util.Map; import java.util.zip.GZIPInputStream; @@ -21,9 +23,6 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.message.BasicNameValuePair; import org.coolreader.crengine.L; import org.coolreader.crengine.Utils; import org.coolreader.db.ServiceThread; @@ -44,7 +43,7 @@ public class LitresConnection { final static String TAG = "litres"; - + public static final String AUTHORIZE_URL = "http://robot.litres.ru/pages/catalit_authorise/"; public static final String REGISTER_URL = "http://robot.litres.ru/pages/catalit_register_user/"; public static final String GENRES_URL = "http://robot.litres.ru/pages/catalit_genres/"; @@ -54,28 +53,57 @@ public class LitresConnection { public static final String PURCHASE_URL = "http://robot.litres.ru/pages/purchase_book/"; public static final String DOWNLOAD_BOOK_URL = "http://robot.litres.ru/pages/catalit_download_book/"; public static final String P_ID = "8786915"; - + ServiceThread workerThread; - + SharedPreferences preferences; + private LitresConnection(SharedPreferences preferences) { workerThread = new ServiceThread("litres"); workerThread.start(); this.preferences = preferences; restorLoginInfo(); } - - public static LitresConnection create (SharedPreferences preferences) { + + public static LitresConnection create(SharedPreferences preferences) { return new LitresConnection(preferences); } public interface ResultHandler { void onResponse(AsyncResponse response); } - - private static final int CONNECT_TIMEOUT = 60000; - private static final int READ_TIMEOUT = 60000; - private static final int MAX_CONTENT_LEN_TO_BUFFER = 5242880; + + private static final int CONNECT_TIMEOUT = 60000; + private static final int READ_TIMEOUT = 60000; + private static final int MAX_CONTENT_LEN_TO_BUFFER = 5242880; + + private String mapParamsToEncodedString(final Map params) { + Iterator> iterator = params.entrySet().iterator(); + String value; + String entry_str; + String postParams = ""; + while (true) { + Map.Entry entry = iterator.next(); + if (null != entry.getValue()) { + try { + value = URLEncoder.encode(entry.getValue(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + value = ""; + } + } + else { + value = ""; + } + entry_str = entry.getKey() + "=" + value; + postParams += entry_str; + if (iterator.hasNext()) + postParams += "&"; + else + break; + } + return postParams; + } + public void sendXMLRequest(final String url, final Map params, final ResponseHandler contentHandler, final ResultHandler resultHandler) { Log.i(TAG, "sending request to " + url); final Handler callbackHandler = new Handler(); @@ -89,7 +117,7 @@ public void run() { } }); } - + @Override public void run() { HttpURLConnection connection = null; @@ -102,50 +130,46 @@ public void run() { onError(0, "Cannot open connection"); return; } - if ( conn instanceof HttpsURLConnection ) { + if (conn instanceof HttpsURLConnection) { onError(0, "HTTPs is not supported yet"); return; } - if ( !(conn instanceof HttpURLConnection) ) { + if (!(conn instanceof HttpURLConnection)) { onError(0, "Only HTTP supported"); return; } - connection = (HttpURLConnection)conn; + connection = (HttpURLConnection) conn; Log.i(TAG, "opened connection"); - connection.setRequestProperty("User-Agent", "CoolReader/3(Android)"); - connection.setInstanceFollowRedirects(true); - connection.setAllowUserInteraction(false); - connection.setConnectTimeout(CONNECT_TIMEOUT); - connection.setReadTimeout(READ_TIMEOUT); - //connection.setDoInput(true); - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - - List list = new LinkedList(); - for (Map.Entry entry : params.entrySet()) - list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); - UrlEncodedFormEntity postParams = new UrlEncodedFormEntity(list, "utf-8"); - //Log.d(TAG, "params: " + postParams.toString()); - OutputStream wr = connection.getOutputStream(); - //OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); - postParams.writeTo(wr); - //wr.write(postParams.toString()); - wr.flush(); + connection.setRequestProperty("User-Agent", "CoolReader/3(Android)"); + connection.setInstanceFollowRedirects(true); + connection.setAllowUserInteraction(false); + connection.setConnectTimeout(CONNECT_TIMEOUT); + connection.setReadTimeout(READ_TIMEOUT); + //connection.setDoInput(true); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + + String postParams = mapParamsToEncodedString(params); + OutputStream outputStream = connection.getOutputStream(); + OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); + wr.write(postParams); wr.close(); - - String fileName = null; - String disp = connection.getHeaderField("Content-Disposition"); - if ( disp!=null ) { - int p = disp.indexOf("filename="); - if ( p>0 ) { - fileName = disp.substring(p + 9); - } - } - //connection.setDoOutput(true); - //connection.set - - int response = -1; - + outputStream.flush(); + outputStream.close(); + + String fileName = null; + String disp = connection.getHeaderField("Content-Disposition"); + if (disp != null) { + int p = disp.indexOf("filename="); + if (p > 0) { + fileName = disp.substring(p + 9); + } + } + //connection.setDoOutput(true); + //connection.set + + int response = -1; + response = connection.getResponseCode(); L.d("Response: " + response); if (response != 200) { @@ -164,14 +188,14 @@ public void run() { onError(0, "Wrong content length"); return; } - + InputStream is = connection.getInputStream(); if ("gzip".equals(contentEncoding)) { L.d("Stream is compressed with GZIP"); is = new GZIPInputStream(new BufferedInputStream(is, 8192)); } - + // byte[] buf = new byte[contentLen]; // if (is.read(buf) != contentLen) { // contentHandler.onError(0, "Wrong content length"); @@ -180,7 +204,7 @@ public void run() { // is.close(); // is = null; // is = new ByteArrayInputStream(buf); - + SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setValidating(false); // spf.setNamespaceAware(true); @@ -189,18 +213,18 @@ public void run() { //XMLReader xr = sp.getXMLReader(); sp.parse(is, contentHandler); is.close(); - + } catch (ParserConfigurationException e) { contentHandler.onError(0, "Error while parsing response"); } catch (SAXException e) { contentHandler.onError(0, "Error while parsing response"); } catch (IOException e) { - contentHandler.onError(0, "Error while accessing litres server"); + contentHandler.onError(0, "Error while accessing litres server, e=" + e); } finally { - if ( connection!=null ) { + if (connection != null) { try { connection.disconnect(); - } catch ( Exception e ) { + } catch (Exception e) { // ignore } } @@ -214,7 +238,7 @@ public void run() { } }); } - + public void sendFileRequest(final String url, final Map params, final File fileToStore, final FileResponse contentHandler, final ResultHandler resultHandler) { Log.i(TAG, "sending request to " + url); final Handler callbackHandler = new Handler(); @@ -228,6 +252,7 @@ public void run() { } }); } + @Override public void run() { HttpURLConnection connection = null; @@ -240,46 +265,55 @@ public void run() { onError(0, "Cannot open connection"); return; } - if ( conn instanceof HttpsURLConnection ) { + if (conn instanceof HttpsURLConnection) { onError(0, "HTTPs is not supported yet"); return; } - if ( !(conn instanceof HttpURLConnection) ) { + if (!(conn instanceof HttpURLConnection)) { onError(0, "Only HTTP supported"); return; } - connection = (HttpURLConnection)conn; + connection = (HttpURLConnection) conn; Log.i(TAG, "opened connection"); - connection.setRequestProperty("User-Agent", "CoolReader/3(Android)"); - connection.setInstanceFollowRedirects(true); - connection.setAllowUserInteraction(false); - connection.setConnectTimeout(CONNECT_TIMEOUT); - connection.setReadTimeout(READ_TIMEOUT); - if (params != null) { - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - List list = new LinkedList(); - for (Map.Entry entry : params.entrySet()) - list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); - UrlEncodedFormEntity postParams = new UrlEncodedFormEntity(list, "utf-8"); + connection.setRequestProperty("User-Agent", "CoolReader/3(Android)"); + connection.setInstanceFollowRedirects(true); + connection.setAllowUserInteraction(false); + connection.setConnectTimeout(CONNECT_TIMEOUT); + connection.setReadTimeout(READ_TIMEOUT); + if (params != null) { + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + /* + List list = new LinkedList(); + for (Map.Entry entry : params.entrySet()) + list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + UrlEncodedFormEntity postParams = new UrlEncodedFormEntity(list, "utf-8"); //Log.d(TAG, "params: " + postParams.toString()); OutputStream wr = connection.getOutputStream(); //OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); postParams.writeTo(wr); - //wr.write(postParams.toString()); + //wr.write(postParams.toString()); wr.flush(); wr.close(); - } else { - //Log.d(TAG, "setting up GET method"); - connection.setDoInput(true); - connection.setRequestMethod("GET"); - //connection.setDoOutput(true); - //connection.setRequestMethod("POST"); - connection.connect(); - } - - int response = -1; - + */ + String postParams = mapParamsToEncodedString(params); + OutputStream outputStream = connection.getOutputStream(); + OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); + wr.write(postParams); + wr.close(); + outputStream.flush(); + outputStream.close(); + } else { + //Log.d(TAG, "setting up GET method"); + connection.setDoInput(true); + connection.setRequestMethod("GET"); + //connection.setDoOutput(true); + //connection.setRequestMethod("POST"); + connection.connect(); + } + + int response = -1; + response = connection.getResponseCode(); L.d("Response: " + response); if (response != 200) { @@ -298,7 +332,7 @@ public void run() { onError(0, "Wrong content length"); return; } - + InputStream is = null; OutputStream os = null; try { @@ -308,7 +342,7 @@ public void run() { L.d("Stream is compressed with GZIP"); is = new GZIPInputStream(new BufferedInputStream(is, 8192)); } - + Log.i(TAG, "downloading file to " + contentHandler.fileToSave + " contentLen=" + contentLen); os = new FileOutputStream(contentHandler.fileToSave); int bytesRead = Utils.copyStreamContent(os, is); @@ -331,10 +365,10 @@ public void run() { contentHandler.fileToSave.delete(); contentHandler.onError(0, "Error while accessing litres server"); } finally { - if ( connection!=null ) { + if (connection != null) { try { connection.disconnect(); - } catch ( Exception e ) { + } catch (Exception e) { // ignore } } @@ -348,7 +382,7 @@ public void run() { } }); } - + public static class LitresGenre implements AsyncResponse { @SuppressWarnings("unused") private static final long serialVersionUID = 1; @@ -357,18 +391,22 @@ public static class LitresGenre implements AsyncResponse { public String token; private LitresGenre parent; private ArrayList children; + public LitresGenre getParent() { return parent; } + public void addChild(LitresGenre child) { if (children == null) children = new ArrayList(); children.add(child); child.parent = this; } + public int getChildCount() { return (children != null) ? children.size() : 0; } + public LitresGenre get(int index) { return children.get(index); } @@ -376,8 +414,9 @@ public LitresGenre get(int index) { private LitresGenre genres; private long genresLastUpdateTimestamp; + public void loadGenres(final ResultHandler resultHandler) { - if (genres != null && System.currentTimeMillis() < genresLastUpdateTimestamp + 24*60*60*1000) { + if (genres != null && System.currentTimeMillis() < genresLastUpdateTimestamp + 24 * 60 * 60 * 1000) { resultHandler.onResponse(genres); return; } @@ -386,9 +425,10 @@ public void loadGenres(final ResultHandler resultHandler) { sendXMLRequest(GENRES_URL, params, new ResponseHandler() { LitresGenre result = new LitresGenre(); LitresGenre currentNode = result; + @Override public AsyncResponse getResponse() { - AsyncResponse res = super.getResponse(); + AsyncResponse res = super.getResponse(); if (res != null) return res; genres = result; @@ -409,7 +449,7 @@ else if ("genre".equals(localName)) { @Override public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { + String qName, Attributes attributes) throws SAXException { if ("catalit-genres".equals(localName)) currentNode = result; else if ("genre".equals(localName)) { @@ -424,7 +464,7 @@ else if ("genre".equals(localName)) { currentNode = item; } } - + } }, resultHandler); } @@ -442,9 +482,10 @@ public void loadAuthors(final Map params, final ResultHandler re OnlineStoreAuthor currentNode; boolean insideCatalitPersons; String currentElement; + @Override public AsyncResponse getResponse() { - AsyncResponse res = super.getResponse(); + AsyncResponse res = super.getResponse(); if (res != null) return res; return result; @@ -468,7 +509,7 @@ else if ("subject".equals(localName)) { @Override public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { + String qName, Attributes attributes) throws SAXException { //Log.d(TAG, "startElement " + localName); if ("catalit-persons".equals(localName)) insideCatalitPersons = true; @@ -480,7 +521,7 @@ else if ("subject".equals(localName)) { } else { currentElement = localName; } - + } @Override @@ -500,11 +541,11 @@ else if ("photo".equals(currentElement)) else if ("main".equals(currentElement)) currentNode.title = text; } - - + + }, resultHandler); } - + public static String generateFileName(OnlineStoreBook book) { StringBuilder buf = new StringBuilder(); buf.append(book.id); @@ -543,9 +584,10 @@ public void loadBooks(final Map params, final ResultHandler resu boolean insideTitleInfo; boolean insideAuthor; String currentElement; + @Override public AsyncResponse getResponse() { - AsyncResponse res = super.getResponse(); + AsyncResponse res = super.getResponse(); if (res != null) return res; return result; @@ -581,7 +623,7 @@ else if ("author".equals(localName)) { @Override public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { + String qName, Attributes attributes) throws SAXException { //Log.d(TAG, "startElement " + localName); if ("catalit-authorization-failed".equals(localName)) { onError(25, "Authorization failed"); @@ -620,7 +662,7 @@ else if ("author".equals(localName) && insideTitleInfo) { } else { currentElement = localName; } - + } @Override @@ -643,8 +685,8 @@ else if ("middle-name".equals(currentElement)) if ("book-title".equals(currentElement)) currentNode.bookTitle = text; } - - + + }, resultHandler); } @@ -703,16 +745,17 @@ public static class LitresAuthInfo implements AsyncResponse { public int userCount; public boolean canRebill; public String login; + @Override public String toString() { return "LitresAuthInfo [id=" + id + ", sid=" + sid + ", login=" + login + ", lastName=" + lastName + ", firstName=" - + firstName + ", middleName=" + middleName + ", bookCount=" + + firstName + ", middleName=" + middleName + ", bookCount=" + bookCount + ", authorCount=" + authorCount + ", userCount=" + userCount + ", canRebill=" + canRebill + "]"; } - + } private long lastAuthorizationTimestamp; @@ -720,8 +763,9 @@ public String toString() { private String lastLogin; private String lastPwd; private LitresAuthInfo authInfo; - + public final static long AUTHORIZATION_TIMEOUT = 3 * 60 * 1000; // 3 minutes + public boolean authorizationValid() { return lastSid != null && (System.currentTimeMillis() < lastAuthorizationTimestamp + AUTHORIZATION_TIMEOUT); } @@ -780,9 +824,10 @@ public void authorize(final String sid, final String login, final String pwd, fi params.put("pwd", pwd); sendXMLRequest(AUTHORIZE_URL, params, new ResponseHandler() { LitresAuthInfo result; + @Override public AsyncResponse getResponse() { - AsyncResponse res = super.getResponse(); + AsyncResponse res = super.getResponse(); if (res != null) return res; return result; @@ -790,7 +835,7 @@ public AsyncResponse getResponse() { @Override public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { + String qName, Attributes attributes) throws SAXException { if ("catalit-authorization-ok".equals(localName)) { result = new LitresAuthInfo(); result.sid = attributes.getValue("sid"); @@ -827,7 +872,7 @@ private static void copyParam(HashMap dst, HashMap registerParams, final ResultHandler resultHandler) { final HashMap params = new HashMap(); final String login = registerParams.get(OnlineStoreRegistrationParam.NEW_ACCOUNT_PARAM_LOGIN); @@ -846,9 +891,10 @@ public void register(final HashMap registerParams, final ResultH params.put("no_present_books", "1"); sendXMLRequest(REGISTER_URL, params, new ResponseHandler() { LitresAuthInfo result; + @Override public AsyncResponse getResponse() { - AsyncResponse res = super.getResponse(); + AsyncResponse res = super.getResponse(); if (res != null) return res; return result; @@ -856,7 +902,7 @@ public AsyncResponse getResponse() { @Override public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { + String qName, Attributes attributes) throws SAXException { if ("catalit-authorization-ok".equals(localName)) { result = new LitresAuthInfo(); result.sid = attributes.getValue("sid"); @@ -900,9 +946,10 @@ public void purchaseBook(final String bookId, final ResultHandler resultHandler) params.put("lfrom", P_ID); sendXMLRequest(PURCHASE_URL, params, new ResponseHandler() { PurchaseStatus result = new PurchaseStatus(); + @Override public AsyncResponse getResponse() { - AsyncResponse res = super.getResponse(); + AsyncResponse res = super.getResponse(); if (res != null) return res; if (result.bookId != null) @@ -912,7 +959,7 @@ public AsyncResponse getResponse() { @Override public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { + String qName, Attributes attributes) throws SAXException { if ("catalit-purchase-ok".equals(localName)) { result.bookId = attributes.getValue("art"); result.newBalance = stringToDouble(attributes.getValue("account"), 0.0); @@ -923,7 +970,7 @@ public void startElement(String uri, String localName, comment = attributes.getValue("coment"); // spelling error in API description onError(errorCode, comment); } - + } }, resultHandler); } @@ -944,7 +991,7 @@ public void downloadBook(final File fileToStore, final OnlineStoreBook book, boo sendFileRequest(url, params, fileToStore, myResponse, resultHandler); } - + public static int stringToInt(String v, int defValue) { if (v == null || v.length() == 0) return defValue; @@ -966,8 +1013,8 @@ public static double stringToDouble(String v, double defValue) { return defValue; } } - - + + public void close() { workerThread.stop(5000); } From 668fd59b2ecd514b009d837a9f6de64445c1d7b6 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Wed, 2 Oct 2019 01:00:42 +0400 Subject: [PATCH 49/80] Clean commented code (tail from previous commit). --- .../coolreader/plugins/litres/LitresConnection.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/android/src/org/coolreader/plugins/litres/LitresConnection.java b/android/src/org/coolreader/plugins/litres/LitresConnection.java index 0aeeefb5ee..1c87dd0412 100644 --- a/android/src/org/coolreader/plugins/litres/LitresConnection.java +++ b/android/src/org/coolreader/plugins/litres/LitresConnection.java @@ -283,19 +283,6 @@ public void run() { if (params != null) { connection.setDoOutput(true); connection.setRequestMethod("POST"); - /* - List list = new LinkedList(); - for (Map.Entry entry : params.entrySet()) - list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); - UrlEncodedFormEntity postParams = new UrlEncodedFormEntity(list, "utf-8"); - //Log.d(TAG, "params: " + postParams.toString()); - OutputStream wr = connection.getOutputStream(); - //OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); - postParams.writeTo(wr); - //wr.write(postParams.toString()); - wr.flush(); - wr.close(); - */ String postParams = mapParamsToEncodedString(params); OutputStream outputStream = connection.getOutputStream(); OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); From 0180ca0fb746e1ddd7ea5d450921716a67579fab Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Wed, 2 Oct 2019 07:53:16 +0400 Subject: [PATCH 50/80] LitRes plugin, last touches. --- .../src/org/coolreader/plugins/litres/LitresConnection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/org/coolreader/plugins/litres/LitresConnection.java b/android/src/org/coolreader/plugins/litres/LitresConnection.java index 1c87dd0412..4793595956 100644 --- a/android/src/org/coolreader/plugins/litres/LitresConnection.java +++ b/android/src/org/coolreader/plugins/litres/LitresConnection.java @@ -151,7 +151,7 @@ public void run() { String postParams = mapParamsToEncodedString(params); OutputStream outputStream = connection.getOutputStream(); - OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); + OutputStreamWriter wr = new OutputStreamWriter(outputStream); wr.write(postParams); wr.close(); outputStream.flush(); @@ -285,7 +285,7 @@ public void run() { connection.setRequestMethod("POST"); String postParams = mapParamsToEncodedString(params); OutputStream outputStream = connection.getOutputStream(); - OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); + OutputStreamWriter wr = new OutputStreamWriter(outputStream); wr.write(postParams); wr.close(); outputStream.flush(); From e11154469e72535f4a5fed584527897805a1440f Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Thu, 3 Oct 2019 01:13:19 +0400 Subject: [PATCH 51/80] Files lvfntman.h, lvfntman.cpp are divided into several, for each class a separate file, for more convenient work. --- android/.idea/codeStyles/codeStyleConfig.xml | 5 + android/app/CMakeLists.txt | 8 + cr3qt/src/main.cpp | 2 + crengine/CMakeLists.txt | 15 +- crengine/include/lvembeddedfont.h | 77 + crengine/include/lvfntman.h | 832 +-- crengine/include/lvfont.h | 204 + crengine/include/lvtextfm.h | 3 +- crengine/include/lvtinydom.h | 1 + crengine/src/lvembeddedfont.cpp | 109 + crengine/src/lvfntman.cpp | 4947 +----------------- crengine/src/lvfont.cpp | 51 + crengine/src/private/lvbasefont.cpp | 70 + crengine/src/private/lvbasefont.h | 42 + crengine/src/private/lvbitmapfont.cpp | 141 + crengine/src/private/lvbitmapfont.h | 94 + crengine/src/private/lvbitmapfontman.cpp | 121 + crengine/src/private/lvbitmapfontman.h | 57 + crengine/src/private/lvfontboldtransform.cpp | 255 + crengine/src/private/lvfontboldtransform.h | 182 + crengine/src/private/lvfontcache.cpp | 184 + crengine/src/private/lvfontcache.h | 117 + crengine/src/private/lvfontdef.cpp | 75 + crengine/src/private/lvfontdef.h | 120 + crengine/src/private/lvfontglyphcache.cpp | 179 + crengine/src/private/lvfontglyphcache.h | 126 + crengine/src/private/lvfreetypeface.cpp | 1328 +++++ crengine/src/private/lvfreetypeface.h | 346 ++ crengine/src/private/lvfreetypefontman.cpp | 1040 ++++ crengine/src/private/lvfreetypefontman.h | 128 + crengine/src/private/lvwin32font.cpp | 656 +++ crengine/src/private/lvwin32font.h | 349 ++ crengine/src/private/lvwin32fontman.cpp | 187 + crengine/src/private/lvwin32fontman.h | 62 + 34 files changed, 6479 insertions(+), 5634 deletions(-) create mode 100644 android/.idea/codeStyles/codeStyleConfig.xml create mode 100644 crengine/include/lvembeddedfont.h create mode 100644 crengine/include/lvfont.h create mode 100644 crengine/src/lvembeddedfont.cpp create mode 100644 crengine/src/lvfont.cpp create mode 100644 crengine/src/private/lvbasefont.cpp create mode 100644 crengine/src/private/lvbasefont.h create mode 100644 crengine/src/private/lvbitmapfont.cpp create mode 100644 crengine/src/private/lvbitmapfont.h create mode 100644 crengine/src/private/lvbitmapfontman.cpp create mode 100644 crengine/src/private/lvbitmapfontman.h create mode 100644 crengine/src/private/lvfontboldtransform.cpp create mode 100644 crengine/src/private/lvfontboldtransform.h create mode 100644 crengine/src/private/lvfontcache.cpp create mode 100644 crengine/src/private/lvfontcache.h create mode 100644 crengine/src/private/lvfontdef.cpp create mode 100644 crengine/src/private/lvfontdef.h create mode 100644 crengine/src/private/lvfontglyphcache.cpp create mode 100644 crengine/src/private/lvfontglyphcache.h create mode 100644 crengine/src/private/lvfreetypeface.cpp create mode 100644 crengine/src/private/lvfreetypeface.h create mode 100644 crengine/src/private/lvfreetypefontman.cpp create mode 100644 crengine/src/private/lvfreetypefontman.h create mode 100644 crengine/src/private/lvwin32font.cpp create mode 100644 crengine/src/private/lvwin32font.h create mode 100644 crengine/src/private/lvwin32fontman.cpp create mode 100644 crengine/src/private/lvwin32fontman.h diff --git a/android/.idea/codeStyles/codeStyleConfig.xml b/android/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..df5f35dccc --- /dev/null +++ b/android/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index 0731215867..7f97933875 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -63,6 +63,8 @@ set(CRENGINE_SRC_FILES ${CR3_ROOT}/crengine/src/lvbmpbuf.cpp ${CR3_ROOT}/crengine/src/lvfnt.cpp ${CR3_ROOT}/crengine/src/hyphman.cpp + ${CR3_ROOT}/crengine/src/lvfont.cpp + ${CR3_ROOT}/crengine/src/lvembeddedfont.cpp ${CR3_ROOT}/crengine/src/lvfntman.cpp ${CR3_ROOT}/crengine/src/lvimg.cpp ${CR3_ROOT}/crengine/src/crskin.cpp @@ -74,6 +76,12 @@ set(CRENGINE_SRC_FILES ${CR3_ROOT}/crengine/src/wolutil.cpp ${CR3_ROOT}/crengine/src/crconcurrent.cpp ${CR3_ROOT}/crengine/src/hist.cpp + ${CR3_ROOT}/crengine/src/private/lvfontglyphcache.cpp + ${CR3_ROOT}/crengine/src/private/lvfontboldtransform.cpp + ${CR3_ROOT}/crengine/src/private/lvfontcache.cpp + ${CR3_ROOT}/crengine/src/private/lvfontdef.cpp + ${CR3_ROOT}/crengine/src/private/lvfreetypeface.cpp + ${CR3_ROOT}/crengine/src/private/lvfreetypefontman.cpp ${CR3_ROOT}/crengine/fc-lang/fc-lang-cat.c ) # ${CR3_ROOT}/crengine/src/cri18n.cpp diff --git a/cr3qt/src/main.cpp b/cr3qt/src/main.cpp index e274f71dce..cc01f6d6e4 100644 --- a/cr3qt/src/main.cpp +++ b/cr3qt/src/main.cpp @@ -421,6 +421,7 @@ bool InitCREngine( const char * exename, lString16Collection & fontDirs ) // fonts are in files font1.lbf, font2.lbf, ... font32.lbf // use fontconfig +#if USE_FREETYPE==1 lString16Collection fontExt; fontExt.add(cs16(".ttf")); fontExt.add(cs16(".otf")); @@ -441,6 +442,7 @@ bool InitCREngine( const char * exename, lString16Collection & fontDirs ) } } //} +#endif // USE_FREETYPE==1 // init hyphenation manager //char hyphfn[1024]; diff --git a/crengine/CMakeLists.txt b/crengine/CMakeLists.txt index f63efd0778..8d1bfb85cb 100644 --- a/crengine/CMakeLists.txt +++ b/crengine/CMakeLists.txt @@ -27,7 +27,9 @@ if ( NOT ${GUI} STREQUAL FB2PROPS ) SET (CRENGINE_SOURCES ${CRENGINE_SOURCES} src/lvbmpbuf.cpp src/lvfnt.cpp - src/hyphman.cpp + src/hyphman.cpp + src/lvembeddedfont.cpp + src/lvfont.cpp src/lvfntman.cpp src/crgui.cpp src/lvimg.cpp @@ -44,6 +46,17 @@ if ( NOT ${GUI} STREQUAL FB2PROPS ) src/pdbfmt.cpp src/wordfmt.cpp src/crconcurrent.cpp + src/private/lvbasefont.cpp + src/private/lvbitmapfont.cpp + src/private/lvbitmapfontman.cpp + src/private/lvfontglyphcache.cpp + src/private/lvfontcache.cpp + src/private/lvfontboldtransform.cpp + src/private/lvfontdef.cpp + src/private/lvwin32font.cpp + src/private/lvwin32fontman.cpp + src/private/lvfreetypeface.cpp + src/private/lvfreetypefontman.cpp #src/xutils.cpp ) endif (NOT ${GUI} STREQUAL FB2PROPS) diff --git a/crengine/include/lvembeddedfont.h b/crengine/include/lvembeddedfont.h new file mode 100644 index 0000000000..917142dbe4 --- /dev/null +++ b/crengine/include/lvembeddedfont.h @@ -0,0 +1,77 @@ +/** @file lvembeddedfont.h + @brief embedded font definition interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_EMBEDDEDFONT_H_INCLUDED__ +#define __LV_EMBEDDEDFONT_H_INCLUDED__ + +#include "crsetup.h" +#include "lvstring.h" +#include "lvptrvec.h" + + +class LVEmbeddedFontDef { + lString16 _url; + lString8 _face; + bool _bold; + bool _italic; +public: + LVEmbeddedFontDef(lString16 url, lString8 face, bool bold, bool italic) : + _url(url), _face(face), _bold(bold), _italic(italic) { + } + + LVEmbeddedFontDef() : _bold(false), _italic(false) { + } + + const lString16 &getUrl() { return _url; } + + const lString8 &getFace() { return _face; } + + bool getBold() { return _bold; } + + bool getItalic() { return _italic; } + + void setFace(const lString8 &face) { _face = face; } + + void setBold(bool bold) { _bold = bold; } + + void setItalic(bool italic) { _italic = italic; } + + bool serialize(SerialBuf &buf); + + bool deserialize(SerialBuf &buf); +}; + +class LVEmbeddedFontList : public LVPtrVector { +public: + LVEmbeddedFontDef *findByUrl(lString16 url); + + void add(LVEmbeddedFontDef *def) { LVPtrVector::add(def); } + + bool add(lString16 url, lString8 face, bool bold, bool italic); + + bool add(lString16 url) { return add(url, lString8::empty_str, false, false); } + + bool addAll(LVEmbeddedFontList &list); + + void set(LVEmbeddedFontList &list) { + clear(); + addAll(list); + } + + bool serialize(SerialBuf &buf); + + bool deserialize(SerialBuf &buf); +}; + +#endif // __LV_EMBEDDEDFONT_H_INCLUDED__ diff --git a/crengine/include/lvfntman.h b/crengine/include/lvfntman.h index bc4e036d55..e3a092a8e5 100644 --- a/crengine/include/lvfntman.h +++ b/crengine/include/lvfntman.h @@ -1,5 +1,5 @@ -/** \file lvfntman.h - \brief font manager interface +/** @file lvfntman.h + @brief font manager interface CoolReader Engine @@ -17,338 +17,12 @@ #include #include "crsetup.h" -#include "lvfnt.h" -#include "cssdef.h" #include "lvstring.h" -#include "lvref.h" -#include "lvptrvec.h" -#include "hyphman.h" -#include "lvdrawbuf.h" - -#if !defined(__SYMBIAN32__) && defined(_WIN32) -extern "C" { -#include -} -#endif - -class LVDrawBuf; - -struct LVFontGlyphCacheItem; - -class LVFontGlobalGlyphCache -{ -private: - LVFontGlyphCacheItem * head; - LVFontGlyphCacheItem * tail; - int size; - int max_size; - void removeNoLock( LVFontGlyphCacheItem * item ); - void putNoLock( LVFontGlyphCacheItem * item ); -public: - LVFontGlobalGlyphCache( int maxSize ) - : head(NULL), tail(NULL), size(0), max_size(maxSize ) - { - } - ~LVFontGlobalGlyphCache() - { - clear(); - } - void put( LVFontGlyphCacheItem * item ); - void remove( LVFontGlyphCacheItem * item ); - void refresh( LVFontGlyphCacheItem * item ); - void clear(); -}; - -class LVFontLocalGlyphCache -{ -private: - LVFontGlyphCacheItem * head; - LVFontGlyphCacheItem * tail; - LVFontGlobalGlyphCache * global_cache; - //int size; -public: - LVFontLocalGlyphCache( LVFontGlobalGlyphCache * globalCache ) - : head(NULL), tail(NULL), global_cache( globalCache ) - { } - ~LVFontLocalGlyphCache() - { - clear(); - } - void clear(); - LVFontGlyphCacheItem * get( lUInt16 ch ); - void put( LVFontGlyphCacheItem * item ); - void remove( LVFontGlyphCacheItem * item ); -}; - -struct LVFontGlyphCacheItem -{ - LVFontGlyphCacheItem * prev_global; - LVFontGlyphCacheItem * next_global; - LVFontGlyphCacheItem * prev_local; - LVFontGlyphCacheItem * next_local; - LVFontLocalGlyphCache * local_cache; - lChar16 ch; - lUInt16 bmp_width; - lUInt16 bmp_height; - lInt16 origin_x; - lInt16 origin_y; - lUInt16 advance; - lUInt8 bmp[1]; - //======================================================================= - int getSize() - { - return sizeof(LVFontGlyphCacheItem) - + (bmp_width * bmp_height - 1) * sizeof(lUInt8); - } - static LVFontGlyphCacheItem * newItem( LVFontLocalGlyphCache * local_cache, lChar16 ch, int w, int h ) - { - LVFontGlyphCacheItem * item = (LVFontGlyphCacheItem *)malloc( sizeof(LVFontGlyphCacheItem) - + (w*h - 1)*sizeof(lUInt8) ); - item->ch = ch; - item->bmp_width = (lUInt16)w; - item->bmp_height = (lUInt16)h; - item->origin_x = 0; - item->origin_y = 0; - item->advance = 0; - item->prev_global = NULL; - item->next_global = NULL; - item->prev_local = NULL; - item->next_local = NULL; - item->local_cache = local_cache; - return item; - } - static void freeItem( LVFontGlyphCacheItem * item ) - { - free( item ); - } -}; - -struct LVFontGlyphIndexCacheItem -{ - lUInt32 gindex; - lUInt16 bmp_width; - lUInt16 bmp_height; - lInt16 origin_x; - lInt16 origin_y; - lUInt16 advance; - lUInt8 bmp[1]; - //======================================================================= - int getSize() - { - return sizeof(LVFontGlyphIndexCacheItem) + (bmp_width * bmp_height - 1) * sizeof(lUInt8); - } - static LVFontGlyphIndexCacheItem * newItem(lUInt32 glyph_index, int w, int h ) - { - LVFontGlyphIndexCacheItem* item = (LVFontGlyphIndexCacheItem*)malloc( sizeof(LVFontGlyphIndexCacheItem) + (w*h - 1)*sizeof(lUInt8) ); - if (item) { - item->gindex = glyph_index; - item->bmp_width = (lUInt16)w; - item->bmp_height = (lUInt16)h; - item->origin_x = 0; - item->origin_y = 0; - item->advance = 0; - } - return item; - } - static void freeItem(LVFontGlyphIndexCacheItem* item) - { - if (item) - free(item); - } -}; - - -enum hinting_mode_t { - HINTING_MODE_DISABLED, - HINTING_MODE_BYTECODE_INTERPRETOR, - HINTING_MODE_AUTOHINT -}; - - -/** \brief base class for fonts - - implements single interface for font of any engine -*/ -class LVFont : public LVRefCounter -{ -protected: - int _visual_alignment_width; -public: - lUInt32 _hash; - /// glyph properties structure - struct glyph_info_t { - lUInt8 blackBoxX; ///< 0: width of glyph - lUInt8 blackBoxY; ///< 1: height of glyph black box - lInt8 originX; ///< 2: X origin for glyph - lInt8 originY; ///< 3: Y origin for glyph - lUInt8 width; ///< 4: full width of glyph - }; - - /// hyphenation character - virtual lChar16 getHyphChar() { return UNICODE_SOFT_HYPHEN_CODE; } - - /// hyphen width - virtual int getHyphenWidth() { return getCharWidth( getHyphChar() ); } - - /** - * Max width of -/./,/!/? to use for visial alignment by width - */ - virtual int getVisualAligmentWidth(); - - /** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found - */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ) = 0; - - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \param max_width is maximum width to measure line - \param def_char is character to replace absent glyphs in font - \param letter_spacing is number of pixels to add between letters - \return number of characters before max_width reached - */ - virtual lUInt16 measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing=0, - bool allow_hyphenation=true - ) = 0; - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \return width of specified string - */ - virtual lUInt32 getTextWidth( - const lChar16 * text, int len - ) = 0; - -// /** \brief get glyph image in 1 byte per pixel format -// \param code is unicode character -// \param buf is buffer [width*height] to place glyph data -// \return true if glyph was found -// */ -// virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0) = 0; - /** \brief get glyph item - \param code is unicode character - \return glyph pointer if glyph was found, NULL otherwise - */ - virtual LVFontGlyphCacheItem * getGlyph(lUInt16 ch, lChar16 def_char=0) = 0; - /// returns font baseline offset - virtual int getBaseline() = 0; - /// returns font height including normal interline space - virtual int getHeight() const = 0; - /// returns font character size - virtual int getSize() const = 0; - /// returns font weight - virtual int getWeight() const = 0; - /// returns italic flag - virtual int getItalic() const = 0; - /// returns char width - virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ) = 0; - /// retrieves font handle - virtual void * GetHandle() = 0; - /// returns font typeface name - virtual lString8 getTypeFace() const = 0; - /// returns font family id - virtual css_font_family_t getFontFamily() const = 0; - /// draws text string - virtual void DrawTextString( LVDrawBuf * buf, int x, int y, - const lChar16 * text, int len, - lChar16 def_char, lUInt32 * palette = NULL, bool addHyphen = false, - lUInt32 flags=0, int letter_spacing=0 ) = 0; - /// constructor - LVFont() : _visual_alignment_width(-1), _hash(0) { } - - /// get bitmap mode (true=monochrome bitmap, false=antialiased) - virtual bool getBitmapMode() { return false; } - /// set bitmap mode (true=monochrome bitmap, false=antialiased) - virtual void setBitmapMode( bool ) { } - - /// get kerning mode: true==ON, false=OFF - virtual bool getKerning() const { return false; } - /// set kerning mode: true==ON, false=OFF - virtual void setKerning( bool ) { } - - /// get ligatures mode: true==allowed, false=not allowed - virtual bool getLigatures() const { return false; } - /// set ligatures mode: true==allowed, false=not allowed - virtual void setLigatures( bool ) { } - - /// sets current hinting mode - virtual void setHintingMode(hinting_mode_t /*mode*/) { } - /// returns current hinting mode - virtual hinting_mode_t getHintingMode() const { return HINTING_MODE_AUTOHINT; } - - /// returns true if font is empty - virtual bool IsNull() const = 0; - virtual bool operator ! () const = 0; - virtual void Clear() = 0; - virtual ~LVFont() { } - - virtual bool kerningEnabled() { return false; } - virtual int getKerningOffset(lChar16 ch1, lChar16 ch2, lChar16 def_char) { CR_UNUSED3(ch1,ch2,def_char); return 0; } - - virtual bool checkFontLangCompat(const lString8& langCode) { return true; } - - /// set fallback font for this font - void setFallbackFont( LVProtectedFastRef font ) { CR_UNUSED(font); } - /// get fallback font for this font - LVFont * getFallbackFont() { return NULL; } -}; - -typedef LVProtectedFastRef LVFontRef; - -enum font_antialiasing_t -{ - font_aa_none, - font_aa_big, - font_aa_all -}; - -class LVEmbeddedFontDef { - lString16 _url; - lString8 _face; - bool _bold; - bool _italic; -public: - LVEmbeddedFontDef(lString16 url, lString8 face, bool bold, bool italic) : - _url(url), _face(face), _bold(bold), _italic(italic) - { - } - LVEmbeddedFontDef() : _bold(false), _italic(false) { - } +#include "lvfont.h" - const lString16 & getUrl() { return _url; } - const lString8 & getFace() { return _face; } - bool getBold() { return _bold; } - bool getItalic() { return _italic; } - void setFace(const lString8 & face) { _face = face; } - void setBold(bool bold) { _bold = bold; } - void setItalic(bool italic) { _italic = italic; } - bool serialize(SerialBuf & buf); - bool deserialize(SerialBuf & buf); -}; - -class LVEmbeddedFontList : public LVPtrVector { -public: - LVEmbeddedFontDef * findByUrl(lString16 url); - void add(LVEmbeddedFontDef * def) { LVPtrVector::add(def); } - bool add(lString16 url, lString8 face, bool bold, bool italic); - bool add(lString16 url) { return add(url, lString8::empty_str, false, false); } - bool addAll(LVEmbeddedFontList & list); - void set(LVEmbeddedFontList & list) { clear(); addAll(list); } - bool serialize(SerialBuf & buf); - bool deserialize(SerialBuf & buf); -}; /// font manager interface class -class LVFontManager -{ +class LVFontManager { protected: int _antialiasMode; bool _allowKerning; @@ -357,487 +31,143 @@ class LVFontManager public: /// garbage collector frees unused fonts virtual void gc() = 0; + /// returns most similar font - virtual LVFontRef GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface, int documentId = -1) = 0; + virtual LVFontRef + GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface, + int documentId = -1) = 0; + /// set fallback font face (returns true if specified font is found) - virtual bool SetFallbackFontFace( lString8 face ) { CR_UNUSED(face); return false; } + virtual bool SetFallbackFontFace(lString8 face) { + CR_UNUSED(face); + return false; + } + /// get fallback font face (returns empty string if no fallback font is set) virtual lString8 GetFallbackFontFace() { return lString8::empty_str; } + /// returns fallback font for specified size virtual LVFontRef GetFallbackFont(int /*size*/) { return LVFontRef(); } + /// registers font by name - virtual bool RegisterFont( lString8 name ) = 0; + virtual bool RegisterFont(lString8 name) = 0; + /// registers font by name and face - virtual bool RegisterExternalFont(lString16 /*name*/, lString8 /*face*/, bool /*bold*/, bool /*italic*/) { return false; } + virtual bool RegisterExternalFont(lString16 /*name*/, lString8 /*face*/, bool /*bold*/, + bool /*italic*/) { return false; } + /// registers document font - virtual bool RegisterDocumentFont(int /*documentId*/, LVContainerRef /*container*/, lString16 /*name*/, lString8 /*face*/, bool /*bold*/, bool /*italic*/) { return false; } + virtual bool + RegisterDocumentFont(int /*documentId*/, LVContainerRef /*container*/, lString16 /*name*/, + lString8 /*face*/, bool /*bold*/, bool /*italic*/) { return false; } + /// unregisters all document fonts - virtual void UnregisterDocumentFonts(int /*documentId*/) { } + virtual void UnregisterDocumentFonts(int /*documentId*/) {} + /// initializes font manager - virtual bool Init( lString8 path ) = 0; + virtual bool Init(lString8 path) = 0; + /// get count of registered fonts virtual int GetFontCount() = 0; + /// get hash of installed fonts and fallback font virtual lUInt32 GetFontListHash(int /*documentId*/) { return 0; } + /// clear glyph cache - virtual void clearGlyphCache() { } + virtual void clearGlyphCache() {} /// get antialiasing mode virtual int GetAntialiasMode() { return _antialiasMode; } + /// set antialiasing mode - virtual void SetAntialiasMode( int mode ) { _antialiasMode = mode; gc(); clearGlyphCache(); } + virtual void SetAntialiasMode(int mode) { + _antialiasMode = mode; + gc(); + clearGlyphCache(); + } /// get kerning mode: true==ON, false=OFF virtual bool getKerning() { return _allowKerning; } + /// get kerning mode: true==ON, false=OFF - virtual void setKerning( bool kerningEnabled ) { _allowKerning = kerningEnabled; gc(); clearGlyphCache(); } + virtual void setKerning(bool kerningEnabled) { + _allowKerning = kerningEnabled; + gc(); + clearGlyphCache(); + } /// get ligatures mode: true==allowed, false=not allowed virtual bool getLigatures() const { return _allowLigatures; } + /// set ligatures mode: true==allowed, false=not allowed - virtual void setLigatures( bool ligaturesEnabled ) { _allowLigatures = ligaturesEnabled; gc(); clearGlyphCache(); } + virtual void setLigatures(bool ligaturesEnabled) { + _allowLigatures = ligaturesEnabled; + gc(); + clearGlyphCache(); + } /// constructor - LVFontManager() : _antialiasMode(font_aa_all), _allowKerning(false), _allowLigatures(false), _hintingMode(HINTING_MODE_AUTOHINT) { } + LVFontManager() : _antialiasMode(font_aa_all), _allowKerning(false), _allowLigatures(false), + _hintingMode(HINTING_MODE_AUTOHINT) {} + /// destructor - virtual ~LVFontManager() { } + virtual ~LVFontManager() {} + /// returns available typefaces - virtual void getFaceList( lString16Collection & ) { } + virtual void getFaceList(lString16Collection &) {} + /// returns available font files - virtual void getFontFileNameList( lString16Collection & ) { } + virtual void getFontFileNameList(lString16Collection &) {} - // check font language compatibility - virtual bool checkFontLangCompat(const lString8& typeface, const lString8& langCode) { return true; } + // check font language compatibility + virtual bool + checkFontLangCompat(const lString8 &typeface, const lString8 &langCode) { return true; } - /// returns first found face from passed list, or return face for font found by family only - virtual lString8 findFontFace(lString8 commaSeparatedFaceList, css_font_family_t fallbackByFamily); + /// returns first found face from passed list, or return face for font found by family only + virtual lString8 + findFontFace(lString8 commaSeparatedFaceList, css_font_family_t fallbackByFamily); /// fills array with list of available gamma levels virtual void GetGammaLevels(LVArray dst); + /// returns current gamma level index - virtual int GetGammaIndex(); + virtual int GetGammaIndex(); + /// sets current gamma level index - virtual void SetGammaIndex( int gammaIndex ); + virtual void SetGammaIndex(int gammaIndex); + /// returns current gamma level virtual double GetGamma(); + /// sets current gamma level - virtual void SetGamma( double gamma ); + virtual void SetGamma(double gamma); /// sets current hinting mode - virtual void SetHintingMode(hinting_mode_t /*mode*/) { } + virtual void SetHintingMode(hinting_mode_t /*mode*/) {} + /// returns current hinting mode - virtual hinting_mode_t GetHintingMode() { return HINTING_MODE_AUTOHINT; } - virtual bool setalias(lString8 alias,lString8 facename,int id,bool italic,bool bold){ + virtual hinting_mode_t GetHintingMode() { return HINTING_MODE_AUTOHINT; } + + virtual bool setalias(lString8 alias, lString8 facename, int id, bool italic, bool bold) { CR_UNUSED5(alias, facename, id, italic, bold); return false; } }; -class LVBaseFont : public LVFont -{ -protected: - lString8 _typeface; - css_font_family_t _family; -public: - /// returns font typeface name - virtual lString8 getTypeFace() const { return _typeface; } - /// returns font family id - virtual css_font_family_t getFontFamily() const { return _family; } - /// draws text string - virtual void DrawTextString( LVDrawBuf * buf, int x, int y, - const lChar16 * text, int len, - lChar16 def_char, lUInt32 * palette, bool addHyphen, lUInt32 flags=0, int letter_spacing=0 ); -}; - -#if (USE_FREETYPE!=1) && (USE_BITMAP_FONTS==1) -/* C++ wrapper class */ -class LBitmapFont : public LVBaseFont -{ -private: - lvfont_handle m_font; -public: - LBitmapFont() : m_font(NULL) { } - virtual bool getGlyphInfo( lUInt16 code, LVFont::glyph_info_t * glyph, lChar16 def_char=0 ); - virtual lUInt16 measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing=0, - bool allow_hyphenation=true - ); - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \return width of specified string - */ - virtual lUInt32 getTextWidth( - const lChar16 * text, int len - ); - /// returns font baseline offset - virtual int getBaseline(); - /// returns font height - virtual int getHeight() const; - - virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0 ); - - /// returns char width - virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ) - { - glyph_info_t glyph; - if ( getGlyphInfo(ch, &glyph, def_char ) ) - return glyph.width; - return 0; - } - - virtual lvfont_handle GetHandle() { return m_font; } - - int LoadFromFile( const char * fname ); - - // LVFont functions overrides - virtual void Clear() { if (m_font) lvfontClose( m_font ); m_font = NULL; } - virtual bool IsNull() const { return m_font==NULL; } - virtual bool operator ! () const { return IsNull(); } - virtual ~LBitmapFont() { Clear(); } -}; -#endif - -#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE!=1 -class LVBaseWin32Font : public LVBaseFont -{ -protected: - HFONT _hfont; - LOGFONTA _logfont; - int _height; - int _baseline; - LVColorDrawBuf _drawbuf; - -public: - - LVBaseWin32Font() : _hfont(NULL), _height(0), _baseline(0), _drawbuf(1,1) - { } - - virtual ~LVBaseWin32Font() { Clear(); } - - /// returns font baseline offset - virtual int getBaseline() - { - return _baseline; - } - - /// returns font height - virtual int getHeight() const - { - return _height; - } - - /// retrieves font handle - virtual void * GetHandle() - { - return (void*)_hfont; - } - - /// returns char width - virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ) - { - glyph_info_t glyph; - if ( getGlyphInfo(ch, &glyph, def_char) ) - return glyph.width; - return 0; - } - /// returns true if font is empty - virtual bool IsNull() const - { - return (_hfont == NULL); - } - - virtual bool operator ! () const - { - return (_hfont == NULL); - } - - virtual void Clear(); - - virtual bool Create( const LOGFONTA & lf ); - - virtual bool Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ); - - virtual int getWeight() const { - return _logfont.lfWeight; - } - - virtual int getItalic() const { - return _logfont.lfItalic; - } - - virtual lString8 getTypeFace() const { - return lString8::empty_str; - } - - virtual css_font_family_t getFontFamily() const { - return css_ff_inherit; - } - - virtual LVFontGlyphCacheItem * getGlyph(lUInt16 ch, lChar16 def_char=0) { - return NULL; - } - - virtual int getSize() const { - return 0; - } - -}; - - -class LVWin32DrawFont : public LVBaseWin32Font -{ -private: - int _hyphen_width; -public: - - LVWin32DrawFont() : _hyphen_width(0) { } - - /** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found - */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ); - - /** \brief measure text - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyph was found - */ - virtual lUInt16 measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing=0, - bool allow_hyphenation=true - ); - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \return width of specified string - */ - virtual lUInt32 getTextWidth( - const lChar16 * text, int len - ); - - /// returns char width - virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ); - - /// draws text string - virtual void DrawTextString( LVDrawBuf * buf, int x, int y, - const lChar16 * text, int len, - lChar16 def_char, lUInt32 * palette, bool addHyphen, lUInt32 flags=0, int letter_spacing=0 ); - - /** \brief get glyph image in 1 byte per pixel format - \param code is unicode character - \param buf is buffer [width*height] to place glyph data - \return true if glyph was found - */ - virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0); - -}; - -struct glyph_t { - lUInt8 * glyph; - lChar16 ch; - bool flgNotExists; - bool flgValid; - LVFont::glyph_info_t gi; - glyph_t * next; - glyph_t(lChar16 c) - : glyph(NULL), ch(c), flgNotExists(false), flgValid(false), next(NULL) - { - memset( &gi, 0, sizeof(gi) ); - } - ~glyph_t() - { - if (glyph) - delete glyph; - } -}; - -class GlyphCache -{ -private: - lUInt32 _size; - glyph_t * * _hashtable; -public: - GlyphCache( lUInt32 size ) - : _size(size) - { - _hashtable = new glyph_t * [_size]; - for (lUInt32 i=0; i<_size; i++) - _hashtable[i] = NULL; - } - void clear() - { - for (lUInt32 i=0; i<_size; i++) - { - glyph_t * p = _hashtable[i]; - while (p) - { - glyph_t * next = p->next; - delete p; - p = next; - } - _hashtable[i] = NULL; - } - } - ~GlyphCache() - { - if (_hashtable) - { - clear(); - delete _hashtable; - } - } - glyph_t * find( lChar16 ch ) - { - lUInt32 index = (((lUInt32)ch)*113) % _size; - glyph_t * p = _hashtable[index]; - // 3 levels - if (!p) - return NULL; - if (p->ch == ch) - return p; - p = p->next; - if (!p) - return NULL; - if (p->ch == ch) - return p; - p = p->next; - if (!p) - return NULL; - if (p->ch == ch) - return p; - return NULL; - } - /// returns found or creates new - glyph_t * get( lChar16 ch ) - { - lUInt32 index = (((lUInt32)ch)*113) % _size; - glyph_t * * p = &_hashtable[index]; - // 3 levels - if (!*p) - { - return (*p = new glyph_t(ch)); - } - if ((*p)->ch == ch) - { - return *p; - } - p = &(*p)->next; - if (!*p) - { - return (*p = new glyph_t(ch)); - } - if ((*p)->ch == ch) - { - return *p; - } - p = &(*p)->next; - if (!*p) - { - return (*p = new glyph_t(ch)); - } - if ((*p)->ch == ch) - { - return *p; - } - - delete (*p); - *p = NULL; - - glyph_t * pp = new glyph_t(ch); - pp->next = _hashtable[index]; - _hashtable[index] = pp; - return pp; - } - -}; - -class LVWin32Font : public LVBaseWin32Font -{ -private: - lChar16 _unknown_glyph_index; - GlyphCache _cache; - - static int GetGlyphIndex( HDC hdc, wchar_t code ); - - glyph_t * GetGlyphRec( lChar16 ch ); - -public: - /** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found - */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ); - - /** \brief measure text - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyph was found - */ - virtual lUInt16 measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing=0, - bool allow_hyphenation=true - ); - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \return width of specified string - */ - virtual lUInt32 getTextWidth( - const lChar16 * text, int len - ); - - /** \brief get glyph image in 1 byte per pixel format - \param code is unicode character - \param buf is buffer [width*height] to place glyph data - \return true if glyph was found - */ - virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0); - - virtual void Clear(); - - virtual bool Create( const LOGFONTA & lf ); - - virtual bool Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ); - - LVWin32Font() : _cache(256) { } - - virtual ~LVWin32Font() { } -}; - -#endif - - #define LVFONT_TRANSFORM_EMBOLDEN 1 + /// create transform for font -LVFontRef LVCreateFontTransform( LVFontRef baseFont, int transformFlags ); +LVFontRef LVCreateFontTransform(LVFontRef baseFont, int transformFlags); /// current font manager pointer -extern LVFontManager * fontMan; +extern LVFontManager *fontMan; /// initializes font manager -bool InitFontManager( lString8 path ); +bool InitFontManager(lString8 path); /// deletes font manager bool ShutdownFontManager(); -LVFontRef LoadFontFromFile( const char * fname ); - -/// to compare two fonts -bool operator == (const LVFont & r1, const LVFont & r2); +LVFontRef LoadFontFromFile(const char *fname); #endif //__LV_FNT_MAN_H_INCLUDED__ diff --git a/crengine/include/lvfont.h b/crengine/include/lvfont.h new file mode 100644 index 0000000000..43f89a35c3 --- /dev/null +++ b/crengine/include/lvfont.h @@ -0,0 +1,204 @@ +/** @file lvfont.h + @brief font interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FONT_H_INCLUDED__ +#define __LV_FONT_H_INCLUDED__ + +#include "crsetup.h" +#include "lvstring.h" +#include "lvref.h" +#include "lvdrawbuf.h" +#include "cssdef.h" + +#define MAX_LINE_CHARS 2048 + +enum hinting_mode_t { + HINTING_MODE_DISABLED, + HINTING_MODE_BYTECODE_INTERPRETOR, + HINTING_MODE_AUTOHINT +}; + +enum font_antialiasing_t { + font_aa_none, + font_aa_big, + font_aa_all +}; + +class LVFontGlyphCacheItem; + +/** \brief base class for fonts + + implements single interface for font of any engine +*/ +class LVFont : public LVRefCounter { +protected: + int _visual_alignment_width; +public: + lUInt32 _hash; + /// glyph properties structure + struct glyph_info_t { + lUInt8 blackBoxX; ///< 0: width of glyph + lUInt8 blackBoxY; ///< 1: height of glyph black box + lInt8 originX; ///< 2: X origin for glyph + lInt8 originY; ///< 3: Y origin for glyph + lUInt8 width; ///< 4: full width of glyph + }; + + /// hyphenation character + virtual lChar16 getHyphChar() { return UNICODE_SOFT_HYPHEN_CODE; } + + /// hyphen width + virtual int getHyphenWidth() { return getCharWidth(getHyphChar()); } + + /** + * Max width of -/./,/!/? to use for visial alignment by width + */ + virtual int getVisualAligmentWidth(); + + /** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found + */ + virtual bool getGlyphInfo(lUInt16 code, glyph_info_t *glyph, lChar16 def_char = 0) = 0; + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \param max_width is maximum width to measure line + \param def_char is character to replace absent glyphs in font + \param letter_spacing is number of pixels to add between letters + \return number of characters before max_width reached + */ + virtual lUInt16 measureText( + const lChar16 *text, int len, + lUInt16 *widths, + lUInt8 *flags, + int max_width, + lChar16 def_char, + int letter_spacing = 0, + bool allow_hyphenation = true + ) = 0; + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return width of specified string + */ + virtual lUInt32 getTextWidth( + const lChar16 *text, int len + ) = 0; + +// /** \brief get glyph image in 1 byte per pixel format +// \param code is unicode character +// \param buf is buffer [width*height] to place glyph data +// \return true if glyph was found +// */ +// virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0) = 0; + /** \brief get glyph item + \param code is unicode character + \return glyph pointer if glyph was found, NULL otherwise + */ + virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0) = 0; + + /// returns font baseline offset + virtual int getBaseline() = 0; + + /// returns font height including normal interline space + virtual int getHeight() const = 0; + + /// returns font character size + virtual int getSize() const = 0; + + /// returns font weight + virtual int getWeight() const = 0; + + /// returns italic flag + virtual int getItalic() const = 0; + + /// returns char width + virtual int getCharWidth(lChar16 ch, lChar16 def_char = 0) = 0; + + /// retrieves font handle + virtual void *GetHandle() = 0; + + /// returns font typeface name + virtual lString8 getTypeFace() const = 0; + + /// returns font family id + virtual css_font_family_t getFontFamily() const = 0; + + /// draws text string + virtual void DrawTextString(LVDrawBuf *buf, int x, int y, + const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette = NULL, bool addHyphen = false, + lUInt32 flags = 0, int letter_spacing = 0) = 0; + + /// constructor + LVFont() : _visual_alignment_width(-1), _hash(0) {} + + /// get bitmap mode (true=monochrome bitmap, false=antialiased) + virtual bool getBitmapMode() { return false; } + + /// set bitmap mode (true=monochrome bitmap, false=antialiased) + virtual void setBitmapMode(bool) {} + + /// get kerning mode: true==ON, false=OFF + virtual bool getKerning() const { return false; } + + /// set kerning mode: true==ON, false=OFF + virtual void setKerning(bool) {} + + /// get ligatures mode: true==allowed, false=not allowed + virtual bool getLigatures() const { return false; } + + /// set ligatures mode: true==allowed, false=not allowed + virtual void setLigatures(bool) {} + + /// sets current hinting mode + virtual void setHintingMode(hinting_mode_t /*mode*/) {} + + /// returns current hinting mode + virtual hinting_mode_t getHintingMode() const { return HINTING_MODE_AUTOHINT; } + + /// returns true if font is empty + virtual bool IsNull() const = 0; + + virtual bool operator!() const = 0; + + virtual void Clear() = 0; + + virtual ~LVFont() {} + + virtual bool kerningEnabled() { return false; } + + virtual int getKerningOffset(lChar16 ch1, lChar16 ch2, lChar16 def_char) { + CR_UNUSED3(ch1, ch2, def_char); + return 0; + } + + virtual bool checkFontLangCompat(const lString8 &langCode) { return true; } + + /// set fallback font for this font + void setFallbackFont(LVProtectedFastRef font) { CR_UNUSED(font); } + + /// get fallback font for this font + LVFont *getFallbackFont() { return NULL; } +}; + +typedef LVProtectedFastRef LVFontRef; + +/// to compare two fonts +bool operator==(const LVFont &r1, const LVFont &r2); + +#endif //__LV_FONT_H_INCLUDED__ diff --git a/crengine/include/lvtextfm.h b/crengine/include/lvtextfm.h index b96754868b..1f955b38b1 100644 --- a/crengine/include/lvtextfm.h +++ b/crengine/include/lvtextfm.h @@ -15,7 +15,8 @@ #ifndef __LVTEXTFM_H_INCLUDED__ #define __LVTEXTFM_H_INCLUDED__ -#include "lvfntman.h" +#include "lvfont.h" +//#include "lvfntman.h" #include "lvbmpbuf.h" // comment out following line to use old formatter diff --git a/crengine/include/lvtinydom.h b/crengine/include/lvtinydom.h index 30549a0921..3f0e51e9c5 100644 --- a/crengine/include/lvtinydom.h +++ b/crengine/include/lvtinydom.h @@ -32,6 +32,7 @@ #include "dtddef.h" #include "lvstyles.h" #include "lvdrawbuf.h" +#include "lvembeddedfont.h" #include "lvstsheet.h" #include "lvpagesplitter.h" #include "lvptrvec.h" diff --git a/crengine/src/lvembeddedfont.cpp b/crengine/src/lvembeddedfont.cpp new file mode 100644 index 0000000000..fddd953d0c --- /dev/null +++ b/crengine/src/lvembeddedfont.cpp @@ -0,0 +1,109 @@ +/** \file lvembeddedfont.cpp + \brief embedded font definition implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "../include/lvembeddedfont.h" + +#include + +static const char *EMBEDDED_FONT_LIST_MAGIC = "FNTL"; +static const char *EMBEDDED_FONT_DEF_MAGIC = "FNTD"; + +//////////////////////////////////////////////////////////////////// +// LVEmbeddedFontDef +//////////////////////////////////////////////////////////////////// +bool LVEmbeddedFontDef::serialize(SerialBuf &buf) { + buf.putMagic(EMBEDDED_FONT_DEF_MAGIC); + buf << _url << _face << _bold << _italic; + return !buf.error(); +} + +bool LVEmbeddedFontDef::deserialize(SerialBuf &buf) { + if (!buf.checkMagic(EMBEDDED_FONT_DEF_MAGIC)) + return false; + buf >> _url >> _face >> _bold >> _italic; + return !buf.error(); +} + +//////////////////////////////////////////////////////////////////// +// LVEmbeddedFontList +//////////////////////////////////////////////////////////////////// +LVEmbeddedFontDef *LVEmbeddedFontList::findByUrl(lString16 url) { + for (int i = 0; i < length(); i++) { + if (get(i)->getUrl() == url) + return get(i); + } + return NULL; +} + +bool LVEmbeddedFontList::addAll(LVEmbeddedFontList &list) { + bool changed = false; + for (int i = 0; i < list.length(); i++) { + LVEmbeddedFontDef *def = list.get(i); + changed = add(def->getUrl(), def->getFace(), def->getBold(), def->getItalic()) || changed; + } + return changed; +} + +bool LVEmbeddedFontList::add(lString16 url, lString8 face, bool bold, bool italic) { + LVEmbeddedFontDef *def = findByUrl(url); + if (def) { + bool changed = false; + if (def->getFace() != face) { + def->setFace(face); + changed = true; + } + if (def->getBold() != bold) { + def->setBold(bold); + changed = true; + } + if (def->getItalic() != italic) { + def->setItalic(italic); + changed = true; + } + return changed; + } + def = new LVEmbeddedFontDef(url, face, bold, italic); + add(def); + return false; +} + +bool LVEmbeddedFontList::serialize(SerialBuf &buf) { + buf.putMagic(EMBEDDED_FONT_LIST_MAGIC); + lUInt32 count = length(); + buf << count; + for (lUInt32 i = 0; i < count; i++) { + get(i)->serialize(buf); + if (buf.error()) + return false; + } + return !buf.error(); +} + +bool LVEmbeddedFontList::deserialize(SerialBuf &buf) { + if (!buf.checkMagic(EMBEDDED_FONT_LIST_MAGIC)) + return false; + lUInt32 count = 0; + buf >> count; + if (buf.error()) + return false; + for (lUInt32 i = 0; i < count; i++) { + LVEmbeddedFontDef *item = new LVEmbeddedFontDef(); + if (!item->deserialize(buf)) { + delete item; + return false; + } + add(item); + } + return !buf.error(); +} diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index 4b51f61e8c..0c0331291d 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -1,5 +1,5 @@ -/** \file lvfntman.cpp - \brief font manager implementation +/** @file lvfntman.cpp + @brief font manager implementation CoolReader Engine @@ -12,108 +12,45 @@ */ -#include -#include -#include - - - -#include "../include/crsetup.h" #include "../include/lvfntman.h" -#include "../include/lvstream.h" -#include "../include/lvdrawbuf.h" #include "../include/lvstyles.h" -#include "../include/lvthread.h" - -// define to filter out all fonts except .ttf -//#define LOAD_TTF_FONTS_ONLY -// DEBUG ONLY -#if 0 -#define USE_FREETYPE 1 -#define USE_FONTCONFIG 1 -//#define DEBUG_FONT_SYNTHESIS 1 -//#define DEBUG_FONT_MAN 1 -//#define DEBUG_FONT_MAN_LOG_FILE "/tmp/font_man.log" -#endif +#include "private/lvfreetypefontman.h" +#include "private/lvwin32fontman.h" +#include "private/lvbitmapfontman.h" #define GAMMA_TABLES_IMPL #include "../include/gammatbl.h" -//#if (USE_FREETYPE==1) - -//#include - -#ifdef ANDROID -#ifdef USE_FREETYPE2 -#include "freetype2/config/ftheader.h" -#include "freetype2/freetype.h" -#else -#include "freetype/config/ftheader.h" -#include "freetype/freetype.h" -#endif -#else - -#ifdef USE_FREETYPE2 -#include -#else -#include -#endif -//#include -#include FT_FREETYPE_H -//#include -#endif - -#if USE_HARFBUZZ==1 -#include -#include -#include "lvhashtable.h" -#endif - -#if (USE_FONTCONFIG==1) - #include -#endif - -// fc-lang database -#include "fc-lang-cat.h" - -#if COLOR_BACKBUFFER==0 -//#define USE_BITMAP_FONT -#endif - -#define MAX_LINE_CHARS 2048 - - //DEFINE_NULL_REF( LVFont ) -inline int myabs(int n) { return n < 0 ? -n : n; } - -LVFontManager * fontMan = NULL; +LVFontManager *fontMan = NULL; static double gammaLevel = 1.0; -static int gammaIndex = GAMMA_LEVELS/2; +int gammaIndex = GAMMA_LEVELS / 2; /// returns first found face from passed list, or return face for font found by family only -lString8 LVFontManager::findFontFace(lString8 commaSeparatedFaceList, css_font_family_t fallbackByFamily) { - // faces we want - lString8Collection list; - splitPropertyValueList(commaSeparatedFaceList.c_str(), list); - // faces we have - lString16Collection faces; - getFaceList(faces); - // find first matched - for (int i = 0; i < list.length(); i++) { - lString8 wantFace = list[i]; - for (int j = 0; j < faces.length(); j++) { - lString16 haveFace = faces[j]; - if (wantFace == haveFace) - return wantFace; - } - } - // not matched - get by family name +lString8 +LVFontManager::findFontFace(lString8 commaSeparatedFaceList, css_font_family_t fallbackByFamily) { + // faces we want + lString8Collection list; + splitPropertyValueList(commaSeparatedFaceList.c_str(), list); + // faces we have + lString16Collection faces; + getFaceList(faces); + // find first matched + for (int i = 0; i < list.length(); i++) { + lString8 wantFace = list[i]; + for (int j = 0; j < faces.length(); j++) { + lString16 haveFace = faces[j]; + if (wantFace == haveFace) + return wantFace; + } + } + // not matched - get by family name LVFontRef fnt = GetFont(10, 400, false, fallbackByFamily, lString8("Arial")); if (fnt.isNull()) - return lString8::empty_str; // not found + return lString8::empty_str; // not found // get face from found font return fnt->getTypeFace(); } @@ -121,22 +58,22 @@ lString8 LVFontManager::findFontFace(lString8 commaSeparatedFaceList, css_font_f /// fills array with list of available gamma levels void LVFontManager::GetGammaLevels(LVArray dst) { dst.clear(); - for ( int i=0; i=GAMMA_LEVELS ) - index = GAMMA_LEVELS-1; - if ( gammaIndex!=index ) { + if (index >= GAMMA_LEVELS) + index = GAMMA_LEVELS - 1; + if (gammaIndex != index) { CRLog::trace("FontManager gamma index changed from %d to %d", gammaIndex, index); gammaIndex = index; gammaLevel = cr_gamma_levels[index]; @@ -150,4816 +87,64 @@ double LVFontManager::GetGamma() { } /// sets current gamma level -void LVFontManager::SetGamma( double gamma ) { +void LVFontManager::SetGamma(double gamma) { // gammaLevel = cr_ft_gamma_levels[GAMMA_LEVELS/2]; // gammaIndex = GAMMA_LEVELS/2; int oldGammaIndex = gammaIndex; - for ( int i=0; i> _url >> _face >> _bold >> _italic; - return !buf.error(); -} - -//////////////////////////////////////////////////////////////////// -// LVEmbeddedFontList -//////////////////////////////////////////////////////////////////// -LVEmbeddedFontDef * LVEmbeddedFontList::findByUrl(lString16 url) { - for (int i=0; igetUrl() == url) - return get(i); - } - return NULL; -} - -bool LVEmbeddedFontList::addAll(LVEmbeddedFontList & list) { - bool changed = false; - for (int i=0; igetUrl(), def->getFace(), def->getBold(), def->getItalic()) || changed; - } - return changed; -} - -bool LVEmbeddedFontList::add(lString16 url, lString8 face, bool bold, bool italic) { - LVEmbeddedFontDef * def = findByUrl(url); - if (def) { - bool changed = false; - if (def->getFace() != face) { - def->setFace(face); - changed = true; - } - if (def->getBold() != bold) { - def->setBold(bold); - changed = true; - } - if (def->getItalic() != italic) { - def->setItalic(italic); - changed = true; - } - return changed; - } - def = new LVEmbeddedFontDef(url, face, bold, italic); - add(def); - return false; -} - -bool LVEmbeddedFontList::serialize(SerialBuf & buf) { - buf.putMagic(EMBEDDED_FONT_LIST_MAGIC); - lUInt32 count = length(); - buf << count; - for (lUInt32 i = 0; i < count; i++) { - get(i)->serialize(buf); - if (buf.error()) - return false; - } - return !buf.error(); -} - -bool LVEmbeddedFontList::deserialize(SerialBuf & buf) { - if (!buf.checkMagic(EMBEDDED_FONT_LIST_MAGIC)) - return false; - lUInt32 count = 0; - buf >> count; - if (buf.error()) - return false; - for (lUInt32 i = 0; i < count; i++) { - LVEmbeddedFontDef * item = new LVEmbeddedFontDef(); - if (!item->deserialize(buf)) { - delete item; - return false; - } - add(item); - } - return !buf.error(); -} - -//////////////////////////////////////////////////////////////////// - - -/** - * Max width of -/./,/!/? to use for visial alignment by width - */ -int LVFont::getVisualAligmentWidth() -{ - FONT_GUARD - if ( _visual_alignment_width==-1 ) { - //lChar16 chars[] = { getHyphChar(), ',', '.', '!', ':', ';', 0 }; - lChar16 chars[] = { getHyphChar(), ',', '.', '!', ':', ';', - (lChar16)L'\xff0c', (lChar16)L'\x3302', (lChar16)L'\xff01', 0 }; - // (lChar16)L',', (lChar16)L'。', (lChar16)L'!', 0 }; - // 65292 12290 65281 - // ff0c 3002 ff01 - int maxw = 0; - for ( int i=0; chars[i]; i++ ) { - int w = getCharWidth( chars[i] ); - if ( w > maxw ) - maxw = w; - } - _visual_alignment_width = maxw; - } - return _visual_alignment_width; -} - -static lChar16 getReplacementChar( lUInt16 code ) { - switch (code) { - case UNICODE_SOFT_HYPHEN_CODE: - return '-'; - case 0x0401: // CYRILLIC CAPITAL LETTER IO - return 0x0415; //CYRILLIC CAPITAL LETTER IE - case 0x0451: // CYRILLIC SMALL LETTER IO - return 0x0435; // CYRILLIC SMALL LETTER IE - case UNICODE_NO_BREAK_SPACE: - return ' '; - case 0x2010: - case 0x2011: - case 0x2012: - case 0x2013: - case 0x2014: - case 0x2015: - return '-'; - case 0x2018: - case 0x2019: - case 0x201a: - case 0x201b: - return '\''; - case 0x201c: - case 0x201d: - case 0x201e: - case 0x201f: - case 0x00ab: - case 0x00bb: - return '\"'; - case 0x2039: - return '<'; - case 0x203A: - return '>'; - case 0x2044: - return '/'; - case 0x2022: // css_lst_disc: - return '*'; - case 0x26AA: // css_lst_disc: - case 0x25E6: // css_lst_disc: - case 0x25CF: // css_lst_disc: - return 'o'; - case 0x25CB: // css_lst_circle: - return '*'; - case 0x25A0: // css_lst_square: - return '-'; - } - return 0; -} - - - -/** - \brief Font properties definition -*/ -class LVFontDef -{ -private: - int _size; - int _weight; - int _italic; - css_font_family_t _family; - lString8 _typeface; - lString8 _name; - int _index; - // for document font: _documentId, _buf, _name - int _documentId; - LVByteArrayRef _buf; -public: - LVFontDef(const lString8 & name, int size, int weight, int italic, css_font_family_t family, const lString8 & typeface, int index=-1, int documentId=-1, LVByteArrayRef buf = LVByteArrayRef()) - : _size(size) - , _weight(weight) - , _italic(italic) - , _family(family) - , _typeface(typeface) - , _name(name) - , _index(index) - , _documentId(documentId) - , _buf(buf) - { - } - LVFontDef(const LVFontDef & def) - : _size(def._size) - , _weight(def._weight) - , _italic(def._italic) - , _family(def._family) - , _typeface(def._typeface) - , _name(def._name) - , _index(def._index) - , _documentId(def._documentId) - , _buf(def._buf) - { - } - - /// returns true if definitions are equal - bool operator == ( const LVFontDef & def ) const - { - return ( _size == def._size || _size == -1 || def._size == -1 ) - && ( _weight == def._weight || _weight==-1 || def._weight==-1 ) - && ( _italic == def._italic || _italic==-1 || def._italic==-1 ) - && _family == def._family - && _typeface == def._typeface - && _name == def._name - && ( _index == def._index || def._index == -1 ) - && (_documentId == def._documentId || _documentId == -1) - ; - } - - lUInt32 getHash() { - return ((((_size * 31) + _weight)*31 + _italic)*31 + _family)*31 + _name.getHash(); - } - - /// returns font file name - lString8 getName() const { return _name; } - void setName( lString8 name) { _name = name; } - int getIndex() const { return _index; } - void setIndex( int index ) { _index = index; } - int getSize() const { return _size; } - void setSize( int size ) { _size = size; } - int getWeight() const { return _weight; } - void setWeight( int weight ) { _weight = weight; } - bool getItalic() const { return _italic!=0; } - bool isRealItalic() const { return _italic==1; } - void setItalic( int italic ) { _italic=italic; } - css_font_family_t getFamily() const { return _family; } - void getFamily( css_font_family_t family ) { _family = family; } - lString8 getTypeFace() const { return _typeface; } - void setTypeFace(lString8 tf) { _typeface = tf; } - int getDocumentId() { return _documentId; } - void setDocumentId(int id) { _documentId = id; } - LVByteArrayRef getBuf() { return _buf; } - void setBuf(LVByteArrayRef buf) { _buf = buf; } - ~LVFontDef() {} - /// calculates difference between two fonts - int CalcMatch( const LVFontDef & def ) const; - /// difference between fonts for duplicates search - int CalcDuplicateMatch( const LVFontDef & def ) const; - /// calc match for fallback font search - int CalcFallbackMatch( lString8 face, int size ) const; -}; - -/// font cache item -class LVFontCacheItem -{ - friend class LVFontCache; - LVFontDef _def; - LVFontRef _fnt; -public: - LVFontDef * getDef() { return &_def; } - LVFontRef & getFont() { return _fnt; } - void setFont(LVFontRef & fnt) { _fnt = fnt; } - LVFontCacheItem( const LVFontDef & def ) - : _def( def ) - { } -}; - -/// font cache -class LVFontCache -{ - LVPtrVector< LVFontCacheItem > _registered_list; - LVPtrVector< LVFontCacheItem > _instance_list; -public: - void clear() { _registered_list.clear(); _instance_list.clear(); } - void gc(); // garbage collector - void update( const LVFontDef * def, LVFontRef ref ); - void removefont(const LVFontDef * def); - void removeDocumentFonts(int documentId); - int length() { return _registered_list.length(); } - void addInstance( const LVFontDef * def, LVFontRef ref ); - LVPtrVector< LVFontCacheItem > * getInstances() { return &_instance_list; } - LVFontCacheItem * find( const LVFontDef * def ); - LVFontCacheItem * findFallback( lString8 face, int size ); - LVFontCacheItem * findDuplicate( const LVFontDef * def ); - LVFontCacheItem * findDocumentFontDuplicate(int documentId, lString8 name); - /// get hash of installed fonts and fallback font - virtual lUInt32 GetFontListHash(int documentId) { - lUInt32 hash = 0; - for ( int i=0; i<_registered_list.length(); i++ ) { - int doc = _registered_list[i]->getDef()->getDocumentId(); - if (doc == -1 || doc == documentId) // skip document fonts - hash = hash + _registered_list[i]->getDef()->getHash(); - } - return 0; - } - virtual void getFaceList( lString16Collection & list ) - { - list.clear(); - for ( int i=0; i<_registered_list.length(); i++ ) { - if (_registered_list[i]->getDef()->getDocumentId() != -1) - continue; - lString16 name = Utf8ToUnicode( _registered_list[i]->getDef()->getTypeFace() ); - if ( !list.contains(name) ) - list.add( name ); - } - list.sort(); - } - virtual void getFontFileNameList(lString16Collection &list) - { - list.clear(); - for ( int i=0; i<_registered_list.length(); i++ ) { - if (_registered_list[i]->getDef()->getDocumentId() == -1) { - lString16 name = Utf8ToUnicode(_registered_list[i]->getDef()->getName()); - if (!list.contains(name)) - list.add(name); - } - } - list.sort(); - } - virtual void clearFallbackFonts() - { - for ( int i=0; i<_registered_list.length(); i++ ) { - _registered_list[i]->getFont()->setFallbackFont(LVFontRef()); - } - } - LVFontCache( ) - { } - virtual ~LVFontCache() { } -}; - - -#if (USE_FREETYPE==1) - - -class LVFontGlyphWidthCache -{ -private: - lUInt8 * ptrs[128]; -public: - lUInt8 get( lChar16 ch ) - { - FONT_GLYPH_CACHE_GUARD - int inx = (ch>>9) & 0x7f; - lUInt8 * ptr = ptrs[inx]; - if ( !ptr ) - return 0xFF; - return ptr[ch & 0x1FF ]; - } - void put( lChar16 ch, lUInt8 w ) - { - FONT_GLYPH_CACHE_GUARD - int inx = (ch>>9) & 0x7f; - lUInt8 * ptr = ptrs[inx]; - if ( !ptr ) { - ptr = new lUInt8[512]; - ptrs[inx] = ptr; - memset( ptr, 0xFF, sizeof(lUInt8) * 512 ); - } - ptr[ ch & 0x1FF ] = w; - } - void clear() - { - FONT_GLYPH_CACHE_GUARD - for ( int i=0; i<128; i++ ) { - if ( ptrs[i] ) - delete [] ptrs[i]; - ptrs[i] = NULL; - } - } - LVFontGlyphWidthCache() - { - memset( ptrs, 0, 128*sizeof(lUInt8*) ); - } - ~LVFontGlyphWidthCache() - { - clear(); - } -}; - -class LVFreeTypeFace; -static LVFontGlyphCacheItem * newItem( LVFontLocalGlyphCache * local_cache, lChar16 ch, FT_GlyphSlot slot ) // , bool drawMonochrome -{ - FONT_LOCAL_GLYPH_CACHE_GUARD - FT_Bitmap* bitmap = &slot->bitmap; - int w = bitmap->width; - int h = bitmap->rows; - LVFontGlyphCacheItem * item = LVFontGlyphCacheItem::newItem(local_cache, ch, w, h ); - if ( bitmap->pixel_mode==FT_PIXEL_MODE_MONO ) { //drawMonochrome - lUInt8 mask = 0x80; - const lUInt8 * ptr = (const lUInt8 *)bitmap->buffer; - lUInt8 * dst = item->bmp; - //int rowsize = ((w + 15) / 16) * 2; - for ( int y=0; y>= 1; - if ( !mask && x!=w-1) { - mask = 0x80; - row++; - } - } - ptr += bitmap->pitch;//rowsize; - } - } else { -#if 0 - if ( bitmap->pixel_mode==FT_PIXEL_MODE_MONO ) { - memset( item->bmp, 0, w*h ); - lUInt8 * srcrow = bitmap->buffer; - lUInt8 * dstrow = item->bmp; - for ( int y=0; y>(x&7)) ) ? 255 : 0; - if ((x&7)==7) - src++; - } - srcrow += bitmap->pitch; - dstrow += w; - } - } else { -#endif - memcpy( item->bmp, bitmap->buffer, w*h ); - // correct gamma - if ( gammaIndex!=GAMMA_LEVELS/2 ) - cr_correct_gamma_buf(item->bmp, w*h, gammaIndex); -// } - } - item->origin_x = (lInt16)slot->bitmap_left; - item->origin_y = (lInt16)slot->bitmap_top; - item->advance = (lUInt16)(myabs(slot->metrics.horiAdvance) >> 6); - return item; -} - -#if USE_HARFBUZZ==1 -static LVFontGlyphIndexCacheItem * newItem(lUInt32 index, FT_GlyphSlot slot ) -{ - FONT_LOCAL_GLYPH_CACHE_GUARD - FT_Bitmap* bitmap = &slot->bitmap; - int w = bitmap->width; - int h = bitmap->rows; - LVFontGlyphIndexCacheItem* item = LVFontGlyphIndexCacheItem::newItem(index, w, h ); - if (!item) - return 0; - if ( bitmap->pixel_mode==FT_PIXEL_MODE_MONO ) { //drawMonochrome - lUInt8 mask = 0x80; - const lUInt8 * ptr = (const lUInt8 *)bitmap->buffer; - lUInt8 * dst = item->bmp; - //int rowsize = ((w + 15) / 16) * 2; - for ( int y=0; y>= 1; - if ( !mask && x!=w-1) { - mask = 0x80; - row++; - } - } - ptr += bitmap->pitch;//rowsize; - } - } else { - memcpy( item->bmp, bitmap->buffer, w*h ); - // correct gamma - if ( gammaIndex!=GAMMA_LEVELS/2 ) - cr_correct_gamma_buf(item->bmp, w*h, gammaIndex); -// } - } - item->origin_x = (lInt16)slot->bitmap_left; - item->origin_y = (lInt16)slot->bitmap_top; - item->advance = (lUInt16)(myabs(slot->metrics.horiAdvance) >> 6); - return item; -} -#endif - -void LVFontLocalGlyphCache::clear() -{ - FONT_LOCAL_GLYPH_CACHE_GUARD - while ( head ) { - LVFontGlyphCacheItem * ptr = head; - remove( ptr ); - global_cache->remove( ptr ); - LVFontGlyphCacheItem::freeItem( ptr ); - } -} - -LVFontGlyphCacheItem * LVFontLocalGlyphCache::get( lUInt16 ch ) -{ - FONT_LOCAL_GLYPH_CACHE_GUARD - LVFontGlyphCacheItem * ptr = head; - for ( ; ptr; ptr = ptr->next_local ) { - if ( ptr->ch == ch ) { - global_cache->refresh( ptr ); - return ptr; - } - } - return NULL; -} - -void LVFontLocalGlyphCache::put( LVFontGlyphCacheItem * item ) -{ - FONT_LOCAL_GLYPH_CACHE_GUARD - global_cache->put( item ); - item->next_local = head; - if ( head ) - head->prev_local = item; - if ( !tail ) - tail = item; - head = item; -} - -/// remove from list, but don't delete -void LVFontLocalGlyphCache::remove( LVFontGlyphCacheItem * item ) -{ - FONT_LOCAL_GLYPH_CACHE_GUARD - if ( item==head ) - head = item->next_local; - if ( item==tail ) - tail = item->prev_local; - if ( !head || !tail ) - return; - if ( item->prev_local ) - item->prev_local->next_local = item->next_local; - if ( item->next_local ) - item->next_local->prev_local = item->prev_local; - item->next_local = NULL; - item->prev_local = NULL; -} - -void LVFontGlobalGlyphCache::refresh( LVFontGlyphCacheItem * item ) -{ - FONT_GLYPH_CACHE_GUARD - if ( tail!=item ) { - //move to head - removeNoLock( item ); - putNoLock( item ); - } -} - -void LVFontGlobalGlyphCache::put( LVFontGlyphCacheItem * item ) -{ - FONT_GLYPH_CACHE_GUARD - putNoLock(item); -} - -void LVFontGlobalGlyphCache::putNoLock( LVFontGlyphCacheItem * item ) -{ - int sz = item->getSize(); - // remove extra items from tail - while ( sz + size > max_size ) { - LVFontGlyphCacheItem * removed_item = tail; - if ( !removed_item ) - break; - removeNoLock( removed_item ); - removed_item->local_cache->remove( removed_item ); - LVFontGlyphCacheItem::freeItem( removed_item ); - } - // add new item to head - item->next_global = head; - if ( head ) - head->prev_global = item; - head = item; - if ( !tail ) - tail = item; - size += sz; -} - -void LVFontGlobalGlyphCache::remove( LVFontGlyphCacheItem * item ) -{ - FONT_GLYPH_CACHE_GUARD - removeNoLock(item); -} - -void LVFontGlobalGlyphCache::removeNoLock( LVFontGlyphCacheItem * item ) -{ - if ( item==head ) - head = item->next_global; - if ( item==tail ) - tail = item->prev_global; - if ( !head || !tail ) - return; - if ( item->prev_global ) - item->prev_global->next_global = item->next_global; - if ( item->next_global ) - item->next_global->prev_global = item->prev_global; - item->next_global = NULL; - item->prev_global = NULL; - size -= item->getSize(); -} - -void LVFontGlobalGlyphCache::clear() -{ - FONT_GLYPH_CACHE_GUARD - while ( head ) { - LVFontGlyphCacheItem * ptr = head; - remove( ptr ); - ptr->local_cache->remove( ptr ); - LVFontGlyphCacheItem::freeItem( ptr ); - } -} - -lString8 familyName( FT_Face face ) -{ - lString8 faceName( face->family_name ); - if ( faceName == "Arial" && face->style_name && !strcmp(face->style_name, "Narrow") ) - faceName << " " << face->style_name; - else if ( /*faceName == "Arial" &&*/ face->style_name && strstr(face->style_name, "Condensed") ) - faceName << " " << "Condensed"; - return faceName; -} - -static lUInt16 char_flags[] = { - 0, 0, 0, 0, 0, 0, 0, 0, // 0 00 - 0, 0, LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER, 0, 0, LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER, 0, 0, // 8 08 - 0, 0, 0, 0, 0, 0, 0, 0, // 16 10 - 0, 0, 0, 0, 0, 0, 0, 0, // 24 18 - LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER, 0, 0, 0, 0, 0, 0, 0, // 32 20 - 0, 0, 0, 0, 0, LCHAR_DEPRECATED_WRAP_AFTER, 0, 0, // 40 28 - 0, 0, 0, 0, 0, 0, 0, 0, // 48 30 -}; - -#define GET_CHAR_FLAGS(ch) \ - (ch<48?char_flags[ch]: \ - (ch==UNICODE_SOFT_HYPHEN_CODE?LCHAR_ALLOW_WRAP_AFTER: \ - (ch==UNICODE_NO_BREAK_SPACE?LCHAR_DEPRECATED_WRAP_AFTER|LCHAR_IS_SPACE: \ - (ch==UNICODE_HYPHEN?LCHAR_DEPRECATED_WRAP_AFTER:0)))) - -#if USE_HARFBUZZ==1 -struct LVCharTriplet -{ - lChar16 prevChar; - lChar16 Char; - lChar16 nextChar; - bool operator==(const struct LVCharTriplet& other) { - return prevChar == other.prevChar && Char == other.Char && nextChar == other.nextChar; - } -}; - -struct LVCharPosInfo -{ - int offset; - int width; -}; - -inline lUInt32 getHash( const struct LVCharTriplet& triplet ) -{ - //return (triplet.prevChar * 1975317 + 164521) ^ (triplet.Char * 1975317 + 164521) ^ (triplet.nextChar * 1975317 + 164521); - return getHash( (lUInt64)triplet.Char - + (((lUInt64)triplet.prevChar) << 16) - + (((lUInt64)triplet.nextChar) << 32) ); -} -#endif - -class LVFreeTypeFace : public LVFont -{ -protected: - LVMutex & _mutex; - lString8 _fileName; - lString8 _faceName; - css_font_family_t _fontFamily; - FT_Library _library; - FT_Face _face; - FT_GlyphSlot _slot; - FT_Matrix _matrix; /* transformation matrix */ - int _size; // caracter height in pixels - int _height; // full line height in pixels - int _hyphen_width; - int _baseline; - int _weight; - int _italic; - LVFontGlyphWidthCache _wcache; - LVFontLocalGlyphCache _glyph_cache; - bool _drawMonochrome; - bool _allowKerning; - bool _allowLigatures; - hinting_mode_t _hintingMode; - bool _fallbackFontIsSet; - LVFontRef _fallbackFont; -#if USE_HARFBUZZ==1 - hb_buffer_t* _hb_buffer; - hb_font_t* _hb_font; - hb_feature_t _hb_features[2]; - LVHashTable _glyph_cache2; - LVHashTable _width_cache2; - hb_buffer_t* _hb_opt_kern_buffer; - hb_feature_t _hb_opt_kern_features[22]; -#endif -public: - - // fallback font support - /// set fallback font for this font - void setFallbackFont( LVFontRef font ) { - _fallbackFont = font; - _fallbackFontIsSet = !font.isNull(); - clearCache(); - } - - /// get fallback font for this font - LVFont * getFallbackFont() { - if ( _fallbackFontIsSet ) - return _fallbackFont.get(); - if ( fontMan->GetFallbackFontFace()!=_faceName ) // to avoid circular link, disable fallback for fallback font - _fallbackFont = fontMan->GetFallbackFont(_size); - _fallbackFontIsSet = true; - return _fallbackFont.get(); - } - - /// returns font weight - virtual int getWeight() const { return _weight; } - /// returns italic flag - virtual int getItalic() const { return _italic; } - /// sets face name - virtual void setFaceName( lString8 face ) { _faceName = face; } - - LVMutex & getMutex() { return _mutex; } - FT_Library getLibrary() { return _library; } - - LVFreeTypeFace( LVMutex &mutex, FT_Library library, LVFontGlobalGlyphCache * globalCache ) - : _mutex(mutex), _fontFamily(css_ff_sans_serif), _library(library), _face(NULL), _size(0), _hyphen_width(0), _baseline(0) - , _weight(400), _italic(0) - , _glyph_cache(globalCache), _drawMonochrome(false), _allowKerning(false), _allowLigatures(false), _hintingMode(HINTING_MODE_AUTOHINT), _fallbackFontIsSet(false) -#if USE_HARFBUZZ==1 - , _glyph_cache2(256), _width_cache2(1024) -#endif - { - _matrix.xx = 0x10000; - _matrix.yy = 0x10000; - _matrix.xy = 0; - _matrix.yx = 0; - _hintingMode = fontMan->GetHintingMode(); -#if USE_HARFBUZZ==1 - _hb_font = 0; - _hb_buffer = hb_buffer_create(); - _hb_opt_kern_buffer = hb_buffer_create(); - // HarfBuzz features for full text shaping - hb_feature_from_string("-kern", -1, &_hb_features[0]); // font kerning - hb_feature_from_string("-liga", -1, &_hb_features[1]); // ligatures - - // HarfBuzz features for lighweight characters width calculating with caching - hb_feature_from_string("-kern", -1, &_hb_opt_kern_features[0]); // Kerning: Fine horizontal positioning of one glyph to the next, based on the shapes of the glyphs - // We can enable these ones: - hb_feature_from_string("+mark", -1, &_hb_opt_kern_features[1]); // Mark Positioning: Fine positioning of a mark glyph to a base character - hb_feature_from_string("+mkmk", -1, &_hb_opt_kern_features[2]); // Mark-to-mark Positioning: Fine positioning of a mark glyph to another mark character - hb_feature_from_string("+curs", -1, &_hb_opt_kern_features[3]); // Cursive Positioning: Precise positioning of a letter's connection to an adjacent one - hb_feature_from_string("+locl", -1, &_hb_opt_kern_features[4]); // Substitutes character with the preferred form based on script language - - // We should disable these ones: - hb_feature_from_string("-liga", -1, &_hb_opt_kern_features[5]); // Standard Ligatures: replaces (by default) sequence of characters with a single ligature glyph - hb_feature_from_string("-rlig", -1, &_hb_opt_kern_features[6]); // Ligatures required for correct text display (any script, but in cursive) - Arabic, semitic - hb_feature_from_string("-clig", -1, &_hb_opt_kern_features[7]); // Applies a second ligature feature based on a match of a character pattern within a context of surrounding patterns - hb_feature_from_string("-ccmp", -1, &_hb_opt_kern_features[8]); // Glyph composition/decomposition: either calls a ligature replacement on a sequence of characters or replaces a character with a sequence of glyphs - // Provides logic that can for example effectively alter the order of input characters - hb_feature_from_string("-calt", -1, &_hb_opt_kern_features[9]); // Contextual Alternates: Applies a second substitution feature based on a match of a character pattern within a context of surrounding patterns - hb_feature_from_string("-rclt", -1, &_hb_opt_kern_features[10]); // Required Contextual Alternates: Contextual alternates required for correct text display which differs from the default join for other letters, required especially important by Arabic - hb_feature_from_string("-rvrn", -1, &_hb_opt_kern_features[11]); // Required Variation Alternates: Special variants of a single character, which need apply to specific font variation, required by variable fonts - hb_feature_from_string("-ltra", -1, &_hb_opt_kern_features[12]); // Left-to-right glyph alternates: Replaces characters with forms befitting left-to-right presentation - hb_feature_from_string("-ltrm", -1, &_hb_opt_kern_features[13]); // Left-to-right mirrored forms: Replaces characters with possibly mirrored forms befitting left-to-right presentation - hb_feature_from_string("-rtla", -1, &_hb_opt_kern_features[14]); // Right-to-left glyph alternates: Replaces characters with forms befitting right-to-left presentation - hb_feature_from_string("-rtlm", -1, &_hb_opt_kern_features[15]); // Right-to-left mirrored forms: Replaces characters with possibly mirrored forms befitting right-to-left presentation - hb_feature_from_string("-frac", -1, &_hb_opt_kern_features[16]); // Fractions: Converts figures separated by slash with diagonal fraction - hb_feature_from_string("-numr", -1, &_hb_opt_kern_features[17]); // Numerator: Converts to appropriate fraction numerator form, invoked by frac - hb_feature_from_string("-dnom", -1, &_hb_opt_kern_features[18]); // Denominator: Converts to appropriate fraction denominator form, invoked by frac - hb_feature_from_string("-rand", -1, &_hb_opt_kern_features[19]); // Replaces character with random forms (meant to simulate handwriting) - hb_feature_from_string("-trak", -1, &_hb_opt_kern_features[20]); // Tracking (?) - hb_feature_from_string("-vert", -1, &_hb_opt_kern_features[21]); // Vertical (?) - // Especially needed with FreeSerif and french texts: -ccmp - // Especially needed with Fedra Serif and "The", "Thuringe": -calt - // These tweaks seem fragile (adding here +smcp to experiment with small caps would break FreeSerif again). - // So, when tuning these, please check it still behave well with FreeSerif. -#endif - } - - virtual ~LVFreeTypeFace() - { -#if USE_HARFBUZZ==1 - if (_hb_buffer) - hb_buffer_destroy(_hb_buffer); - if (_hb_opt_kern_buffer) - hb_buffer_destroy(_hb_opt_kern_buffer); -#endif - Clear(); - } - - void clearCache() { - _glyph_cache.clear(); - _wcache.clear(); -#if USE_HARFBUZZ==1 - LVHashTable::pair* pair; - LVHashTable::iterator it = _glyph_cache2.forwardIterator(); - while ((pair = it.next())) { - LVFontGlyphIndexCacheItem* item = pair->value; - if (item) - LVFontGlyphIndexCacheItem::freeItem(item); - } - _glyph_cache2.clear(); - _width_cache2.clear(); -#endif - } - - virtual int getHyphenWidth() - { - FONT_GUARD - if ( !_hyphen_width ) { - _hyphen_width = getCharWidth( UNICODE_SOFT_HYPHEN_CODE ); - } - return _hyphen_width; - } - - /// get kerning mode: true==ON, false=OFF - virtual bool getKerning() const { return _allowKerning; } - /// set kerning mode: true==ON, false=OFF - virtual void setKerning( bool kerningEnabled ) { - _allowKerning = kerningEnabled; -#if USE_HARFBUZZ==1 - if (_allowKerning) { - hb_feature_from_string("+kern", -1, &_hb_features[0]); - hb_feature_from_string("+kern", -1, &_hb_opt_kern_features[0]); - } - else { - hb_feature_from_string("-kern", -1, &_hb_features[0]); - hb_feature_from_string("-kern", -1, &_hb_opt_kern_features[0]); - } - // in cache may be found some ligatures, so clear it - clearCache(); -#endif - } - - /// get ligatures mode: true==allowed, false=not allowed - virtual bool getLigatures() const { return _allowLigatures; } - /// set ligatures mode: true==allowed, false=not allowed - virtual void setLigatures( bool ligaturesAllowed ) { - // don't touch _hb_opt_kern_features here - lightweight characters width calculation always performed without any ligatures! - _allowLigatures = ligaturesAllowed; -#if USE_HARFBUZZ==1 - if (_allowLigatures) - hb_feature_from_string("+liga", -1, &_hb_features[1]); - else - hb_feature_from_string("-liga", -1, &_hb_features[1]); - // in cache may be found some ligatures, so clear it - clearCache(); -#endif - } - - /// sets current hinting mode - virtual void setHintingMode(hinting_mode_t mode) { - if (_hintingMode == mode) - return; - _hintingMode = mode; - clearCache(); - } - /// returns current hinting mode - virtual hinting_mode_t getHintingMode() const { return _hintingMode; } - - /// get bitmap mode (true=bitmap, false=antialiased) - virtual bool getBitmapMode() { return _drawMonochrome; } - /// set bitmap mode (true=bitmap, false=antialiased) - virtual void setBitmapMode( bool drawBitmap ) - { - if ( _drawMonochrome == drawBitmap ) - return; - _drawMonochrome = drawBitmap; - clearCache(); - } - - bool loadFromBuffer(LVByteArrayRef buf, int index, int size, css_font_family_t fontFamily, bool monochrome, bool italicize ) - { - FONT_GUARD - _hintingMode = fontMan->GetHintingMode(); - _drawMonochrome = monochrome; - _fontFamily = fontFamily; - if (_face) - FT_Done_Face(_face); - int error = FT_New_Memory_Face( _library, buf->get(), buf->length(), index, &_face ); /* create face object */ - if (error) - return false; - if ( _fileName.endsWith(".pfb") || _fileName.endsWith(".pfa") ) { - lString8 kernFile = _fileName.substr(0, _fileName.length()-4); - if ( LVFileExists(Utf8ToUnicode(kernFile) + ".afm" ) ) { - kernFile += ".afm"; - } else if ( LVFileExists(Utf8ToUnicode(kernFile) + ".pfm" ) ) { - kernFile += ".pfm"; - } else { - kernFile.clear(); - } - if ( !kernFile.empty() ) - error = FT_Attach_File( _face, kernFile.c_str() ); - } - //FT_Face_SetUnpatentedHinting( _face, 1 ); - _slot = _face->glyph; - _faceName = familyName(_face); - CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str() ); - //if ( !FT_IS_SCALABLE( _face ) ) { - // Clear(); - // return false; - // } - error = FT_Set_Pixel_Sizes( - _face, /* handle to face object */ - 0, /* pixel_width */ - size ); /* pixel_height */ -#if USE_HARFBUZZ==1 - if (FT_Err_Ok == error) { - if (_hb_font) - hb_font_destroy(_hb_font); - _hb_font = hb_ft_font_create(_face, 0); - if (!_hb_font) - error = FT_Err_Invalid_Argument; - } -#endif - if (error) { - Clear(); - return false; - } -#if 0 - int nheight = _face->size->metrics.height; - int targetheight = size << 6; - error = FT_Set_Pixel_Sizes( - _face, /* handle to face object */ - 0, /* pixel_width */ - (size * targetheight + nheight/2)/ nheight ); /* pixel_height */ -#endif - _height = _face->size->metrics.height >> 6; - _size = size; //(_face->size->metrics.height >> 6); - _baseline = _height + (_face->size->metrics.descender >> 6); - _weight = _face->style_flags & FT_STYLE_FLAG_BOLD ? 700 : 400; - _italic = _face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0; +#if (USE_FREETYPE == 1) - if ( !error && italicize && !_italic ) { - _matrix.xy = 0x10000*3/10; - FT_Set_Transform(_face, &_matrix, NULL); - _italic = true; - } - if ( error ) { - // error - return false; - } - return true; - } +/// create transform for font +//LVFontRef LVCreateFontTransform( LVFontRef baseFont, int transformFlags ) +//{ +// if ( transformFlags & LVFONT_TRANSFORM_EMBOLDEN ) { +// // BOLD transform +// return LVFontRef( new LVFontBoldTransform( baseFont ) ); +// } else { +// return baseFont; // no transform +// } +//} - bool loadFromFile( const char * fname, int index, int size, css_font_family_t fontFamily, bool monochrome, bool italicize ) - { - FONT_GUARD - _hintingMode = fontMan->GetHintingMode(); - _drawMonochrome = monochrome; - _fontFamily = fontFamily; - if ( fname ) - _fileName = fname; - if ( _fileName.empty() ) - return false; - if (_face) - FT_Done_Face(_face); - int error = FT_New_Face( _library, _fileName.c_str(), index, &_face ); /* create face object */ - if (error) - return false; - if ( _fileName.endsWith(".pfb") || _fileName.endsWith(".pfa") ) { - lString8 kernFile = _fileName.substr(0, _fileName.length()-4); - if ( LVFileExists(Utf8ToUnicode(kernFile) + ".afm") ) { - kernFile += ".afm"; - } else if ( LVFileExists(Utf8ToUnicode(kernFile) + ".pfm" ) ) { - kernFile += ".pfm"; - } else { - kernFile.clear(); - } - if ( !kernFile.empty() ) - error = FT_Attach_File( _face, kernFile.c_str() ); - } - //FT_Face_SetUnpatentedHinting( _face, 1 ); - _slot = _face->glyph; - _faceName = familyName(_face); - CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str() ); - //if ( !FT_IS_SCALABLE( _face ) ) { - // Clear(); - // return false; - // } - error = FT_Set_Pixel_Sizes( - _face, /* handle to face object */ - 0, /* pixel_width */ - size ); /* pixel_height */ -#if USE_HARFBUZZ==1 - if (FT_Err_Ok == error) { - if (_hb_font) - hb_font_destroy(_hb_font); - _hb_font = hb_ft_font_create(_face, 0); - if (!_hb_font) - error = FT_Err_Invalid_Argument; - } -#endif - if (error) { - Clear(); - return false; - } -#if 0 - int nheight = _face->size->metrics.height; - int targetheight = size << 6; - error = FT_Set_Pixel_Sizes( - _face, /* handle to face object */ - 0, /* pixel_width */ - (size * targetheight + nheight/2)/ nheight ); /* pixel_height */ -#endif - _height = _face->size->metrics.height >> 6; - _size = size; //(_face->size->metrics.height >> 6); - _baseline = _height + (_face->size->metrics.descender >> 6); - _weight = _face->style_flags & FT_STYLE_FLAG_BOLD ? 700 : 400; - _italic = _face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0; +#endif // (USE_FREETYPE==1) - if ( !error && italicize && !_italic ) { - _matrix.xy = 0x10000*3/10; - FT_Set_Transform(_face, &_matrix, NULL); - _italic = true; - } - if ( error ) { - // error - return false; - } +bool InitFontManager(lString8 path) { + if (fontMan) { return true; + //delete fontMan; } - -#if USE_HARFBUZZ==1 - lChar16 filterChar(lChar16 code) { - lChar16 res; - if (code == '\t') - code = ' '; - FT_UInt ch_glyph_index = FT_Get_Char_Index(_face, code); - if (0 != ch_glyph_index) - res = code; - else { - res = getReplacementChar(code); - if (0 == res) - res = code; - } - return res; - } - - bool hbCalcCharWidth(struct LVCharPosInfo* posInfo, const struct LVCharTriplet& triplet, lChar16 def_char) { - if (!posInfo) - return false; - unsigned int segLen = 0; - int cluster; - hb_buffer_clear_contents(_hb_opt_kern_buffer); - if (0 != triplet.prevChar) { - hb_buffer_add(_hb_opt_kern_buffer, (hb_codepoint_t)triplet.prevChar, segLen); - segLen++; - } - hb_buffer_add(_hb_opt_kern_buffer, (hb_codepoint_t)triplet.Char, segLen); - cluster = segLen; - segLen++; - if (0 != triplet.nextChar) { - hb_buffer_add(_hb_opt_kern_buffer, (hb_codepoint_t)triplet.nextChar, segLen); - segLen++; - } - hb_buffer_set_content_type(_hb_opt_kern_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); - hb_buffer_guess_segment_properties(_hb_opt_kern_buffer); - hb_shape(_hb_font, _hb_opt_kern_buffer, _hb_opt_kern_features, 22); - unsigned int glyph_count = hb_buffer_get_length(_hb_opt_kern_buffer); - if (segLen == glyph_count) { - hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(_hb_opt_kern_buffer, &glyph_count); - hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(_hb_opt_kern_buffer, &glyph_count); - if (0 != glyph_info[cluster].codepoint) { // glyph found for this char in this font - posInfo->offset = glyph_pos[cluster].x_offset >> 6; - posInfo->width = glyph_pos[cluster].x_advance >> 6; - } else { - // hb_shape() failed or glyph omitted in this font, use fallback font - glyph_info_t glyph; - LVFont *fallback = getFallbackFont(); - if (fallback) { - if (fallback->getGlyphInfo(triplet.Char, &glyph, def_char)) { - posInfo->offset = 0; - posInfo->width = glyph.width; - } - } else { - if (getGlyphInfo(def_char, &glyph, 0)) { - posInfo->offset = 0; - posInfo->width = glyph.width; - } else { - posInfo->offset = 0; - posInfo->width = _size; - } - } - } - } else { -#ifdef _DEBUG - CRLog::debug("hbCalcCharWidthWithKerning(): hb_buffer_get_length() return %d, must be %d, return value (-1)", glyph_count, segLen); +#if (USE_WIN32_FONTS == 1) + fontMan = new LVWin32FontManager; +#elif (USE_FREETYPE == 1) + fontMan = new LVFreeTypeFontManager; +#else + fontMan = new LVBitmapFontManager; #endif - return false; - } - return true; - } -#endif // USE_HARFBUZZ==1 - - FT_UInt getCharIndex( lChar16 code, lChar16 def_char ) { - if ( code=='\t' ) - code = ' '; - FT_UInt ch_glyph_index = FT_Get_Char_Index( _face, code ); - if ( ch_glyph_index==0 ) { - lUInt16 replacement = getReplacementChar( code ); - if ( replacement ) - ch_glyph_index = FT_Get_Char_Index( _face, replacement ); - if ( ch_glyph_index==0 && def_char ) - ch_glyph_index = FT_Get_Char_Index( _face, def_char ); - } - return ch_glyph_index; - } + return fontMan->Init(path); +} - /** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found - */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ) - { - //FONT_GUARD - int glyph_index = getCharIndex( code, 0 ); - if ( glyph_index==0 ) { - LVFont * fallback = getFallbackFont(); - if ( !fallback ) { - // No fallback - glyph_index = getCharIndex( code, def_char ); - if ( glyph_index==0 ) - return false; - } else { - // Fallback - return fallback->getGlyphInfo(code, glyph, def_char); - } - } - int flags = FT_LOAD_DEFAULT; - flags |= (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO); - if (_hintingMode == HINTING_MODE_AUTOHINT) - flags |= FT_LOAD_FORCE_AUTOHINT; - else if (_hintingMode == HINTING_MODE_DISABLED) - flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; - updateTransform(); - int error = FT_Load_Glyph( - _face, /* handle to face object */ - glyph_index, /* glyph index */ - flags ); /* load flags, see below */ - if ( error ) - return false; - glyph->blackBoxX = (lUInt8)(_slot->metrics.width >> 6); - glyph->blackBoxY = (lUInt8)(_slot->metrics.height >> 6); - glyph->originX = (lInt8)(_slot->metrics.horiBearingX >> 6); - glyph->originY = (lInt8)(_slot->metrics.horiBearingY >> 6); - glyph->width = (lUInt8)(myabs(_slot->metrics.horiAdvance) >> 6); - return true; - } -/* - // USE GET_CHAR_FLAGS instead - inline int calcCharFlags( lChar16 ch ) - { - switch ( ch ) { - case 0x0020: - return LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER; - case UNICODE_SOFT_HYPHEN_CODE: - return LCHAR_ALLOW_WRAP_AFTER; - case '-': - return LCHAR_DEPRECATED_WRAP_AFTER; - case '\r': - case '\n': - return LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER; - default: - return 0; - } - } - */ - - /** - * @brief Check font for compatibility with language with langCode - * @param langCode language code, for example, "en" - English, "ru" - Russian - * @return true if font contains all glyphs for given language, false otherwise. - */ - virtual bool checkFontLangCompat(const lString8& langCode) - { -#define FC_LANG_START_INTERVAL_CODE 2 - bool fullSupport = false; - bool partialSupport = false; - struct fc_lang_catalog* lang_ptr = fc_lang_cat; - unsigned int i; - bool found = false; - for (i = 0; i < fc_lang_cat_sz; i++) - { - if (langCode.compare(lang_ptr->lang_code) == 0) - { - found = true; - break; - } - lang_ptr++; - } - if (found) - { - unsigned int codePoint = 0; - unsigned int tmp; - unsigned int first, second = 0; - bool inRange = false; - FT_UInt glyphIndex; - fullSupport = true; - for (i = 0; ; ) - { - // get next codePoint - if (inRange && codePoint < second) - { - codePoint++; - } - else - { - if (i >= lang_ptr->char_set_sz) - break; - tmp = lang_ptr->char_set[i]; - if (FC_LANG_START_INTERVAL_CODE == tmp) // code of start interval - { - if (i + 2 < lang_ptr->char_set_sz) - { - i++; - first = lang_ptr->char_set[i]; - i++; - second = lang_ptr->char_set[i]; - inRange = true; - codePoint = first; - i++; - } - else - { - // broken language char set - //qDebug() << "broken language char set"; - fullSupport = false; - break; - } - } - else - { - codePoint = tmp; - inRange = false; - i++; - } - } - // check codePoint in this font - glyphIndex = FT_Get_Char_Index(_face, codePoint); - if (0 == glyphIndex) - { - fullSupport = false; - } - else - { - partialSupport = true; - } - } - if (fullSupport) - CRLog::debug("checkFontLangCompat(): Font have full support of language %s", langCode.c_str()); - else if (partialSupport) - CRLog::debug("checkFontLangCompat(): Font have partial support of language %s", langCode.c_str()); - else - CRLog::debug("checkFontLangCompat(): Font DON'T have support of language %s", langCode.c_str()); - } - else - CRLog::debug("checkFontLangCompat(): Unsupported language code: %s", langCode.c_str()); - return fullSupport; - } - - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \return number of characters before max_width reached - */ - virtual lUInt16 measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing = 0, - bool allow_hyphenation = true - ) - { - FONT_GUARD - if ( len <= 0 || _face==NULL ) - return 0; - - if ( letter_spacing<0 || letter_spacing>50 ) - letter_spacing = 0; - - int i; - - lUInt16 prev_width = 0; - uint32_t lastFitChar = 0; - updateTransform(); - // measure character widths -#if USE_HARFBUZZ==1 - unsigned int glyph_count; - hb_glyph_info_t* glyph_info = 0; - hb_glyph_position_t* glyph_pos = 0; - if (/*_allowKerning &&*/ _allowLigatures) { - // Full shaping with HarfBuzz, very slow method but support ligatures. - hb_buffer_clear_contents(_hb_buffer); - hb_buffer_set_replacement_codepoint(_hb_buffer, def_char); - // fill HarfBuzz buffer with filtering - for (i = 0; i < len; i++) - hb_buffer_add(_hb_buffer, (hb_codepoint_t)filterChar(text[i]), i); - hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); - hb_buffer_guess_segment_properties(_hb_buffer); - // shape - hb_shape(_hb_font, _hb_buffer, _hb_features, 2); - glyph_count = hb_buffer_get_length(_hb_buffer); - glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, 0); - glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, 0); -#ifdef _DEBUG - if ((int)glyph_count != len) { - CRLog::debug( - "measureText(): glyph_count not equal source text length (ligature detected?), glyph_count=%d, len=%d", - glyph_count, len); - } -#endif - uint32_t j; - uint32_t cluster; - uint32_t prev_cluster = 0; - int skipped_chars = 0; // to add to 'i' at end of loop, as 'i' is used later and should be accurate - for (i = 0; i < (int)glyph_count; i++) { - cluster = glyph_info[i].cluster; - lChar16 ch = text[cluster]; - bool isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE); - flags[cluster] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch ); - hb_codepoint_t ch_glyph_index = glyph_info[i].codepoint; - if (0 != ch_glyph_index) // glyph found for this char in this font - widths[cluster] = prev_width + (glyph_pos[i].x_advance >> 6) + letter_spacing; - else { - // hb_shape() failed or glyph skipped in this font, use fallback font - int w = _wcache.get(ch); - if (0xFF == w) { - glyph_info_t glyph; - LVFont *fallback = getFallbackFont(); - if (fallback) { - if (fallback->getGlyphInfo(ch, &glyph, def_char)) { - w = glyph.width; - _wcache.put(ch, w); - } else { - w = _size; - _wcache.put(ch, w); - } - } else { - // use def_char if possible - if (getGlyphInfo(def_char, &glyph, 0)) { - w = glyph.width; - _wcache.put(ch, w); - } else { - w = _size; - _wcache.put(ch, w); - } - } - } - widths[cluster] = prev_width + w + letter_spacing; - } - for (j = prev_cluster + 1; j < cluster; j++) { - flags[j] = GET_CHAR_FLAGS(text[j]); - widths[j] = prev_width; // for chars replaced by ligature, so next chars of a ligature has width=0 - skipped_chars++; - } - prev_cluster = cluster; - if (!isHyphen) // avoid soft hyphens inside text string - prev_width = widths[cluster]; - if (prev_width > max_width) { - if (lastFitChar < cluster + 7) - break; - } else { - lastFitChar = cluster + 1; - } - } - // For case when ligature is the last glyph in measured text - if (prev_cluster < (uint32_t)(len - 1) && prev_width < (lUInt16)max_width) { - for (j = prev_cluster + 1; j < (uint32_t)len; j++) { - flags[j] = GET_CHAR_FLAGS(text[j]); - widths[j] = prev_width; - skipped_chars++; - } - } - // i is used below to "fill props for rest of chars", so make it accurate - i += skipped_chars; - } else { - // no ligatures, no HarfBuzz text shaping, only individual char shaping in hbCalcCharWidth() - struct LVCharTriplet triplet; - struct LVCharPosInfo posInfo; - triplet.Char = 0; - for ( i=0; i max_width ) { - if ( lastFitChar < (uint32_t)(i + 7)) - break; - } else { - lastFitChar = i + 1; - } - } - } -#else // USE_HARFBUZZ==1 - FT_UInt previous = 0; - int error; -#if (ALLOW_KERNING==1) - int use_kerning = _allowKerning && FT_HAS_KERNING( _face ); -#endif - for ( i=0; i0 ) { - if ( ch_glyph_index==(FT_UInt)-1 ) - ch_glyph_index = getCharIndex( ch, def_char ); - if ( ch_glyph_index != 0 ) { - FT_Vector delta; - error = FT_Get_Kerning( _face, /* handle to face object */ - previous, /* left glyph index */ - ch_glyph_index, /* right glyph index */ - FT_KERNING_DEFAULT, /* kerning mode */ - &delta ); /* target vector */ - if ( !error ) - kerning = delta.x; - } - } -#endif - - flags[i] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch ); - - /* load glyph image into the slot (erase previous one) */ - int w = _wcache.get(ch); - if ( w==0xFF ) { - glyph_info_t glyph; - if ( getGlyphInfo( ch, &glyph, def_char ) ) { - w = glyph.width; - _wcache.put(ch, w); - } else { - widths[i] = prev_width; - lastFitChar = i + 1; - continue; /* ignore errors */ - } - if ( ch_glyph_index==(FT_UInt)-1 ) - ch_glyph_index = getCharIndex( ch, 0 ); -// error = FT_Load_Glyph( _face, /* handle to face object */ -// ch_glyph_index, /* glyph index */ -// FT_LOAD_DEFAULT ); /* load flags, see below */ -// if ( error ) { -// widths[i] = prev_width; -// continue; /* ignore errors */ -// } - } - widths[i] = prev_width + w + (kerning >> 6) + letter_spacing; - previous = ch_glyph_index; - if ( !isHyphen ) // avoid soft hyphens inside text string - prev_width = widths[i]; - if ( prev_width > max_width ) { - if ( lastFitChar < (uint32_t)(i + 7)) - break; - } else { - lastFitChar = i + 1; - } - } -#endif // USE_HARFBUZZ==1 - - // fill props for rest of chars - for ( int ii=i; ii 3 ) { - int hwStart, hwEnd; - lStr_findWordBounds( text, len, lastFitChar-1, hwStart, hwEnd ); - if ( hwStart < (int)(lastFitChar-1) && hwEnd > hwStart+3 ) { - //int maxw = max_width - (hwStart>0 ? widths[hwStart-1] : 0); - HyphMan::hyphenate(text+hwStart, hwEnd-hwStart, widths+hwStart, flags+hwStart, _hyphen_width, max_width); - } - } - } - return lastFitChar; //i; - } - - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \return width of specified string - */ - virtual lUInt32 getTextWidth( - const lChar16 * text, int len - ) - { - static lUInt16 widths[MAX_LINE_CHARS+1]; - static lUInt8 flags[MAX_LINE_CHARS+1]; - if ( len>MAX_LINE_CHARS ) - len = MAX_LINE_CHARS; - if ( len<=0 ) - return 0; - lUInt16 res = measureText( - text, len, - widths, - flags, - 2048, // max_width, - L' ', // def_char - 0 - ); - if ( res>0 && resgetGlyph(ch, def_char); - } - } - LVFontGlyphCacheItem * item = _glyph_cache.get( ch ); - if ( !item ) { - - int rend_flags = FT_LOAD_RENDER | ( !_drawMonochrome ? FT_LOAD_TARGET_NORMAL : (FT_LOAD_TARGET_MONO) ); //|FT_LOAD_MONOCHROME|FT_LOAD_FORCE_AUTOHINT - if (_hintingMode == HINTING_MODE_AUTOHINT) - rend_flags |= FT_LOAD_FORCE_AUTOHINT; - else if (_hintingMode == HINTING_MODE_DISABLED) - rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; - /* load glyph image into the slot (erase previous one) */ - - updateTransform(); - int error = FT_Load_Glyph( _face, /* handle to face object */ - ch_glyph_index, /* glyph index */ - rend_flags ); /* load flags, see below */ - if ( error ) { - return NULL; /* ignore errors */ - } - item = newItem( &_glyph_cache, ch, _slot ); //, _drawMonochrome - _glyph_cache.put( item ); - } - return item; - } - -#if USE_HARFBUZZ==1 - LVFontGlyphIndexCacheItem * getGlyphByIndex(lUInt32 index) { - //FONT_GUARD - LVFontGlyphIndexCacheItem * item = 0; - if (!_glyph_cache2.get(index, item)) { - // glyph not found in cache, rendering... - int rend_flags = FT_LOAD_RENDER | ( !_drawMonochrome ? FT_LOAD_TARGET_NORMAL : (FT_LOAD_TARGET_MONO) ); //|FT_LOAD_MONOCHROME|FT_LOAD_FORCE_AUTOHINT - if (_hintingMode == HINTING_MODE_AUTOHINT) - rend_flags |= FT_LOAD_FORCE_AUTOHINT; - else if (_hintingMode == HINTING_MODE_DISABLED) - rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; - /* load glyph image into the slot (erase previous one) */ - - updateTransform(); - int error = FT_Load_Glyph( _face, /* handle to face object */ - index, /* glyph index */ - rend_flags ); /* load flags, see below */ - if ( error ) { - return NULL; /* ignore errors */ - } - item = newItem(index, _slot); - if (item) - _glyph_cache2.set(index, item); - } - return item; - } -#endif - -// /** \brief get glyph image in 1 byte per pixel format -// \param code is unicode character -// \param buf is buffer [width*height] to place glyph data -// \return true if glyph was found -// */ -// virtual bool getGlyphImage(lUInt16 ch, lUInt8 * bmp, lChar16 def_char=0) -// { -// LVFontGlyphCacheItem * item = getGlyph(ch); -// if ( item ) -// memcpy( bmp, item->bmp, item->bmp_width * item->bmp_height ); -// return item; -// } - - /// returns font baseline offset - virtual int getBaseline() - { - return _baseline; - } - - /// returns font height - virtual int getHeight() const - { - return _height; - } - - /// returns font character size - virtual int getSize() const - { - return _size; - } - - /// returns char width - virtual int getCharWidth( lChar16 ch, lChar16 def_char='?' ) - { - int w = _wcache.get(ch); - if ( w==0xFF ) { - glyph_info_t glyph; - if ( getGlyphInfo( ch, &glyph, def_char ) ) { - w = glyph.width; - } else { - w = 0; - } - _wcache.put(ch, w); - } - return w; - } - - /// retrieves font handle - virtual void * GetHandle() - { - return NULL; - } - - /// returns font typeface name - virtual lString8 getTypeFace() const - { - return _faceName; - } - - /// returns font family id - virtual css_font_family_t getFontFamily() const - { - return _fontFamily; - } - - virtual bool kerningEnabled() { -#if (ALLOW_KERNING==1) - #if USE_HARFBUZZ==1 - return _allowKerning; - #else - return _allowKerning && FT_HAS_KERNING( _face ); - #endif -#else - return false; -#endif - } - - /// draws text string - virtual void DrawTextString( LVDrawBuf * buf, int x, int y, - const lChar16 * text, int len, - lChar16 def_char, lUInt32 * palette, bool addHyphen, lUInt32 flags, int letter_spacing ) - { - FONT_GUARD - if ( len <= 0 || _face==NULL ) - return; - if ( letter_spacing<0 || letter_spacing>50 ) - letter_spacing = 0; - lvRect clip; - buf->GetClipRect( &clip ); - updateTransform(); - if ( y + _height < clip.top || y >= clip.bottom ) - return; - - //lUInt16 prev_width = 0; - lChar16 ch; - // measure character widths - bool isHyphen = false; - int x0 = x; -#if USE_HARFBUZZ==1 - unsigned int i; - hb_glyph_info_t *glyph_info = 0; - hb_glyph_position_t *glyph_pos = 0; - unsigned int glyph_count; - int w; - unsigned int len_new = 0; - if (/*_allowKerning &&*/ _allowLigatures) { - // Full shaping with HarfBuzz, very slow method but support ligatures. - hb_buffer_clear_contents(_hb_buffer); - hb_buffer_set_replacement_codepoint(_hb_buffer, 0); - // fill HarfBuzz buffer with filtering - for (i = 0; i < (unsigned int)len; i++) { - ch = text[i]; - isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE) && (i < (unsigned int)(len - 1)); - if (!isHyphen) { // avoid soft hyphens inside text string - // Also replaced any chars to similar if the glyph is not found - hb_buffer_add(_hb_buffer, (hb_codepoint_t)filterChar(ch), i); - len_new++; - } - } - hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); - hb_buffer_guess_segment_properties(_hb_buffer); - // shape - hb_shape(_hb_font, _hb_buffer, _hb_features, 2); - glyph_count = hb_buffer_get_length(_hb_buffer); - glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, 0); - glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, 0); -#ifdef _DEBUG - if (glyph_count != len_new) { - CRLog::debug( - "DrawTextString(): glyph_count not equal source text length, glyph_count=%d, len=%d", - glyph_count, len_new); - } -#endif - for (i = 0; i < glyph_count; i++) { - if (0 == glyph_info[i].codepoint) { - // If HarfBuzz can't find glyph in current font - // using fallback font that used in getGlyph() - ch = text[glyph_info[i].cluster]; - LVFontGlyphCacheItem *item = getGlyph(ch, def_char); - if (item) { - w = item->advance; - buf->Draw(x + item->origin_x, - y + _baseline - item->origin_y, - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - x += w + letter_spacing; - } - } else { - LVFontGlyphIndexCacheItem *item = getGlyphByIndex(glyph_info[i].codepoint); - if (item) { - w = glyph_pos[i].x_advance >> 6; - buf->Draw(x + item->origin_x + (glyph_pos[i].x_offset >> 6), - y + _baseline - item->origin_y + (glyph_pos[i].y_offset >> 6), - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - x += w + letter_spacing; - } - } - } - } else { // ligatures disabled, kerning any - struct LVCharTriplet triplet; - struct LVCharPosInfo posInfo; - triplet.Char = 0; - for (i = 0; i < (unsigned int)len; i++) { - ch = text[i]; - isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE) && (i < (unsigned int)(len - 1)); - // avoid soft hyphens inside text string - if (isHyphen) - continue; - LVFontGlyphCacheItem *item = getGlyph(ch, def_char); - if (item) { - triplet.prevChar = triplet.Char; - triplet.Char = ch; - if (i < (unsigned int)(len - 1)) - triplet.nextChar = text[i + 1]; - else - triplet.nextChar = 0; - if (!_width_cache2.get(triplet, posInfo)) { - if (!hbCalcCharWidth(&posInfo, triplet, def_char)) { - posInfo.offset = 0; - posInfo.width = item->advance; - } - _width_cache2.set(triplet, posInfo); - } - buf->Draw(x + item->origin_x + posInfo.offset, - y + _baseline - item->origin_y, - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - x += posInfo.width + letter_spacing; - } - } - } - if (addHyphen) { - ch = UNICODE_SOFT_HYPHEN_CODE; - LVFontGlyphCacheItem *item = getGlyph(ch, def_char); - if (item) { - w = item->advance; - buf->Draw( x + item->origin_x, - y + _baseline - item->origin_y, - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - x += w + letter_spacing; - } - } -#else - FT_UInt previous = 0; - int i; - int error; -#if (ALLOW_KERNING==1) - int use_kerning = _allowKerning && FT_HAS_KERNING( _face ); -#endif - for ( i=0; i<=len; i++) { - if ( i==len && (!addHyphen || isHyphen) ) - break; - if ( i0 && ch_glyph_index>0 ) { - FT_Vector delta; - error = FT_Get_Kerning( _face, /* handle to face object */ - previous, /* left glyph index */ - ch_glyph_index, /* right glyph index */ - FT_KERNING_DEFAULT, /* kerning mode */ - &delta ); /* target vector */ - if ( !error ) - kerning = delta.x; - } -#endif - LVFontGlyphCacheItem * item = getGlyph(ch, def_char); - if ( !item ) - continue; - if ( (item && !isHyphen) || i>=len-1 ) { // avoid soft hyphens inside text string - int w = item->advance + (kerning >> 6); - buf->Draw( x + (kerning>>6) + item->origin_x, - y + _baseline - item->origin_y, - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - - x += w + letter_spacing; - previous = ch_glyph_index; - } - } -#endif - if ( flags & LTEXT_TD_MASK ) { - // text decoration: underline, etc. - int h = _size > 30 ? 2 : 1; - lUInt32 cl = buf->GetTextColor(); - if ( (flags & LTEXT_TD_UNDERLINE) || (flags & LTEXT_TD_BLINK) ) { - int liney = y + _baseline + h; - buf->FillRect( x0, liney, x, liney+h, cl ); - } - if ( flags & LTEXT_TD_OVERLINE ) { - int liney = y + h; - buf->FillRect( x0, liney, x, liney+h, cl ); - } - if ( flags & LTEXT_TD_LINE_THROUGH ) { -// int liney = y + _baseline - _size/4 - h/2; - int liney = y + _baseline - _size*2/7; - buf->FillRect( x0, liney, x, liney+h, cl ); - } - } - } - - /// returns true if font is empty - virtual bool IsNull() const - { - return _face == NULL; - } - - virtual bool operator ! () const - { - return _face == NULL; - } - - virtual void Clear() - { - LVLock lock(_mutex); - clearCache(); -#if USE_HARFBUZZ==1 - if (_hb_font) { - hb_font_destroy(_hb_font); - _hb_font = 0; - } -#endif - if ( _face ) { - FT_Done_Face(_face); - _face = NULL; - } - } - -}; - -class LVFontBoldTransform : public LVFont -{ - LVFontRef _baseFontRef; - LVFont * _baseFont; - int _hyphWidth; - int _hShift; - int _vShift; - int _size; // glyph height in pixels - int _height; // line height in pixels - //int _hyphen_width; - int _baseline; - LVFontLocalGlyphCache _glyph_cache; -public: - /// returns font weight - virtual int getWeight() const - { - int w = _baseFont->getWeight() + 200; - if ( w>900 ) - w = 900; - return w; - } - /// returns italic flag - virtual int getItalic() const - { - return _baseFont->getItalic(); - } - LVFontBoldTransform( LVFontRef baseFont, LVFontGlobalGlyphCache * globalCache ) - : _baseFontRef( baseFont ), _baseFont( baseFont.get() ), _hyphWidth(-1), _glyph_cache(globalCache) - { - _size = _baseFont->getSize(); - _height = _baseFont->getHeight(); - _hShift = _size <= 36 ? 1 : 2; - _vShift = _size <= 36 ? 0 : 1; - _baseline = _baseFont->getBaseline(); - } - - /// hyphenation character - virtual lChar16 getHyphChar() { return UNICODE_SOFT_HYPHEN_CODE; } - - /// hyphen width - virtual int getHyphenWidth() { - FONT_GUARD - if ( _hyphWidth<0 ) - _hyphWidth = getCharWidth( getHyphChar() ); - return _hyphWidth; - } - - /** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found - */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ) - { - bool res = _baseFont->getGlyphInfo( code, glyph, def_char ); - if ( !res ) - return res; - glyph->blackBoxX += glyph->blackBoxX>0 ? _hShift : 0; - glyph->blackBoxY += _vShift; - glyph->width += _hShift; - - return true; - } - - /** \brief measure text - \param text is text string pointer - \param len is number of characters to measure - \param max_width is maximum width to measure line - \param def_char is character to replace absent glyphs in font - \param letter_spacing is number of pixels to add between letters - \return number of characters before max_width reached - */ - virtual lUInt16 measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing=0, - bool allow_hyphenation=true - ) - { - CR_UNUSED(allow_hyphenation); - lUInt16 res = _baseFont->measureText( - text, len, - widths, - flags, - max_width, - def_char, - letter_spacing - ); - int w = 0; - for ( int i=0; iMAX_LINE_CHARS ) - len = MAX_LINE_CHARS; - if ( len<=0 ) - return 0; - lUInt16 res = measureText( - text, len, - widths, - flags, - 2048, // max_width, - L' ', // def_char - 0 - ); - if ( res>0 && resgetGlyph( ch, def_char ); - if ( !olditem ) - return NULL; - - int oldx = olditem->bmp_width; - int oldy = olditem->bmp_height; - int dx = oldx ? oldx + _hShift : 0; - int dy = oldy ? oldy + _vShift : 0; - - item = LVFontGlyphCacheItem::newItem( &_glyph_cache, ch, dx, dy ); //, _drawMonochrome - item->advance = olditem->advance + _hShift; - item->origin_x = olditem->origin_x; - item->origin_y = olditem->origin_y; - - if ( dx && dy ) { - for ( int y=0; ybmp + y*dx; - for ( int x=0; x=oldy ) - continue; - lUInt8 * src = olditem->bmp + srcy*oldx; - for ( int xx=-_hShift; xx<=0; xx++ ) { - int srcx = x+xx; - if ( srcx>=0 && srcx s ) - s = src[srcx]; - } - } - dst[x] = s; - } - } - } - _glyph_cache.put( item ); - return item; - } - - /** \brief get glyph image in 1 byte per pixel format - \param code is unicode character - \param buf is buffer [width*height] to place glyph data - \return true if glyph was found - */ -// virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0 ) -// { -// LVFontGlyphCacheItem * item = getGlyph( code, def_char ); -// if ( !item ) -// return false; -// glyph_info_t glyph; -// if ( !_baseFont->getGlyphInfo( code, &glyph, def_char ) ) -// return 0; -// int oldx = glyph.blackBoxX; -// int oldy = glyph.blackBoxY; -// int dx = oldx + _hShift; -// int dy = oldy + _vShift; -// if ( !oldx || !oldy ) -// return true; -// LVAutoPtr tmp( new lUInt8[oldx*oldy+2000] ); -// memset(buf, 0, dx*dy); -// tmp[oldx*oldy]=123; -// bool res = _baseFont->getGlyphImage( code, tmp.get(), def_char ); -// if ( tmp[oldx*oldy]!=123 ) { -// //CRLog::error("Glyph buffer corrupted!"); -// // clear cache -// for ( int i=32; i<4000; i++ ) { -// _baseFont->getGlyphInfo( i, &glyph, def_char ); -// _baseFont->getGlyphImage( i, tmp.get(), def_char ); -// } -// _baseFont->getGlyphInfo( code, &glyph, def_char ); -// _baseFont->getGlyphImage( code, tmp.get(), def_char ); -// } -// for ( int y=0; y=oldy ) -// continue; -// lUInt8 * src = tmp.get() + srcy*oldx; -// for ( int xx=-_hShift; xx<=0; xx++ ) { -// int srcx = x+xx; -// if ( srcx>=0 && srcx s ) -// s = src[srcx]; -// } -// } -// dst[x] = s; -// } -// } -// return res; -// return false; -// } - - /// returns font baseline offset - virtual int getBaseline() - { - return _baseline; - } - - /// returns font height - virtual int getHeight() const - { - return _height; - } - - /// returns font character size - virtual int getSize() const - { - return _size; - } - - /// returns char width - virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ) - { - int w = _baseFont->getCharWidth( ch, def_char ) + _hShift; - return w; - } - - /// retrieves font handle - virtual void * GetHandle() - { - return NULL; - } - - /// returns font typeface name - virtual lString8 getTypeFace() const - { - return _baseFont->getTypeFace(); - } - - /// returns font family id - virtual css_font_family_t getFontFamily() const - { - return _baseFont->getFontFamily(); - } - - /// draws text string - virtual void DrawTextString( LVDrawBuf * buf, int x, int y, - const lChar16 * text, int len, - lChar16 def_char, lUInt32 * palette, bool addHyphen, - lUInt32 flags, int letter_spacing ) - { - if ( len <= 0 ) - return; - if ( letter_spacing<0 || letter_spacing>50 ) - letter_spacing = 0; - lvRect clip; - buf->GetClipRect( &clip ); - if ( y + _height < clip.top || y >= clip.bottom ) - return; - - //int error; - - int i; - - //lUInt16 prev_width = 0; - lChar16 ch; - // measure character widths - bool isHyphen = false; - int x0 = x; - for ( i=0; i<=len; i++) { - if ( i==len && (!addHyphen || isHyphen) ) - break; - if ( iadvance; - if ( item->bmp_width && item->bmp_height && (!isHyphen || i>=len-1) ) { - buf->Draw( x + item->origin_x, - y + _baseline - item->origin_y, - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - } - } - x += w + letter_spacing; - } - if ( flags & LTEXT_TD_MASK ) { - // text decoration: underline, etc. - int h = _size > 30 ? 2 : 1; - lUInt32 cl = buf->GetTextColor(); - if ( (flags & LTEXT_TD_UNDERLINE) || (flags & LTEXT_TD_BLINK) ) { - int liney = y + _baseline + h; - buf->FillRect( x0, liney, x, liney+h, cl ); - } - if ( flags & LTEXT_TD_OVERLINE ) { - int liney = y + h; - buf->FillRect( x0, liney, x, liney+h, cl ); - } - if ( flags & LTEXT_TD_LINE_THROUGH ) { - int liney = y + _height/2 - h/2; - buf->FillRect( x0, liney, x, liney+h, cl ); - } - } - } - - /// get bitmap mode (true=monochrome bitmap, false=antialiased) - virtual bool getBitmapMode() - { - return _baseFont->getBitmapMode(); - } - - /// set bitmap mode (true=monochrome bitmap, false=antialiased) - virtual void setBitmapMode( bool m ) - { - _baseFont->setBitmapMode( m ); - } - - /// sets current hinting mode - virtual void setHintingMode(hinting_mode_t mode) { _baseFont->setHintingMode(mode); } - /// returns current hinting mode - virtual hinting_mode_t getHintingMode() const { return _baseFont->getHintingMode(); } - - /// get kerning mode: true==ON, false=OFF - virtual bool getKerning() const { return _baseFont->getKerning(); } - - /// get kerning mode: true==ON, false=OFF - virtual void setKerning( bool b ) { _baseFont->setKerning( b ); } - - /// get ligatures mode: true==allowed, false=not allowed - virtual bool getLigatures() const { return _baseFont->getLigatures(); } - /// set ligatures mode: true==allowed, false=not allowed - virtual void setLigatures( bool b ) { _baseFont->setLigatures( b ); } - - /// returns true if font is empty - virtual bool IsNull() const - { - return _baseFont->IsNull(); - } - - virtual bool operator ! () const - { - return !(*_baseFont); - } - virtual void Clear() - { - _baseFont->Clear(); - } - virtual ~LVFontBoldTransform() - { - } -}; - -/// create transform for font -//LVFontRef LVCreateFontTransform( LVFontRef baseFont, int transformFlags ) -//{ -// if ( transformFlags & LVFONT_TRANSFORM_EMBOLDEN ) { -// // BOLD transform -// return LVFontRef( new LVFontBoldTransform( baseFont ) ); -// } else { -// return baseFont; // no transform -// } -//} - -#if (DEBUG_FONT_SYNTHESIS==1) -static LVFontRef dumpFontRef( LVFontRef fnt ) { - CRLog::trace("%s %d (%d) w=%d %s", fnt->getTypeFace().c_str(), fnt->getSize(), fnt->getHeight(), fnt->getWeight(), fnt->getItalic()?"italic":"" ); - return fnt; -} -#endif - -class LVFreeTypeFontManager : public LVFontManager -{ -private: - lString8 _path; - lString8 _fallbackFontFace; - LVFontCache _cache; - FT_Library _library; - LVFontGlobalGlyphCache _globalCache; - lString16 _requiredChars; - #if (DEBUG_FONT_MAN==1) - FILE * _log; - #endif - LVMutex _lock; -public: - - /// get hash of installed fonts and fallback font - virtual lUInt32 GetFontListHash(int documentId) { - FONT_MAN_GUARD - return _cache.GetFontListHash(documentId) * 75 + _fallbackFontFace.getHash(); - } - - /// set fallback font - virtual bool SetFallbackFontFace( lString8 face ) { - FONT_MAN_GUARD - if ( face!=_fallbackFontFace ) { - _cache.clearFallbackFonts(); - CRLog::trace("Looking for fallback font %s", face.c_str()); - LVFontCacheItem * item = _cache.findFallback( face, -1 ); - if ( !item ) - face.clear(); - _fallbackFontFace = face; - // Somehow, with Fedra Serif (only!), changing the fallback font does - // not prevent glyphs from previous fallback font to be re-used... - // So let's clear glyphs caches too. - gc(); - clearGlyphCache(); - } - return !_fallbackFontFace.empty(); - } - - /// get fallback font face (returns empty string if no fallback font is set) - virtual lString8 GetFallbackFontFace() { return _fallbackFontFace; } - - /// returns fallback font for specified size - virtual LVFontRef GetFallbackFont(int size) { - FONT_MAN_GUARD - if ( _fallbackFontFace.empty() ) - return LVFontRef(); - // reduce number of possible distinct sizes for fallback font - if ( size>40 ) - size &= 0xFFF8; - else if ( size>28 ) - size &= 0xFFFC; - else if ( size>16 ) - size &= 0xFFFE; - LVFontCacheItem * item = _cache.findFallback( _fallbackFontFace, size ); - if ( !item->getFont().isNull() ) - return item->getFont(); - return GetFont(size, 400, false, css_ff_sans_serif, _fallbackFontFace, -1); - } - - bool isBitmapModeForSize( int size ) - { - bool bitmap = false; - switch ( _antialiasMode ) { - case font_aa_none: - bitmap = true; - break; - case font_aa_big: - bitmap = size<20 ? true : false; - break; - case font_aa_all: - default: - bitmap = false; - break; - } - return bitmap; - } - - /// set antialiasing mode - virtual void SetAntialiasMode( int mode ) - { - _antialiasMode = mode; - gc(); - clearGlyphCache(); - FONT_MAN_GUARD - LVPtrVector< LVFontCacheItem > * fonts = _cache.getInstances(); - for ( int i=0; ilength(); i++ ) { - fonts->get(i)->getFont()->setBitmapMode( isBitmapModeForSize( fonts->get(i)->getFont()->getHeight() ) ); - } - } - - /// sets current gamma level - virtual void SetHintingMode(hinting_mode_t mode) { - if (_hintingMode == mode) - return; - FONT_MAN_GUARD - CRLog::debug("Hinting mode is changed: %d", (int)mode); - _hintingMode = mode; - gc(); - clearGlyphCache(); - LVPtrVector< LVFontCacheItem > * fonts = _cache.getInstances(); - for ( int i=0; ilength(); i++ ) { - fonts->get(i)->getFont()->setHintingMode(mode); - } - } - - /// sets current gamma level - virtual hinting_mode_t GetHintingMode() { - return _hintingMode; - } - - /// set kerning mode - virtual void setKerning( bool kerning ) - { - FONT_MAN_GUARD - _allowKerning = kerning; - gc(); - clearGlyphCache(); - LVPtrVector< LVFontCacheItem > * fonts = _cache.getInstances(); - for ( int i=0; ilength(); i++ ) { - fonts->get(i)->getFont()->setKerning( kerning ); - } - } - /// set ligatures mode - virtual void setLigatures( bool ligatures ) - { - FONT_MAN_GUARD - _allowLigatures = ligatures; - gc(); - clearGlyphCache(); - LVPtrVector< LVFontCacheItem > * fonts = _cache.getInstances(); - for ( int i=0; ilength(); i++ ) { - fonts->get(i)->getFont()->setLigatures( ligatures ); - } - } - /// clear glyph cache - virtual void clearGlyphCache() - { - FONT_MAN_GUARD - _globalCache.clear(); - } - - virtual int GetFontCount() - { - return _cache.length(); - } - - bool initSystemFonts() - { - #if (DEBUG_FONT_SYNTHESIS==1) - fontMan->RegisterFont(lString8("/usr/share/fonts/liberation/LiberationSans-Regular.ttf")); - CRLog::debug("fonts:"); - LVFontRef fnt4 = dumpFontRef( fontMan->GetFont(24, 200, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); - LVFontRef fnt1 = dumpFontRef( fontMan->GetFont(18, 200, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); - LVFontRef fnt2 = dumpFontRef( fontMan->GetFont(20, 400, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); - LVFontRef fnt3 = dumpFontRef( fontMan->GetFont(22, 600, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); - LVFontRef fnt5 = dumpFontRef( fontMan->GetFont(26, 400, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); - LVFontRef fnt6 = dumpFontRef( fontMan->GetFont(28, 600, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); - CRLog::debug("end of font testing"); - #elif (USE_FONTCONFIG==1) - { - CRLog::info("Reading list of system fonts using FONTCONFIG"); - lString16Collection fonts; - - int facesFound = 0; - - FcFontSet *fontset; - - FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_WEIGHT, FC_FAMILY, - FC_SLANT, FC_SPACING, FC_INDEX, - FC_STYLE, NULL); - FcPattern *pat = FcPatternCreate(); - //FcBool b = 1; - FcPatternAddBool(pat, FC_SCALABLE, 1); - - fontset = FcFontList(NULL, pat, os); - - FcPatternDestroy(pat); - FcObjectSetDestroy(os); - - // load fonts from file - CRLog::debug("FONTCONFIG: %d font files found", fontset->nfont); - for(int i = 0; i < fontset->nfont; i++) { - FcChar8 *s=(FcChar8*)""; - FcChar8 *family=(FcChar8*)""; - FcChar8 *style=(FcChar8*)""; - //FcBool b; - FcResult res; - //FC_SCALABLE - //res = FcPatternGetBool( fontset->fonts[i], FC_OUTLINE, 0, (FcBool*)&b); - //if(res != FcResultMatch) - // continue; - //if ( !b ) - // continue; // skip non-scalable fonts - res = FcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8 **)&s); - if(res != FcResultMatch) { - continue; - } - lString8 fn( (const char *)s ); - lString16 fn16( fn.c_str() ); - fn16.lowercase(); - if (!fn16.endsWith(".ttf") && !fn16.endsWith(".odf") && !fn16.endsWith(".otf") && !fn16.endsWith(".pfb") && !fn16.endsWith(".pfa") ) { - continue; - } - int weight = FC_WEIGHT_MEDIUM; - res = FcPatternGetInteger(fontset->fonts[i], FC_WEIGHT, 0, &weight); - if(res != FcResultMatch) { - CRLog::debug("no FC_WEIGHT for %s", s); - //continue; - } - switch ( weight ) { - case FC_WEIGHT_THIN: // 0 - weight = 100; - break; - case FC_WEIGHT_EXTRALIGHT: // 40 - //case FC_WEIGHT_ULTRALIGHT FC_WEIGHT_EXTRALIGHT - weight = 200; - break; - case FC_WEIGHT_LIGHT: // 50 - case FC_WEIGHT_BOOK: // 75 - case FC_WEIGHT_REGULAR: // 80 - //case FC_WEIGHT_NORMAL: FC_WEIGHT_REGULAR - weight = 400; - break; - case FC_WEIGHT_MEDIUM: // 100 - weight = 500; - break; - case FC_WEIGHT_DEMIBOLD: // 180 - //case FC_WEIGHT_SEMIBOLD: FC_WEIGHT_DEMIBOLD - weight = 600; - break; - case FC_WEIGHT_BOLD: // 200 - weight = 700; - break; - case FC_WEIGHT_EXTRABOLD: // 205 - //case FC_WEIGHT_ULTRABOLD: FC_WEIGHT_EXTRABOLD - weight = 800; - break; - case FC_WEIGHT_BLACK: // 210 - //case FC_WEIGHT_HEAVY: FC_WEIGHT_BLACK - weight = 900; - break; -#ifdef FC_WEIGHT_EXTRABLACK - case FC_WEIGHT_EXTRABLACK: // 215 - //case FC_WEIGHT_ULTRABLACK: FC_WEIGHT_EXTRABLACK - weight = 900; - break; -#endif - default: - weight = 400; - break; - } - FcBool scalable = 0; - res = FcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable); - int index = 0; - res = FcPatternGetInteger(fontset->fonts[i], FC_INDEX, 0, &index); - if(res != FcResultMatch) { - CRLog::debug("no FC_INDEX for %s", s); - //continue; - } - res = FcPatternGetString(fontset->fonts[i], FC_FAMILY, 0, (FcChar8 **)&family); - if(res != FcResultMatch) { - CRLog::debug("no FC_FAMILY for %s", s); - continue; - } - res = FcPatternGetString(fontset->fonts[i], FC_STYLE, 0, (FcChar8 **)&style); - if(res != FcResultMatch) { - CRLog::debug("no FC_STYLE for %s", s); - style = (FcChar8*)""; - //continue; - } - int slant = FC_SLANT_ROMAN; - res = FcPatternGetInteger(fontset->fonts[i], FC_SLANT, 0, &slant); - if(res != FcResultMatch) { - CRLog::debug("no FC_SLANT for %s", s); - //continue; - } - int spacing = 0; - res = FcPatternGetInteger(fontset->fonts[i], FC_SPACING, 0, &spacing); - if(res != FcResultMatch) { - //CRLog::debug("no FC_SPACING for %s", s); - //continue; - } -// int cr_weight; -// switch(weight) { -// case FC_WEIGHT_LIGHT: cr_weight = 200; break; -// case FC_WEIGHT_MEDIUM: cr_weight = 300; break; -// case FC_WEIGHT_DEMIBOLD: cr_weight = 500; break; -// case FC_WEIGHT_BOLD: cr_weight = 700; break; -// case FC_WEIGHT_BLACK: cr_weight = 800; break; -// default: cr_weight=300; break; -// } - css_font_family_t fontFamily = css_ff_sans_serif; - lString16 face16((const char *)family); - face16.lowercase(); - if ( spacing==FC_MONO ) - fontFamily = css_ff_monospace; - else if (face16.pos("sans") >= 0) - fontFamily = css_ff_sans_serif; - else if (face16.pos("serif") >= 0) - fontFamily = css_ff_serif; - - //css_ff_inherit, - //css_ff_serif, - //css_ff_sans_serif, - //css_ff_cursive, - //css_ff_fantasy, - //css_ff_monospace, - bool italic = (slant!=FC_SLANT_ROMAN); - - lString8 face((const char*)family); - lString16 style16((const char*)style); - style16.lowercase(); - if (style16.pos("condensed") >= 0) - face << " Condensed"; - else if (style16.pos("extralight") >= 0) - face << " Extra Light"; - - LVFontDef def( - lString8((const char*)s), - -1, // height==-1 for scalable fonts - weight, - italic, - fontFamily, - face, - index - ); - - CRLog::debug("FONTCONFIG: Font family:%s style:%s weight:%d slant:%d spacing:%d file:%s", family, style, weight, slant, spacing, s); - if ( _cache.findDuplicate( &def ) ) { - CRLog::debug("is duplicate, skipping"); - continue; - } - _cache.update( &def, LVFontRef(NULL) ); - - if ( scalable && !def.getItalic() ) { - LVFontDef newDef( def ); - newDef.setItalic(2); // can italicize - if ( !_cache.findDuplicate( &newDef ) ) - _cache.update( &newDef, LVFontRef(NULL) ); - } - - facesFound++; - - - } - - FcFontSetDestroy(fontset); - CRLog::info("FONTCONFIG: %d fonts registered", facesFound); - - const char * fallback_faces [] = { - "Arial Unicode MS", - "AR PL ShanHeiSun Uni", - "Liberation Sans", - "Roboto", - "DejaVu Sans", - "Noto Sans", - "Droid Sans", - NULL - }; - - for ( int i=0; fallback_faces[i]; i++ ) - if ( SetFallbackFontFace(lString8(fallback_faces[i])) ) { - CRLog::info("Fallback font %s is found", fallback_faces[i]); - break; - } else { - CRLog::trace("Fallback font %s is not found", fallback_faces[i]); - } - - return facesFound > 0; - } - #elif (CR3_OSX == 1) - - int facesFound = 0; - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Bold.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Italic.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Bold Italic.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Unicode.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow Bold.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow Italic.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Bold.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Italic.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Bold Italic.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Bold.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Italic.ttf")); - facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Bold Italic.ttf")); - - return facesFound > 0; - - #else - return false; - #endif - } - - virtual ~LVFreeTypeFontManager() - { - FONT_MAN_GUARD - _globalCache.clear(); - _cache.clear(); - if ( _library ) - FT_Done_FreeType( _library ); - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - fclose(_log); - } - #endif - } - - LVFreeTypeFontManager() - : _library(NULL), _globalCache(GLYPH_CACHE_SIZE) - { - FONT_MAN_GUARD - int error = FT_Init_FreeType( &_library ); - if ( error ) { - // error - CRLog::error("Error while initializing freetype library"); - } - #if (DEBUG_FONT_MAN==1) - _log = fopen(DEBUG_FONT_MAN_LOG_FILE, "at"); - if ( _log ) { - fprintf(_log, "=========================== LOGGING STARTED ===================\n"); - } - #endif - _requiredChars = L"azAZ09?";//\x0410\x042F\x0430\x044F"; - } - - virtual void gc() // garbage collector - { - FONT_MAN_GUARD - _cache.gc(); - } - - lString8 makeFontFileName( lString8 name ) - { - lString8 filename = _path; - if (!filename.empty() && _path[_path.length()-1]!=PATH_SEPARATOR_CHAR) - filename << PATH_SEPARATOR_CHAR; - filename << name; - return filename; - } - - /// returns available typefaces - virtual void getFaceList( lString16Collection & list ) - { - FONT_MAN_GUARD - _cache.getFaceList( list ); - } - /// returns registered font files - virtual void getFontFileNameList( lString16Collection & list ) - { - FONT_MAN_GUARD - _cache.getFontFileNameList(list); - } -bool setalias(lString8 alias,lString8 facename,int id,bool italic, bool bold) -{ - FONT_MAN_GUARD - lString8 fontname=lString8("\0"); - LVFontDef def( - fontname, - 10, - 400, - true, - css_ff_inherit, - facename, - -1, - id - ); - LVFontCacheItem * item = _cache.find( &def); - LVFontDef def1( - fontname, - 10, - 400, - false, - css_ff_inherit, - alias, - -1, - id - ); - if (!item->getDef()->getName().empty()) { - _cache.removefont(&def1); - /*def.setTypeFace(alias); - def.setName(item->getDef()->getName()); - def.setItalic(1); - LVFontDef newDef(*item->getDef()); - newDef.setTypeFace(alias); - LVFontRef ref = item->getFont(); - _cache.update(&newDef, ref);*/ - int index = 0; - - FT_Face face = NULL; - - // for all faces in file - for ( ;; index++ ) { - int error = FT_New_Face( _library, item->getDef()->getName().c_str(), index, &face ); /* create face object */ - if ( error ) { - if (index == 0) { - CRLog::error("FT_New_Face returned error %d", error); - } - break; - } - //bool scal = FT_IS_SCALABLE( face ); - //bool charset = checkCharSet( face ); - - int num_faces = face->num_faces; - - css_font_family_t fontFamily = css_ff_sans_serif; - if ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) - fontFamily = css_ff_monospace; - lString8 familyName(!facename.empty() ? facename : ::familyName(face)); - if ( familyName=="Times" || familyName=="Times New Roman" ) - fontFamily = css_ff_serif; - - bool boldFlag = !facename.empty() ? bold : (face->style_flags & FT_STYLE_FLAG_BOLD) != 0; - bool italicFlag = !facename.empty() ? italic : (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0; - - LVFontDef def2( - item->getDef()->getName(), - -1, // height==-1 for scalable fonts - boldFlag ? 700 : 400, - italicFlag, - fontFamily, - alias, - index, - id - ); - - if ( face ) { - FT_Done_Face( face ); - face = NULL; - } - - if ( _cache.findDuplicate( &def2 ) ) { - CRLog::trace("font definition is duplicate"); - return false; - } - _cache.update( &def2, LVFontRef(NULL) ); - if (!def.getItalic()) { - LVFontDef newDef( def2 ); - newDef.setItalic(2); // can italicize - if ( !_cache.findDuplicate( &newDef ) ) - _cache.update( &newDef, LVFontRef(NULL) ); - } - if ( index>=num_faces-1 ) - break; - } - return true; - } - else - { - return false; - } -} - virtual LVFontRef GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface, int documentId) - { - FONT_MAN_GUARD - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - fprintf(_log, "GetFont(size=%d, weight=%d, italic=%d, family=%d, typeface='%s')\n", - size, weight, italic?1:0, (int)family, typeface.c_str() ); - } - #endif - lString8 fontname; - LVFontDef def( - fontname, - size, - weight, - italic, - family, - typeface, - -1, - documentId - ); - #if (DEBUG_FONT_MAN==1) - if ( _log ) - fprintf( _log, "GetFont: %s %d %s %s\n", - typeface.c_str(), - size, - weight>400?"bold":"", - italic?"italic":"" ); - #endif - LVFontCacheItem * item = _cache.find( &def ); - #if (DEBUG_FONT_MAN==1) - if ( item && _log ) { //_log && - fprintf(_log, " found item: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s, weightDelta=%d) FontRef=%d\n", - item->getDef()->getName().c_str(), item->getDef()->getIndex(), item->getDef()->getSize(), item->getDef()->getWeight(), item->getDef()->getItalic()?1:0, - (int)item->getDef()->getFamily(), item->getDef()->getTypeFace().c_str(), - weight - item->getDef()->getWeight(), item->getFont().isNull()?0:item->getFont()->getHeight() - ); - } - #endif - bool italicize = false; - - LVFontDef newDef(*item->getDef()); - - if (!item->getFont().isNull()) - { - int deltaWeight = weight - item->getDef()->getWeight(); - if ( deltaWeight >= 200 ) { - // embolden - CRLog::debug("font: apply Embolding to increase weight from %d to %d", newDef.getWeight(), newDef.getWeight() + 200 ); - newDef.setWeight( newDef.getWeight() + 200 ); - LVFontRef ref = LVFontRef( new LVFontBoldTransform( item->getFont(), &_globalCache ) ); - _cache.update( &newDef, ref ); - return ref; - } else { - //fprintf(_log, " : fount existing\n"); - return item->getFont(); - } - } - lString8 fname = item->getDef()->getName(); - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - int index = item->getDef()->getIndex(); - fprintf(_log, " no instance: adding new one for filename=%s, index = %d\n", fname.c_str(), index ); - } - #endif - LVFreeTypeFace * font = new LVFreeTypeFace(_lock, _library, &_globalCache); - lString8 pathname = makeFontFileName( fname ); - //def.setName( fname ); - //def.setIndex( index ); - - //if ( fname.empty() || pathname.empty() ) { - // pathname = lString8("arial.ttf"); - //} - - if ( !item->getDef()->isRealItalic() && italic ) { - //CRLog::debug("font: fake italic"); - newDef.setItalic(true); - italicize = true; - } - - //printf("going to load font file %s\n", fname.c_str()); - bool loaded = false; - if (item->getDef()->getBuf().isNull()) - loaded = font->loadFromFile( pathname.c_str(), item->getDef()->getIndex(), size, family, isBitmapModeForSize(size), italicize ); - else - loaded = font->loadFromBuffer(item->getDef()->getBuf(), item->getDef()->getIndex(), size, family, isBitmapModeForSize(size), italicize ); - if (loaded) { - //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), - // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); - LVFontRef ref(font); - font->setKerning( getKerning() ); - font->setLigatures( getLigatures() ); - font->setFaceName( item->getDef()->getTypeFace() ); - newDef.setSize( size ); - //item->setFont( ref ); - //_cache.update( def, ref ); - _cache.update( &newDef, ref ); - int deltaWeight = weight - newDef.getWeight(); - if ( 1 && deltaWeight >= 200 ) { - // embolden - CRLog::debug("font: apply Embolding to increase weight from %d to %d", newDef.getWeight(), newDef.getWeight() + 200 ); - newDef.setWeight( newDef.getWeight() + 200 ); - ref = LVFontRef( new LVFontBoldTransform( ref, &_globalCache ) ); - _cache.update( &newDef, ref ); - } -// int rsz = ref->getSize(); -// if ( rsz!=size ) { -// size++; -// } - //delete def; - return ref; - } - else - { - //printf(" not found!\n"); - } - //delete def; - delete font; - return LVFontRef(NULL); - } - - bool checkCharSet( FT_Face face ) - { - // TODO: check existance of required characters (e.g. cyrillic) - if (face==NULL) - return false; // invalid face - for ( int i=0; i<_requiredChars.length(); i++ ) { - lChar16 ch = _requiredChars[i]; - FT_UInt ch_glyph_index = FT_Get_Char_Index( face, ch ); - if ( ch_glyph_index==0 ) { - CRLog::debug("Required char not found in font: %04x", ch); - return false; // no required char!!! - } - } - return true; - } - - virtual bool checkFontLangCompat(const lString8& typeface, const lString8& langCode) - { - LVFontRef fntRef = GetFont(10, 400, false, css_ff_inherit, typeface, -1); - if (!fntRef.isNull()) - return fntRef->checkFontLangCompat(langCode); - else - CRLog::debug("checkFontLangCompat(): typeface not found: %s", typeface.c_str()); - return true; - } - - /* - bool isMonoSpaced( FT_Face face ) - { - // TODO: check existance of required characters (e.g. cyrillic) - if (face==NULL) - return false; // invalid face - lChar16 ch1 = 'i'; - FT_UInt ch_glyph_index1 = FT_Get_Char_Index( face, ch1 ); - if ( ch_glyph_index1==0 ) - return false; // no required char!!! - int w1, w2; - int error1 = FT_Load_Glyph( face, // handle to face object - ch_glyph_index1, // glyph index - FT_LOAD_DEFAULT ); // load flags, see below - if ( error1 ) - w1 = 0; - else - w1 = (face->glyph->metrics.horiAdvance >> 6); - int error2 = FT_Load_Glyph( face, // handle to face object - ch_glyph_index2, // glyph index - FT_LOAD_DEFAULT ); // load flags, see below - if ( error2 ) - w2 = 0; - else - w2 = (face->glyph->metrics.horiAdvance >> 6); - - lChar16 ch2 = 'W'; - FT_UInt ch_glyph_index2 = FT_Get_Char_Index( face, ch2 ); - if ( ch_glyph_index2==0 ) - return false; // no required char!!! - return w1==w2; - } - */ - - /// registers document font - virtual bool RegisterDocumentFont(int documentId, LVContainerRef container, lString16 name, lString8 faceName, bool bold, bool italic) { - FONT_MAN_GUARD - lString8 name8 = UnicodeToUtf8(name); - CRLog::debug("RegisterDocumentFont(documentId=%d, path=%s)", documentId, name8.c_str()); - if (_cache.findDocumentFontDuplicate(documentId, name8)) { - return false; - } - LVStreamRef stream = container->OpenStream(name.c_str(), LVOM_READ); - if (stream.isNull()) - return false; - lUInt32 size = (lUInt32)stream->GetSize(); - if (size < 100 || size > 5000000) - return false; - LVByteArrayRef buf(new LVByteArray(size, 0)); - lvsize_t bytesRead = 0; - if (stream->Read(buf->get(), size, &bytesRead) != LVERR_OK || bytesRead != size) - return false; - bool res = false; - - int index = 0; - - FT_Face face = NULL; - - // for all faces in file - for ( ;; index++ ) { - int error = FT_New_Memory_Face( _library, buf->get(), buf->length(), index, &face ); /* create face object */ - if ( error ) { - if (index == 0) { - CRLog::error("FT_New_Memory_Face returned error %d", error); - } - break; - } -// bool scal = FT_IS_SCALABLE( face ); -// bool charset = checkCharSet( face ); -// //bool monospaced = isMonoSpaced( face ); -// if ( !scal || !charset ) { -// //#if (DEBUG_FONT_MAN==1) -// // if ( _log ) { -// CRLog::debug(" won't register font %s: %s", -// name.c_str(), !charset?"no mandatory characters in charset" : "font is not scalable" -// ); -// // } -// //#endif -// if ( face ) { -// FT_Done_Face( face ); -// face = NULL; -// } -// break; -// } - int num_faces = face->num_faces; - - css_font_family_t fontFamily = css_ff_sans_serif; - if ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) - fontFamily = css_ff_monospace; - lString8 familyName(!faceName.empty() ? faceName : ::familyName(face)); - if ( familyName=="Times" || familyName=="Times New Roman" ) - fontFamily = css_ff_serif; - - bool boldFlag = !faceName.empty() ? bold : (face->style_flags & FT_STYLE_FLAG_BOLD) != 0; - bool italicFlag = !faceName.empty() ? italic : (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0; - - LVFontDef def( - name8, - -1, // height==-1 for scalable fonts - boldFlag ? 700 : 400, - italicFlag, - fontFamily, - familyName, - index, - documentId, - buf - ); - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n", - def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str() - ); - } - #endif - if ( face ) { - FT_Done_Face( face ); - face = NULL; - } - - if ( _cache.findDuplicate( &def ) ) { - CRLog::trace("font definition is duplicate"); - return false; - } - _cache.update( &def, LVFontRef(NULL) ); - if (!def.getItalic()) { - LVFontDef newDef( def ); - newDef.setItalic(2); // can italicize - if ( !_cache.findDuplicate( &newDef ) ) - _cache.update( &newDef, LVFontRef(NULL) ); - } - res = true; - - if ( index>=num_faces-1 ) - break; - } - - return res; - } - /// unregisters all document fonts - virtual void UnregisterDocumentFonts(int documentId) { - _cache.removeDocumentFonts(documentId); - } - - virtual bool RegisterExternalFont( lString16 name, lString8 family_name, bool bold, bool italic) { - if (name.startsWithNoCase(lString16("res://"))) - name = name.substr(6); - else if (name.startsWithNoCase(lString16("file://"))) - name = name.substr(7); - lString8 fname = UnicodeToUtf8(name); - - bool res = false; - - int index = 0; - - FT_Face face = NULL; - - // for all faces in file - for ( ;; index++ ) { - int error = FT_New_Face( _library, fname.c_str(), index, &face ); /* create face object */ - if ( error ) { - if (index == 0) { - CRLog::error("FT_New_Face returned error %d", error); - } - break; - } - bool scal = FT_IS_SCALABLE( face ); - bool charset = checkCharSet( face ); - //bool monospaced = isMonoSpaced( face ); - if ( !scal || !charset ) { - //#if (DEBUG_FONT_MAN==1) - // if ( _log ) { - CRLog::debug(" won't register font %s: %s", - name.c_str(), !charset?"no mandatory characters in charset" : "font is not scalable" - ); - // } - //#endif - if ( face ) { - FT_Done_Face( face ); - face = NULL; - } - break; - } - int num_faces = face->num_faces; - - css_font_family_t fontFamily = css_ff_sans_serif; - if ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) - fontFamily = css_ff_monospace; - lString8 familyName( ::familyName(face) ); - if ( familyName=="Times" || familyName=="Times New Roman" ) - fontFamily = css_ff_serif; - - LVFontDef def( - fname, - -1, // height==-1 for scalable fonts - bold?700:400, - italic?true:false, - fontFamily, - family_name, - index - ); - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n", - def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str() - ); - } - #endif - if ( _cache.findDuplicate( &def ) ) { - CRLog::trace("font definition is duplicate"); - return false; - } - _cache.update( &def, LVFontRef(NULL) ); - if ( scal && !def.getItalic() ) { - LVFontDef newDef( def ); - newDef.setItalic(2); // can italicize - if ( !_cache.findDuplicate( &newDef ) ) - _cache.update( &newDef, LVFontRef(NULL) ); - } - res = true; - - if ( face ) { - FT_Done_Face( face ); - face = NULL; - } - - if ( index>=num_faces-1 ) - break; - } - - return res; - } - - virtual bool RegisterFont( lString8 name ) - { - FONT_MAN_GUARD -#ifdef LOAD_TTF_FONTS_ONLY - if ( name.pos( cs8(".ttf") ) < 0 && name.pos( cs8(".TTF") ) < 0 ) - return false; // load ttf fonts only -#endif - //CRLog::trace("RegisterFont(%s)", name.c_str()); - lString8 fname = makeFontFileName( name ); - //CRLog::trace("font file name : %s", fname.c_str()); - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - fprintf(_log, "RegisterFont( %s ) path=%s\n", - name.c_str(), fname.c_str() - ); - } - #endif - bool res = false; - - int index = 0; - - FT_Face face = NULL; - - // for all faces in file - for ( ;; index++ ) { - int error = FT_New_Face( _library, fname.c_str(), index, &face ); /* create face object */ - if ( error ) { - if (index == 0) { - CRLog::error("FT_New_Face returned error %d", error); - } - break; - } - bool scal = FT_IS_SCALABLE( face ) != 0; - bool charset = checkCharSet( face ); - //bool monospaced = isMonoSpaced( face ); - if ( !scal || !charset ) { - //#if (DEBUG_FONT_MAN==1) - // if ( _log ) { - CRLog::debug(" won't register font %s: %s", - name.c_str(), !charset?"no mandatory characters in charset" : "font is not scalable" - ); - // } - //#endif - if ( face ) { - FT_Done_Face( face ); - face = NULL; - } - break; - } - int num_faces = face->num_faces; - - css_font_family_t fontFamily = css_ff_sans_serif; - if ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) - fontFamily = css_ff_monospace; - lString8 familyName( ::familyName(face) ); - if ( familyName=="Times" || familyName=="Times New Roman" ) - fontFamily = css_ff_serif; - - LVFontDef def( - name, - -1, // height==-1 for scalable fonts - ( face->style_flags & FT_STYLE_FLAG_BOLD ) ? 700 : 400, - ( face->style_flags & FT_STYLE_FLAG_ITALIC ) ? true : false, - fontFamily, - familyName, - index - ); - #if (DEBUG_FONT_MAN==1) - if ( _log ) { - fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n", - def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str() - ); - } - #endif - - if ( face ) { - FT_Done_Face( face ); - face = NULL; - } - - if ( _cache.findDuplicate( &def ) ) { - CRLog::trace("font definition is duplicate"); - return false; - } - _cache.update( &def, LVFontRef(NULL) ); - if ( scal && !def.getItalic() ) { - LVFontDef newDef( def ); - newDef.setItalic(2); // can italicize - if ( !_cache.findDuplicate( &newDef ) ) - _cache.update( &newDef, LVFontRef(NULL) ); - } - res = true; - - if ( index>=num_faces-1 ) - break; - } - - return res; - } - - virtual bool Init( lString8 path ) - { - _path = path; - initSystemFonts(); - return (_library != NULL); - } -}; -#endif - -#if (USE_BITMAP_FONTS==1) -class LVBitmapFontManager : public LVFontManager -{ -private: - lString8 _path; - LVFontCache _cache; - //FILE * _log; -public: - virtual int GetFontCount() - { - return _cache.length(); - } - virtual ~LVBitmapFontManager() - { - //if (_log) - // fclose(_log); - } - LVBitmapFontManager() - { - //_log = fopen( "fonts.log", "wt" ); - } - virtual void gc() // garbage collector - { - _cache.gc(); - } - lString8 makeFontFileName( lString8 name ) - { - lString8 filename = _path; - if (!filename.empty() && _path[filename.length()-1]!=PATH_SEPARATOR_CHAR) - filename << PATH_SEPARATOR_CHAR; - filename << name; - return filename; - } - virtual LVFontRef GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface, int documentId) - { - LVFontDef * def = new LVFontDef( - lString8::empty_str, - size, - weight, - italic, - family, - typeface, - documentId - ); - //fprintf( _log, "GetFont: %s %d %s %s\n", - // typeface.c_str(), - // size, - // weight>400?"bold":"", - // italic?"italic":"" ); - LVFontCacheItem * item = _cache.find( def ); - delete def; - if (!item->getFont().isNull()) - { - //fprintf(_log, " : fount existing\n"); - return item->getFont(); - } - LBitmapFont * font = new LBitmapFont; - lString8 fname = makeFontFileName( item->getDef()->getName() ); - //printf("going to load font file %s\n", fname.c_str()); - if (font->LoadFromFile( fname.c_str() ) ) - { - //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), - // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); - LVFontRef ref(font); - item->setFont( ref ); - return ref; - } - else - { - //printf(" not found!\n"); - } - delete font; - return LVFontRef(NULL); - } - virtual bool RegisterFont( lString8 name ) - { - lString8 fname = makeFontFileName( name ); - //printf("going to load font file %s\n", fname.c_str()); - LVStreamRef stream = LVOpenFileStream( fname.c_str(), LVOM_READ ); - if (!stream) - { - //printf(" not found!\n"); - return false; - } - tag_lvfont_header hdr; - bool res = false; - lvsize_t bytes_read = 0; - if ( stream->Read( &hdr, sizeof(hdr), &bytes_read ) == LVERR_OK && bytes_read == sizeof(hdr) ) - { - LVFontDef def( - name, - hdr.fontHeight, - hdr.flgBold?700:400, - hdr.flgItalic?true:false, - (css_font_family_t)hdr.fontFamily, - lString8(hdr.fontName) - ); - //fprintf( _log, "Register: %s %s %d %s %s\n", - // name.c_str(), hdr.fontName, - // hdr.fontHeight, - // hdr.flgBold?"bold":"", - // hdr.flgItalic?"italic":"" ); - _cache.update( &def, LVFontRef(NULL) ); - res = true; - } - return res; - } - /// returns registered font files - virtual void getFontFileNameList( lString16Collection & list ) - { - FONT_MAN_GUARD - _cache.getFontFileNameList(list); - } - virtual bool Init( lString8 path ) - { - _path = path; - return true; - } -}; -#endif - - -#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE!=1 - -// prototype -int CALLBACK LVWin32FontEnumFontFamExProc( - const LOGFONTA *lpelfe, // logical-font data - const TEXTMETRICA *lpntme, // physical-font data - //ENUMLOGFONTEX *lpelfe, // logical-font data - //NEWTEXTMETRICEX *lpntme, // physical-font data - DWORD FontType, // type of font - LPARAM lParam // application-defined data -); - -class LVWin32FontManager : public LVFontManager -{ -private: - lString8 _path; - LVFontCache _cache; - //FILE * _log; -public: - virtual int GetFontCount() - { - return _cache.length(); - } - virtual ~LVWin32FontManager() - { - //if (_log) - // fclose(_log); - } - LVWin32FontManager() - { - //_log = fopen( "fonts.log", "wt" ); - } - virtual void gc() // garbage collector - { - _cache.gc(); - } - virtual LVFontRef GetFont(int size, int weight, bool bitalic, css_font_family_t family, lString8 typeface ) - { - int italic = bitalic?1:0; - if (size < 8) - size = 8; - if (size > 255) - size = 255; - - LVFontDef def( - lString8::empty_str, - size, - weight, - italic, - family, - typeface - ); - - //fprintf( _log, "GetFont: %s %d %s %s\n", - // typeface.c_str(), - // size, - // weight>400?"bold":"", - // italic?"italic":"" ); - LVFontCacheItem * item = _cache.find( &def ); - if (!item->getFont().isNull()) - { - //fprintf(_log, " : fount existing\n"); - return item->getFont(); - } - -#if COLOR_BACKBUFFER==0 - LVWin32Font * font = new LVWin32Font; -#else - LVWin32DrawFont * font = new LVWin32DrawFont; -#endif - - LVFontDef * fdef = item->getDef(); - LVFontDef def2( fdef->getName(), size, weight, italic, - fdef->getFamily(), fdef->getTypeFace() ); - - if ( font->Create(size, weight, italic?true:false, fdef->getFamily(), fdef->getTypeFace()) ) - { - //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), - // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); - LVFontRef ref(font); - _cache.addInstance( &def2, ref ); - return ref; - } - delete font; - return LVFontRef(NULL); - } - - virtual bool RegisterFont( const LOGFONTA * lf ) - { - lString8 face(lf->lfFaceName); - css_font_family_t ff; - switch (lf->lfPitchAndFamily & 0x70) - { - case FF_ROMAN: - ff = css_ff_serif; - break; - case FF_SWISS: - ff = css_ff_sans_serif; - break; - case FF_SCRIPT: - ff = css_ff_cursive; - break; - case FF_DECORATIVE: - ff = css_ff_fantasy; - break; - case FF_MODERN: - ff = css_ff_monospace; - break; - default: - ff = css_ff_sans_serif; - break; - } - LVFontDef def( - face, - -1, //lf->lfHeight>0 ? lf->lfHeight : -lf->lfHeight, - -1, //lf->lfWeight, - -1, //lf->lfItalic!=0, - ff, - face - ); - _cache.update( &def, LVFontRef(NULL) ); - return true; - } - virtual bool RegisterFont( lString8 name ) - { - return false; - } - virtual bool Init( lString8 path ) - { - LVColorDrawBuf drawbuf(1,1); - LOGFONTA lf; - memset(&lf, 0, sizeof(lf)); - lf.lfCharSet = ANSI_CHARSET; - int res = - EnumFontFamiliesExA( - drawbuf.GetDC(), // handle to DC - &lf, // font information - LVWin32FontEnumFontFamExProc, // callback function (FONTENUMPROC) - (LPARAM)this, // additional data - 0 // not used; must be 0 - ); - - return res!=0; - } - - virtual void getFaceList( lString16Collection & list ) - { - _cache.getFaceList(list); - } - /// returns registered font files - virtual void getFontFileNameList( lString16Collection & list ) - { - FONT_MAN_GUARD - _cache.getFontFileNameList(list); - } -}; - -// definition -int CALLBACK LVWin32FontEnumFontFamExProc( - const LOGFONTA *lf, // logical-font data - const TEXTMETRICA *lpntme, // physical-font data - //ENUMLOGFONTEX *lpelfe, // logical-font data - //NEWTEXTMETRICEX *lpntme, // physical-font data - DWORD FontType, // type of font - LPARAM lParam // application-defined data -) -{ - // - if (FontType == TRUETYPE_FONTTYPE) - { - LVWin32FontManager * fontman = (LVWin32FontManager *)lParam; - LVWin32Font fnt; - //if (strcmp(lf->lfFaceName, "Courier New")) - // return 1; - if ( fnt.Create( *lf ) ) - { - // - static lChar16 chars[] = {0, 0xBF, 0xE9, 0x106, 0x410, 0x44F, 0 }; - for (int i=0; chars[i]; i++) - { - LVFont::glyph_info_t glyph; - if (!fnt.getGlyphInfo( chars[i], &glyph, L' ' )) //def_char - return 1; - } - fontman->RegisterFont( lf ); //&lpelfe->elfLogFont - } - } - return 1; -} -#endif - -#if (USE_BITMAP_FONTS==1) - -LVFontRef LoadFontFromFile( const char * fname ) -{ - LVFontRef ref; - LBitmapFont * font = new LBitmapFont; - if (font->LoadFromFile( fname ) ) - { - ref = font; - } - else - { - delete font; - } - return ref; -} - -#endif - -bool InitFontManager( lString8 path ) -{ - if ( fontMan ) { - return true; - //delete fontMan; - } -#if (USE_WIN32_FONTS==1) - fontMan = new LVWin32FontManager; -#elif (USE_FREETYPE==1) - fontMan = new LVFreeTypeFontManager; -#else - fontMan = new LVBitmapFontManager; -#endif - return fontMan->Init( path ); -} - -bool ShutdownFontManager() -{ - if ( fontMan ) - { - delete fontMan; - fontMan = NULL; +bool ShutdownFontManager() { + if (fontMan) { + delete fontMan; + fontMan = NULL; return true; } return false; } - -int LVFontDef::CalcDuplicateMatch( const LVFontDef & def ) const -{ - if (def._documentId != -1 && _documentId != def._documentId) - return false; - bool size_match = (_size==-1 || def._size==-1) ? true - : (def._size == _size); - bool weight_match = (_weight==-1 || def._weight==-1) ? true - : (def._weight == _weight); - bool italic_match = (_italic == def._italic || _italic==-1 || def._italic==-1); - bool family_match = (_family==css_ff_inherit || def._family==css_ff_inherit || def._family == _family); - bool typeface_match = (_typeface == def._typeface); - return size_match && weight_match && italic_match && family_match && typeface_match; -} - -int LVFontDef::CalcMatch( const LVFontDef & def ) const -{ - if (_documentId != -1 && _documentId != def._documentId) - return 0; - int size_match = (_size==-1 || def._size==-1) ? 256 - : (def._size>_size ? _size*256/def._size : def._size*256/_size ); - int weight_diff = def._weight - _weight; - if ( weight_diff<0 ) - weight_diff = -weight_diff; - if ( weight_diff > 800 ) - weight_diff = 800; - int weight_match = (_weight==-1 || def._weight==-1) ? 256 - : ( 256 - weight_diff * 256 / 800 ); - int italic_match = (_italic == def._italic || _italic==-1 || def._italic==-1) ? 256 : 0; - if ( (_italic==2 || def._italic==2) && _italic>0 && def._italic>0 ) - italic_match = 128; - int family_match = (_family==css_ff_inherit || def._family==css_ff_inherit || def._family == _family) - ? 256 - : ( (_family==css_ff_monospace)==(def._family==css_ff_monospace) ? 64 : 0 ); - int typeface_match = (_typeface == def._typeface) ? 256 : 0; - return - + (size_match * 100) - + (weight_match * 5) - + (italic_match * 5) - + (family_match * 100) - + (typeface_match * 1000); -} - -int LVFontDef::CalcFallbackMatch( lString8 face, int size ) const -{ - if (_typeface != face) { - //CRLog::trace("'%s'' != '%s'", face.c_str(), _typeface.c_str()); - return 0; - } - int size_match = (_size==-1 || size==-1 || _size==size) ? 256 : 0; - int weight_match = (_weight==-1) ? 256 : ( 256 - _weight * 256 / 800 ); - int italic_match = _italic == 0 ? 256 : 0; - return - + (size_match * 100) - + (weight_match * 5) - + (italic_match * 5); -} - - - - - - - - -void LVBaseFont::DrawTextString( LVDrawBuf * buf, int x, int y, - const lChar16 * text, int len, - lChar16 def_char, lUInt32 * palette, bool addHyphen, lUInt32 , int ) -{ - //static lUInt8 glyph_buf[16384]; - //LVFont::glyph_info_t info; - int baseline = getBaseline(); - while (len>=(addHyphen?0:1)) - { - if (len<=1 || *text != UNICODE_SOFT_HYPHEN_CODE) - { - lChar16 ch = ((len==0)?UNICODE_SOFT_HYPHEN_CODE:*text); - - LVFontGlyphCacheItem * item = getGlyph(ch, def_char); - int w = 0; - if ( item ) { - // avoid soft hyphens inside text string - w = item->advance; - if ( item->bmp_width && item->bmp_height ) { - buf->Draw( x + item->origin_x, - y + baseline - item->origin_y, - item->bmp, - item->bmp_width, - item->bmp_height, - palette); - } - } - x += w; // + letter_spacing; - -// if ( !getGlyphInfo( ch, &info, def_char ) ) -// { -// ch = def_char; -// if ( !getGlyphInfo( ch, &info, def_char ) ) -// ch = 0; -// } -// if (ch && getGlyphImage( ch, glyph_buf, def_char )) -// { -// if (info.blackBoxX && info.blackBoxY) -// { -// buf->Draw( x + info.originX, -// y + baseline - info.originY, -// glyph_buf, -// info.blackBoxX, -// info.blackBoxY, -// palette); -// } -// x += info.width; -// } - } - else if (*text != UNICODE_SOFT_HYPHEN_CODE) - { - //len = len; - } - len--; - text++; - } -} - -#if (USE_BITMAP_FONTS==1) -bool LBitmapFont::getGlyphInfo( lUInt16 code, LVFont::glyph_info_t * glyph, lChar16 def_char=0 ) -{ - const lvfont_glyph_t * ptr = lvfontGetGlyph( m_font, code ); - if (!ptr) - return false; - glyph->blackBoxX = ptr->blackBoxX; - glyph->blackBoxY = ptr->blackBoxY; - glyph->originX = ptr->originX; - glyph->originY = ptr->originY; - glyph->width = ptr->width; - return true; -} - -lUInt16 LBitmapFont::measureText( - const lChar16 * text, int len, - lUInt16 * widths, - lUInt8 * flags, - int max_width, - lChar16 def_char, - int letter_spacing, - bool allow_hyphenation - ) -{ - return lvfontMeasureText( m_font, text, len, widths, flags, max_width, def_char ); -} - -lUInt32 LBitmapFont::getTextWidth( const lChar16 * text, int len ) -{ - // - static lUInt16 widths[MAX_LINE_CHARS+1]; - static lUInt8 flags[MAX_LINE_CHARS+1]; - if ( len>MAX_LINE_CHARS ) - len = MAX_LINE_CHARS; - if ( len<=0 ) - return 0; - lUInt16 res = measureText( - text, len, - widths, - flags, - 2048, // max_width, - L' ' // def_char - ); - if ( res>0 && resfontBaseline; -} -/// returns font height -int LBitmapFont::getHeight() const -{ - const lvfont_header_t * hdr = lvfontGetHeader( m_font ); - return hdr->fontHeight; -} -bool LBitmapFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0) -{ - const lvfont_glyph_t * ptr = lvfontGetGlyph( m_font, code ); - if (!ptr) - return false; - const hrle_decode_info_t * pDecodeTable = lvfontGetDecodeTable( m_font ); - int sz = ptr->blackBoxX*ptr->blackBoxY; - if (sz) - lvfontUnpackGlyph(ptr->glyph, pDecodeTable, buf, sz); - return true; -} -int LBitmapFont::LoadFromFile( const char * fname ) -{ - Clear(); - int res = (void*)lvfontOpen( fname, &m_font )!=NULL; - if (!res) - return 0; - lvfont_header_t * hdr = (lvfont_header_t*) m_font; - _typeface = lString8( hdr->fontName ); - _family = (css_font_family_t) hdr->fontFamily; - return 1; -} -#endif - -LVFontCacheItem * LVFontCache::findDuplicate( const LVFontDef * def ) -{ - for (int i=0; i<_registered_list.length(); i++) - { - if ( _registered_list[i]->_def.CalcDuplicateMatch( *def ) ) - return _registered_list[i]; - } - return NULL; -} - -LVFontCacheItem * LVFontCache::findDocumentFontDuplicate(int documentId, lString8 name) -{ - for (int i=0; i<_registered_list.length(); i++) { - if (_registered_list[i]->_def.getDocumentId() == documentId && _registered_list[i]->_def.getName() == name) - return _registered_list[i]; - } - return NULL; -} - -LVFontCacheItem * LVFontCache::findFallback( lString8 face, int size ) -{ - int best_index = -1; - int best_match = -1; - int best_instance_index = -1; - int best_instance_match = -1; - int i; - for (i=0; i<_instance_list.length(); i++) - { - int match = _instance_list[i]->_def.CalcFallbackMatch( face, size ); - if (match > best_instance_match) - { - best_instance_match = match; - best_instance_index = i; - } - } - for (i=0; i<_registered_list.length(); i++) - { - int match = _registered_list[i]->_def.CalcFallbackMatch( face, size ); - if (match > best_match) - { - best_match = match; - best_index = i; - } - } - if (best_index<=0) - return NULL; - if (best_instance_match >= best_match) - return _instance_list[best_instance_index]; - return _registered_list[best_index]; -} - -LVFontCacheItem * LVFontCache::find( const LVFontDef * fntdef ) -{ - int best_index = -1; - int best_match = -1; - int best_instance_index = -1; - int best_instance_match = -1; - int i; - LVFontDef def(*fntdef); - lString8Collection list; - splitPropertyValueList( fntdef->getTypeFace().c_str(), list ); - for (int nindex=0; nindex==0 || nindex_def.CalcMatch( def ); - if (match > best_instance_match) - { - best_instance_match = match; - best_instance_index = i; - } - } - for (i=0; i<_registered_list.length(); i++) - { - int match = _registered_list[i]->_def.CalcMatch( def ); - if (match > best_match) - { - best_match = match; - best_index = i; - } - } - } - if (best_index<0) - return NULL; - if (best_instance_match >= best_match) - return _instance_list[best_instance_index]; - return _registered_list[best_index]; -} - -void LVFontCache::addInstance( const LVFontDef * def, LVFontRef ref ) -{ - if ( ref.isNull() ) - printf("Adding null font instance!"); - LVFontCacheItem * item = new LVFontCacheItem(*def); - item->_fnt = ref; - _instance_list.add( item ); -} - -void LVFontCache::removefont(const LVFontDef * def) -{ - int i; - for (i=0; i<_instance_list.length(); i++) - { - if ( _instance_list[i]->_def.getTypeFace() == def->getTypeFace() ) - { - _instance_list.remove(i); - } - - } - for (i=0; i<_registered_list.length(); i++) - { - if ( _registered_list[i]->_def.getTypeFace() == def->getTypeFace() ) - { - _registered_list.remove(i); - } - } - -} -void LVFontCache::update( const LVFontDef * def, LVFontRef ref ) -{ - int i; - if ( !ref.isNull() ) { - for (i=0; i<_instance_list.length(); i++) - { - if ( _instance_list[i]->_def == *def ) - { - if (ref.isNull()) - { - _instance_list.erase(i, 1); - } - else - { - _instance_list[i]->_fnt = ref; - } - return; - } - } - // add new - //LVFontCacheItem * item; - //item = new LVFontCacheItem(*def); - addInstance( def, ref ); - } else { - for (i=0; i<_registered_list.length(); i++) - { - if ( _registered_list[i]->_def == *def ) - { - return; - } - } - // add new - LVFontCacheItem * item; - item = new LVFontCacheItem(*def); - _registered_list.add( item ); - } -} - -void LVFontCache::removeDocumentFonts(int documentId) -{ - int i; - for (i=_instance_list.length()-1; i>=0; i--) { - if (_instance_list[i]->_def.getDocumentId() == documentId) - delete _instance_list.remove(i); - } - for (i=_registered_list.length()-1; i>=0; i--) { - if (_registered_list[i]->_def.getDocumentId() == documentId) - delete _registered_list.remove(i); - } -} - -// garbage collector -void LVFontCache::gc() -{ - int droppedCount = 0; - int usedCount = 0; - for (int i=_instance_list.length()-1; i>=0; i--) - { - if ( _instance_list[i]->_fnt.getRefCount()<=1 ) - { - if ( CRLog::isTraceEnabled() ) - CRLog::trace("dropping font instance %s[%d] by gc()", _instance_list[i]->getDef()->getTypeFace().c_str(), _instance_list[i]->getDef()->getSize() ); - _instance_list.erase(i,1); - droppedCount++; - } else { - usedCount++; - } - } - if ( CRLog::isDebugEnabled() ) - CRLog::debug("LVFontCache::gc() : %d fonts still used, %d fonts dropped", usedCount, droppedCount ); -} - -#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE!=1 -void LVBaseWin32Font::Clear() -{ - if (_hfont) - { - DeleteObject(_hfont); - _hfont = NULL; - _height = 0; - _baseline = 0; - } -} - -bool LVBaseWin32Font::Create( const LOGFONTA & lf ) -{ - if (!IsNull()) - Clear(); - memcpy( &_logfont, &lf, sizeof(LOGFONTA)); - _hfont = CreateFontIndirectA( &lf ); - if (!_hfont) - return false; - //memcpy( &_logfont, &lf, sizeof(LOGFONT) ); - // get text metrics - SelectObject( _drawbuf.GetDC(), _hfont ); - TEXTMETRICW tm; - GetTextMetricsW( _drawbuf.GetDC(), &tm ); - _logfont.lfHeight = tm.tmHeight; - _logfont.lfWeight = tm.tmWeight; - _logfont.lfItalic = tm.tmItalic; - _logfont.lfCharSet = tm.tmCharSet; - GetTextFaceA( _drawbuf.GetDC(), sizeof(_logfont.lfFaceName)-1, _logfont.lfFaceName ); - _height = tm.tmHeight; - _baseline = _height - tm.tmDescent; - return true; -} - -bool LVBaseWin32Font::Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ) -{ - if (!IsNull()) - Clear(); - // - LOGFONTA lf; - memset(&lf, 0, sizeof(LOGFONTA)); - lf.lfHeight = size; - lf.lfWeight = weight; - lf.lfItalic = italic?1:0; - lf.lfCharSet = DEFAULT_CHARSET; - lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - //lf.lfQuality = ANTIALIASED_QUALITY; //PROOF_QUALITY; -#ifdef USE_BITMAP_FONT - lf.lfQuality = NONANTIALIASED_QUALITY; //CLEARTYPE_QUALITY; //PROOF_QUALITY; -#else - lf.lfQuality = 5; //CLEARTYPE_QUALITY; //PROOF_QUALITY; -#endif - strcpy(lf.lfFaceName, typeface.c_str()); - _typeface = typeface; - _family = family; - switch (family) - { - case css_ff_serif: - lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN; - break; - case css_ff_sans_serif: - lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS; - break; - case css_ff_cursive: - lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SCRIPT; - break; - case css_ff_fantasy: - lf.lfPitchAndFamily = VARIABLE_PITCH | FF_DECORATIVE; - break; - case css_ff_monospace: - lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN; - break; - default: - lf.lfPitchAndFamily = VARIABLE_PITCH | FF_DONTCARE; - break; - } - _hfont = CreateFontIndirectA( &lf ); - if (!_hfont) - return false; - //memcpy( &_logfont, &lf, sizeof(LOGFONT) ); - // get text metrics - SelectObject( _drawbuf.GetDC(), _hfont ); - TEXTMETRICW tm; - GetTextMetricsW( _drawbuf.GetDC(), &tm ); - memset(&_logfont, 0, sizeof(LOGFONT)); - _logfont.lfHeight = tm.tmHeight; - _logfont.lfWeight = tm.tmWeight; - _logfont.lfItalic = tm.tmItalic; - _logfont.lfCharSet = tm.tmCharSet; - GetTextFaceA( _drawbuf.GetDC(), sizeof(_logfont.lfFaceName)-1, _logfont.lfFaceName ); - _height = tm.tmHeight; - _baseline = _height - tm.tmDescent; - return true; -} - - -/** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found -*/ -bool LVWin32DrawFont::getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char ) -{ - return false; -} - -/// returns char width -int LVWin32DrawFont::getCharWidth( lChar16 ch, lChar16 def_char ) -{ - if (_hfont==NULL) - return 0; - // measure character widths - GCP_RESULTSW gcpres; - memset( &gcpres, 0, sizeof(gcpres) ); - gcpres.lStructSize = sizeof(gcpres); - lChar16 str[2]; - str[0] = ch; - str[1] = 0; - int dx[2]; - gcpres.lpDx = dx; - gcpres.nMaxFit = 1; - gcpres.nGlyphs = 1; - - lUInt32 res = GetCharacterPlacementW( - _drawbuf.GetDC(), - str, - 1, - 100, - &gcpres, - GCP_MAXEXTENT); //|GCP_USEKERNING - - if (!res) - { - // ERROR - return 0; - } - - return dx[0]; -} - -lUInt32 LVWin32DrawFont::getTextWidth( const lChar16 * text, int len ) -{ - // - static lUInt16 widths[MAX_LINE_CHARS+1]; - static lUInt8 flags[MAX_LINE_CHARS+1]; - if ( len>MAX_LINE_CHARS ) - len = MAX_LINE_CHARS; - if ( len<=0 ) - return 0; - lUInt16 res = measureText( - text, len, - widths, - flags, - 2048, // max_width, - L' ' // def_char - ); - if ( res>0 && res dx( len+1, 0 ); - gcpres.lpDx = dx.ptr(); - gcpres.nMaxFit = len; - gcpres.nGlyphs = len; - - lUInt32 res = GetCharacterPlacementW( - _drawbuf.GetDC(), - pstr, - len, - max_width, - &gcpres, - GCP_MAXEXTENT); //|GCP_USEKERNING - if (!res) - { - // ERROR - widths[0] = 0; - flags[0] = 0; - return 1; - } - - if ( !_hyphen_width ) - _hyphen_width = getCharWidth( UNICODE_SOFT_HYPHEN_CODE ); - - lUInt16 wsum = 0; - lUInt16 nchars = 0; - lUInt16 gwidth = 0; - lUInt8 bflags; - int isSpace; - lChar16 ch; - int hwStart, hwEnd; - - assert(pstr[len]==0); - - for ( ; wsum < max_width && nchars < len && ncharsgi.width : 0; - - //try to add hyphen - for (hwStart=nchars-1; hwStart>0; hwStart--) - { - if (lvfontIsUnicodeSpace(text[hwStart])) - { - hwStart++; - break; - } - } - for (hwEnd=nchars; hwEndGetClipRect(&clip); - if (y > clip.bottom || y+_height < clip.top) - return; - if (buf->GetBitsPerPixel()<16) - { - // draw using backbuffer - SIZE sz; - if ( !GetTextExtentPoint32W(_drawbuf.GetDC(), - str.c_str(), str.length(), &sz) ) - return; - LVColorDrawBuf colorbuf( sz.cx, sz.cy ); - colorbuf.Clear(0xFFFFFF); - HDC dc = colorbuf.GetDC(); - SelectObject(dc, _hfont); - SetTextColor(dc, 0x000000); - SetBkMode(dc, TRANSPARENT); - //ETO_OPAQUE - if (ExtTextOutW( dc, 0, 0, - 0, //ETO_OPAQUE - NULL, - str.c_str(), str.length(), NULL )) - { - // COPY colorbuf to buf with converting - colorbuf.DrawTo( buf, x, y, 0, NULL ); - } - } - else - { - // draw directly on DC - //TODO - HDC dc = ((LVColorDrawBuf*)buf)->GetDC(); - HFONT oldfont = (HFONT)SelectObject( dc, _hfont ); - SetTextColor( dc, RevRGB(buf->GetTextColor()) ); - SetBkMode(dc, TRANSPARENT); - ExtTextOutW( dc, x, y, - 0, //ETO_OPAQUE - NULL, - str.c_str(), str.length(), NULL ); - SelectObject( dc, oldfont ); - } -} - -/** \brief get glyph image in 1 byte per pixel format - \param code is unicode character - \param buf is buffer [width*height] to place glyph data - \return true if glyph was found -*/ -bool LVWin32DrawFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) -{ - return false; -} - - - -int LVWin32Font::GetGlyphIndex( HDC hdc, wchar_t code ) -{ - wchar_t s[2]; - wchar_t g[2]; - s[0] = code; - s[1] = 0; - g[0] = 0; - GCP_RESULTSW gcp; - gcp.lStructSize = sizeof(GCP_RESULTSW); - gcp.lpOutString = NULL; - gcp.lpOrder = NULL; - gcp.lpDx = NULL; - gcp.lpCaretPos = NULL; - gcp.lpClass = NULL; - gcp.lpGlyphs = g; - gcp.nGlyphs = 2; - gcp.nMaxFit = 2; - - DWORD res = GetCharacterPlacementW( - hdc, s, 1, - 1000, - &gcp, - 0 - ); - if (!res) - return 0; - return g[0]; -} - - -glyph_t * LVWin32Font::GetGlyphRec( lChar16 ch ) -{ - glyph_t * p = _cache.get( ch ); - if (p->flgNotExists) - return NULL; - if (p->flgValid) - return p; - p->flgNotExists = true; - lUInt16 gi = GetGlyphIndex( _drawbuf.GetDC(), ch ); - if (gi==0 || gi==0xFFFF || (gi==_unknown_glyph_index && ch!=' ')) - { - // glyph not found - p->flgNotExists = true; - return NULL; - } - GLYPHMETRICS metrics; - p->glyph = NULL; - - MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; - lUInt32 res; - res = GetGlyphOutlineW( _drawbuf.GetDC(), ch, - GGO_METRICS, - &metrics, - 0, - NULL, - &identity ); - if (res==GDI_ERROR) - return false; - int gs = GetGlyphOutlineW( _drawbuf.GetDC(), ch, -#ifdef USE_BITMAP_FONT - GGO_BITMAP, //GGO_METRICS -#else - GGO_GRAY8_BITMAP, //GGO_METRICS -#endif - &metrics, - 0, - NULL, - &identity ); - if (gs>0x10000 || gs<0) - return false; - - p->gi.blackBoxX = metrics.gmBlackBoxX; - p->gi.blackBoxY = metrics.gmBlackBoxY; - p->gi.originX = (lInt8)metrics.gmptGlyphOrigin.x; - p->gi.originY = (lInt8)metrics.gmptGlyphOrigin.y; - p->gi.width = (lUInt8)metrics.gmCellIncX; - - if (p->gi.blackBoxX>0 && p->gi.blackBoxY>0) - { - p->glyph = new unsigned char[p->gi.blackBoxX * p->gi.blackBoxY]; - if (gs>0) - { - lUInt8 * glyph = new unsigned char[gs]; - res = GetGlyphOutlineW( _drawbuf.GetDC(), ch, -#ifdef USE_BITMAP_FONT - GGO_BITMAP, //GGO_METRICS -#else - GGO_GRAY8_BITMAP, //GGO_METRICS -#endif - &metrics, - gs, - glyph, - &identity ); - if (res==GDI_ERROR) - { - delete[] glyph; - return NULL; - } -#ifdef USE_BITMAP_FONT - int glyph_row_size = (p->gi.blackBoxX + 31) / 8 / 4 * 4; -#else - int glyph_row_size = (p->gi.blackBoxX + 3)/ 4 * 4; -#endif - lUInt8 * src = glyph; - lUInt8 * dst = p->glyph; - for (int y=0; ygi.blackBoxY; y++) - { - for (int x = 0; xgi.blackBoxX; x++) - { -#ifdef USE_BITMAP_FONT - lUInt8 b = (src[x>>3] & (0x80>>(x&7))) ? 0xFC : 0; -#else - lUInt8 b = src[x]; - if (b>=64) - b = 63; - b = (b<<2) & 0xFC; -#endif - dst[x] = b; - } - src += glyph_row_size; - dst += p->gi.blackBoxX; - } - delete[] glyph; - //*(dst-1) = 0xFF; - } - else - { - // empty glyph - for (int i=p->gi.blackBoxX * p->gi.blackBoxY-1; i>=0; i--) - p->glyph[i] = 0; - } - } - // found! - p->flgValid = true; - p->flgNotExists = false; - return p; -} - -/** \brief get glyph info - \param glyph is pointer to glyph_info_t struct to place retrieved info - \return true if glyh was found -*/ -bool LVWin32Font::getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char ) -{ - if (_hfont==NULL) - return false; - glyph_t * p = GetGlyphRec( code ); - if (!p) - return false; - *glyph = p->gi; - return true; -} - -lUInt32 LVWin32Font::getTextWidth( const lChar16 * text, int len ) -{ - // - static lUInt16 widths[MAX_LINE_CHARS+1]; - static lUInt8 flags[MAX_LINE_CHARS+1]; - if ( len>MAX_LINE_CHARS ) - len = MAX_LINE_CHARS; - if ( len<=0 ) - return 0; - lUInt16 res = measureText( - text, len, - widths, - flags, - 2048, // max_width, - L' ' // def_char - ); - if ( res>0 && resgi.width : 0; - - for ( ; wsum < max_width && nchars < len; nchars++ ) - { - bflags = 0; - ch = text[nchars]; - isSpace = lvfontIsUnicodeSpace(ch); - if (isSpace || ch == UNICODE_SOFT_HYPHEN_CODE ) - bflags |= LCHAR_ALLOW_WRAP_AFTER; - if (ch == '-') - bflags |= LCHAR_DEPRECATED_WRAP_AFTER; - if (isSpace) - bflags |= LCHAR_IS_SPACE; - glyph = GetGlyphRec( ch ); - if (!glyph && def_char) - glyph = GetGlyphRec( def_char ); - gwidth = glyph ? glyph->gi.width : 0; - widths[nchars] = wsum + gwidth; - if ( ch != UNICODE_SOFT_HYPHEN_CODE ) - wsum += gwidth; /* don't include hyphens to width */ - flags[nchars] = bflags; - } - - //try to add hyphen - for (hwStart=nchars-1; hwStart>0; hwStart--) - { - if (lvfontIsUnicodeSpace(text[hwStart])) - { - hwStart++; - break; - } - } - for (hwEnd=nchars; hwEndgi.blackBoxX*p->gi.blackBoxY; - if (gs>0) - memcpy( buf, p->glyph, gs ); - return true; -} - -void LVWin32Font::Clear() -{ - LVBaseWin32Font::Clear(); - _cache.clear(); -} - -bool LVWin32Font::Create( const LOGFONTA & lf ) -{ - if (!LVBaseWin32Font::Create(lf)) - return false; - _unknown_glyph_index = GetGlyphIndex( _drawbuf.GetDC(), 1 ); - return true; -} - -bool LVWin32Font::Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ) -{ - if (!LVBaseWin32Font::Create(size, weight, italic, family, typeface)) - return false; - _unknown_glyph_index = GetGlyphIndex( _drawbuf.GetDC(), 1 ); - return true; -} - -#endif - -/// to compare two fonts -bool operator == (const LVFont & r1, const LVFont & r2) -{ - if ( &r1 == &r2 ) - return true; - return r1.getSize()==r2.getSize() - && r1.getWeight()==r2.getWeight() - && r1.getItalic()==r2.getItalic() - && r1.getFontFamily()==r2.getFontFamily() - && r1.getTypeFace()==r2.getTypeFace() - && r1.getKerning()==r2.getKerning() - && r1.getHintingMode()==r2.getHintingMode() - ; -} - diff --git a/crengine/src/lvfont.cpp b/crengine/src/lvfont.cpp new file mode 100644 index 0000000000..ba50613684 --- /dev/null +++ b/crengine/src/lvfont.cpp @@ -0,0 +1,51 @@ +/** @file lvfntman.cpp + @brief font implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "../include/lvfont.h" + +/** + * Max width of -/./,/!/? to use for visial alignment by width + */ +int LVFont::getVisualAligmentWidth() { + FONT_GUARD + if (_visual_alignment_width == -1) { + //lChar16 chars[] = { getHyphChar(), ',', '.', '!', ':', ';', 0 }; + lChar16 chars[] = {getHyphChar(), ',', '.', '!', ':', ';', + (lChar16) L'\xff0c', (lChar16) L'\x3302', (lChar16) L'\xff01', 0}; + // (lChar16)L',', (lChar16)L'。', (lChar16)L'!', 0 }; + // 65292 12290 65281 + // ff0c 3002 ff01 + int maxw = 0; + for (int i = 0; chars[i]; i++) { + int w = getCharWidth(chars[i]); + if (w > maxw) + maxw = w; + } + _visual_alignment_width = maxw; + } + return _visual_alignment_width; +} + +/// to compare two fonts +bool operator==(const LVFont &r1, const LVFont &r2) { + if (&r1 == &r2) + return true; + return r1.getSize() == r2.getSize() + && r1.getWeight() == r2.getWeight() + && r1.getItalic() == r2.getItalic() + && r1.getFontFamily() == r2.getFontFamily() + && r1.getTypeFace() == r2.getTypeFace() + && r1.getKerning() == r2.getKerning() + && r1.getHintingMode() == r2.getHintingMode(); +} diff --git a/crengine/src/private/lvbasefont.cpp b/crengine/src/private/lvbasefont.cpp new file mode 100644 index 0000000000..142245c542 --- /dev/null +++ b/crengine/src/private/lvbasefont.cpp @@ -0,0 +1,70 @@ +/** @file lvbasefont.cpp + @brief base font implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvbasefont.h" +#include "lvfontglyphcache.h" + + +void LVBaseFont::DrawTextString(LVDrawBuf *buf, int x, int y, + const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette, bool addHyphen, lUInt32, int) { + //static lUInt8 glyph_buf[16384]; + //LVFont::glyph_info_t info; + int baseline = getBaseline(); + while (len >= (addHyphen ? 0 : 1)) { + if (len <= 1 || *text != UNICODE_SOFT_HYPHEN_CODE) { + lChar16 ch = ((len == 0) ? UNICODE_SOFT_HYPHEN_CODE : *text); + + LVFontGlyphCacheItem *item = getGlyph(ch, def_char); + int w = 0; + if (item) { + // avoid soft hyphens inside text string + w = item->advance; + if (item->bmp_width && item->bmp_height) { + buf->Draw(x + item->origin_x, + y + baseline - item->origin_y, + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + } + } + x += w; // + letter_spacing; + +// if ( !getGlyphInfo( ch, &info, def_char ) ) +// { +// ch = def_char; +// if ( !getGlyphInfo( ch, &info, def_char ) ) +// ch = 0; +// } +// if (ch && getGlyphImage( ch, glyph_buf, def_char )) +// { +// if (info.blackBoxX && info.blackBoxY) +// { +// buf->Draw( x + info.originX, +// y + baseline - info.originY, +// glyph_buf, +// info.blackBoxX, +// info.blackBoxY, +// palette); +// } +// x += info.width; +// } + } else if (*text != UNICODE_SOFT_HYPHEN_CODE) { + //len = len; + } + len--; + text++; + } +} diff --git a/crengine/src/private/lvbasefont.h b/crengine/src/private/lvbasefont.h new file mode 100644 index 0000000000..9e3f62350e --- /dev/null +++ b/crengine/src/private/lvbasefont.h @@ -0,0 +1,42 @@ +/** @file lvbasefont.h + @brief base font interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_BASEFONT_H_INCLUDED__ +#define __LV_BASEFONT_H_INCLUDED__ + +#include +#include "crsetup.h" +#include "lvfont.h" + +class LVDrawBuf; + +class LVBaseFont : public LVFont { +protected: + lString8 _typeface; + css_font_family_t _family; +public: + /// returns font typeface name + virtual lString8 getTypeFace() const { return _typeface; } + + /// returns font family id + virtual css_font_family_t getFontFamily() const { return _family; } + + /// draws text string + virtual void DrawTextString(LVDrawBuf *buf, int x, int y, + const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette, bool addHyphen, + lUInt32 flags = 0, int letter_spacing = 0); +}; + +#endif // __LV_BASEFONT_H_INCLUDED__ diff --git a/crengine/src/private/lvbitmapfont.cpp b/crengine/src/private/lvbitmapfont.cpp new file mode 100644 index 0000000000..c1664d9f4c --- /dev/null +++ b/crengine/src/private/lvbitmapfont.cpp @@ -0,0 +1,141 @@ +/** @file lvbitmapfont.cpp + @brief bitmap font implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvbitmapfont.h" + + +#if (USE_BITMAP_FONTS == 1) + +LVFontRef LoadFontFromFile( const char * fname ) +{ + LVFontRef ref; + LBitmapFont * font = new LBitmapFont; + if (font->LoadFromFile( fname ) ) + { + ref = font; + } + else + { + delete font; + } + return ref; +} + +bool LBitmapFont::getGlyphInfo( lUInt16 code, LVFont::glyph_info_t * glyph, lChar16 def_char ) +{ + const lvfont_glyph_t * ptr = lvfontGetGlyph( m_font, code ); + if (!ptr) + return false; + glyph->blackBoxX = ptr->blackBoxX; + glyph->blackBoxY = ptr->blackBoxY; + glyph->originX = ptr->originX; + glyph->originY = ptr->originY; + glyph->width = ptr->width; + return true; +} + +lUInt16 LBitmapFont::measureText( + const lChar16 * text, int len, + lUInt16 * widths, + lUInt8 * flags, + int max_width, + lChar16 def_char, + int letter_spacing, + bool allow_hyphenation + ) +{ + return lvfontMeasureText( m_font, text, len, widths, flags, max_width, def_char ); +} + +lUInt32 LBitmapFont::getTextWidth( const lChar16 * text, int len ) +{ + // + static lUInt16 widths[MAX_LINE_CHARS+1]; + static lUInt8 flags[MAX_LINE_CHARS+1]; + if ( len>MAX_LINE_CHARS ) + len = MAX_LINE_CHARS; + if ( len<=0 ) + return 0; + lUInt16 res = measureText( + text, len, + widths, + flags, + 2048, // max_width, + L' ' // def_char + ); + if ( res>0 && resfontBaseline; +} +/// returns font height +int LBitmapFont::getHeight() const +{ + const lvfont_header_t * hdr = lvfontGetHeader( m_font ); + return hdr->fontHeight; +} +/// returns font character size +int LBitmapFont::getSize() const +{ + const lvfont_header_t * hdr = lvfontGetHeader( m_font ); + return hdr->fontHeight; +} +/// returns font weight +int LBitmapFont::getWeight() const +{ + const lvfont_header_t * hdr = lvfontGetHeader( m_font ); + return hdr->flgBold ? 800 : 400; +} +/// returns italic flag +int LBitmapFont::getItalic() const +{ + const lvfont_header_t * hdr = lvfontGetHeader( m_font ); + return hdr->flgItalic; +} +/* +bool LBitmapFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) +{ + const lvfont_glyph_t * ptr = lvfontGetGlyph( m_font, code ); + if (!ptr) + return false; + const hrle_decode_info_t * pDecodeTable = lvfontGetDecodeTable( m_font ); + int sz = ptr->blackBoxX*ptr->blackBoxY; + if (sz) + lvfontUnpackGlyph(ptr->glyph, pDecodeTable, buf, sz); + return true; +} +*/ +LVFontGlyphCacheItem *LBitmapFont::getGlyph(lUInt16 ch, lChar16 def_char) { + // TODO: + return NULL; +} +int LBitmapFont::LoadFromFile( const char * fname ) +{ + Clear(); + int res = (void*)lvfontOpen( fname, &m_font )!=NULL; + if (!res) + return 0; + lvfont_header_t * hdr = (lvfont_header_t*) m_font; + _typeface = lString8( hdr->fontName ); + _family = (css_font_family_t) hdr->fontFamily; + return 1; +} + +#endif // (USE_BITMAP_FONTS==1) diff --git a/crengine/src/private/lvbitmapfont.h b/crengine/src/private/lvbitmapfont.h new file mode 100644 index 0000000000..4c5c487a23 --- /dev/null +++ b/crengine/src/private/lvbitmapfont.h @@ -0,0 +1,94 @@ +/** @file lvbitmapfont.h + @brief bitmap font interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_BITMAPFONT_H_INCLUDED__ +#define __LV_BITMAPFONT_H_INCLUDED__ + +#include +#include "crsetup.h" +#include "lvfnt.h" +#include "lvbasefont.h" +#include "lvfontcache.h" + +#if (USE_FREETYPE != 1) && (USE_BITMAP_FONTS == 1) + +/* C++ wrapper class */ +class LBitmapFont : public LVBaseFont { +private: + lvfont_handle m_font; +public: + LBitmapFont() : m_font(NULL) {} + + virtual bool getGlyphInfo(lUInt16 code, LVFont::glyph_info_t *glyph, lChar16 def_char = 0); + + virtual lUInt16 + measureText(const lChar16 *text, int len, lUInt16 *widths, lUInt8 *flags, int max_width, + lChar16 def_char, int letter_spacing = 0, bool allow_hyphenation = true); + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return width of specified string + */ + virtual lUInt32 getTextWidth( + const lChar16 *text, int len + ); + + /// returns font baseline offset + virtual int getBaseline(); + + /// returns font height + virtual int getHeight() const; + + /// returns font character size + virtual int getSize() const; + + /// returns font weight + virtual int getWeight() const; + + /// returns italic flag + virtual int getItalic() const; + + //virtual bool getGlyphImage(lUInt16 code, lUInt8 *buf, lChar16 def_char = 0); + + virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0); + + /// returns char width + virtual int getCharWidth(lChar16 ch, lChar16 def_char = 0) { + glyph_info_t glyph; + if (getGlyphInfo(ch, &glyph, def_char)) + return glyph.width; + return 0; + } + + virtual lvfont_handle GetHandle() { return m_font; } + + int LoadFromFile(const char *fname); + + // LVFont functions overrides + virtual void Clear() { + if (m_font) lvfontClose(m_font); + m_font = NULL; + } + + virtual bool IsNull() const { return m_font == NULL; } + + virtual bool operator!() const { return IsNull(); } + + virtual ~LBitmapFont() { Clear(); } +}; + +#endif // (USE_FREETYPE!=1) && (USE_BITMAP_FONTS==1) + +#endif // __LV_BITMAPFONT_H_INCLUDED__ diff --git a/crengine/src/private/lvbitmapfontman.cpp b/crengine/src/private/lvbitmapfontman.cpp new file mode 100644 index 0000000000..3b2967ce40 --- /dev/null +++ b/crengine/src/private/lvbitmapfontman.cpp @@ -0,0 +1,121 @@ +/** @file lvbitmapfontman.cpp + @brief bitmap font manager implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvbitmapfontman.h" +#include "lvbitmapfont.h" + +#if (USE_BITMAP_FONTS == 1) + +LVBitmapFontManager::~LVBitmapFontManager() { + //if (_log) + // fclose(_log); +} + +LVBitmapFontManager::LVBitmapFontManager() { + //_log = fopen( "fonts.log", "wt" ); +} + +void LVBitmapFontManager::gc() // garbage collector +{ + _cache.gc(); +} + +lString8 LVBitmapFontManager::makeFontFileName(lString8 name) { + lString8 filename = _path; + if (!filename.empty() && _path[filename.length() - 1] != PATH_SEPARATOR_CHAR) + filename << PATH_SEPARATOR_CHAR; + filename << name; + return filename; +} + +LVFontRef LVBitmapFontManager::GetFont(int size, int weight, bool italic, css_font_family_t family, + lString8 typeface, int documentId) { + LVFontDef *def = new LVFontDef( + lString8::empty_str, + size, + weight, + italic, + family, + typeface, + documentId + ); + //fprintf( _log, "GetFont: %s %d %s %s\n", + // typeface.c_str(), + // size, + // weight>400?"bold":"", + // italic?"italic":"" ); + LVFontCacheItem *item = _cache.find(def); + delete def; + if (!item->getFont().isNull()) { + //fprintf(_log, " : fount existing\n"); + return item->getFont(); + } + LBitmapFont *font = new LBitmapFont; + lString8 fname = makeFontFileName(item->getDef()->getName()); + //printf("going to load font file %s\n", fname.c_str()); + if (font->LoadFromFile(fname.c_str())) { + //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), + // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); + LVFontRef ref(font); + item->setFont(ref); + return ref; + } else { + //printf(" not found!\n"); + } + delete font; + return LVFontRef(NULL); +} + +bool LVBitmapFontManager::RegisterFont(lString8 name) { + lString8 fname = makeFontFileName(name); + //printf("going to load font file %s\n", fname.c_str()); + LVStreamRef stream = LVOpenFileStream(fname.c_str(), LVOM_READ); + if (!stream) { + //printf(" not found!\n"); + return false; + } + tag_lvfont_header hdr; + bool res = false; + lvsize_t bytes_read = 0; + if (stream->Read(&hdr, sizeof(hdr), &bytes_read) == LVERR_OK && bytes_read == sizeof(hdr)) { + LVFontDef def( + name, + hdr.fontHeight, + hdr.flgBold ? 700 : 400, + hdr.flgItalic ? true : false, + (css_font_family_t) hdr.fontFamily, + lString8(hdr.fontName) + ); + //fprintf( _log, "Register: %s %s %d %s %s\n", + // name.c_str(), hdr.fontName, + // hdr.fontHeight, + // hdr.flgBold?"bold":"", + // hdr.flgItalic?"italic":"" ); + _cache.update(&def, LVFontRef(NULL)); + res = true; + } + return res; +} + +void LVBitmapFontManager::getFontFileNameList(lString16Collection &list) { + FONT_MAN_GUARD + _cache.getFontFileNameList(list); +} + +bool LVBitmapFontManager::Init(lString8 path) { + _path = path; + return true; +} + +#endif // (USE_BITMAP_FONTS==1) diff --git a/crengine/src/private/lvbitmapfontman.h b/crengine/src/private/lvbitmapfontman.h new file mode 100644 index 0000000000..cd149d89b3 --- /dev/null +++ b/crengine/src/private/lvbitmapfontman.h @@ -0,0 +1,57 @@ +/** @file lvbitmapfontman.h + @brief bitmap font manager interface + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_BITMAPFONTMAN_H_INCLUDED__ +#define __LV_BITMAPFONTMAN_H_INCLUDED__ + +#include "../../include/crsetup.h" +#include "../../include/lvfntman.h" +#include "lvfontcache.h" + + +#if (USE_BITMAP_FONTS == 1) + +class LVBitmapFontManager : public LVFontManager { +private: + lString8 _path; + LVFontCache _cache; + //FILE * _log; +public: + virtual int GetFontCount() { + return _cache.length(); + } + + virtual ~LVBitmapFontManager(); + + LVBitmapFontManager(); + + virtual void gc(); + + lString8 makeFontFileName(lString8 name); + + virtual LVFontRef + GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface, + int documentId); + + virtual bool RegisterFont(lString8 name); + + /// returns registered font files + virtual void getFontFileNameList(lString16Collection &list); + + virtual bool Init(lString8 path); +}; + +#endif // (USE_BITMAP_FONTS==1) + +#endif // __LV_BITMAPFONTMAN_H_INCLUDED__ diff --git a/crengine/src/private/lvfontboldtransform.cpp b/crengine/src/private/lvfontboldtransform.cpp new file mode 100644 index 0000000000..70cd07b4a9 --- /dev/null +++ b/crengine/src/private/lvfontboldtransform.cpp @@ -0,0 +1,255 @@ +/** \file lvfontboldtransform.h + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvfontboldtransform.h" + + +int LVFontBoldTransform::getWeight() const { + int w = _baseFont->getWeight() + 200; + if (w > 900) + w = 900; + return w; +} + +LVFontBoldTransform::LVFontBoldTransform(LVFontRef baseFont, LVFontGlobalGlyphCache *globalCache) + : _baseFontRef(baseFont), _baseFont(baseFont.get()), _hyphWidth(-1), + _glyph_cache(globalCache) { + _size = _baseFont->getSize(); + _height = _baseFont->getHeight(); + _hShift = _size <= 36 ? 1 : 2; + _vShift = _size <= 36 ? 0 : 1; + _baseline = _baseFont->getBaseline(); +} + +int LVFontBoldTransform::getHyphenWidth() { + FONT_GUARD + if (_hyphWidth < 0) + _hyphWidth = getCharWidth(getHyphChar()); + return _hyphWidth; +} + +bool +LVFontBoldTransform::getGlyphInfo(lUInt16 code, LVFont::glyph_info_t *glyph, lChar16 def_char) { + bool res = _baseFont->getGlyphInfo(code, glyph, def_char); + if (!res) + return res; + glyph->blackBoxX += glyph->blackBoxX > 0 ? _hShift : 0; + glyph->blackBoxY += _vShift; + glyph->width += _hShift; + + return true; +} + +lUInt16 +LVFontBoldTransform::measureText(const lChar16 *text, int len, lUInt16 *widths, lUInt8 *flags, + int max_width, lChar16 def_char, int letter_spacing, + bool allow_hyphenation) { + CR_UNUSED(allow_hyphenation); + lUInt16 res = _baseFont->measureText( + text, len, + widths, + flags, + max_width, + def_char, + letter_spacing + ); + int w = 0; + for (int i = 0; i < res; i++) { + w += _hShift; + widths[i] += w; + } + return res; +} + +lUInt32 LVFontBoldTransform::getTextWidth(const lChar16 *text, int len) { + static lUInt16 widths[MAX_LINE_CHARS + 1]; + static lUInt8 flags[MAX_LINE_CHARS + 1]; + if (len > MAX_LINE_CHARS) + len = MAX_LINE_CHARS; + if (len <= 0) + return 0; + lUInt16 res = measureText( + text, len, + widths, + flags, + 2048, // max_width, + L' ', // def_char + 0 + ); + if (res > 0 && res < MAX_LINE_CHARS) + return widths[res - 1]; + return 0; +} + +LVFontGlyphCacheItem *LVFontBoldTransform::getGlyph(lUInt16 ch, lChar16 def_char) { + + LVFontGlyphCacheItem *item = _glyph_cache.get(ch); + if (item) + return item; + + LVFontGlyphCacheItem *olditem = _baseFont->getGlyph(ch, def_char); + if (!olditem) + return NULL; + + int oldx = olditem->bmp_width; + int oldy = olditem->bmp_height; + int dx = oldx ? oldx + _hShift : 0; + int dy = oldy ? oldy + _vShift : 0; + + item = LVFontGlyphCacheItem::newItem(&_glyph_cache, ch, dx, dy); //, _drawMonochrome + item->advance = olditem->advance + _hShift; + item->origin_x = olditem->origin_x; + item->origin_y = olditem->origin_y; + + if (dx && dy) { + for (int y = 0; y < dy; y++) { + lUInt8 *dst = item->bmp + y * dx; + for (int x = 0; x < dx; x++) { + int s = 0; + for (int yy = -_vShift; yy <= 0; yy++) { + int srcy = y + yy; + if (srcy < 0 || srcy >= oldy) + continue; + lUInt8 *src = olditem->bmp + srcy * oldx; + for (int xx = -_hShift; xx <= 0; xx++) { + int srcx = x + xx; + if (srcx >= 0 && srcx < oldx && src[srcx] > s) + s = src[srcx]; + } + } + dst[x] = s; + } + } + } + _glyph_cache.put(item); + return item; +} + +void LVFontBoldTransform::DrawTextString(LVDrawBuf *buf, int x, int y, const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette, bool addHyphen, + lUInt32 flags, int letter_spacing) { + if (len <= 0) + return; + if (letter_spacing < 0 || letter_spacing > 50) + letter_spacing = 0; + lvRect clip; + buf->GetClipRect(&clip); + if (y + _height < clip.top || y >= clip.bottom) + return; + + //int error; + + int i; + + //lUInt16 prev_width = 0; + lChar16 ch; + // measure character widths + bool isHyphen = false; + int x0 = x; + for (i = 0; i <= len; i++) { + if (i == len && (!addHyphen || isHyphen)) + break; + if (i < len) { + ch = text[i]; + isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE) && (i < len - 1); + } else { + ch = UNICODE_SOFT_HYPHEN_CODE; + isHyphen = 0; + } + + LVFontGlyphCacheItem *item = getGlyph(ch, def_char); + int w = 0; + if (item) { + // avoid soft hyphens inside text string + w = item->advance; + if (item->bmp_width && item->bmp_height && (!isHyphen || i >= len - 1)) { + buf->Draw(x + item->origin_x, + y + _baseline - item->origin_y, + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + } + } + x += w + letter_spacing; + } + if (flags & LTEXT_TD_MASK) { + // text decoration: underline, etc. + int h = _size > 30 ? 2 : 1; + lUInt32 cl = buf->GetTextColor(); + if ((flags & LTEXT_TD_UNDERLINE) || (flags & LTEXT_TD_BLINK)) { + int liney = y + _baseline + h; + buf->FillRect(x0, liney, x, liney + h, cl); + } + if (flags & LTEXT_TD_OVERLINE) { + int liney = y + h; + buf->FillRect(x0, liney, x, liney + h, cl); + } + if (flags & LTEXT_TD_LINE_THROUGH) { + int liney = y + _height / 2 - h / 2; + buf->FillRect(x0, liney, x, liney + h, cl); + } + } +} + +/* +bool LVFontBoldTransform::getGlyphImage(lUInt16 code, lUInt8 *buf, lChar16 def_char) +{ + LVFontGlyphCacheItem * item = getGlyph( code, def_char ); + if ( !item ) + return false; + glyph_info_t glyph; + if ( !_baseFont->getGlyphInfo( code, &glyph, def_char ) ) + return 0; + int oldx = glyph.blackBoxX; + int oldy = glyph.blackBoxY; + int dx = oldx + _hShift; + int dy = oldy + _vShift; + if ( !oldx || !oldy ) + return true; + LVAutoPtr tmp( new lUInt8[oldx*oldy+2000] ); + memset(buf, 0, dx*dy); + tmp[oldx*oldy]=123; + bool res = _baseFont->getGlyphImage( code, tmp.get(), def_char ); + if ( tmp[oldx*oldy]!=123 ) { + //CRLog::error("Glyph buffer corrupted!"); + // clear cache + for ( int i=32; i<4000; i++ ) { + _baseFont->getGlyphInfo( i, &glyph, def_char ); + _baseFont->getGlyphImage( i, tmp.get(), def_char ); + } + _baseFont->getGlyphInfo( code, &glyph, def_char ); + _baseFont->getGlyphImage( code, tmp.get(), def_char ); + } + for ( int y=0; y=oldy ) + continue; + lUInt8 * src = tmp.get() + srcy*oldx; + for ( int xx=-_hShift; xx<=0; xx++ ) { + int srcx = x+xx; + if ( srcx>=0 && srcx s ) + s = src[srcx]; + } + } + dst[x] = s; + } + } + return res; + return false; +} +*/ diff --git a/crengine/src/private/lvfontboldtransform.h b/crengine/src/private/lvfontboldtransform.h new file mode 100644 index 0000000000..9e63163546 --- /dev/null +++ b/crengine/src/private/lvfontboldtransform.h @@ -0,0 +1,182 @@ +/** \file lvfontboldtransform.h + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FONTBOLDTRANSFORM_H_INCLUDED__ +#define __LV_FONTBOLDTRANSFORM_H_INCLUDED__ + +#include "../../include/lvfont.h" +#include "lvfontglyphcache.h" +#include "../../include/lvtextfm.h" + +class LVFontBoldTransform : public LVFont { + LVFontRef _baseFontRef; + LVFont *_baseFont; + int _hyphWidth; + int _hShift; + int _vShift; + int _size; // glyph height in pixels + int _height; // line height in pixels + //int _hyphen_width; + int _baseline; + LVFontLocalGlyphCache _glyph_cache; +public: + /// returns font weight + virtual int getWeight() const; + + /// returns italic flag + virtual int getItalic() const { + return _baseFont->getItalic(); + } + + LVFontBoldTransform(LVFontRef baseFont, LVFontGlobalGlyphCache *globalCache); + + /// hyphenation character + virtual lChar16 getHyphChar() { return UNICODE_SOFT_HYPHEN_CODE; } + + /// hyphen width + virtual int getHyphenWidth(); + + /** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found + */ + virtual bool getGlyphInfo(lUInt16 code, glyph_info_t *glyph, lChar16 def_char = 0); + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \param max_width is maximum width to measure line + \param def_char is character to replace absent glyphs in font + \param letter_spacing is number of pixels to add between letters + \return number of characters before max_width reached + */ + virtual lUInt16 measureText( + const lChar16 *text, int len, + lUInt16 *widths, + lUInt8 *flags, + int max_width, + lChar16 def_char, + int letter_spacing = 0, + bool allow_hyphenation = true + ); + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return width of specified string + */ + virtual lUInt32 getTextWidth( + const lChar16 *text, int len + ); + + /** \brief get glyph item + \param code is unicode character + \return glyph pointer if glyph was found, NULL otherwise + */ + virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0); + + /** \brief get glyph image in 1 byte per pixel format + \param code is unicode character + \param buf is buffer [width*height] to place glyph data + \return true if glyph was found + */ + //virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0 ); + + /// returns font baseline offset + virtual int getBaseline() { + return _baseline; + } + + /// returns font height + virtual int getHeight() const { + return _height; + } + + /// returns font character size + virtual int getSize() const { + return _size; + } + + /// returns char width + virtual int getCharWidth(lChar16 ch, lChar16 def_char = 0) { + int w = _baseFont->getCharWidth(ch, def_char) + _hShift; + return w; + } + + /// retrieves font handle + virtual void *GetHandle() { + return NULL; + } + + /// returns font typeface name + virtual lString8 getTypeFace() const { + return _baseFont->getTypeFace(); + } + + /// returns font family id + virtual css_font_family_t getFontFamily() const { + return _baseFont->getFontFamily(); + } + + /// draws text string + virtual void DrawTextString(LVDrawBuf *buf, int x, int y, + const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette, bool addHyphen, + lUInt32 flags, int letter_spacing); + + /// get bitmap mode (true=monochrome bitmap, false=antialiased) + virtual bool getBitmapMode() { + return _baseFont->getBitmapMode(); + } + + /// set bitmap mode (true=monochrome bitmap, false=antialiased) + virtual void setBitmapMode(bool m) { + _baseFont->setBitmapMode(m); + } + + /// sets current hinting mode + virtual void setHintingMode(hinting_mode_t mode) { _baseFont->setHintingMode(mode); } + + /// returns current hinting mode + virtual hinting_mode_t getHintingMode() const { return _baseFont->getHintingMode(); } + + /// get kerning mode: true==ON, false=OFF + virtual bool getKerning() const { return _baseFont->getKerning(); } + + /// get kerning mode: true==ON, false=OFF + virtual void setKerning(bool b) { _baseFont->setKerning(b); } + + /// get ligatures mode: true==allowed, false=not allowed + virtual bool getLigatures() const { return _baseFont->getLigatures(); } + + /// set ligatures mode: true==allowed, false=not allowed + virtual void setLigatures(bool b) { _baseFont->setLigatures(b); } + + /// returns true if font is empty + virtual bool IsNull() const { + return _baseFont->IsNull(); + } + + virtual bool operator!() const { + return !(*_baseFont); + } + + virtual void Clear() { + _baseFont->Clear(); + } + + virtual ~LVFontBoldTransform() { + } +}; + +#endif // __LV_FONTBOLDTRANSFORM_H_INCLUDED__ diff --git a/crengine/src/private/lvfontcache.cpp b/crengine/src/private/lvfontcache.cpp new file mode 100644 index 0000000000..df68fea90d --- /dev/null +++ b/crengine/src/private/lvfontcache.cpp @@ -0,0 +1,184 @@ +/** \file lvfontcache.cpp + \brief font cache implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvfontcache.h" +#include "../../include/lvstyles.h" + + +LVFontCacheItem *LVFontCache::findDuplicate(const LVFontDef *def) { + for (int i = 0; i < _registered_list.length(); i++) { + if (_registered_list[i]->_def.CalcDuplicateMatch(*def)) + return _registered_list[i]; + } + return NULL; +} + +LVFontCacheItem *LVFontCache::findDocumentFontDuplicate(int documentId, lString8 name) { + for (int i = 0; i < _registered_list.length(); i++) { + if (_registered_list[i]->_def.getDocumentId() == documentId && + _registered_list[i]->_def.getName() == name) + return _registered_list[i]; + } + return NULL; +} + +LVFontCacheItem *LVFontCache::findFallback(lString8 face, int size) { + int best_index = -1; + int best_match = -1; + int best_instance_index = -1; + int best_instance_match = -1; + int i; + for (i = 0; i < _instance_list.length(); i++) { + int match = _instance_list[i]->_def.CalcFallbackMatch(face, size); + if (match > best_instance_match) { + best_instance_match = match; + best_instance_index = i; + } + } + for (i = 0; i < _registered_list.length(); i++) { + int match = _registered_list[i]->_def.CalcFallbackMatch(face, size); + if (match > best_match) { + best_match = match; + best_index = i; + } + } + if (best_index <= 0) + return NULL; + if (best_instance_match >= best_match) + return _instance_list[best_instance_index]; + return _registered_list[best_index]; +} + +LVFontCacheItem *LVFontCache::find(const LVFontDef *fntdef) { + int best_index = -1; + int best_match = -1; + int best_instance_index = -1; + int best_instance_match = -1; + int i; + LVFontDef def(*fntdef); + lString8Collection list; + splitPropertyValueList(fntdef->getTypeFace().c_str(), list); + for (int nindex = 0; nindex == 0 || nindex < list.length(); nindex++) { + if (nindex < list.length()) + def.setTypeFace(list[nindex]); + else + def.setTypeFace(lString8::empty_str); + for (i = 0; i < _instance_list.length(); i++) { + int match = _instance_list[i]->_def.CalcMatch(def); + if (match > best_instance_match) { + best_instance_match = match; + best_instance_index = i; + } + } + for (i = 0; i < _registered_list.length(); i++) { + int match = _registered_list[i]->_def.CalcMatch(def); + if (match > best_match) { + best_match = match; + best_index = i; + } + } + } + if (best_index < 0) + return NULL; + if (best_instance_match >= best_match) + return _instance_list[best_instance_index]; + return _registered_list[best_index]; +} + +void LVFontCache::addInstance(const LVFontDef *def, LVFontRef ref) { + if (ref.isNull()) + printf("Adding null font instance!"); + LVFontCacheItem *item = new LVFontCacheItem(*def); + item->_fnt = ref; + _instance_list.add(item); +} + +void LVFontCache::removefont(const LVFontDef *def) { + int i; + for (i = 0; i < _instance_list.length(); i++) { + if (_instance_list[i]->_def.getTypeFace() == def->getTypeFace()) { + _instance_list.remove(i); + } + + } + for (i = 0; i < _registered_list.length(); i++) { + if (_registered_list[i]->_def.getTypeFace() == def->getTypeFace()) { + _registered_list.remove(i); + } + } + +} + +void LVFontCache::update(const LVFontDef *def, LVFontRef ref) { + int i; + if (!ref.isNull()) { + for (i = 0; i < _instance_list.length(); i++) { + if (_instance_list[i]->_def == *def) { + if (ref.isNull()) { + _instance_list.erase(i, 1); + } else { + _instance_list[i]->_fnt = ref; + } + return; + } + } + // add new + //LVFontCacheItem * item; + //item = new LVFontCacheItem(*def); + addInstance(def, ref); + } else { + for (i = 0; i < _registered_list.length(); i++) { + if (_registered_list[i]->_def == *def) { + return; + } + } + // add new + LVFontCacheItem *item; + item = new LVFontCacheItem(*def); + _registered_list.add(item); + } +} + +void LVFontCache::removeDocumentFonts(int documentId) { + int i; + for (i = _instance_list.length() - 1; i >= 0; i--) { + if (_instance_list[i]->_def.getDocumentId() == documentId) + delete _instance_list.remove(i); + } + for (i = _registered_list.length() - 1; i >= 0; i--) { + if (_registered_list[i]->_def.getDocumentId() == documentId) + delete _registered_list.remove(i); + } +} + +// garbage collector +void LVFontCache::gc() { + int droppedCount = 0; + int usedCount = 0; + for (int i = _instance_list.length() - 1; i >= 0; i--) { + if (_instance_list[i]->_fnt.getRefCount() <= 1) { + if (CRLog::isTraceEnabled()) + CRLog::trace("dropping font instance %s[%d] by gc()", + _instance_list[i]->getDef()->getTypeFace().c_str(), + _instance_list[i]->getDef()->getSize()); + _instance_list.erase(i, 1); + droppedCount++; + } else { + usedCount++; + } + } + if (CRLog::isDebugEnabled()) + CRLog::debug("LVFontCache::gc() : %d fonts still used, %d fonts dropped", usedCount, + droppedCount); +} diff --git a/crengine/src/private/lvfontcache.h b/crengine/src/private/lvfontcache.h new file mode 100644 index 0000000000..a1ea5d37c8 --- /dev/null +++ b/crengine/src/private/lvfontcache.h @@ -0,0 +1,117 @@ +/** \file lvfontcache.h + \brief font cache + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FONTCACHE_H_INCLUDED__ +#define __LV_FONTCACHE_H_INCLUDED__ + +#include "../../include/crsetup.h" +#include "../../include/lvfont.h" +#include "../../include/lvptrvec.h" +#include "lvfontdef.h" + +/// font cache item +class LVFontCacheItem { + friend class LVFontCache; + + LVFontDef _def; + LVFontRef _fnt; +public: + LVFontDef *getDef() { return &_def; } + + LVFontRef &getFont() { return _fnt; } + + void setFont(LVFontRef &fnt) { _fnt = fnt; } + + LVFontCacheItem(const LVFontDef &def) + : _def(def) {} +}; + +/// font cache +class LVFontCache { + LVPtrVector _registered_list; + LVPtrVector _instance_list; +public: + void clear() { + _registered_list.clear(); + _instance_list.clear(); + } + + void gc(); // garbage collector + void update(const LVFontDef *def, LVFontRef ref); + + void removefont(const LVFontDef *def); + + void removeDocumentFonts(int documentId); + + int length() { return _registered_list.length(); } + + void addInstance(const LVFontDef *def, LVFontRef ref); + + LVPtrVector *getInstances() { return &_instance_list; } + + LVFontCacheItem *find(const LVFontDef *def); + + LVFontCacheItem *findFallback(lString8 face, int size); + + LVFontCacheItem *findDuplicate(const LVFontDef *def); + + LVFontCacheItem *findDocumentFontDuplicate(int documentId, lString8 name); + + /// get hash of installed fonts and fallback font + virtual lUInt32 GetFontListHash(int documentId) { + lUInt32 hash = 0; + for (int i = 0; i < _registered_list.length(); i++) { + int doc = _registered_list[i]->getDef()->getDocumentId(); + if (doc == -1 || doc == documentId) // skip document fonts + hash = hash + _registered_list[i]->getDef()->getHash(); + } + return 0; + } + + virtual void getFaceList(lString16Collection &list) { + list.clear(); + for (int i = 0; i < _registered_list.length(); i++) { + if (_registered_list[i]->getDef()->getDocumentId() != -1) + continue; + lString16 name = Utf8ToUnicode(_registered_list[i]->getDef()->getTypeFace()); + if (!list.contains(name)) + list.add(name); + } + list.sort(); + } + + virtual void getFontFileNameList(lString16Collection &list) { + list.clear(); + for (int i = 0; i < _registered_list.length(); i++) { + if (_registered_list[i]->getDef()->getDocumentId() == -1) { + lString16 name = Utf8ToUnicode(_registered_list[i]->getDef()->getName()); + if (!list.contains(name)) + list.add(name); + } + } + list.sort(); + } + + virtual void clearFallbackFonts() { + for (int i = 0; i < _registered_list.length(); i++) { + _registered_list[i]->getFont()->setFallbackFont(LVFontRef()); + } + } + + LVFontCache() {} + + virtual ~LVFontCache() {} +}; + +#endif // __LV_FONTCACHE_H_INCLUDED__ diff --git a/crengine/src/private/lvfontdef.cpp b/crengine/src/private/lvfontdef.cpp new file mode 100644 index 0000000000..55e9e16b04 --- /dev/null +++ b/crengine/src/private/lvfontdef.cpp @@ -0,0 +1,75 @@ +/** \file lvfontdef.cpp + @brief font definition implemetation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvfontdef.h" + + +int LVFontDef::CalcDuplicateMatch(const LVFontDef &def) const { + if (def._documentId != -1 && _documentId != def._documentId) + return false; + bool size_match = (_size == -1 || def._size == -1) ? true + : (def._size == _size); + bool weight_match = (_weight == -1 || def._weight == -1) ? true + : (def._weight == _weight); + bool italic_match = (_italic == def._italic || _italic == -1 || def._italic == -1); + bool family_match = (_family == css_ff_inherit || def._family == css_ff_inherit || + def._family == _family); + bool typeface_match = (_typeface == def._typeface); + return size_match && weight_match && italic_match && family_match && typeface_match; +} + +int LVFontDef::CalcMatch(const LVFontDef &def) const { + if (_documentId != -1 && _documentId != def._documentId) + return 0; + int size_match = (_size == -1 || def._size == -1) ? 256 + : (def._size > _size ? _size * 256 / def._size + : def._size * 256 / + _size); + int weight_diff = def._weight - _weight; + if (weight_diff < 0) + weight_diff = -weight_diff; + if (weight_diff > 800) + weight_diff = 800; + int weight_match = (_weight == -1 || def._weight == -1) ? 256 + : (256 - weight_diff * 256 / 800); + int italic_match = (_italic == def._italic || _italic == -1 || def._italic == -1) ? 256 : 0; + if ((_italic == 2 || def._italic == 2) && _italic > 0 && def._italic > 0) + italic_match = 128; + int family_match = (_family == css_ff_inherit || def._family == css_ff_inherit || + def._family == _family) + ? 256 + : ((_family == css_ff_monospace) == (def._family == css_ff_monospace) ? 64 + : 0); + int typeface_match = (_typeface == def._typeface) ? 256 : 0; + return + +(size_match * 100) + + (weight_match * 5) + + (italic_match * 5) + + (family_match * 100) + + (typeface_match * 1000); +} + +int LVFontDef::CalcFallbackMatch(lString8 face, int size) const { + if (_typeface != face) { + //CRLog::trace("'%s'' != '%s'", face.c_str(), _typeface.c_str()); + return 0; + } + int size_match = (_size == -1 || size == -1 || _size == size) ? 256 : 0; + int weight_match = (_weight == -1) ? 256 : (256 - _weight * 256 / 800); + int italic_match = _italic == 0 ? 256 : 0; + return + +(size_match * 100) + + (weight_match * 5) + + (italic_match * 5); +} diff --git a/crengine/src/private/lvfontdef.h b/crengine/src/private/lvfontdef.h new file mode 100644 index 0000000000..a9f42f059a --- /dev/null +++ b/crengine/src/private/lvfontdef.h @@ -0,0 +1,120 @@ +/** @file lvfontdef.h + @brief font definition interface + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FONTDEF_H_INCLUDED__ +#define __LV_FONTDEF_H_INCLUDED__ + +#include "../../include/crsetup.h" +#include "../../include/lvfont.h" +#include "../../include/lvstring.h" +#include "../../include/cssdef.h" +#include "../../include/lvarray.h" + +/** + @brief Font properties definition +*/ +class LVFontDef { +private: + int _size; + int _weight; + int _italic; + css_font_family_t _family; + lString8 _typeface; + lString8 _name; + int _index; + // for document font: _documentId, _buf, _name + int _documentId; + LVByteArrayRef _buf; +public: + LVFontDef(const lString8 &name, int size, int weight, int italic, css_font_family_t family, + const lString8 &typeface, int index = -1, int documentId = -1, + LVByteArrayRef buf = LVByteArrayRef()) + : _size(size), _weight(weight), _italic(italic), _family(family), _typeface(typeface), + _name(name), _index(index), _documentId(documentId), _buf(buf) { + } + + LVFontDef(const LVFontDef &def) + : _size(def._size), _weight(def._weight), _italic(def._italic), _family(def._family), + _typeface(def._typeface), _name(def._name), _index(def._index), + _documentId(def._documentId), _buf(def._buf) { + } + + /// returns true if definitions are equal + bool operator==(const LVFontDef &def) const { + return (_size == def._size || _size == -1 || def._size == -1) + && (_weight == def._weight || _weight == -1 || def._weight == -1) + && (_italic == def._italic || _italic == -1 || def._italic == -1) + && _family == def._family + && _typeface == def._typeface + && _name == def._name + && (_index == def._index || def._index == -1) + && (_documentId == def._documentId || _documentId == -1); + } + + lUInt32 getHash() { + return ((((_size * 31) + _weight) * 31 + _italic) * 31 + _family) * 31 + _name.getHash(); + } + + /// returns font file name + lString8 getName() const { return _name; } + + void setName(lString8 name) { _name = name; } + + int getIndex() const { return _index; } + + void setIndex(int index) { _index = index; } + + int getSize() const { return _size; } + + void setSize(int size) { _size = size; } + + int getWeight() const { return _weight; } + + void setWeight(int weight) { _weight = weight; } + + bool getItalic() const { return _italic != 0; } + + bool isRealItalic() const { return _italic == 1; } + + void setItalic(int italic) { _italic = italic; } + + css_font_family_t getFamily() const { return _family; } + + void getFamily(css_font_family_t family) { _family = family; } + + lString8 getTypeFace() const { return _typeface; } + + void setTypeFace(lString8 tf) { _typeface = tf; } + + int getDocumentId() { return _documentId; } + + void setDocumentId(int id) { _documentId = id; } + + LVByteArrayRef getBuf() { return _buf; } + + void setBuf(LVByteArrayRef buf) { _buf = buf; } + + ~LVFontDef() {} + + /// calculates difference between two fonts + int CalcMatch(const LVFontDef &def) const; + + /// difference between fonts for duplicates search + int CalcDuplicateMatch(const LVFontDef &def) const; + + /// calc match for fallback font search + int CalcFallbackMatch(lString8 face, int size) const; +}; + +#endif // __LV_FONTDEF_H_INCLUDED__ diff --git a/crengine/src/private/lvfontglyphcache.cpp b/crengine/src/private/lvfontglyphcache.cpp new file mode 100644 index 0000000000..b83480f695 --- /dev/null +++ b/crengine/src/private/lvfontglyphcache.cpp @@ -0,0 +1,179 @@ +/** @file lvfntman.cpp + @brief font glyph cache implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvfontglyphcache.h" +#include "../../include/crlocks.h" + + +void LVFontLocalGlyphCache::clear() { + FONT_LOCAL_GLYPH_CACHE_GUARD + while (head) { + LVFontGlyphCacheItem *ptr = head; + remove(ptr); + global_cache->remove(ptr); + LVFontGlyphCacheItem::freeItem(ptr); + } +} + +LVFontGlyphCacheItem *LVFontLocalGlyphCache::get(lUInt16 ch) { + FONT_LOCAL_GLYPH_CACHE_GUARD + LVFontGlyphCacheItem *ptr = head; + for (; ptr; ptr = ptr->next_local) { + if (ptr->ch == ch) { + global_cache->refresh(ptr); + return ptr; + } + } + return NULL; +} + +void LVFontLocalGlyphCache::put(LVFontGlyphCacheItem *item) { + FONT_LOCAL_GLYPH_CACHE_GUARD + global_cache->put(item); + item->next_local = head; + if (head) + head->prev_local = item; + if (!tail) + tail = item; + head = item; +} + +/// remove from list, but don't delete +void LVFontLocalGlyphCache::remove(LVFontGlyphCacheItem *item) { + FONT_LOCAL_GLYPH_CACHE_GUARD + if (item == head) + head = item->next_local; + if (item == tail) + tail = item->prev_local; + if (!head || !tail) + return; + if (item->prev_local) + item->prev_local->next_local = item->next_local; + if (item->next_local) + item->next_local->prev_local = item->prev_local; + item->next_local = NULL; + item->prev_local = NULL; +} + +void LVFontGlobalGlyphCache::refresh(LVFontGlyphCacheItem *item) { + FONT_GLYPH_CACHE_GUARD + if (tail != item) { + //move to head + removeNoLock(item); + putNoLock(item); + } +} + +void LVFontGlobalGlyphCache::put(LVFontGlyphCacheItem *item) { + FONT_GLYPH_CACHE_GUARD + putNoLock(item); +} + +void LVFontGlobalGlyphCache::putNoLock(LVFontGlyphCacheItem *item) { + int sz = item->getSize(); + // remove extra items from tail + while (sz + size > max_size) { + LVFontGlyphCacheItem *removed_item = tail; + if (!removed_item) + break; + removeNoLock(removed_item); + removed_item->local_cache->remove(removed_item); + LVFontGlyphCacheItem::freeItem(removed_item); + } + // add new item to head + item->next_global = head; + if (head) + head->prev_global = item; + head = item; + if (!tail) + tail = item; + size += sz; +} + +void LVFontGlobalGlyphCache::remove(LVFontGlyphCacheItem *item) { + FONT_GLYPH_CACHE_GUARD + removeNoLock(item); +} + +void LVFontGlobalGlyphCache::removeNoLock(LVFontGlyphCacheItem *item) { + if (item == head) + head = item->next_global; + if (item == tail) + tail = item->prev_global; + if (!head || !tail) + return; + if (item->prev_global) + item->prev_global->next_global = item->next_global; + if (item->next_global) + item->next_global->prev_global = item->prev_global; + item->next_global = NULL; + item->prev_global = NULL; + size -= item->getSize(); +} + +void LVFontGlobalGlyphCache::clear() { + FONT_GLYPH_CACHE_GUARD + while (head) { + LVFontGlyphCacheItem *ptr = head; + remove(ptr); + ptr->local_cache->remove(ptr); + LVFontGlyphCacheItem::freeItem(ptr); + } +} + +LVFontGlyphCacheItem * +LVFontGlyphCacheItem::newItem(LVFontLocalGlyphCache *local_cache, lChar16 ch, int w, int h) { + LVFontGlyphCacheItem *item = (LVFontGlyphCacheItem *) malloc(sizeof(LVFontGlyphCacheItem) + + (w * h - 1) * sizeof(lUInt8)); + item->ch = ch; + item->bmp_width = (lUInt16) w; + item->bmp_height = (lUInt16) h; + item->origin_x = 0; + item->origin_y = 0; + item->advance = 0; + item->prev_global = NULL; + item->next_global = NULL; + item->prev_local = NULL; + item->next_local = NULL; + item->local_cache = local_cache; + return item; +} + +void LVFontGlyphCacheItem::freeItem(LVFontGlyphCacheItem *item) { + if (item) + ::free(item); +} + +#if USE_HARFBUZZ == 1 + +LVFontGlyphIndexCacheItem *LVFontGlyphIndexCacheItem::newItem(lUInt32 glyph_index, int w, int h) { + LVFontGlyphIndexCacheItem *item = (LVFontGlyphIndexCacheItem *) malloc( + sizeof(LVFontGlyphIndexCacheItem) + (w * h - 1) * sizeof(lUInt8)); + if (item) { + item->gindex = glyph_index; + item->bmp_width = (lUInt16) w; + item->bmp_height = (lUInt16) h; + item->origin_x = 0; + item->origin_y = 0; + item->advance = 0; + } + return item; +} + +void LVFontGlyphIndexCacheItem::freeItem(LVFontGlyphIndexCacheItem *item) { + if (item) + free(item); +} + +#endif // USE_HARFBUZZ==1 diff --git a/crengine/src/private/lvfontglyphcache.h b/crengine/src/private/lvfontglyphcache.h new file mode 100644 index 0000000000..2653dce93c --- /dev/null +++ b/crengine/src/private/lvfontglyphcache.h @@ -0,0 +1,126 @@ +/** @file lvfont_glyphcache.h + @brief font glyph cache interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FONTGLYPHCACHE_H_INCLUDED__ +#define __LV_FONTGLYPHCACHE_H_INCLUDED__ + +#include +#include "crsetup.h" +#include "lvtypes.h" + + +struct LVFontGlyphCacheItem; + +class LVFontGlobalGlyphCache { +private: + LVFontGlyphCacheItem *head; + LVFontGlyphCacheItem *tail; + int size; + int max_size; + + void removeNoLock(LVFontGlyphCacheItem *item); + + void putNoLock(LVFontGlyphCacheItem *item); + +public: + LVFontGlobalGlyphCache(int maxSize) + : head(NULL), tail(NULL), size(0), max_size(maxSize) { + } + + ~LVFontGlobalGlyphCache() { + clear(); + } + + void put(LVFontGlyphCacheItem *item); + + void remove(LVFontGlyphCacheItem *item); + + void refresh(LVFontGlyphCacheItem *item); + + void clear(); +}; + +class LVFontLocalGlyphCache { +private: + LVFontGlyphCacheItem *head; + LVFontGlyphCacheItem *tail; + LVFontGlobalGlyphCache *global_cache; + //int size; +public: + LVFontLocalGlyphCache(LVFontGlobalGlyphCache *globalCache) + : head(NULL), tail(NULL), global_cache(globalCache) {} + + ~LVFontLocalGlyphCache() { + clear(); + } + + void clear(); + + LVFontGlyphCacheItem *get(lUInt16 ch); + + void put(LVFontGlyphCacheItem *item); + + void remove(LVFontGlyphCacheItem *item); +}; + +struct LVFontGlyphCacheItem { + LVFontGlyphCacheItem *prev_global; + LVFontGlyphCacheItem *next_global; + LVFontGlyphCacheItem *prev_local; + LVFontGlyphCacheItem *next_local; + LVFontLocalGlyphCache *local_cache; + lChar16 ch; + lUInt16 bmp_width; + lUInt16 bmp_height; + lInt16 origin_x; + lInt16 origin_y; + lUInt16 advance; + lUInt8 bmp[1]; + + //======================================================================= + int getSize() { + return sizeof(LVFontGlyphCacheItem) + + (bmp_width * bmp_height - 1) * sizeof(lUInt8); + } + + static LVFontGlyphCacheItem * + newItem(LVFontLocalGlyphCache *local_cache, lChar16 ch, int w, int h); + + static void freeItem(LVFontGlyphCacheItem *item); +}; + +#if USE_HARFBUZZ == 1 + +struct LVFontGlyphIndexCacheItem { + lUInt32 gindex; + lUInt16 bmp_width; + lUInt16 bmp_height; + lInt16 origin_x; + lInt16 origin_y; + lUInt16 advance; + lUInt8 bmp[1]; + + //======================================================================= + int getSize() { + return sizeof(LVFontGlyphIndexCacheItem) + (bmp_width * bmp_height - 1) * sizeof(lUInt8); + } + + static LVFontGlyphIndexCacheItem *newItem(lUInt32 glyph_index, int w, int h); + + static void freeItem(LVFontGlyphIndexCacheItem *item); +}; + +#endif // USE_HARFBUZZ==1 + +#endif //__LV_FONTGLYPHCACHE_H_INCLUDED__ diff --git a/crengine/src/private/lvfreetypeface.cpp b/crengine/src/private/lvfreetypeface.cpp new file mode 100644 index 0000000000..113adc31d2 --- /dev/null +++ b/crengine/src/private/lvfreetypeface.cpp @@ -0,0 +1,1328 @@ +/** \file lvfreetypeface.cpp + \brief FreeType font implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + + +#include "lvfreetypeface.h" + +#include "../../include/lvfntman.h" +#include "../../include/lvfnt.h" +#include "../../include/lvtextfm.h" +#include "lvfontglyphcache.h" +#include "lvfontdef.h" +#include "lvfontcache.h" + + +#include "../include/gammatbl.h" + + +// fc-lang database +#include "fc-lang-cat.h" + +#if COLOR_BACKBUFFER == 0 +//#define USE_BITMAP_FONT +#endif + + + +//DEFINE_NULL_REF( LVFont ) + +#if (USE_FREETYPE == 1) + +extern int gammaIndex; // lvfntman.cpp + +extern lString8 familyName(FT_Face face); + + +inline int myabs(int n) { return n < 0 ? -n : n; } + +static lChar16 getReplacementChar(lUInt16 code) { + switch (code) { + case UNICODE_SOFT_HYPHEN_CODE: + return '-'; + case 0x0401: // CYRILLIC CAPITAL LETTER IO + return 0x0415; //CYRILLIC CAPITAL LETTER IE + case 0x0451: // CYRILLIC SMALL LETTER IO + return 0x0435; // CYRILLIC SMALL LETTER IE + case UNICODE_NO_BREAK_SPACE: + return ' '; + case 0x2010: + case 0x2011: + case 0x2012: + case 0x2013: + case 0x2014: + case 0x2015: + return '-'; + case 0x2018: + case 0x2019: + case 0x201a: + case 0x201b: + return '\''; + case 0x201c: + case 0x201d: + case 0x201e: + case 0x201f: + case 0x00ab: + case 0x00bb: + return '\"'; + case 0x2039: + return '<'; + case 0x203A: + return '>'; + case 0x2044: + return '/'; + case 0x2022: // css_lst_disc: + return '*'; + case 0x26AA: // css_lst_disc: + case 0x25E6: // css_lst_disc: + case 0x25CF: // css_lst_disc: + return 'o'; + case 0x25CB: // css_lst_circle: + return '*'; + case 0x25A0: // css_lst_square: + return '-'; + } + return 0; +} + + +static LVFontGlyphCacheItem * +newItem(LVFontLocalGlyphCache *local_cache, lChar16 ch, FT_GlyphSlot slot) // , bool drawMonochrome +{ + FONT_LOCAL_GLYPH_CACHE_GUARD + FT_Bitmap *bitmap = &slot->bitmap; + int w = bitmap->width; + int h = bitmap->rows; + LVFontGlyphCacheItem *item = LVFontGlyphCacheItem::newItem(local_cache, ch, w, h); + if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { //drawMonochrome + lUInt8 mask = 0x80; + const lUInt8 *ptr = (const lUInt8 *) bitmap->buffer; + lUInt8 *dst = item->bmp; + //int rowsize = ((w + 15) / 16) * 2; + for (int y = 0; y < h; y++) { + const lUInt8 *row = ptr; + mask = 0x80; + for (int x = 0; x < w; x++) { + *dst++ = (*row & mask) ? 0xFF : 00; + mask >>= 1; + if (!mask && x != w - 1) { + mask = 0x80; + row++; + } + } + ptr += bitmap->pitch;//rowsize; + } + } else { +#if 0 + if ( bitmap->pixel_mode==FT_PIXEL_MODE_MONO ) { + memset( item->bmp, 0, w*h ); + lUInt8 * srcrow = bitmap->buffer; + lUInt8 * dstrow = item->bmp; + for ( int y=0; y>(x&7)) ) ? 255 : 0; + if ((x&7)==7) + src++; + } + srcrow += bitmap->pitch; + dstrow += w; + } + } else { +#endif + memcpy(item->bmp, bitmap->buffer, w * h); + // correct gamma + if (gammaIndex != GAMMA_LEVELS / 2) + cr_correct_gamma_buf(item->bmp, w * h, gammaIndex); +// } + } + item->origin_x = (lInt16) slot->bitmap_left; + item->origin_y = (lInt16) slot->bitmap_top; + item->advance = (lUInt16) (myabs(slot->metrics.horiAdvance) >> 6); + return item; +} + +#if USE_HARFBUZZ == 1 + +static LVFontGlyphIndexCacheItem *newItem(lUInt32 index, FT_GlyphSlot slot) { + FONT_LOCAL_GLYPH_CACHE_GUARD + FT_Bitmap *bitmap = &slot->bitmap; + int w = bitmap->width; + int h = bitmap->rows; + LVFontGlyphIndexCacheItem *item = LVFontGlyphIndexCacheItem::newItem(index, w, h); + if (!item) + return 0; + if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { //drawMonochrome + lUInt8 mask = 0x80; + const lUInt8 *ptr = (const lUInt8 *) bitmap->buffer; + lUInt8 *dst = item->bmp; + //int rowsize = ((w + 15) / 16) * 2; + for (int y = 0; y < h; y++) { + const lUInt8 *row = ptr; + mask = 0x80; + for (int x = 0; x < w; x++) { + *dst++ = (*row & mask) ? 0xFF : 00; + mask >>= 1; + if (!mask && x != w - 1) { + mask = 0x80; + row++; + } + } + ptr += bitmap->pitch;//rowsize; + } + } else { + memcpy(item->bmp, bitmap->buffer, w * h); + // correct gamma + if (gammaIndex != GAMMA_LEVELS / 2) + cr_correct_gamma_buf(item->bmp, w * h, gammaIndex); +// } + } + item->origin_x = (lInt16) slot->bitmap_left; + item->origin_y = (lInt16) slot->bitmap_top; + item->advance = (lUInt16) (myabs(slot->metrics.horiAdvance) >> 6); + return item; +} + +#endif + +static lUInt16 char_flags[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // 0 00 + 0, 0, LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER, 0, 0, + LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER, 0, 0, // 8 08 + 0, 0, 0, 0, 0, 0, 0, 0, // 16 10 + 0, 0, 0, 0, 0, 0, 0, 0, // 24 18 + LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER, 0, 0, 0, 0, 0, 0, 0, // 32 20 + 0, 0, 0, 0, 0, LCHAR_DEPRECATED_WRAP_AFTER, 0, 0, // 40 28 + 0, 0, 0, 0, 0, 0, 0, 0, // 48 30 +}; + +#define GET_CHAR_FLAGS(ch) \ + (ch<48?char_flags[ch]: \ + (ch==UNICODE_SOFT_HYPHEN_CODE?LCHAR_ALLOW_WRAP_AFTER: \ + (ch==UNICODE_NO_BREAK_SPACE?LCHAR_DEPRECATED_WRAP_AFTER|LCHAR_IS_SPACE: \ + (ch==UNICODE_HYPHEN?LCHAR_DEPRECATED_WRAP_AFTER:0)))) + +#if USE_HARFBUZZ == 1 + +struct LVCharTriplet { + lChar16 prevChar; + lChar16 Char; + lChar16 nextChar; + + bool operator==(const struct LVCharTriplet &other) { + return prevChar == other.prevChar && Char == other.Char && nextChar == other.nextChar; + } +}; + +struct LVCharPosInfo { + int offset; + int width; +}; + +inline lUInt32 getHash(const struct LVCharTriplet &triplet) { + //return (triplet.prevChar * 1975317 + 164521) ^ (triplet.Char * 1975317 + 164521) ^ (triplet.nextChar * 1975317 + 164521); + return getHash((lUInt64) triplet.Char + + (((lUInt64) triplet.prevChar) << 16) + + (((lUInt64) triplet.nextChar) << 32)); +} + +#endif // USE_HARFBUZZ==1 + +void LVFreeTypeFace::setFallbackFont(LVFontRef font) { + _fallbackFont = font; + _fallbackFontIsSet = !font.isNull(); + clearCache(); +} + +LVFont *LVFreeTypeFace::getFallbackFont() { + if (_fallbackFontIsSet) + return _fallbackFont.get(); + if (fontMan->GetFallbackFontFace() != + _faceName) // to avoid circular link, disable fallback for fallback font + _fallbackFont = fontMan->GetFallbackFont(_size); + _fallbackFontIsSet = true; + return _fallbackFont.get(); +} + +LVFreeTypeFace::LVFreeTypeFace(LVMutex &mutex, FT_Library library, + LVFontGlobalGlyphCache *globalCache) + : _mutex(mutex), _fontFamily(css_ff_sans_serif), _library(library), _face(NULL), _size(0), + _hyphen_width(0), _baseline(0), _weight(400), _italic(0), _glyph_cache(globalCache), + _drawMonochrome(false), _allowKerning(false), _allowLigatures(false), + _hintingMode(HINTING_MODE_AUTOHINT), _fallbackFontIsSet(false) +#if USE_HARFBUZZ == 1 + , _glyph_cache2(256), _width_cache2(1024) +#endif +{ + _matrix.xx = 0x10000; + _matrix.yy = 0x10000; + _matrix.xy = 0; + _matrix.yx = 0; + _hintingMode = fontMan->GetHintingMode(); +#if USE_HARFBUZZ == 1 + _hb_font = 0; + _hb_buffer = hb_buffer_create(); + _hb_opt_kern_buffer = hb_buffer_create(); + // HarfBuzz features for full text shaping + hb_feature_from_string("-kern", -1, &_hb_features[0]); // font kerning + hb_feature_from_string("-liga", -1, &_hb_features[1]); // ligatures + + // HarfBuzz features for lighweight characters width calculating with caching + hb_feature_from_string("-kern", -1, + &_hb_opt_kern_features[0]); // Kerning: Fine horizontal positioning of one glyph to the next, based on the shapes of the glyphs + // We can enable these ones: + hb_feature_from_string("+mark", -1, + &_hb_opt_kern_features[1]); // Mark Positioning: Fine positioning of a mark glyph to a base character + hb_feature_from_string("+mkmk", -1, + &_hb_opt_kern_features[2]); // Mark-to-mark Positioning: Fine positioning of a mark glyph to another mark character + hb_feature_from_string("+curs", -1, + &_hb_opt_kern_features[3]); // Cursive Positioning: Precise positioning of a letter's connection to an adjacent one + hb_feature_from_string("+locl", -1, + &_hb_opt_kern_features[4]); // Substitutes character with the preferred form based on script language + + // We should disable these ones: + hb_feature_from_string("-liga", -1, + &_hb_opt_kern_features[5]); // Standard Ligatures: replaces (by default) sequence of characters with a single ligature glyph + hb_feature_from_string("-rlig", -1, + &_hb_opt_kern_features[6]); // Ligatures required for correct text display (any script, but in cursive) - Arabic, semitic + hb_feature_from_string("-clig", -1, + &_hb_opt_kern_features[7]); // Applies a second ligature feature based on a match of a character pattern within a context of surrounding patterns + hb_feature_from_string("-ccmp", -1, + &_hb_opt_kern_features[8]); // Glyph composition/decomposition: either calls a ligature replacement on a sequence of characters or replaces a character with a sequence of glyphs + // Provides logic that can for example effectively alter the order of input characters + hb_feature_from_string("-calt", -1, + &_hb_opt_kern_features[9]); // Contextual Alternates: Applies a second substitution feature based on a match of a character pattern within a context of surrounding patterns + hb_feature_from_string("-rclt", -1, + &_hb_opt_kern_features[10]); // Required Contextual Alternates: Contextual alternates required for correct text display which differs from the default join for other letters, required especially important by Arabic + hb_feature_from_string("-rvrn", -1, + &_hb_opt_kern_features[11]); // Required Variation Alternates: Special variants of a single character, which need apply to specific font variation, required by variable fonts + hb_feature_from_string("-ltra", -1, + &_hb_opt_kern_features[12]); // Left-to-right glyph alternates: Replaces characters with forms befitting left-to-right presentation + hb_feature_from_string("-ltrm", -1, + &_hb_opt_kern_features[13]); // Left-to-right mirrored forms: Replaces characters with possibly mirrored forms befitting left-to-right presentation + hb_feature_from_string("-rtla", -1, + &_hb_opt_kern_features[14]); // Right-to-left glyph alternates: Replaces characters with forms befitting right-to-left presentation + hb_feature_from_string("-rtlm", -1, + &_hb_opt_kern_features[15]); // Right-to-left mirrored forms: Replaces characters with possibly mirrored forms befitting right-to-left presentation + hb_feature_from_string("-frac", -1, + &_hb_opt_kern_features[16]); // Fractions: Converts figures separated by slash with diagonal fraction + hb_feature_from_string("-numr", -1, + &_hb_opt_kern_features[17]); // Numerator: Converts to appropriate fraction numerator form, invoked by frac + hb_feature_from_string("-dnom", -1, + &_hb_opt_kern_features[18]); // Denominator: Converts to appropriate fraction denominator form, invoked by frac + hb_feature_from_string("-rand", -1, + &_hb_opt_kern_features[19]); // Replaces character with random forms (meant to simulate handwriting) + hb_feature_from_string("-trak", -1, &_hb_opt_kern_features[20]); // Tracking (?) + hb_feature_from_string("-vert", -1, &_hb_opt_kern_features[21]); // Vertical (?) + // Especially needed with FreeSerif and french texts: -ccmp + // Especially needed with Fedra Serif and "The", "Thuringe": -calt + // These tweaks seem fragile (adding here +smcp to experiment with small caps would break FreeSerif again). + // So, when tuning these, please check it still behave well with FreeSerif. +#endif +} + +LVFreeTypeFace::~LVFreeTypeFace() { +#if USE_HARFBUZZ == 1 + if (_hb_buffer) + hb_buffer_destroy(_hb_buffer); + if (_hb_opt_kern_buffer) + hb_buffer_destroy(_hb_opt_kern_buffer); +#endif + Clear(); +} + +void LVFreeTypeFace::clearCache() { + _glyph_cache.clear(); + _wcache.clear(); +#if USE_HARFBUZZ == 1 + LVHashTable::pair *pair; + LVHashTable::iterator it = _glyph_cache2.forwardIterator(); + while ((pair = it.next())) { + LVFontGlyphIndexCacheItem *item = pair->value; + if (item) + LVFontGlyphIndexCacheItem::freeItem(item); + } + _glyph_cache2.clear(); + _width_cache2.clear(); +#endif +} + +int LVFreeTypeFace::getHyphenWidth() { + FONT_GUARD + if (!_hyphen_width) { + _hyphen_width = getCharWidth(UNICODE_SOFT_HYPHEN_CODE); + } + return _hyphen_width; +} + +void LVFreeTypeFace::setKerning(bool kerningEnabled) { + _allowKerning = kerningEnabled; +#if USE_HARFBUZZ == 1 + if (_allowKerning) { + hb_feature_from_string("+kern", -1, &_hb_features[0]); + hb_feature_from_string("+kern", -1, &_hb_opt_kern_features[0]); + } else { + hb_feature_from_string("-kern", -1, &_hb_features[0]); + hb_feature_from_string("-kern", -1, &_hb_opt_kern_features[0]); + } + // in cache may be found some ligatures, so clear it + clearCache(); +#endif +} + +void LVFreeTypeFace::setLigatures(bool ligaturesAllowed) { + // don't touch _hb_opt_kern_features here - lightweight characters width calculation always performed without any ligatures! + _allowLigatures = ligaturesAllowed; +#if USE_HARFBUZZ == 1 + if (_allowLigatures) + hb_feature_from_string("+liga", -1, &_hb_features[1]); + else + hb_feature_from_string("-liga", -1, &_hb_features[1]); + // in cache may be found some ligatures, so clear it + clearCache(); +#endif +} + +void LVFreeTypeFace::setHintingMode(hinting_mode_t mode) { + if (_hintingMode == mode) + return; + _hintingMode = mode; + clearCache(); +} + +void LVFreeTypeFace::setBitmapMode(bool drawBitmap) { + if (_drawMonochrome == drawBitmap) + return; + _drawMonochrome = drawBitmap; + clearCache(); +} + +bool LVFreeTypeFace::loadFromBuffer(LVByteArrayRef buf, int index, int size, + css_font_family_t fontFamily, bool monochrome, bool italicize) { + FONT_GUARD + _hintingMode = fontMan->GetHintingMode(); + _drawMonochrome = monochrome; + _fontFamily = fontFamily; + if (_face) + FT_Done_Face(_face); + int error = FT_New_Memory_Face(_library, buf->get(), buf->length(), index, + &_face); /* create face object */ + if (error) + return false; + if (_fileName.endsWith(".pfb") || _fileName.endsWith(".pfa")) { + lString8 kernFile = _fileName.substr(0, _fileName.length() - 4); + if (LVFileExists(Utf8ToUnicode(kernFile) + ".afm")) { + kernFile += ".afm"; + } else if (LVFileExists(Utf8ToUnicode(kernFile) + ".pfm")) { + kernFile += ".pfm"; + } else { + kernFile.clear(); + } + if (!kernFile.empty()) + error = FT_Attach_File(_face, kernFile.c_str()); + } + //FT_Face_SetUnpatentedHinting( _face, 1 ); + _slot = _face->glyph; + _faceName = familyName(_face); + CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str()); + //if ( !FT_IS_SCALABLE( _face ) ) { + // Clear(); + // return false; + // } + error = FT_Set_Pixel_Sizes( + _face, /* handle to face object */ + 0, /* pixel_width */ + size); /* pixel_height */ +#if USE_HARFBUZZ == 1 + if (FT_Err_Ok == error) { + if (_hb_font) + hb_font_destroy(_hb_font); + _hb_font = hb_ft_font_create(_face, 0); + if (!_hb_font) + error = FT_Err_Invalid_Argument; + } +#endif + if (error) { + Clear(); + return false; + } +#if 0 + int nheight = _face->size->metrics.height; + int targetheight = size << 6; + error = FT_Set_Pixel_Sizes( + _face, /* handle to face object */ + 0, /* pixel_width */ + (size * targetheight + nheight/2)/ nheight ); /* pixel_height */ +#endif + _height = _face->size->metrics.height >> 6; + _size = size; //(_face->size->metrics.height >> 6); + _baseline = _height + (_face->size->metrics.descender >> 6); + _weight = _face->style_flags & FT_STYLE_FLAG_BOLD ? 700 : 400; + _italic = _face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0; + + if (!error && italicize && !_italic) { + _matrix.xy = 0x10000 * 3 / 10; + FT_Set_Transform(_face, &_matrix, NULL); + _italic = true; + } + + if (error) { + // error + return false; + } + return true; +} + +bool +LVFreeTypeFace::loadFromFile(const char *fname, int index, int size, css_font_family_t fontFamily, + bool monochrome, bool italicize) { + FONT_GUARD + _hintingMode = fontMan->GetHintingMode(); + _drawMonochrome = monochrome; + _fontFamily = fontFamily; + if (fname) + _fileName = fname; + if (_fileName.empty()) + return false; + if (_face) + FT_Done_Face(_face); + int error = FT_New_Face(_library, _fileName.c_str(), index, &_face); /* create face object */ + if (error) + return false; + if (_fileName.endsWith(".pfb") || _fileName.endsWith(".pfa")) { + lString8 kernFile = _fileName.substr(0, _fileName.length() - 4); + if (LVFileExists(Utf8ToUnicode(kernFile) + ".afm")) { + kernFile += ".afm"; + } else if (LVFileExists(Utf8ToUnicode(kernFile) + ".pfm")) { + kernFile += ".pfm"; + } else { + kernFile.clear(); + } + if (!kernFile.empty()) + error = FT_Attach_File(_face, kernFile.c_str()); + } + //FT_Face_SetUnpatentedHinting( _face, 1 ); + _slot = _face->glyph; + _faceName = familyName(_face); + CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str()); + //if ( !FT_IS_SCALABLE( _face ) ) { + // Clear(); + // return false; + // } + error = FT_Set_Pixel_Sizes( + _face, /* handle to face object */ + 0, /* pixel_width */ + size); /* pixel_height */ +#if USE_HARFBUZZ == 1 + if (FT_Err_Ok == error) { + if (_hb_font) + hb_font_destroy(_hb_font); + _hb_font = hb_ft_font_create(_face, 0); + if (!_hb_font) + error = FT_Err_Invalid_Argument; + } +#endif + if (error) { + Clear(); + return false; + } +#if 0 + int nheight = _face->size->metrics.height; + int targetheight = size << 6; + error = FT_Set_Pixel_Sizes( + _face, /* handle to face object */ + 0, /* pixel_width */ + (size * targetheight + nheight/2)/ nheight ); /* pixel_height */ +#endif + _height = _face->size->metrics.height >> 6; + _size = size; //(_face->size->metrics.height >> 6); + _baseline = _height + (_face->size->metrics.descender >> 6); + _weight = _face->style_flags & FT_STYLE_FLAG_BOLD ? 700 : 400; + _italic = _face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0; + + if (!error && italicize && !_italic) { + _matrix.xy = 0x10000 * 3 / 10; + FT_Set_Transform(_face, &_matrix, NULL); + _italic = true; + } + + if (error) { + // error + return false; + } + return true; +} + +#if USE_HARFBUZZ == 1 + +lChar16 LVFreeTypeFace::filterChar(lChar16 code) { + lChar16 res; + if (code == '\t') + code = ' '; + FT_UInt ch_glyph_index = FT_Get_Char_Index(_face, code); + if (0 != ch_glyph_index) + res = code; + else { + res = getReplacementChar(code); + if (0 == res) + res = code; + } + return res; +} + +bool LVFreeTypeFace::hbCalcCharWidth(LVCharPosInfo *posInfo, const LVCharTriplet &triplet, + lChar16 def_char) { + if (!posInfo) + return false; + unsigned int segLen = 0; + int cluster; + hb_buffer_clear_contents(_hb_opt_kern_buffer); + if (0 != triplet.prevChar) { + hb_buffer_add(_hb_opt_kern_buffer, (hb_codepoint_t) triplet.prevChar, segLen); + segLen++; + } + hb_buffer_add(_hb_opt_kern_buffer, (hb_codepoint_t) triplet.Char, segLen); + cluster = segLen; + segLen++; + if (0 != triplet.nextChar) { + hb_buffer_add(_hb_opt_kern_buffer, (hb_codepoint_t) triplet.nextChar, segLen); + segLen++; + } + hb_buffer_set_content_type(_hb_opt_kern_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_guess_segment_properties(_hb_opt_kern_buffer); + hb_shape(_hb_font, _hb_opt_kern_buffer, _hb_opt_kern_features, 22); + unsigned int glyph_count = hb_buffer_get_length(_hb_opt_kern_buffer); + if (segLen == glyph_count) { + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(_hb_opt_kern_buffer, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(_hb_opt_kern_buffer, + &glyph_count); + if (0 != glyph_info[cluster].codepoint) { // glyph found for this char in this font + posInfo->offset = glyph_pos[cluster].x_offset >> 6; + posInfo->width = glyph_pos[cluster].x_advance >> 6; + } else { + // hb_shape() failed or glyph omitted in this font, use fallback font + glyph_info_t glyph; + LVFont *fallback = getFallbackFont(); + if (fallback) { + if (fallback->getGlyphInfo(triplet.Char, &glyph, def_char)) { + posInfo->offset = 0; + posInfo->width = glyph.width; + } + } else { + if (getGlyphInfo(def_char, &glyph, 0)) { + posInfo->offset = 0; + posInfo->width = glyph.width; + } else { + posInfo->offset = 0; + posInfo->width = _size; + } + } + } + } else { +#ifdef _DEBUG + CRLog::debug("hbCalcCharWidthWithKerning(): hb_buffer_get_length() return %d, must be %d, return value (-1)", glyph_count, segLen); +#endif + return false; + } + return true; +} + +#endif // USE_HARFBUZZ==1 + +FT_UInt LVFreeTypeFace::getCharIndex(lChar16 code, lChar16 def_char) { + if (code == '\t') + code = ' '; + FT_UInt ch_glyph_index = FT_Get_Char_Index(_face, code); + if (ch_glyph_index == 0) { + lUInt16 replacement = getReplacementChar(code); + if (replacement) + ch_glyph_index = FT_Get_Char_Index(_face, replacement); + if (ch_glyph_index == 0 && def_char) + ch_glyph_index = FT_Get_Char_Index(_face, def_char); + } + return ch_glyph_index; +} + +bool LVFreeTypeFace::getGlyphInfo(lUInt16 code, LVFont::glyph_info_t *glyph, lChar16 def_char) { + //FONT_GUARD + int glyph_index = getCharIndex(code, 0); + if (glyph_index == 0) { + LVFont *fallback = getFallbackFont(); + if (!fallback) { + // No fallback + glyph_index = getCharIndex(code, def_char); + if (glyph_index == 0) + return false; + } else { + // Fallback + return fallback->getGlyphInfo(code, glyph, def_char); + } + } + int flags = FT_LOAD_DEFAULT; + flags |= (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO); + if (_hintingMode == HINTING_MODE_AUTOHINT) + flags |= FT_LOAD_FORCE_AUTOHINT; + else if (_hintingMode == HINTING_MODE_DISABLED) + flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; + updateTransform(); + int error = FT_Load_Glyph( + _face, /* handle to face object */ + glyph_index, /* glyph index */ + flags); /* load flags, see below */ + if (error) + return false; + glyph->blackBoxX = (lUInt8) (_slot->metrics.width >> 6); + glyph->blackBoxY = (lUInt8) (_slot->metrics.height >> 6); + glyph->originX = (lInt8) (_slot->metrics.horiBearingX >> 6); + glyph->originY = (lInt8) (_slot->metrics.horiBearingY >> 6); + glyph->width = (lUInt8) (myabs(_slot->metrics.horiAdvance) >> 6); + return true; +} + +bool LVFreeTypeFace::checkFontLangCompat(const lString8 &langCode) { +#define FC_LANG_START_INTERVAL_CODE 2 + bool fullSupport = false; + bool partialSupport = false; + struct fc_lang_catalog *lang_ptr = fc_lang_cat; + unsigned int i; + bool found = false; + for (i = 0; i < fc_lang_cat_sz; i++) { + if (langCode.compare(lang_ptr->lang_code) == 0) { + found = true; + break; + } + lang_ptr++; + } + if (found) { + unsigned int codePoint = 0; + unsigned int tmp; + unsigned int first, second = 0; + bool inRange = false; + FT_UInt glyphIndex; + fullSupport = true; + for (i = 0;;) { + // get next codePoint + if (inRange && codePoint < second) { + codePoint++; + } else { + if (i >= lang_ptr->char_set_sz) + break; + tmp = lang_ptr->char_set[i]; + if (FC_LANG_START_INTERVAL_CODE == tmp) // code of start interval + { + if (i + 2 < lang_ptr->char_set_sz) { + i++; + first = lang_ptr->char_set[i]; + i++; + second = lang_ptr->char_set[i]; + inRange = true; + codePoint = first; + i++; + } else { + // broken language char set + //qDebug() << "broken language char set"; + fullSupport = false; + break; + } + } else { + codePoint = tmp; + inRange = false; + i++; + } + } + // check codePoint in this font + glyphIndex = FT_Get_Char_Index(_face, codePoint); + if (0 == glyphIndex) { + fullSupport = false; + } else { + partialSupport = true; + } + } + if (fullSupport) + CRLog::debug("checkFontLangCompat(): Font have full support of language %s", + langCode.c_str()); + else if (partialSupport) + CRLog::debug("checkFontLangCompat(): Font have partial support of language %s", + langCode.c_str()); + else + CRLog::debug("checkFontLangCompat(): Font DON'T have support of language %s", + langCode.c_str()); + } else + CRLog::debug("checkFontLangCompat(): Unsupported language code: %s", langCode.c_str()); + return fullSupport; +} + +lUInt16 LVFreeTypeFace::measureText(const lChar16 *text, int len, lUInt16 *widths, lUInt8 *flags, + int max_width, lChar16 def_char, int letter_spacing, + bool allow_hyphenation) { + FONT_GUARD + if (len <= 0 || _face == NULL) + return 0; + + if (letter_spacing < 0 || letter_spacing > 50) + letter_spacing = 0; + + int i; + + lUInt16 prev_width = 0; + uint32_t lastFitChar = 0; + updateTransform(); + // measure character widths +#if USE_HARFBUZZ == 1 + unsigned int glyph_count; + hb_glyph_info_t *glyph_info = 0; + hb_glyph_position_t *glyph_pos = 0; + if (/*_allowKerning &&*/ _allowLigatures) { + // Full shaping with HarfBuzz, very slow method but support ligatures. + hb_buffer_clear_contents(_hb_buffer); + hb_buffer_set_replacement_codepoint(_hb_buffer, def_char); + // fill HarfBuzz buffer with filtering + for (i = 0; i < len; i++) + hb_buffer_add(_hb_buffer, (hb_codepoint_t) filterChar(text[i]), i); + hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_guess_segment_properties(_hb_buffer); + // shape + hb_shape(_hb_font, _hb_buffer, _hb_features, 2); + glyph_count = hb_buffer_get_length(_hb_buffer); + glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, 0); + glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, 0); +#ifdef _DEBUG + if ((int)glyph_count != len) { + CRLog::debug( + "measureText(): glyph_count not equal source text length (ligature detected?), glyph_count=%d, len=%d", + glyph_count, len); + } +#endif + uint32_t j; + uint32_t cluster; + uint32_t prev_cluster = 0; + int skipped_chars = 0; // to add to 'i' at end of loop, as 'i' is used later and should be accurate + for (i = 0; i < (int) glyph_count; i++) { + cluster = glyph_info[i].cluster; + lChar16 ch = text[cluster]; + bool isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE); + flags[cluster] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch ); + hb_codepoint_t ch_glyph_index = glyph_info[i].codepoint; + if (0 != ch_glyph_index) // glyph found for this char in this font + widths[cluster] = prev_width + (glyph_pos[i].x_advance >> 6) + letter_spacing; + else { + // hb_shape() failed or glyph skipped in this font, use fallback font + int w = _wcache.get(ch); + if (0xFF == w) { + glyph_info_t glyph; + LVFont *fallback = getFallbackFont(); + if (fallback) { + if (fallback->getGlyphInfo(ch, &glyph, def_char)) { + w = glyph.width; + _wcache.put(ch, w); + } else { + w = _size; + _wcache.put(ch, w); + } + } else { + // use def_char if possible + if (getGlyphInfo(def_char, &glyph, 0)) { + w = glyph.width; + _wcache.put(ch, w); + } else { + w = _size; + _wcache.put(ch, w); + } + } + } + widths[cluster] = prev_width + w + letter_spacing; + } + for (j = prev_cluster + 1; j < cluster; j++) { + flags[j] = GET_CHAR_FLAGS(text[j]); + widths[j] = prev_width; // for chars replaced by ligature, so next chars of a ligature has width=0 + skipped_chars++; + } + prev_cluster = cluster; + if (!isHyphen) // avoid soft hyphens inside text string + prev_width = widths[cluster]; + if (prev_width > max_width) { + if (lastFitChar < cluster + 7) + break; + } else { + lastFitChar = cluster + 1; + } + } + // For case when ligature is the last glyph in measured text + if (prev_cluster < (uint32_t) (len - 1) && prev_width < (lUInt16) max_width) { + for (j = prev_cluster + 1; j < (uint32_t) len; j++) { + flags[j] = GET_CHAR_FLAGS(text[j]); + widths[j] = prev_width; + skipped_chars++; + } + } + // i is used below to "fill props for rest of chars", so make it accurate + i += skipped_chars; + } else { + // no ligatures, no HarfBuzz text shaping, only individual char shaping in hbCalcCharWidth() + struct LVCharTriplet triplet; + struct LVCharPosInfo posInfo; + triplet.Char = 0; + for (i = 0; i < len; i++) { + lChar16 ch = text[i]; + bool isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE); + + flags[i] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch ); + + triplet.prevChar = triplet.Char; + triplet.Char = ch; + if (i < len - 1) + triplet.nextChar = text[i + 1]; + else + triplet.nextChar = 0; + if (!_width_cache2.get(triplet, posInfo)) { + if (hbCalcCharWidth(&posInfo, triplet, def_char)) + _width_cache2.set(triplet, posInfo); + else { + posInfo.offset = 0; + posInfo.width = prev_width; + lastFitChar = i + 1; + continue; /* ignore errors */ + } + } + widths[i] = prev_width + posInfo.width + letter_spacing; + if (!isHyphen) // avoid soft hyphens inside text string + prev_width = widths[i]; + if (prev_width > max_width) { + if (lastFitChar < (uint32_t) (i + 7)) + break; + } else { + lastFitChar = i + 1; + } + } + } +#else // USE_HARFBUZZ==1 + FT_UInt previous = 0; + int error; +#if (ALLOW_KERNING==1) + int use_kerning = _allowKerning && FT_HAS_KERNING( _face ); +#endif + for ( i=0; i0 ) { + if ( ch_glyph_index==(FT_UInt)-1 ) + ch_glyph_index = getCharIndex( ch, def_char ); + if ( ch_glyph_index != 0 ) { + FT_Vector delta; + error = FT_Get_Kerning( _face, /* handle to face object */ + previous, /* left glyph index */ + ch_glyph_index, /* right glyph index */ + FT_KERNING_DEFAULT, /* kerning mode */ + &delta ); /* target vector */ + if ( !error ) + kerning = delta.x; + } + } +#endif + + flags[i] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch ); + + /* load glyph image into the slot (erase previous one) */ + int w = _wcache.get(ch); + if ( w==0xFF ) { + glyph_info_t glyph; + if ( getGlyphInfo( ch, &glyph, def_char ) ) { + w = glyph.width; + _wcache.put(ch, w); + } else { + widths[i] = prev_width; + lastFitChar = i + 1; + continue; /* ignore errors */ + } + if ( ch_glyph_index==(FT_UInt)-1 ) + ch_glyph_index = getCharIndex( ch, 0 ); + // error = FT_Load_Glyph( _face, /* handle to face object */ + // ch_glyph_index, /* glyph index */ + // FT_LOAD_DEFAULT ); /* load flags, see below */ + // if ( error ) { + // widths[i] = prev_width; + // continue; /* ignore errors */ + // } + } + widths[i] = prev_width + w + (kerning >> 6) + letter_spacing; + previous = ch_glyph_index; + if ( !isHyphen ) // avoid soft hyphens inside text string + prev_width = widths[i]; + if ( prev_width > max_width ) { + if ( lastFitChar < (uint32_t)(i + 7)) + break; + } else { + lastFitChar = i + 1; + } + } +#endif // USE_HARFBUZZ==1 + + // fill props for rest of chars + for (int ii = i; ii < len; ii++) { + flags[ii] = GET_CHAR_FLAGS(text[ii]); + } + + //maxFit = i; + + + // find last word + if (allow_hyphenation) { + if (!_hyphen_width) + _hyphen_width = getCharWidth(UNICODE_SOFT_HYPHEN_CODE); + if (lastFitChar > 3) { + int hwStart, hwEnd; + lStr_findWordBounds(text, len, lastFitChar - 1, hwStart, hwEnd); + if (hwStart < (int) (lastFitChar - 1) && hwEnd > hwStart + 3) { + //int maxw = max_width - (hwStart>0 ? widths[hwStart-1] : 0); + HyphMan::hyphenate(text + hwStart, hwEnd - hwStart, widths + hwStart, + flags + hwStart, _hyphen_width, max_width); + } + } + } + return lastFitChar; //i; +} + +lUInt32 LVFreeTypeFace::getTextWidth(const lChar16 *text, int len) { + static lUInt16 widths[MAX_LINE_CHARS + 1]; + static lUInt8 flags[MAX_LINE_CHARS + 1]; + if (len > MAX_LINE_CHARS) + len = MAX_LINE_CHARS; + if (len <= 0) + return 0; + lUInt16 res = measureText( + text, len, + widths, + flags, + 2048, // max_width, + L' ', // def_char + 0 + ); + if (res > 0 && res < MAX_LINE_CHARS) + return widths[res - 1]; + return 0; +} + +void LVFreeTypeFace::updateTransform() { + // static void * transformOwner = NULL; + // if ( transformOwner!=this ) { + // FT_Set_Transform(_face, &_matrix, NULL); + // transformOwner = this; + // } +} + +LVFontGlyphCacheItem *LVFreeTypeFace::getGlyph(lUInt16 ch, lChar16 def_char) { + //FONT_GUARD + FT_UInt ch_glyph_index = getCharIndex(ch, 0); + if (ch_glyph_index == 0) { + LVFont *fallback = getFallbackFont(); + if (!fallback) { + // No fallback + ch_glyph_index = getCharIndex(ch, def_char); + if (ch_glyph_index == 0) + return NULL; + } else { + // Fallback + return fallback->getGlyph(ch, def_char); + } + } + LVFontGlyphCacheItem *item = _glyph_cache.get(ch); + if (!item) { + + int rend_flags = FT_LOAD_RENDER | (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL + : (FT_LOAD_TARGET_MONO)); //|FT_LOAD_MONOCHROME|FT_LOAD_FORCE_AUTOHINT + if (_hintingMode == HINTING_MODE_AUTOHINT) + rend_flags |= FT_LOAD_FORCE_AUTOHINT; + else if (_hintingMode == HINTING_MODE_DISABLED) + rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; + /* load glyph image into the slot (erase previous one) */ + + updateTransform(); + int error = FT_Load_Glyph(_face, /* handle to face object */ + ch_glyph_index, /* glyph index */ + rend_flags); /* load flags, see below */ + if (error) { + return NULL; /* ignore errors */ + } + item = newItem(&_glyph_cache, ch, _slot); //, _drawMonochrome + _glyph_cache.put(item); + } + return item; +} + +#if USE_HARFBUZZ == 1 + +LVFontGlyphIndexCacheItem *LVFreeTypeFace::getGlyphByIndex(lUInt32 index) { + //FONT_GUARD + LVFontGlyphIndexCacheItem *item = 0; + if (!_glyph_cache2.get(index, item)) { + // glyph not found in cache, rendering... + int rend_flags = FT_LOAD_RENDER | (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL + : (FT_LOAD_TARGET_MONO)); //|FT_LOAD_MONOCHROME|FT_LOAD_FORCE_AUTOHINT + if (_hintingMode == HINTING_MODE_AUTOHINT) + rend_flags |= FT_LOAD_FORCE_AUTOHINT; + else if (_hintingMode == HINTING_MODE_DISABLED) + rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; + /* load glyph image into the slot (erase previous one) */ + + updateTransform(); + int error = FT_Load_Glyph(_face, /* handle to face object */ + index, /* glyph index */ + rend_flags); /* load flags, see below */ + if (error) { + return NULL; /* ignore errors */ + } + item = newItem(index, _slot); + if (item) + _glyph_cache2.set(index, item); + } + return item; +} + +#endif // USE_HARFBUZZ==1 + +int LVFreeTypeFace::getCharWidth(lChar16 ch, lChar16 def_char) { + int w = _wcache.get(ch); + if (w == 0xFF) { + glyph_info_t glyph; + if (getGlyphInfo(ch, &glyph, def_char)) { + w = glyph.width; + } else { + w = 0; + } + _wcache.put(ch, w); + } + return w; +} + +void LVFreeTypeFace::DrawTextString(LVDrawBuf *buf, int x, int y, const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette, bool addHyphen, + lUInt32 flags, int letter_spacing) { + FONT_GUARD + if (len <= 0 || _face == NULL) + return; + if (letter_spacing < 0 || letter_spacing > 50) + letter_spacing = 0; + lvRect clip; + buf->GetClipRect(&clip); + updateTransform(); + if (y + _height < clip.top || y >= clip.bottom) + return; + + //lUInt16 prev_width = 0; + lChar16 ch; + // measure character widths + bool isHyphen = false; + int x0 = x; +#if USE_HARFBUZZ == 1 + unsigned int i; + hb_glyph_info_t *glyph_info = 0; + hb_glyph_position_t *glyph_pos = 0; + unsigned int glyph_count; + int w; + unsigned int len_new = 0; + if (/*_allowKerning &&*/ _allowLigatures) { + // Full shaping with HarfBuzz, very slow method but support ligatures. + hb_buffer_clear_contents(_hb_buffer); + hb_buffer_set_replacement_codepoint(_hb_buffer, 0); + // fill HarfBuzz buffer with filtering + for (i = 0; i < (unsigned int) len; i++) { + ch = text[i]; + isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE) && (i < (unsigned int) (len - 1)); + if (!isHyphen) { // avoid soft hyphens inside text string + // Also replaced any chars to similar if the glyph is not found + hb_buffer_add(_hb_buffer, (hb_codepoint_t) filterChar(ch), i); + len_new++; + } + } + hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_guess_segment_properties(_hb_buffer); + // shape + hb_shape(_hb_font, _hb_buffer, _hb_features, 2); + glyph_count = hb_buffer_get_length(_hb_buffer); + glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, 0); + glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, 0); +#ifdef _DEBUG + if (glyph_count != len_new) { + CRLog::debug( + "DrawTextString(): glyph_count not equal source text length, glyph_count=%d, len=%d", + glyph_count, len_new); + } +#endif + for (i = 0; i < glyph_count; i++) { + if (0 == glyph_info[i].codepoint) { + // If HarfBuzz can't find glyph in current font + // using fallback font that used in getGlyph() + ch = text[glyph_info[i].cluster]; + LVFontGlyphCacheItem *item = getGlyph(ch, def_char); + if (item) { + w = item->advance; + buf->Draw(x + item->origin_x, + y + _baseline - item->origin_y, + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + x += w + letter_spacing; + } + } else { + LVFontGlyphIndexCacheItem *item = getGlyphByIndex(glyph_info[i].codepoint); + if (item) { + w = glyph_pos[i].x_advance >> 6; + buf->Draw(x + item->origin_x + (glyph_pos[i].x_offset >> 6), + y + _baseline - item->origin_y + (glyph_pos[i].y_offset >> 6), + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + x += w + letter_spacing; + } + } + } + } else { // ligatures disabled, kerning any + struct LVCharTriplet triplet; + struct LVCharPosInfo posInfo; + triplet.Char = 0; + for (i = 0; i < (unsigned int) len; i++) { + ch = text[i]; + isHyphen = (ch == UNICODE_SOFT_HYPHEN_CODE) && (i < (unsigned int) (len - 1)); + // avoid soft hyphens inside text string + if (isHyphen) + continue; + LVFontGlyphCacheItem *item = getGlyph(ch, def_char); + if (item) { + triplet.prevChar = triplet.Char; + triplet.Char = ch; + if (i < (unsigned int) (len - 1)) + triplet.nextChar = text[i + 1]; + else + triplet.nextChar = 0; + if (!_width_cache2.get(triplet, posInfo)) { + if (!hbCalcCharWidth(&posInfo, triplet, def_char)) { + posInfo.offset = 0; + posInfo.width = item->advance; + } + _width_cache2.set(triplet, posInfo); + } + buf->Draw(x + item->origin_x + posInfo.offset, + y + _baseline - item->origin_y, + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + x += posInfo.width + letter_spacing; + } + } + } + if (addHyphen) { + ch = UNICODE_SOFT_HYPHEN_CODE; + LVFontGlyphCacheItem *item = getGlyph(ch, def_char); + if (item) { + w = item->advance; + buf->Draw(x + item->origin_x, + y + _baseline - item->origin_y, + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + x += w + letter_spacing; + } + } +#else + FT_UInt previous = 0; + int i; + int error; +#if (ALLOW_KERNING==1) + int use_kerning = _allowKerning && FT_HAS_KERNING( _face ); +#endif + for ( i=0; i<=len; i++) { + if ( i==len && (!addHyphen || isHyphen) ) + break; + if ( i0 && ch_glyph_index>0 ) { + FT_Vector delta; + error = FT_Get_Kerning( _face, /* handle to face object */ + previous, /* left glyph index */ + ch_glyph_index, /* right glyph index */ + FT_KERNING_DEFAULT, /* kerning mode */ + &delta ); /* target vector */ + if ( !error ) + kerning = delta.x; + } +#endif + LVFontGlyphCacheItem * item = getGlyph(ch, def_char); + if ( !item ) + continue; + if ( (item && !isHyphen) || i>=len-1 ) { // avoid soft hyphens inside text string + int w = item->advance + (kerning >> 6); + buf->Draw( x + (kerning>>6) + item->origin_x, + y + _baseline - item->origin_y, + item->bmp, + item->bmp_width, + item->bmp_height, + palette); + + x += w + letter_spacing; + previous = ch_glyph_index; + } + } +#endif + if (flags & LTEXT_TD_MASK) { + // text decoration: underline, etc. + int h = _size > 30 ? 2 : 1; + lUInt32 cl = buf->GetTextColor(); + if ((flags & LTEXT_TD_UNDERLINE) || (flags & LTEXT_TD_BLINK)) { + int liney = y + _baseline + h; + buf->FillRect(x0, liney, x, liney + h, cl); + } + if (flags & LTEXT_TD_OVERLINE) { + int liney = y + h; + buf->FillRect(x0, liney, x, liney + h, cl); + } + if (flags & LTEXT_TD_LINE_THROUGH) { + // int liney = y + _baseline - _size/4 - h/2; + int liney = y + _baseline - _size * 2 / 7; + buf->FillRect(x0, liney, x, liney + h, cl); + } + } +} + +void LVFreeTypeFace::Clear() { + LVLock lock(_mutex); + clearCache(); +#if USE_HARFBUZZ == 1 + if (_hb_font) { + hb_font_destroy(_hb_font); + _hb_font = 0; + } +#endif + if (_face) { + FT_Done_Face(_face); + _face = NULL; + } +} + +#endif // (USE_FREETYPE==1) diff --git a/crengine/src/private/lvfreetypeface.h b/crengine/src/private/lvfreetypeface.h new file mode 100644 index 0000000000..a8219adc89 --- /dev/null +++ b/crengine/src/private/lvfreetypeface.h @@ -0,0 +1,346 @@ +/** \file lvfreetypeface.h + \brief FreeType font interface + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FREETYPEFACE_H_INCLUDED__ +#define __LV_FREETYPEFACE_H_INCLUDED__ + + +#include "../../include/crsetup.h" +#include "../../include/lvthread.h" +#include "lvfontglyphcache.h" +#include "lvfontdef.h" +#include "lvfontcache.h" + +// define to filter out all fonts except .ttf +//#define LOAD_TTF_FONTS_ONLY +// DEBUG ONLY +#if 0 +#define USE_FREETYPE 1 +#define USE_FONTCONFIG 1 +//#define DEBUG_FONT_SYNTHESIS 1 +//#define DEBUG_FONT_MAN 1 +//#define DEBUG_FONT_MAN_LOG_FILE "/tmp/font_man.log" +#endif + +#if (USE_FREETYPE == 1) + +#include +#include FT_FREETYPE_H + +#if USE_HARFBUZZ == 1 + +#include +#include +#include "../../include/lvhashtable.h" + +#endif + + +class LVFontGlyphWidthCache { +private: + lUInt8 *ptrs[128]; +public: + lUInt8 get(lChar16 ch) { + FONT_GLYPH_CACHE_GUARD + int inx = (ch >> 9) & 0x7f; + lUInt8 *ptr = ptrs[inx]; + if (!ptr) + return 0xFF; + return ptr[ch & 0x1FF]; + } + + void put(lChar16 ch, lUInt8 w) { + FONT_GLYPH_CACHE_GUARD + int inx = (ch >> 9) & 0x7f; + lUInt8 *ptr = ptrs[inx]; + if (!ptr) { + ptr = new lUInt8[512]; + ptrs[inx] = ptr; + memset(ptr, 0xFF, sizeof(lUInt8) * 512); + } + ptr[ch & 0x1FF] = w; + } + + void clear() { + FONT_GLYPH_CACHE_GUARD + for (int i = 0; i < 128; i++) { + if (ptrs[i]) + delete[] ptrs[i]; + ptrs[i] = NULL; + } + } + + LVFontGlyphWidthCache() { + memset(ptrs, 0, 128 * sizeof(lUInt8 *)); + } + + ~LVFontGlyphWidthCache() { + clear(); + } +}; + + +class LVFreeTypeFace : public LVFont { +protected: + LVMutex &_mutex; + lString8 _fileName; + lString8 _faceName; + css_font_family_t _fontFamily; + FT_Library _library; + FT_Face _face; + FT_GlyphSlot _slot; + FT_Matrix _matrix; /* transformation matrix */ + int _size; // caracter height in pixels + int _height; // full line height in pixels + int _hyphen_width; + int _baseline; + int _weight; + int _italic; + LVFontGlyphWidthCache _wcache; + LVFontLocalGlyphCache _glyph_cache; + bool _drawMonochrome; + bool _allowKerning; + bool _allowLigatures; + hinting_mode_t _hintingMode; + bool _fallbackFontIsSet; + LVFontRef _fallbackFont; +#if USE_HARFBUZZ == 1 + hb_buffer_t *_hb_buffer; + hb_font_t *_hb_font; + hb_feature_t _hb_features[2]; + LVHashTable _glyph_cache2; + LVHashTable _width_cache2; + hb_buffer_t *_hb_opt_kern_buffer; + hb_feature_t _hb_opt_kern_features[22]; +#endif +public: + + // fallback font support + /// set fallback font for this font + void setFallbackFont(LVFontRef font); + + /// get fallback font for this font + LVFont *getFallbackFont(); + + /// returns font weight + virtual int getWeight() const { return _weight; } + + /// returns italic flag + virtual int getItalic() const { return _italic; } + + /// sets face name + virtual void setFaceName(lString8 face) { _faceName = face; } + + LVMutex &getMutex() { return _mutex; } + + FT_Library getLibrary() { return _library; } + + LVFreeTypeFace(LVMutex &mutex, FT_Library library, LVFontGlobalGlyphCache *globalCache); + + virtual ~LVFreeTypeFace(); + + void clearCache(); + + virtual int getHyphenWidth(); + + /// get kerning mode: true==ON, false=OFF + virtual bool getKerning() const { return _allowKerning; } + + /// set kerning mode: true==ON, false=OFF + virtual void setKerning(bool kerningEnabled); + + /// get ligatures mode: true==allowed, false=not allowed + virtual bool getLigatures() const { return _allowLigatures; } + + /// set ligatures mode: true==allowed, false=not allowed + virtual void setLigatures(bool ligaturesAllowed); + + /// sets current hinting mode + virtual void setHintingMode(hinting_mode_t mode); + + /// returns current hinting mode + virtual hinting_mode_t getHintingMode() const { return _hintingMode; } + + /// get bitmap mode (true=bitmap, false=antialiased) + virtual bool getBitmapMode() { return _drawMonochrome; } + + /// set bitmap mode (true=bitmap, false=antialiased) + virtual void setBitmapMode(bool drawBitmap); + + bool loadFromBuffer(LVByteArrayRef buf, int index, int size, css_font_family_t fontFamily, + bool monochrome, bool italicize); + + bool loadFromFile(const char *fname, int index, int size, css_font_family_t fontFamily, + bool monochrome, bool italicize); + +#if USE_HARFBUZZ == 1 + + lChar16 filterChar(lChar16 code); + + bool hbCalcCharWidth(struct LVCharPosInfo *posInfo, const struct LVCharTriplet &triplet, + lChar16 def_char); + +#endif // USE_HARFBUZZ==1 + + FT_UInt getCharIndex(lChar16 code, lChar16 def_char); + + /** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found + */ + virtual bool getGlyphInfo(lUInt16 code, glyph_info_t *glyph, lChar16 def_char = 0); +/* + // USE GET_CHAR_FLAGS instead + inline int calcCharFlags( lChar16 ch ) + { + switch ( ch ) { + case 0x0020: + return LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER; + case UNICODE_SOFT_HYPHEN_CODE: + return LCHAR_ALLOW_WRAP_AFTER; + case '-': + return LCHAR_DEPRECATED_WRAP_AFTER; + case '\r': + case '\n': + return LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER; + default: + return 0; + } + } + */ + + /** + * @brief Check font for compatibility with language with langCode + * @param langCode language code, for example, "en" - English, "ru" - Russian + * @return true if font contains all glyphs for given language, false otherwise. + */ + virtual bool checkFontLangCompat(const lString8 &langCode); + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return number of characters before max_width reached + */ + virtual lUInt16 measureText( + const lChar16 *text, int len, + lUInt16 *widths, + lUInt8 *flags, + int max_width, + lChar16 def_char, + int letter_spacing = 0, + bool allow_hyphenation = true + ); + + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return width of specified string + */ + virtual lUInt32 getTextWidth( + const lChar16 *text, int len + ); + + void updateTransform(); + + /** \brief get glyph item + \param code is unicode character + \return glyph pointer if glyph was found, NULL otherwise + */ + virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0); + +#if USE_HARFBUZZ == 1 + + LVFontGlyphIndexCacheItem *getGlyphByIndex(lUInt32 index); + +#endif + +// /** \brief get glyph image in 1 byte per pixel format +// \param code is unicode character +// \param buf is buffer [width*height] to place glyph data +// \return true if glyph was found +// */ +// virtual bool getGlyphImage(lUInt16 ch, lUInt8 * bmp, lChar16 def_char=0) +// { +// LVFontGlyphCacheItem * item = getGlyph(ch); +// if ( item ) +// memcpy( bmp, item->bmp, item->bmp_width * item->bmp_height ); +// return item; +// } + + /// returns font baseline offset + virtual int getBaseline() { + return _baseline; + } + + /// returns font height + virtual int getHeight() const { + return _height; + } + + /// returns font character size + virtual int getSize() const { + return _size; + } + + /// returns char width + virtual int getCharWidth(lChar16 ch, lChar16 def_char = '?'); + + /// retrieves font handle + virtual void *GetHandle() { + return NULL; + } + + /// returns font typeface name + virtual lString8 getTypeFace() const { + return _faceName; + } + + /// returns font family id + virtual css_font_family_t getFontFamily() const { + return _fontFamily; + } + + virtual bool kerningEnabled() { +#if (ALLOW_KERNING == 1) +#if USE_HARFBUZZ == 1 + return _allowKerning; +#else + return _allowKerning && FT_HAS_KERNING( _face ); +#endif +#else + return false; +#endif + } + + /// draws text string + virtual void DrawTextString(LVDrawBuf *buf, int x, int y, + const lChar16 *text, int len, + lChar16 def_char, lUInt32 *palette, bool addHyphen, lUInt32 flags, + int letter_spacing); + + /// returns true if font is empty + virtual bool IsNull() const { + return _face == NULL; + } + + virtual bool operator!() const { + return _face == NULL; + } + + virtual void Clear(); +}; + +#endif // (USE_FREETYPE==1) + +#endif // __LV_FREETYPEFACE_H_INCLUDED__ diff --git a/crengine/src/private/lvfreetypefontman.cpp b/crengine/src/private/lvfreetypefontman.cpp new file mode 100644 index 0000000000..cf25afa1a3 --- /dev/null +++ b/crengine/src/private/lvfreetypefontman.cpp @@ -0,0 +1,1040 @@ +/** \file lvfreetypefontman.c + \brief FreeType font manager implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvfreetypefontman.h" +#include "lvfreetypeface.h" +#include "lvfontboldtransform.h" + +#if (USE_FONTCONFIG == 1) +#include +#endif + +#if (USE_FREETYPE == 1) + +lString8 familyName(FT_Face face) { + lString8 faceName(face->family_name); + if (faceName == "Arial" && face->style_name && !strcmp(face->style_name, "Narrow")) + faceName << " " << face->style_name; + else if ( /*faceName == "Arial" &&*/ face->style_name && strstr(face->style_name, "Condensed")) + faceName << " " << "Condensed"; + return faceName; +} + + +lUInt32 LVFreeTypeFontManager::GetFontListHash(int documentId) { + FONT_MAN_GUARD + return _cache.GetFontListHash(documentId) * 75 + _fallbackFontFace.getHash(); +} + +bool LVFreeTypeFontManager::SetFallbackFontFace(lString8 face) { + FONT_MAN_GUARD + if (face != _fallbackFontFace) { + _cache.clearFallbackFonts(); + CRLog::trace("Looking for fallback font %s", face.c_str()); + LVFontCacheItem *item = _cache.findFallback(face, -1); + if (!item) + face.clear(); + _fallbackFontFace = face; + // Somehow, with Fedra Serif (only!), changing the fallback font does + // not prevent glyphs from previous fallback font to be re-used... + // So let's clear glyphs caches too. + gc(); + clearGlyphCache(); + } + return !_fallbackFontFace.empty(); +} + +LVFontRef LVFreeTypeFontManager::GetFallbackFont(int size) { + FONT_MAN_GUARD + if (_fallbackFontFace.empty()) + return LVFontRef(); + // reduce number of possible distinct sizes for fallback font + if (size > 40) + size &= 0xFFF8; + else if (size > 28) + size &= 0xFFFC; + else if (size > 16) + size &= 0xFFFE; + LVFontCacheItem *item = _cache.findFallback(_fallbackFontFace, size); + if (!item->getFont().isNull()) + return item->getFont(); + return GetFont(size, 400, false, css_ff_sans_serif, _fallbackFontFace, -1); +} + +bool LVFreeTypeFontManager::isBitmapModeForSize(int size) { + bool isBitmap = false; + switch (_antialiasMode) { + case font_aa_none: + isBitmap = true; + break; + case font_aa_big: + isBitmap = size < 20 ? true : false; + break; + case font_aa_all: + default: + isBitmap = false; + break; + } + return isBitmap; +} + +void LVFreeTypeFontManager::SetAntialiasMode(int mode) { + _antialiasMode = mode; + gc(); + clearGlyphCache(); + FONT_MAN_GUARD + LVPtrVector *fonts = _cache.getInstances(); + for (int i = 0; i < fonts->length(); i++) { + fonts->get(i)->getFont()->setBitmapMode( + isBitmapModeForSize(fonts->get(i)->getFont()->getHeight())); + } +} + +void LVFreeTypeFontManager::SetHintingMode(hinting_mode_t mode) { + if (_hintingMode == mode) + return; + FONT_MAN_GUARD + CRLog::debug("Hinting mode is changed: %d", (int) mode); + _hintingMode = mode; + gc(); + clearGlyphCache(); + LVPtrVector *fonts = _cache.getInstances(); + for (int i = 0; i < fonts->length(); i++) { + fonts->get(i)->getFont()->setHintingMode(mode); + } +} + +void LVFreeTypeFontManager::setKerning(bool kerning) { + FONT_MAN_GUARD + _allowKerning = kerning; + gc(); + clearGlyphCache(); + LVPtrVector *fonts = _cache.getInstances(); + for (int i = 0; i < fonts->length(); i++) { + fonts->get(i)->getFont()->setKerning(kerning); + } +} + +void LVFreeTypeFontManager::setLigatures(bool ligatures) { + FONT_MAN_GUARD + _allowLigatures = ligatures; + gc(); + clearGlyphCache(); + LVPtrVector *fonts = _cache.getInstances(); + for (int i = 0; i < fonts->length(); i++) { + fonts->get(i)->getFont()->setLigatures(ligatures); + } +} + +void LVFreeTypeFontManager::clearGlyphCache() { + FONT_MAN_GUARD + _globalCache.clear(); +} + +bool LVFreeTypeFontManager::initSystemFonts() { +#if (DEBUG_FONT_SYNTHESIS == 1) + fontMan->RegisterFont(lString8("/usr/share/fonts/liberation/LiberationSans-Regular.ttf")); + CRLog::debug("fonts:"); + LVFontRef fnt4 = dumpFontRef( fontMan->GetFont(24, 200, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); + LVFontRef fnt1 = dumpFontRef( fontMan->GetFont(18, 200, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); + LVFontRef fnt2 = dumpFontRef( fontMan->GetFont(20, 400, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); + LVFontRef fnt3 = dumpFontRef( fontMan->GetFont(22, 600, false, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); + LVFontRef fnt5 = dumpFontRef( fontMan->GetFont(26, 400, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); + LVFontRef fnt6 = dumpFontRef( fontMan->GetFont(28, 600, true, css_ff_sans_serif, cs8("Arial, Helvetica") ) ); + CRLog::debug("end of font testing"); +#elif (USE_FONTCONFIG == 1) + { + CRLog::info("Reading list of system fonts using FONTCONFIG"); + lString16Collection fonts; + + int facesFound = 0; + + FcFontSet *fontset; + + FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_WEIGHT, FC_FAMILY, + FC_SLANT, FC_SPACING, FC_INDEX, + FC_STYLE, NULL); + FcPattern *pat = FcPatternCreate(); + //FcBool b = 1; + FcPatternAddBool(pat, FC_SCALABLE, 1); + + fontset = FcFontList(NULL, pat, os); + + FcPatternDestroy(pat); + FcObjectSetDestroy(os); + + // load fonts from file + CRLog::debug("FONTCONFIG: %d font files found", fontset->nfont); + for(int i = 0; i < fontset->nfont; i++) { + FcChar8 *s=(FcChar8*)""; + FcChar8 *family=(FcChar8*)""; + FcChar8 *style=(FcChar8*)""; + //FcBool b; + FcResult res; + //FC_SCALABLE + //res = FcPatternGetBool( fontset->fonts[i], FC_OUTLINE, 0, (FcBool*)&b); + //if(res != FcResultMatch) + // continue; + //if ( !b ) + // continue; // skip non-scalable fonts + res = FcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8 **)&s); + if(res != FcResultMatch) { + continue; + } + lString8 fn( (const char *)s ); + lString16 fn16( fn.c_str() ); + fn16.lowercase(); + if (!fn16.endsWith(".ttf") && !fn16.endsWith(".odf") && !fn16.endsWith(".otf") && !fn16.endsWith(".pfb") && !fn16.endsWith(".pfa") ) { + continue; + } + int weight = FC_WEIGHT_MEDIUM; + res = FcPatternGetInteger(fontset->fonts[i], FC_WEIGHT, 0, &weight); + if(res != FcResultMatch) { + CRLog::debug("no FC_WEIGHT for %s", s); + //continue; + } + switch ( weight ) { + case FC_WEIGHT_THIN: // 0 + weight = 100; + break; + case FC_WEIGHT_EXTRALIGHT: // 40 + //case FC_WEIGHT_ULTRALIGHT FC_WEIGHT_EXTRALIGHT + weight = 200; + break; + case FC_WEIGHT_LIGHT: // 50 + case FC_WEIGHT_BOOK: // 75 + case FC_WEIGHT_REGULAR: // 80 + //case FC_WEIGHT_NORMAL: FC_WEIGHT_REGULAR + weight = 400; + break; + case FC_WEIGHT_MEDIUM: // 100 + weight = 500; + break; + case FC_WEIGHT_DEMIBOLD: // 180 + //case FC_WEIGHT_SEMIBOLD: FC_WEIGHT_DEMIBOLD + weight = 600; + break; + case FC_WEIGHT_BOLD: // 200 + weight = 700; + break; + case FC_WEIGHT_EXTRABOLD: // 205 + //case FC_WEIGHT_ULTRABOLD: FC_WEIGHT_EXTRABOLD + weight = 800; + break; + case FC_WEIGHT_BLACK: // 210 + //case FC_WEIGHT_HEAVY: FC_WEIGHT_BLACK + weight = 900; + break; +#ifdef FC_WEIGHT_EXTRABLACK + case FC_WEIGHT_EXTRABLACK: // 215 + //case FC_WEIGHT_ULTRABLACK: FC_WEIGHT_EXTRABLACK + weight = 900; + break; +#endif + default: + weight = 400; + break; + } + FcBool scalable = 0; + res = FcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable); + int index = 0; + res = FcPatternGetInteger(fontset->fonts[i], FC_INDEX, 0, &index); + if(res != FcResultMatch) { + CRLog::debug("no FC_INDEX for %s", s); + //continue; + } + res = FcPatternGetString(fontset->fonts[i], FC_FAMILY, 0, (FcChar8 **)&family); + if(res != FcResultMatch) { + CRLog::debug("no FC_FAMILY for %s", s); + continue; + } + res = FcPatternGetString(fontset->fonts[i], FC_STYLE, 0, (FcChar8 **)&style); + if(res != FcResultMatch) { + CRLog::debug("no FC_STYLE for %s", s); + style = (FcChar8*)""; + //continue; + } + int slant = FC_SLANT_ROMAN; + res = FcPatternGetInteger(fontset->fonts[i], FC_SLANT, 0, &slant); + if(res != FcResultMatch) { + CRLog::debug("no FC_SLANT for %s", s); + //continue; + } + int spacing = 0; + res = FcPatternGetInteger(fontset->fonts[i], FC_SPACING, 0, &spacing); + if(res != FcResultMatch) { + //CRLog::debug("no FC_SPACING for %s", s); + //continue; + } + // int cr_weight; + // switch(weight) { + // case FC_WEIGHT_LIGHT: cr_weight = 200; break; + // case FC_WEIGHT_MEDIUM: cr_weight = 300; break; + // case FC_WEIGHT_DEMIBOLD: cr_weight = 500; break; + // case FC_WEIGHT_BOLD: cr_weight = 700; break; + // case FC_WEIGHT_BLACK: cr_weight = 800; break; + // default: cr_weight=300; break; + // } + css_font_family_t fontFamily = css_ff_sans_serif; + lString16 face16((const char *)family); + face16.lowercase(); + if ( spacing==FC_MONO ) + fontFamily = css_ff_monospace; + else if (face16.pos("sans") >= 0) + fontFamily = css_ff_sans_serif; + else if (face16.pos("serif") >= 0) + fontFamily = css_ff_serif; + + //css_ff_inherit, + //css_ff_serif, + //css_ff_sans_serif, + //css_ff_cursive, + //css_ff_fantasy, + //css_ff_monospace, + bool italic = (slant!=FC_SLANT_ROMAN); + + lString8 face((const char*)family); + lString16 style16((const char*)style); + style16.lowercase(); + if (style16.pos("condensed") >= 0) + face << " Condensed"; + else if (style16.pos("extralight") >= 0) + face << " Extra Light"; + + LVFontDef def( + lString8((const char*)s), + -1, // height==-1 for scalable fonts + weight, + italic, + fontFamily, + face, + index + ); + + CRLog::debug("FONTCONFIG: Font family:%s style:%s weight:%d slant:%d spacing:%d file:%s", family, style, weight, slant, spacing, s); + if ( _cache.findDuplicate( &def ) ) { + CRLog::debug("is duplicate, skipping"); + continue; + } + _cache.update( &def, LVFontRef(NULL) ); + + if ( scalable && !def.getItalic() ) { + LVFontDef newDef( def ); + newDef.setItalic(2); // can italicize + if ( !_cache.findDuplicate( &newDef ) ) + _cache.update( &newDef, LVFontRef(NULL) ); + } + + facesFound++; + + + } + + FcFontSetDestroy(fontset); + CRLog::info("FONTCONFIG: %d fonts registered", facesFound); + + const char * fallback_faces [] = { + "Arial Unicode MS", + "AR PL ShanHeiSun Uni", + "Liberation Sans", + "Roboto", + "DejaVu Sans", + "Noto Sans", + "Droid Sans", + NULL + }; + + for ( int i=0; fallback_faces[i]; i++ ) + if ( SetFallbackFontFace(lString8(fallback_faces[i])) ) { + CRLog::info("Fallback font %s is found", fallback_faces[i]); + break; + } else { + CRLog::trace("Fallback font %s is not found", fallback_faces[i]); + } + + return facesFound > 0; + } +#elif (CR3_OSX == 1) + + int facesFound = 0; + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Bold.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Italic.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Bold Italic.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Unicode.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow Bold.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Arial Narrow Italic.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Bold.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Italic.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Courier New Bold Italic.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Bold.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Italic.ttf")); + facesFound += fontMan->RegisterFont(lString8("/Library/Fonts/Georgia Bold Italic.ttf")); + + return facesFound > 0; + +#else + return false; +#endif +} + +LVFreeTypeFontManager::~LVFreeTypeFontManager() { + FONT_MAN_GUARD + _globalCache.clear(); + _cache.clear(); + if (_library) + FT_Done_FreeType(_library); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { + fclose(_log); + } +#endif +} + +LVFreeTypeFontManager::LVFreeTypeFontManager() + : _library(NULL), _globalCache(GLYPH_CACHE_SIZE) { + FONT_MAN_GUARD + int error = FT_Init_FreeType(&_library); + if (error) { + // error + CRLog::error("Error while initializing freetype library"); + } +#if (DEBUG_FONT_MAN == 1) + _log = fopen(DEBUG_FONT_MAN_LOG_FILE, "at"); + if ( _log ) { + fprintf(_log, "=========================== LOGGING STARTED ===================\n"); + } +#endif + _requiredChars = L"azAZ09?";//\x0410\x042F\x0430\x044F"; +} + +void LVFreeTypeFontManager::gc() // garbage collector +{ + FONT_MAN_GUARD + _cache.gc(); +} + +lString8 LVFreeTypeFontManager::makeFontFileName(lString8 name) { + lString8 filename = _path; + if (!filename.empty() && _path[_path.length() - 1] != PATH_SEPARATOR_CHAR) + filename << PATH_SEPARATOR_CHAR; + filename << name; + return filename; +} + +void LVFreeTypeFontManager::getFaceList(lString16Collection &list) { + FONT_MAN_GUARD + _cache.getFaceList(list); +} + +void LVFreeTypeFontManager::getFontFileNameList(lString16Collection &list) { + FONT_MAN_GUARD + _cache.getFontFileNameList(list); +} + +bool +LVFreeTypeFontManager::setalias(lString8 alias, lString8 facename, int id, bool italic, bool bold) { + FONT_MAN_GUARD + lString8 fontname = lString8("\0"); + LVFontDef def( + fontname, + 10, + 400, + true, + css_ff_inherit, + facename, + -1, + id + ); + LVFontCacheItem *item = _cache.find(&def); + LVFontDef def1( + fontname, + 10, + 400, + false, + css_ff_inherit, + alias, + -1, + id + ); + if (!item->getDef()->getName().empty()) { + _cache.removefont(&def1); + /*def.setTypeFace(alias); + def.setName(item->getDef()->getName()); + def.setItalic(1); + LVFontDef newDef(*item->getDef()); + newDef.setTypeFace(alias); + LVFontRef ref = item->getFont(); + _cache.update(&newDef, ref);*/ + int index = 0; + + FT_Face face = NULL; + + // for all faces in file + for (;; index++) { + int error = FT_New_Face(_library, item->getDef()->getName().c_str(), index, + &face); /* create face object */ + if (error) { + if (index == 0) { + CRLog::error("FT_New_Face returned error %d", error); + } + break; + } + //bool scal = FT_IS_SCALABLE( face ); + //bool charset = checkCharSet( face ); + + int num_faces = face->num_faces; + + css_font_family_t fontFamily = css_ff_sans_serif; + if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) + fontFamily = css_ff_monospace; + lString8 familyName(!facename.empty() ? facename : ::familyName(face)); + if (familyName == "Times" || familyName == "Times New Roman") + fontFamily = css_ff_serif; + + bool boldFlag = !facename.empty() ? bold : (face->style_flags & FT_STYLE_FLAG_BOLD) != + 0; + bool italicFlag = !facename.empty() ? italic : + (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0; + + LVFontDef def2( + item->getDef()->getName(), + -1, // height==-1 for scalable fonts + boldFlag ? 700 : 400, + italicFlag, + fontFamily, + alias, + index, + id + ); + + if (face) { + FT_Done_Face(face); + face = NULL; + } + + if (_cache.findDuplicate(&def2)) { + CRLog::trace("font definition is duplicate"); + return false; + } + _cache.update(&def2, LVFontRef(NULL)); + if (!def.getItalic()) { + LVFontDef newDef(def2); + newDef.setItalic(2); // can italicize + if (!_cache.findDuplicate(&newDef)) + _cache.update(&newDef, LVFontRef(NULL)); + } + if (index >= num_faces - 1) + break; + } + return true; + } else { + return false; + } +} + +LVFontRef +LVFreeTypeFontManager::GetFont(int size, int weight, bool italic, css_font_family_t family, + lString8 typeface, int documentId) { + FONT_MAN_GUARD +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { +fprintf(_log, "GetFont(size=%d, weight=%d, italic=%d, family=%d, typeface='%s')\n", + size, weight, italic?1:0, (int)family, typeface.c_str() ); +} +#endif + lString8 fontname; + LVFontDef def( + fontname, + size, + weight, + italic, + family, + typeface, + -1, + documentId + ); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) + fprintf( _log, "GetFont: %s %d %s %s\n", + typeface.c_str(), + size, + weight>400?"bold":"", + italic?"italic":"" ); +#endif + LVFontCacheItem *item = _cache.find(&def); +#if (DEBUG_FONT_MAN == 1) + if ( item && _log ) { //_log && + fprintf(_log, " found item: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s, weightDelta=%d) FontRef=%d\n", + item->getDef()->getName().c_str(), item->getDef()->getIndex(), item->getDef()->getSize(), item->getDef()->getWeight(), item->getDef()->getItalic()?1:0, + (int)item->getDef()->getFamily(), item->getDef()->getTypeFace().c_str(), + weight - item->getDef()->getWeight(), item->getFont().isNull()?0:item->getFont()->getHeight() + ); + } +#endif + bool italicize = false; + + LVFontDef newDef(*item->getDef()); + + if (!item->getFont().isNull()) { + int deltaWeight = weight - item->getDef()->getWeight(); + if (deltaWeight >= 200) { + // embolden + CRLog::debug("font: apply Embolding to increase weight from %d to %d", + newDef.getWeight(), newDef.getWeight() + 200); + newDef.setWeight(newDef.getWeight() + 200); + LVFontRef ref = LVFontRef(new LVFontBoldTransform(item->getFont(), &_globalCache)); + _cache.update(&newDef, ref); + return ref; + } else { + //fprintf(_log, " : fount existing\n"); + return item->getFont(); + } + } + lString8 fname = item->getDef()->getName(); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { + int index = item->getDef()->getIndex(); + fprintf(_log, " no instance: adding new one for filename=%s, index = %d\n", fname.c_str(), index ); + } +#endif + LVFreeTypeFace *font = new LVFreeTypeFace(_lock, _library, &_globalCache); + lString8 pathname = makeFontFileName(fname); + //def.setName( fname ); + //def.setIndex( index ); + + //if ( fname.empty() || pathname.empty() ) { + // pathname = lString8("arial.ttf"); + //} + + if (!item->getDef()->isRealItalic() && italic) { + //CRLog::debug("font: fake italic"); + newDef.setItalic(true); + italicize = true; + } + + //printf("going to load font file %s\n", fname.c_str()); + bool loaded = false; + if (item->getDef()->getBuf().isNull()) + loaded = font->loadFromFile(pathname.c_str(), item->getDef()->getIndex(), size, family, + isBitmapModeForSize(size), italicize); + else + loaded = font->loadFromBuffer(item->getDef()->getBuf(), item->getDef()->getIndex(), size, + family, isBitmapModeForSize(size), italicize); + if (loaded) { + //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), + // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); + LVFontRef ref(font); + font->setKerning(getKerning()); + font->setLigatures(getLigatures()); + font->setFaceName(item->getDef()->getTypeFace()); + newDef.setSize(size); + //item->setFont( ref ); + //_cache.update( def, ref ); + _cache.update(&newDef, ref); + int deltaWeight = weight - newDef.getWeight(); + if (1 && deltaWeight >= 200) { + // embolden + CRLog::debug("font: apply Embolding to increase weight from %d to %d", + newDef.getWeight(), newDef.getWeight() + 200); + newDef.setWeight(newDef.getWeight() + 200); + ref = LVFontRef(new LVFontBoldTransform(ref, &_globalCache)); + _cache.update(&newDef, ref); + } + // int rsz = ref->getSize(); + // if ( rsz!=size ) { + // size++; + // } + //delete def; + return ref; + } else { + //printf(" not found!\n"); + } + //delete def; + delete font; + return LVFontRef(NULL); +} + +bool LVFreeTypeFontManager::checkCharSet(FT_Face face) { + // TODO: check existance of required characters (e.g. cyrillic) + if (face == NULL) + return false; // invalid face + for (int i = 0; i < _requiredChars.length(); i++) { + lChar16 ch = _requiredChars[i]; + FT_UInt ch_glyph_index = FT_Get_Char_Index(face, ch); + if (ch_glyph_index == 0) { + CRLog::debug("Required char not found in font: %04x", ch); + return false; // no required char!!! + } + } + return true; +} + +bool +LVFreeTypeFontManager::checkFontLangCompat(const lString8 &typeface, const lString8 &langCode) { + LVFontRef fntRef = GetFont(10, 400, false, css_ff_inherit, typeface, -1); + if (!fntRef.isNull()) + return fntRef->checkFontLangCompat(langCode); + else + CRLog::debug("checkFontLangCompat(): typeface not found: %s", typeface.c_str()); + return true; +} + +/* +bool LVFreeTypeFontManager::isMonoSpaced( FT_Face face ) +{ + // TODO: check existance of required characters (e.g. cyrillic) + if (face==NULL) + return false; // invalid face + lChar16 ch1 = 'i'; + FT_UInt ch_glyph_index1 = FT_Get_Char_Index( face, ch1 ); + if ( ch_glyph_index1==0 ) + return false; // no required char!!! + int w1, w2; + int error1 = FT_Load_Glyph( face, // handle to face object + ch_glyph_index1, // glyph index + FT_LOAD_DEFAULT ); // load flags, see below + if ( error1 ) + w1 = 0; + else + w1 = (face->glyph->metrics.horiAdvance >> 6); + int error2 = FT_Load_Glyph( face, // handle to face object + ch_glyph_index2, // glyph index + FT_LOAD_DEFAULT ); // load flags, see below + if ( error2 ) + w2 = 0; + else + w2 = (face->glyph->metrics.horiAdvance >> 6); + + lChar16 ch2 = 'W'; + FT_UInt ch_glyph_index2 = FT_Get_Char_Index( face, ch2 ); + if ( ch_glyph_index2==0 ) + return false; // no required char!!! + return w1==w2; +} +*/ + +bool LVFreeTypeFontManager::RegisterDocumentFont(int documentId, LVContainerRef container, + lString16 name, lString8 faceName, bool bold, + bool italic) { + FONT_MAN_GUARD + lString8 name8 = UnicodeToUtf8(name); + CRLog::debug("RegisterDocumentFont(documentId=%d, path=%s)", documentId, name8.c_str()); + if (_cache.findDocumentFontDuplicate(documentId, name8)) { + return false; + } + LVStreamRef stream = container->OpenStream(name.c_str(), LVOM_READ); + if (stream.isNull()) + return false; + lUInt32 size = (lUInt32) stream->GetSize(); + if (size < 100 || size > 5000000) + return false; + LVByteArrayRef buf(new LVByteArray(size, 0)); + lvsize_t bytesRead = 0; + if (stream->Read(buf->get(), size, &bytesRead) != LVERR_OK || bytesRead != size) + return false; + bool res = false; + + int index = 0; + + FT_Face face = NULL; + + // for all faces in file + for (;; index++) { + int error = FT_New_Memory_Face(_library, buf->get(), buf->length(), index, + &face); /* create face object */ + if (error) { + if (index == 0) { + CRLog::error("FT_New_Memory_Face returned error %d", error); + } + break; + } + // bool scal = FT_IS_SCALABLE( face ); + // bool charset = checkCharSet( face ); + // //bool monospaced = isMonoSpaced( face ); + // if ( !scal || !charset ) { + // //#if (DEBUG_FONT_MAN==1) + // // if ( _log ) { + // CRLog::debug(" won't register font %s: %s", + // name.c_str(), !charset?"no mandatory characters in charset" : "font is not scalable" + // ); + // // } + // //#endif + // if ( face ) { + // FT_Done_Face( face ); + // face = NULL; + // } + // break; + // } + int num_faces = face->num_faces; + + css_font_family_t fontFamily = css_ff_sans_serif; + if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) + fontFamily = css_ff_monospace; + lString8 familyName(!faceName.empty() ? faceName : ::familyName(face)); + if (familyName == "Times" || familyName == "Times New Roman") + fontFamily = css_ff_serif; + + bool boldFlag = !faceName.empty() ? bold : (face->style_flags & FT_STYLE_FLAG_BOLD) != 0; + bool italicFlag = !faceName.empty() ? italic : (face->style_flags & FT_STYLE_FLAG_ITALIC) != + 0; + + LVFontDef def( + name8, + -1, // height==-1 for scalable fonts + boldFlag ? 700 : 400, + italicFlag, + fontFamily, + familyName, + index, + documentId, + buf + ); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { + fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n", + def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str() + ); + } +#endif + if (face) { + FT_Done_Face(face); + face = NULL; + } + + if (_cache.findDuplicate(&def)) { + CRLog::trace("font definition is duplicate"); + return false; + } + _cache.update(&def, LVFontRef(NULL)); + if (!def.getItalic()) { + LVFontDef newDef(def); + newDef.setItalic(2); // can italicize + if (!_cache.findDuplicate(&newDef)) + _cache.update(&newDef, LVFontRef(NULL)); + } + res = true; + + if (index >= num_faces - 1) + break; + } + + return res; +} + +void LVFreeTypeFontManager::UnregisterDocumentFonts(int documentId) { + _cache.removeDocumentFonts(documentId); +} + +bool LVFreeTypeFontManager::RegisterExternalFont(lString16 name, lString8 family_name, bool bold, + bool italic) { + if (name.startsWithNoCase(lString16("res://"))) + name = name.substr(6); + else if (name.startsWithNoCase(lString16("file://"))) + name = name.substr(7); + lString8 fname = UnicodeToUtf8(name); + + bool res = false; + + int index = 0; + + FT_Face face = NULL; + + // for all faces in file + for (;; index++) { + int error = FT_New_Face(_library, fname.c_str(), index, &face); /* create face object */ + if (error) { + if (index == 0) { + CRLog::error("FT_New_Face returned error %d", error); + } + break; + } + bool scal = FT_IS_SCALABLE(face); + bool charset = checkCharSet(face); + //bool monospaced = isMonoSpaced( face ); + if (!scal || !charset) { + //#if (DEBUG_FONT_MAN==1) + // if ( _log ) { + CRLog::debug(" won't register font %s: %s", + name.c_str(), + !charset ? "no mandatory characters in charset" : "font is not scalable" + ); + // } + //#endif + if (face) { + FT_Done_Face(face); + face = NULL; + } + break; + } + int num_faces = face->num_faces; + + css_font_family_t fontFamily = css_ff_sans_serif; + if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) + fontFamily = css_ff_monospace; + lString8 familyName(::familyName(face)); + if (familyName == "Times" || familyName == "Times New Roman") + fontFamily = css_ff_serif; + + LVFontDef def( + fname, + -1, // height==-1 for scalable fonts + bold ? 700 : 400, + italic ? true : false, + fontFamily, + family_name, + index + ); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { + fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n", + def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str() + ); + } +#endif + if (_cache.findDuplicate(&def)) { + CRLog::trace("font definition is duplicate"); + return false; + } + _cache.update(&def, LVFontRef(NULL)); + if (scal && !def.getItalic()) { + LVFontDef newDef(def); + newDef.setItalic(2); // can italicize + if (!_cache.findDuplicate(&newDef)) + _cache.update(&newDef, LVFontRef(NULL)); + } + res = true; + + if (face) { + FT_Done_Face(face); + face = NULL; + } + + if (index >= num_faces - 1) + break; + } + + return res; +} + +bool LVFreeTypeFontManager::RegisterFont(lString8 name) { + FONT_MAN_GUARD +#ifdef LOAD_TTF_FONTS_ONLY + if ( name.pos( cs8(".ttf") ) < 0 && name.pos( cs8(".TTF") ) < 0 ) + return false; // load ttf fonts only +#endif + //CRLog::trace("RegisterFont(%s)", name.c_str()); + lString8 fname = makeFontFileName(name); + //CRLog::trace("font file name : %s", fname.c_str()); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { + fprintf(_log, "RegisterFont( %s ) path=%s\n", + name.c_str(), fname.c_str() + ); + } +#endif + bool res = false; + + int index = 0; + + FT_Face face = NULL; + + // for all faces in file + for (;; index++) { + int error = FT_New_Face(_library, fname.c_str(), index, &face); /* create face object */ + if (error) { + if (index == 0) { + CRLog::error("FT_New_Face returned error %d", error); + } + break; + } + bool scal = FT_IS_SCALABLE(face) != 0; + bool charset = checkCharSet(face); + //bool monospaced = isMonoSpaced( face ); + if (!scal || !charset) { + //#if (DEBUG_FONT_MAN==1) + // if ( _log ) { + CRLog::debug(" won't register font %s: %s", + name.c_str(), + !charset ? "no mandatory characters in charset" : "font is not scalable" + ); + // } + //#endif + if (face) { + FT_Done_Face(face); + face = NULL; + } + break; + } + int num_faces = face->num_faces; + + css_font_family_t fontFamily = css_ff_sans_serif; + if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) + fontFamily = css_ff_monospace; + lString8 familyName(::familyName(face)); + if (familyName == "Times" || familyName == "Times New Roman") + fontFamily = css_ff_serif; + + LVFontDef def( + name, + -1, // height==-1 for scalable fonts + (face->style_flags & FT_STYLE_FLAG_BOLD) ? 700 : 400, + (face->style_flags & FT_STYLE_FLAG_ITALIC) ? true : false, + fontFamily, + familyName, + index + ); +#if (DEBUG_FONT_MAN == 1) + if ( _log ) { + fprintf(_log, "registering font: (file=%s[%d], size=%d, weight=%d, italic=%d, family=%d, typeface=%s)\n", + def.getName().c_str(), def.getIndex(), def.getSize(), def.getWeight(), def.getItalic()?1:0, (int)def.getFamily(), def.getTypeFace().c_str() + ); + } +#endif + + if (face) { + FT_Done_Face(face); + face = NULL; + } + + if (_cache.findDuplicate(&def)) { + CRLog::trace("font definition is duplicate"); + return false; + } + _cache.update(&def, LVFontRef(NULL)); + if (scal && !def.getItalic()) { + LVFontDef newDef(def); + newDef.setItalic(2); // can italicize + if (!_cache.findDuplicate(&newDef)) + _cache.update(&newDef, LVFontRef(NULL)); + } + res = true; + + if (index >= num_faces - 1) + break; + } + + return res; +} + +bool LVFreeTypeFontManager::Init(lString8 path) { + _path = path; + initSystemFonts(); + return (_library != NULL); +} + +#endif // (USE_FREETYPE==1) diff --git a/crengine/src/private/lvfreetypefontman.h b/crengine/src/private/lvfreetypefontman.h new file mode 100644 index 0000000000..fbfeba9615 --- /dev/null +++ b/crengine/src/private/lvfreetypefontman.h @@ -0,0 +1,128 @@ +/** @file lvfreetypefontman.h + @brief FreeType font manager interface + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_FREETYPEFONTMAN_H_INCLUDED__ +#define __LV_FREETYPEFONTMAN_H_INCLUDED__ + +#include "../../include/lvfntman.h" +#include "../../include/lvthread.h" +#include "lvfontglyphcache.h" +#include "lvfontdef.h" +#include "lvfontcache.h" + +#if (DEBUG_FONT_MAN == 1) +#include +#endif + +#if (USE_FREETYPE == 1) + +#include +#include FT_FREETYPE_H + + +class LVFreeTypeFontManager : public LVFontManager { +private: + lString8 _path; + lString8 _fallbackFontFace; + LVFontCache _cache; + FT_Library _library; + LVFontGlobalGlyphCache _globalCache; + lString16 _requiredChars; +#if (DEBUG_FONT_MAN == 1) + FILE * _log; +#endif + LVMutex _lock; +public: + /// get hash of installed fonts and fallback font + virtual lUInt32 GetFontListHash(int documentId); + + /// set fallback font + virtual bool SetFallbackFontFace(lString8 face); + + /// get fallback font face (returns empty string if no fallback font is set) + virtual lString8 GetFallbackFontFace() { return _fallbackFontFace; } + + /// returns fallback font for specified size + virtual LVFontRef GetFallbackFont(int size); + + bool isBitmapModeForSize(int size); + + /// set antialiasing mode + virtual void SetAntialiasMode(int mode); + + /// sets current gamma level + virtual void SetHintingMode(hinting_mode_t mode); + + /// sets current gamma level + virtual hinting_mode_t GetHintingMode() { + return _hintingMode; + } + + /// set kerning mode + virtual void setKerning(bool kerning); + + /// set ligatures mode + virtual void setLigatures(bool ligatures); + + /// clear glyph cache + virtual void clearGlyphCache(); + + virtual int GetFontCount() { + return _cache.length(); + } + + bool initSystemFonts(); + + virtual ~LVFreeTypeFontManager(); + + LVFreeTypeFontManager(); + + virtual void gc(); + + lString8 makeFontFileName(lString8 name); + + /// returns available typefaces + virtual void getFaceList(lString16Collection &list); + + /// returns registered font files + virtual void getFontFileNameList(lString16Collection &list); + + bool setalias(lString8 alias, lString8 facename, int id, bool italic, bool bold); + + virtual LVFontRef + GetFont(int size, int weight, bool italic, css_font_family_t family, lString8 typeface, + int documentId); + + bool checkCharSet(FT_Face face); + + virtual bool checkFontLangCompat(const lString8 &typeface, const lString8 &langCode); + + //bool isMonoSpaced( FT_Face face ); + /// registers document font + virtual bool RegisterDocumentFont(int documentId, LVContainerRef container, lString16 name, + lString8 faceName, bool bold, bool italic); + + /// unregisters all document fonts + virtual void UnregisterDocumentFonts(int documentId); + + virtual bool RegisterExternalFont(lString16 name, lString8 family_name, bool bold, bool italic); + + virtual bool RegisterFont(lString8 name); + + virtual bool Init(lString8 path); +}; + +#endif // (USE_FREETYPE==1) + +#endif // __LV_FREETYPEFONTMAN_H_INCLUDED__ diff --git a/crengine/src/private/lvwin32font.cpp b/crengine/src/private/lvwin32font.cpp new file mode 100644 index 0000000000..d15d516684 --- /dev/null +++ b/crengine/src/private/lvwin32font.cpp @@ -0,0 +1,656 @@ +/** @file lvwin32font.cpp + @brief Win32 font implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvwin32font.h" +#include "../../include/lvfnt.h" + + +#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE != 1 +void LVBaseWin32Font::Clear() +{ + if (_hfont) + { + DeleteObject(_hfont); + _hfont = NULL; + _height = 0; + _baseline = 0; + } +} + +bool LVBaseWin32Font::Create( const LOGFONTA & lf ) +{ + if (!IsNull()) + Clear(); + memcpy( &_logfont, &lf, sizeof(LOGFONTA)); + _hfont = CreateFontIndirectA( &lf ); + if (!_hfont) + return false; + //memcpy( &_logfont, &lf, sizeof(LOGFONT) ); + // get text metrics + SelectObject( _drawbuf.GetDC(), _hfont ); + TEXTMETRICW tm; + GetTextMetricsW( _drawbuf.GetDC(), &tm ); + _logfont.lfHeight = tm.tmHeight; + _logfont.lfWeight = tm.tmWeight; + _logfont.lfItalic = tm.tmItalic; + _logfont.lfCharSet = tm.tmCharSet; + GetTextFaceA( _drawbuf.GetDC(), sizeof(_logfont.lfFaceName)-1, _logfont.lfFaceName ); + _height = tm.tmHeight; + _baseline = _height - tm.tmDescent; + return true; +} + +bool LVBaseWin32Font::Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ) +{ + if (!IsNull()) + Clear(); + // + LOGFONTA lf; + memset(&lf, 0, sizeof(LOGFONTA)); + lf.lfHeight = size; + lf.lfWeight = weight; + lf.lfItalic = italic?1:0; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + //lf.lfQuality = ANTIALIASED_QUALITY; //PROOF_QUALITY; +#ifdef USE_BITMAP_FONT + lf.lfQuality = NONANTIALIASED_QUALITY; //CLEARTYPE_QUALITY; //PROOF_QUALITY; +#else + lf.lfQuality = 5; //CLEARTYPE_QUALITY; //PROOF_QUALITY; +#endif + strcpy(lf.lfFaceName, typeface.c_str()); + _typeface = typeface; + _family = family; + switch (family) + { + case css_ff_serif: + lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN; + break; + case css_ff_sans_serif: + lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS; + break; + case css_ff_cursive: + lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SCRIPT; + break; + case css_ff_fantasy: + lf.lfPitchAndFamily = VARIABLE_PITCH | FF_DECORATIVE; + break; + case css_ff_monospace: + lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN; + break; + default: + lf.lfPitchAndFamily = VARIABLE_PITCH | FF_DONTCARE; + break; + } + _hfont = CreateFontIndirectA( &lf ); + if (!_hfont) + return false; + //memcpy( &_logfont, &lf, sizeof(LOGFONT) ); + // get text metrics + SelectObject( _drawbuf.GetDC(), _hfont ); + TEXTMETRICW tm; + GetTextMetricsW( _drawbuf.GetDC(), &tm ); + memset(&_logfont, 0, sizeof(LOGFONT)); + _logfont.lfHeight = tm.tmHeight; + _logfont.lfWeight = tm.tmWeight; + _logfont.lfItalic = tm.tmItalic; + _logfont.lfCharSet = tm.tmCharSet; + GetTextFaceA( _drawbuf.GetDC(), sizeof(_logfont.lfFaceName)-1, _logfont.lfFaceName ); + _height = tm.tmHeight; + _baseline = _height - tm.tmDescent; + return true; +} + + +/** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found +*/ +bool LVWin32DrawFont::getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char ) +{ + return false; +} + +/// returns char width +int LVWin32DrawFont::getCharWidth( lChar16 ch, lChar16 def_char ) +{ + if (_hfont==NULL) + return 0; + // measure character widths + GCP_RESULTSW gcpres; + memset( &gcpres, 0, sizeof(gcpres) ); + gcpres.lStructSize = sizeof(gcpres); + lChar16 str[2]; + str[0] = ch; + str[1] = 0; + int dx[2]; + gcpres.lpDx = dx; + gcpres.nMaxFit = 1; + gcpres.nGlyphs = 1; + + lUInt32 res = GetCharacterPlacementW( + _drawbuf.GetDC(), + str, + 1, + 100, + &gcpres, + GCP_MAXEXTENT); //|GCP_USEKERNING + + if (!res) + { + // ERROR + return 0; + } + + return dx[0]; +} + +lUInt32 LVWin32DrawFont::getTextWidth( const lChar16 * text, int len ) +{ + // + static lUInt16 widths[MAX_LINE_CHARS+1]; + static lUInt8 flags[MAX_LINE_CHARS+1]; + if ( len>MAX_LINE_CHARS ) + len = MAX_LINE_CHARS; + if ( len<=0 ) + return 0; + lUInt16 res = measureText( + text, len, + widths, + flags, + 2048, // max_width, + L' ' // def_char + ); + if ( res>0 && res dx( len+1, 0 ); + gcpres.lpDx = dx.ptr(); + gcpres.nMaxFit = len; + gcpres.nGlyphs = len; + + lUInt32 res = GetCharacterPlacementW( + _drawbuf.GetDC(), + pstr, + len, + max_width, + &gcpres, + GCP_MAXEXTENT); //|GCP_USEKERNING + if (!res) + { + // ERROR + widths[0] = 0; + flags[0] = 0; + return 1; + } + + if ( !_hyphen_width ) + _hyphen_width = getCharWidth( UNICODE_SOFT_HYPHEN_CODE ); + + lUInt16 wsum = 0; + lUInt16 nchars = 0; + lUInt16 gwidth = 0; + lUInt8 bflags; + int isSpace; + lChar16 ch; + int hwStart, hwEnd; + + assert(pstr[len]==0); + + for ( ; wsum < max_width && nchars < len && ncharsgi.width : 0; + + //try to add hyphen + for (hwStart=nchars-1; hwStart>0; hwStart--) + { + if (lvfontIsUnicodeSpace(text[hwStart])) + { + hwStart++; + break; + } + } + for (hwEnd=nchars; hwEndGetClipRect(&clip); + if (y > clip.bottom || y+_height < clip.top) + return; + if (buf->GetBitsPerPixel()<16) + { + // draw using backbuffer + SIZE sz; + if ( !GetTextExtentPoint32W(_drawbuf.GetDC(), + str.c_str(), str.length(), &sz) ) + return; + LVColorDrawBuf colorbuf( sz.cx, sz.cy ); + colorbuf.Clear(0xFFFFFF); + HDC dc = colorbuf.GetDC(); + SelectObject(dc, _hfont); + SetTextColor(dc, 0x000000); + SetBkMode(dc, TRANSPARENT); + //ETO_OPAQUE + if (ExtTextOutW( dc, 0, 0, + 0, //ETO_OPAQUE + NULL, + str.c_str(), str.length(), NULL )) + { + // COPY colorbuf to buf with converting + colorbuf.DrawTo( buf, x, y, 0, NULL ); + } + } + else + { + // draw directly on DC + //TODO + HDC dc = ((LVColorDrawBuf*)buf)->GetDC(); + HFONT oldfont = (HFONT)SelectObject( dc, _hfont ); + SetTextColor( dc, RevRGB(buf->GetTextColor()) ); + SetBkMode(dc, TRANSPARENT); + ExtTextOutW( dc, x, y, + 0, //ETO_OPAQUE + NULL, + str.c_str(), str.length(), NULL ); + SelectObject( dc, oldfont ); + } +} + +/** \brief get glyph image in 1 byte per pixel format + \param code is unicode character + \param buf is buffer [width*height] to place glyph data + \return true if glyph was found +*/ +bool LVWin32DrawFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) +{ + return false; +} + + + +int LVWin32Font::GetGlyphIndex( HDC hdc, wchar_t code ) +{ + wchar_t s[2]; + wchar_t g[2]; + s[0] = code; + s[1] = 0; + g[0] = 0; + GCP_RESULTSW gcp; + gcp.lStructSize = sizeof(GCP_RESULTSW); + gcp.lpOutString = NULL; + gcp.lpOrder = NULL; + gcp.lpDx = NULL; + gcp.lpCaretPos = NULL; + gcp.lpClass = NULL; + gcp.lpGlyphs = g; + gcp.nGlyphs = 2; + gcp.nMaxFit = 2; + + DWORD res = GetCharacterPlacementW( + hdc, s, 1, + 1000, + &gcp, + 0 + ); + if (!res) + return 0; + return g[0]; +} + + +glyph_t * LVWin32Font::GetGlyphRec( lChar16 ch ) +{ + glyph_t * p = _cache.get( ch ); + if (p->flgNotExists) + return NULL; + if (p->flgValid) + return p; + p->flgNotExists = true; + lUInt16 gi = GetGlyphIndex( _drawbuf.GetDC(), ch ); + if (gi==0 || gi==0xFFFF || (gi==_unknown_glyph_index && ch!=' ')) + { + // glyph not found + p->flgNotExists = true; + return NULL; + } + GLYPHMETRICS metrics; + p->glyph = NULL; + + MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; + lUInt32 res; + res = GetGlyphOutlineW( _drawbuf.GetDC(), ch, + GGO_METRICS, + &metrics, + 0, + NULL, + &identity ); + if (res==GDI_ERROR) + return NULL; + int gs = GetGlyphOutlineW( _drawbuf.GetDC(), ch, +#ifdef USE_BITMAP_FONT + GGO_BITMAP, //GGO_METRICS +#else + GGO_GRAY8_BITMAP, //GGO_METRICS +#endif + &metrics, + 0, + NULL, + &identity ); + if (gs>0x10000 || gs<0) + return NULL; + + p->gi.blackBoxX = metrics.gmBlackBoxX; + p->gi.blackBoxY = metrics.gmBlackBoxY; + p->gi.originX = (lInt8)metrics.gmptGlyphOrigin.x; + p->gi.originY = (lInt8)metrics.gmptGlyphOrigin.y; + p->gi.width = (lUInt8)metrics.gmCellIncX; + + if (p->gi.blackBoxX>0 && p->gi.blackBoxY>0) + { + p->glyph = new unsigned char[p->gi.blackBoxX * p->gi.blackBoxY]; + if (gs>0) + { + lUInt8 * glyph = new unsigned char[gs]; + res = GetGlyphOutlineW( _drawbuf.GetDC(), ch, +#ifdef USE_BITMAP_FONT + GGO_BITMAP, //GGO_METRICS +#else + GGO_GRAY8_BITMAP, //GGO_METRICS +#endif + &metrics, + gs, + glyph, + &identity ); + if (res==GDI_ERROR) + { + delete[] glyph; + return NULL; + } +#ifdef USE_BITMAP_FONT + int glyph_row_size = (p->gi.blackBoxX + 31) / 8 / 4 * 4; +#else + int glyph_row_size = (p->gi.blackBoxX + 3)/ 4 * 4; +#endif + lUInt8 * src = glyph; + lUInt8 * dst = p->glyph; + for (int y=0; ygi.blackBoxY; y++) + { + for (int x = 0; xgi.blackBoxX; x++) + { +#ifdef USE_BITMAP_FONT + lUInt8 b = (src[x>>3] & (0x80>>(x&7))) ? 0xFC : 0; +#else + lUInt8 b = src[x]; + if (b>=64) + b = 63; + b = (b<<2) & 0xFC; +#endif + dst[x] = b; + } + src += glyph_row_size; + dst += p->gi.blackBoxX; + } + delete[] glyph; + //*(dst-1) = 0xFF; + } + else + { + // empty glyph + for (int i=p->gi.blackBoxX * p->gi.blackBoxY-1; i>=0; i--) + p->glyph[i] = 0; + } + } + // found! + p->flgValid = true; + p->flgNotExists = false; + return p; +} + +/** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found +*/ +bool LVWin32Font::getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char ) +{ + if (_hfont==NULL) + return false; + glyph_t * p = GetGlyphRec( code ); + if (!p) + return false; + *glyph = p->gi; + return true; +} + +lUInt32 LVWin32Font::getTextWidth( const lChar16 * text, int len ) +{ + // + static lUInt16 widths[MAX_LINE_CHARS+1]; + static lUInt8 flags[MAX_LINE_CHARS+1]; + if ( len>MAX_LINE_CHARS ) + len = MAX_LINE_CHARS; + if ( len<=0 ) + return 0; + lUInt16 res = measureText( + text, len, + widths, + flags, + 2048, // max_width, + L' ' // def_char + ); + if ( res>0 && resgi.width : 0; + + for ( ; wsum < max_width && nchars < len; nchars++ ) + { + bflags = 0; + ch = text[nchars]; + isSpace = lvfontIsUnicodeSpace(ch); + if (isSpace || ch == UNICODE_SOFT_HYPHEN_CODE ) + bflags |= LCHAR_ALLOW_WRAP_AFTER; + if (ch == '-') + bflags |= LCHAR_DEPRECATED_WRAP_AFTER; + if (isSpace) + bflags |= LCHAR_IS_SPACE; + glyph = GetGlyphRec( ch ); + if (!glyph && def_char) + glyph = GetGlyphRec( def_char ); + gwidth = glyph ? glyph->gi.width : 0; + widths[nchars] = wsum + gwidth; + if ( ch != UNICODE_SOFT_HYPHEN_CODE ) + wsum += gwidth; /* don't include hyphens to width */ + flags[nchars] = bflags; + } + + //try to add hyphen + for (hwStart=nchars-1; hwStart>0; hwStart--) + { + if (lvfontIsUnicodeSpace(text[hwStart])) + { + hwStart++; + break; + } + } + for (hwEnd=nchars; hwEndgi.blackBoxX*p->gi.blackBoxY; + if (gs>0) + memcpy( buf, p->glyph, gs ); + return true; +} + +void LVWin32Font::Clear() +{ + LVBaseWin32Font::Clear(); + _cache.clear(); +} + +bool LVWin32Font::Create( const LOGFONTA & lf ) +{ + if (!LVBaseWin32Font::Create(lf)) + return false; + _unknown_glyph_index = GetGlyphIndex( _drawbuf.GetDC(), 1 ); + return true; +} + +bool LVWin32Font::Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ) +{ + if (!LVBaseWin32Font::Create(size, weight, italic, family, typeface)) + return false; + _unknown_glyph_index = GetGlyphIndex( _drawbuf.GetDC(), 1 ); + return true; +} + +#endif // !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE != 1 diff --git a/crengine/src/private/lvwin32font.h b/crengine/src/private/lvwin32font.h new file mode 100644 index 0000000000..65c1ec6e90 --- /dev/null +++ b/crengine/src/private/lvwin32font.h @@ -0,0 +1,349 @@ +/** \file lvwin32font.h + \brief Win32 font interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_WIN32FONT_H_INCLUDED__ +#define __LV_WIN32FONT_H_INCLUDED__ + +#include "../../include/crsetup.h" +#include "../../include/cssdef.h" +#include "../../include/lvstring.h" +#include "../../include/lvdrawbuf.h" +#include "lvbasefont.h" + +#if !defined(__SYMBIAN32__) && defined(_WIN32) +extern "C" { +#include +} +#endif + + +#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE != 1 +class LVBaseWin32Font : public LVBaseFont +{ +protected: + HFONT _hfont; + LOGFONTA _logfont; + int _height; + int _baseline; + LVColorDrawBuf _drawbuf; + +public: + + LVBaseWin32Font() : _hfont(NULL), _height(0), _baseline(0), _drawbuf(1,1) + { } + + virtual ~LVBaseWin32Font() { Clear(); } + + /// returns font baseline offset + virtual int getBaseline() + { + return _baseline; + } + + /// returns font height + virtual int getHeight() const + { + return _height; + } + + /// retrieves font handle + virtual void * GetHandle() + { + return (void*)_hfont; + } + + /// returns char width + virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ) + { + glyph_info_t glyph; + if ( getGlyphInfo(ch, &glyph, def_char) ) + return glyph.width; + return 0; + } + /// returns true if font is empty + virtual bool IsNull() const + { + return (_hfont == NULL); + } + + virtual bool operator ! () const + { + return (_hfont == NULL); + } + + virtual void Clear(); + + virtual bool Create( const LOGFONTA & lf ); + + virtual bool Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ); + + virtual int getWeight() const { + return _logfont.lfWeight; + } + + virtual int getItalic() const { + return _logfont.lfItalic; + } + + virtual lString8 getTypeFace() const { + return lString8::empty_str; + } + + virtual css_font_family_t getFontFamily() const { + return css_ff_inherit; + } + + virtual LVFontGlyphCacheItem * getGlyph(lUInt16 ch, lChar16 def_char=0) { + return NULL; + } + + virtual int getSize() const { + return 0; + } + +}; + + +class LVWin32DrawFont : public LVBaseWin32Font +{ +private: + int _hyphen_width; +public: + + LVWin32DrawFont() : _hyphen_width(0) { } + + /** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found + */ + virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ); + + /** \brief measure text + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyph was found + */ + virtual lUInt16 measureText( + const lChar16 * text, int len, + lUInt16 * widths, + lUInt8 * flags, + int max_width, + lChar16 def_char, + int letter_spacing=0, + bool allow_hyphenation=true + ); + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return width of specified string + */ + virtual lUInt32 getTextWidth( + const lChar16 * text, int len + ); + + /// returns char width + virtual int getCharWidth( lChar16 ch, lChar16 def_char=0 ); + + /// draws text string + virtual void DrawTextString( LVDrawBuf * buf, int x, int y, + const lChar16 * text, int len, + lChar16 def_char, lUInt32 * palette, bool addHyphen, lUInt32 flags=0, int letter_spacing=0 ); + + /** \brief get glyph image in 1 byte per pixel format + \param code is unicode character + \param buf is buffer [width*height] to place glyph data + \return true if glyph was found + */ + virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0); + +}; + +struct glyph_t { + lUInt8 * glyph; + lChar16 ch; + bool flgNotExists; + bool flgValid; + LVFont::glyph_info_t gi; + glyph_t * next; + glyph_t(lChar16 c) + : glyph(NULL), ch(c), flgNotExists(false), flgValid(false), next(NULL) + { + memset( &gi, 0, sizeof(gi) ); + } + ~glyph_t() + { + if (glyph) + delete glyph; + } +}; + +class GlyphCache +{ +private: + lUInt32 _size; + glyph_t * * _hashtable; +public: + GlyphCache( lUInt32 size ) + : _size(size) + { + _hashtable = new glyph_t * [_size]; + for (lUInt32 i=0; i<_size; i++) + _hashtable[i] = NULL; + } + void clear() + { + for (lUInt32 i=0; i<_size; i++) + { + glyph_t * p = _hashtable[i]; + while (p) + { + glyph_t * next = p->next; + delete p; + p = next; + } + _hashtable[i] = NULL; + } + } + ~GlyphCache() + { + if (_hashtable) + { + clear(); + delete _hashtable; + } + } + glyph_t * find( lChar16 ch ) + { + lUInt32 index = (((lUInt32)ch)*113) % _size; + glyph_t * p = _hashtable[index]; + // 3 levels + if (!p) + return NULL; + if (p->ch == ch) + return p; + p = p->next; + if (!p) + return NULL; + if (p->ch == ch) + return p; + p = p->next; + if (!p) + return NULL; + if (p->ch == ch) + return p; + return NULL; + } + /// returns found or creates new + glyph_t * get( lChar16 ch ) + { + lUInt32 index = (((lUInt32)ch)*113) % _size; + glyph_t * * p = &_hashtable[index]; + // 3 levels + if (!*p) + { + return (*p = new glyph_t(ch)); + } + if ((*p)->ch == ch) + { + return *p; + } + p = &(*p)->next; + if (!*p) + { + return (*p = new glyph_t(ch)); + } + if ((*p)->ch == ch) + { + return *p; + } + p = &(*p)->next; + if (!*p) + { + return (*p = new glyph_t(ch)); + } + if ((*p)->ch == ch) + { + return *p; + } + + delete (*p); + *p = NULL; + + glyph_t * pp = new glyph_t(ch); + pp->next = _hashtable[index]; + _hashtable[index] = pp; + return pp; + } + +}; + +class LVWin32Font : public LVBaseWin32Font +{ +private: + lChar16 _unknown_glyph_index; + GlyphCache _cache; + + static int GetGlyphIndex( HDC hdc, wchar_t code ); + + glyph_t * GetGlyphRec( lChar16 ch ); + +public: + /** \brief get glyph info + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyh was found + */ + virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ); + + /** \brief measure text + \param glyph is pointer to glyph_info_t struct to place retrieved info + \return true if glyph was found + */ + virtual lUInt16 measureText( + const lChar16 * text, int len, + lUInt16 * widths, + lUInt8 * flags, + int max_width, + lChar16 def_char, + int letter_spacing=0, + bool allow_hyphenation=true + ); + /** \brief measure text + \param text is text string pointer + \param len is number of characters to measure + \return width of specified string + */ + virtual lUInt32 getTextWidth( + const lChar16 * text, int len + ); + + /** \brief get glyph image in 1 byte per pixel format + \param code is unicode character + \param buf is buffer [width*height] to place glyph data + \return true if glyph was found + */ + virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0); + + virtual void Clear(); + + virtual bool Create( const LOGFONTA & lf ); + + virtual bool Create(int size, int weight, bool italic, css_font_family_t family, lString8 typeface ); + + LVWin32Font() : _cache(256) { } + + virtual ~LVWin32Font() { } +}; + +#endif // !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE!=1 + +#endif // __LV_WIN32FONT_H_INCLUDED__ diff --git a/crengine/src/private/lvwin32fontman.cpp b/crengine/src/private/lvwin32fontman.cpp new file mode 100644 index 0000000000..36fe363128 --- /dev/null +++ b/crengine/src/private/lvwin32fontman.cpp @@ -0,0 +1,187 @@ +/** \file lvwin32fontman.h + \brief Win32 font manager implementation + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#include "lvwin32fontman.h" +#include "lvwin32font.h" + +#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE != 1 + +// prototype +int CALLBACK LVWin32FontEnumFontFamExProc( + const LOGFONTA *lpelfe, // logical-font data + const TEXTMETRICA *lpntme, // physical-font data + //ENUMLOGFONTEX *lpelfe, // logical-font data + //NEWTEXTMETRICEX *lpntme, // physical-font data + DWORD FontType, // type of font + LPARAM lParam // application-defined data +); + +LVWin32FontManager::~LVWin32FontManager() +{ + //if (_log) + // fclose(_log); +} + +LVWin32FontManager::LVWin32FontManager() +{ + //_log = fopen( "fonts.log", "wt" ); +} + +LVFontRef LVWin32FontManager::GetFont(int size, int weight, bool bitalic, css_font_family_t family, lString8 typeface, int documentId) +{ + int italic = bitalic?1:0; + if (size < 8) + size = 8; + if (size > 255) + size = 255; + + LVFontDef def( + lString8::empty_str, + size, + weight, + italic, + family, + typeface + ); + + //fprintf( _log, "GetFont: %s %d %s %s\n", + // typeface.c_str(), + // size, + // weight>400?"bold":"", + // italic?"italic":"" ); + LVFontCacheItem * item = _cache.find( &def ); + if (!item->getFont().isNull()) + { + //fprintf(_log, " : fount existing\n"); + return item->getFont(); + } + +#if COLOR_BACKBUFFER==0 + LVWin32Font * font = new LVWin32Font; +#else + LVWin32DrawFont * font = new LVWin32DrawFont; +#endif + + LVFontDef * fdef = item->getDef(); + LVFontDef def2( fdef->getName(), size, weight, italic, + fdef->getFamily(), fdef->getTypeFace() ); + + if ( font->Create(size, weight, italic?true:false, fdef->getFamily(), fdef->getTypeFace()) ) + { + //fprintf(_log, " : loading from file %s : %s %d\n", item->getDef()->getName().c_str(), + // item->getDef()->getTypeFace().c_str(), item->getDef()->getSize() ); + LVFontRef ref(font); + _cache.addInstance( &def2, ref ); + return ref; + } + delete font; + return LVFontRef(NULL); +} + +bool LVWin32FontManager::RegisterFont(const LOGFONTA *lf) +{ + lString8 face(lf->lfFaceName); + css_font_family_t ff; + switch (lf->lfPitchAndFamily & 0x70) + { + case FF_ROMAN: + ff = css_ff_serif; + break; + case FF_SWISS: + ff = css_ff_sans_serif; + break; + case FF_SCRIPT: + ff = css_ff_cursive; + break; + case FF_DECORATIVE: + ff = css_ff_fantasy; + break; + case FF_MODERN: + ff = css_ff_monospace; + break; + default: + ff = css_ff_sans_serif; + break; + } + LVFontDef def( + face, + -1, //lf->lfHeight>0 ? lf->lfHeight : -lf->lfHeight, + -1, //lf->lfWeight, + -1, //lf->lfItalic!=0, + ff, + face + ); + _cache.update( &def, LVFontRef(NULL) ); + return true; +} + +bool LVWin32FontManager::Init(lString8 path) +{ + LVColorDrawBuf drawbuf(1,1); + LOGFONTA lf; + memset(&lf, 0, sizeof(lf)); + lf.lfCharSet = ANSI_CHARSET; + int res = + EnumFontFamiliesExA( + drawbuf.GetDC(), // handle to DC + &lf, // font information + LVWin32FontEnumFontFamExProc, // callback function (FONTENUMPROC) + (LPARAM)this, // additional data + 0 // not used; must be 0 + ); + + return res!=0; +} + +void LVWin32FontManager::getFontFileNameList(lString16Collection &list) +{ + FONT_MAN_GUARD + _cache.getFontFileNameList(list); +} + + +// definition +int CALLBACK LVWin32FontEnumFontFamExProc( + const LOGFONTA *lf, // logical-font data + const TEXTMETRICA *lpntme, // physical-font data + //ENUMLOGFONTEX *lpelfe, // logical-font data + //NEWTEXTMETRICEX *lpntme, // physical-font data + DWORD FontType, // type of font + LPARAM lParam // application-defined data +) +{ + // + if (FontType == TRUETYPE_FONTTYPE) + { + LVWin32FontManager * fontman = (LVWin32FontManager *)lParam; + LVWin32Font fnt; + //if (strcmp(lf->lfFaceName, "Courier New")) + // return 1; + if ( fnt.Create( *lf ) ) + { + // + static lChar16 chars[] = {0, 0xBF, 0xE9, 0x106, 0x410, 0x44F, 0 }; + for (int i=0; chars[i]; i++) + { + LVFont::glyph_info_t glyph; + if (!fnt.getGlyphInfo( chars[i], &glyph, L' ' )) //def_char + return 1; + } + fontman->RegisterFont( lf ); //&lpelfe->elfLogFont + } + } + return 1; +} + +#endif // !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE != 1 diff --git a/crengine/src/private/lvwin32fontman.h b/crengine/src/private/lvwin32fontman.h new file mode 100644 index 0000000000..f3c07e987c --- /dev/null +++ b/crengine/src/private/lvwin32fontman.h @@ -0,0 +1,62 @@ +/** \file lvwin32fontman.cpp + \brief Win32 font manager interface + + CoolReader Engine + + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. + +*/ + +#ifndef __LV_WIN32FONTMAN_H_INCLUDED__ +#define __LV_WIN32FONTMAN_H_INCLUDED__ + +#include "../../include/crsetup.h" +#include "../../include/lvfntman.h" + +#include "lvfontcache.h" + + +#if !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE != 1 + +class LVWin32FontManager : public LVFontManager +{ +private: + lString8 _path; + LVFontCache _cache; + //FILE * _log; +public: + virtual int GetFontCount() + { + return _cache.length(); + } + virtual ~LVWin32FontManager(); + LVWin32FontManager(); + virtual void gc() // garbage collector + { + _cache.gc(); + } + virtual LVFontRef GetFont(int size, int weight, bool bitalic, css_font_family_t family, lString8 typeface, int documentId ); + + virtual bool RegisterFont( const LOGFONTA * lf ); + virtual bool RegisterFont( lString8 name ) + { + return false; + } + virtual bool Init( lString8 path ); + + virtual void getFaceList( lString16Collection & list ) + { + _cache.getFaceList(list); + } + /// returns registered font files + virtual void getFontFileNameList( lString16Collection & list ); +}; + +#endif // !defined(__SYMBIAN32__) && defined(_WIN32) && USE_FREETYPE!=1 + +#endif // __LV_WIN32FONTMAN_H_INCLUDED__ From 0f8cc88c0af5f3ea45c0941d378162d71fc1707e Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Thu, 3 Oct 2019 09:52:12 +0400 Subject: [PATCH 52/80] Fonts engine refactoring, last touches. Unified cache for characters and glyph's indexes. --- android/.idea/codeStyles/Project.xml | 109 +++++++++++++++++++ android/.idea/codeStyles/codeStyleConfig.xml | 2 +- cr3qt/src/cr3.rc | 0 crengine/include/lvfntman.h | 11 +- crengine/include/lvtextfm.h | 1 - crengine/src/lvfntman.cpp | 3 +- crengine/src/private/lvbitmapfont.cpp | 2 +- crengine/src/private/lvfontboldtransform.cpp | 46 ++++---- crengine/src/private/lvfontglyphcache.cpp | 75 ++++++++----- crengine/src/private/lvfontglyphcache.h | 41 +++---- crengine/src/private/lvfreetypeface.cpp | 44 ++++---- crengine/src/private/lvfreetypeface.h | 5 +- crengine/src/private/lvfreetypefontman.cpp | 11 +- 13 files changed, 227 insertions(+), 123 deletions(-) mode change 100755 => 100644 cr3qt/src/cr3.rc diff --git a/android/.idea/codeStyles/Project.xml b/android/.idea/codeStyles/Project.xml index 276dee540c..5781ead325 100644 --- a/android/.idea/codeStyles/Project.xml +++ b/android/.idea/codeStyles/Project.xml @@ -6,5 +6,114 @@