| //======================================================================== |
| // |
| // GlobalParams.cc |
| // |
| // Copyright 2001-2003 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org> |
| // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com> |
| // Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2019 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2005 Jonathan Blandford <jrb@redhat.com> |
| // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net> |
| // Copyright (C) 2006 Takashi Iwai <tiwai@suse.de> |
| // Copyright (C) 2006 Ed Catmur <ed@catmur.co.uk> |
| // Copyright (C) 2007 Krzysztof Kowalczyk <kkowalczyk@gmail.com> |
| // Copyright (C) 2007, 2009 Jonathan Kew <jonathan_kew@sil.org> |
| // Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com> |
| // Copyright (C) 2009, 2011, 2012, 2015 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net> |
| // Copyright (C) 2010, 2012 Hib Eris <hib@hiberis.nl> |
| // Copyright (C) 2010 Patrick Spendrin <ps_ml@gmx.de> |
| // Copyright (C) 2010 Jakub Wilk <jwilk@jwilk.net> |
| // Copyright (C) 2011 Pino Toscano <pino@kde.org> |
| // Copyright (C) 2011 Koji Otani <sho@bbr.jp> |
| // Copyright (C) 2012 Yi Yang <ahyangyi@gmail.com> |
| // Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2012 Peter Breitenlohner <peb@mppmu.mpg.de> |
| // Copyright (C) 2013, 2014 Jason Crain <jason@aquaticape.us> |
| // Copyright (C) 2017 Christoph Cullmann <cullmann@kde.org> |
| // Copyright (C) 2017 Jean Ghali <jghali@libertysurf.fr> |
| // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2019 Christian Persch <chpe@src.gnome.org> |
| // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #ifdef _WIN32 |
| # include <shlobj.h> |
| # include <mbstring.h> |
| #endif |
| #include "goo/glibc.h" |
| #include "goo/gmem.h" |
| #include "goo/GooString.h" |
| #include "goo/gfile.h" |
| #include "goo/gdir.h" |
| #include "Error.h" |
| #include "NameToCharCode.h" |
| #include "CharCodeToUnicode.h" |
| #include "UnicodeMap.h" |
| #include "CMap.h" |
| #include "BuiltinFontTables.h" |
| #include "FontEncodingTables.h" |
| #include "GlobalParams.h" |
| #include "GfxFont.h" |
| |
| #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
| #include <fontconfig/fontconfig.h> |
| #endif |
| |
| #ifdef _MSC_VER |
| # define strcasecmp stricmp |
| #else |
| # include <strings.h> |
| #endif |
| |
| #ifndef FC_WEIGHT_BOOK |
| #define FC_WEIGHT_BOOK 75 |
| #endif |
| |
| #include "NameToUnicodeTable.h" |
| #include "UnicodeMapTables.h" |
| #include "UnicodeMapFuncs.h" |
| |
| //------------------------------------------------------------------------ |
| |
| #define cidToUnicodeCacheSize 4 |
| #define unicodeToUnicodeCacheSize 4 |
| |
| //------------------------------------------------------------------------ |
| |
| GlobalParams *globalParams = nullptr; |
| |
| #if defined(ENABLE_RELOCATABLE) && defined(_WIN32) |
| |
| /* search for data relative to where we are installed */ |
| |
| static HMODULE hmodule; |
| |
| extern "C" { |
| /* Provide declaration to squelch -Wmissing-declarations warning */ |
| BOOL WINAPI |
| DllMain (HINSTANCE hinstDLL, |
| DWORD fdwReason, |
| LPVOID lpvReserved); |
| |
| BOOL WINAPI |
| DllMain (HINSTANCE hinstDLL, |
| DWORD fdwReason, |
| LPVOID lpvReserved) |
| { |
| switch (fdwReason) { |
| case DLL_PROCESS_ATTACH: |
| hmodule = hinstDLL; |
| break; |
| } |
| |
| return TRUE; |
| } |
| } |
| |
| static const char * |
| get_poppler_datadir (void) |
| { |
| static char retval[MAX_PATH]; |
| static int beenhere = 0; |
| |
| unsigned char *p; |
| |
| if (beenhere) |
| return retval; |
| |
| if (!GetModuleFileNameA (hmodule, (CHAR *) retval, sizeof(retval) - 20)) |
| return POPPLER_DATADIR; |
| |
| p = _mbsrchr ((unsigned char *) retval, '\\'); |
| *p = '\0'; |
| p = _mbsrchr ((unsigned char *) retval, '\\'); |
| if (p) { |
| if (stricmp ((const char *) (p+1), "bin") == 0) |
| *p = '\0'; |
| } |
| strcat (retval, "\\share\\poppler"); |
| |
| beenhere = 1; |
| |
| return retval; |
| } |
| |
| #undef POPPLER_DATADIR |
| #define POPPLER_DATADIR get_poppler_datadir () |
| |
| #endif |
| |
| //------------------------------------------------------------------------ |
| // SysFontInfo |
| //------------------------------------------------------------------------ |
| |
| class SysFontInfo { |
| public: |
| |
| GooString *name; |
| bool bold; |
| bool italic; |
| bool oblique; |
| bool fixedWidth; |
| GooString *path; |
| SysFontType type; |
| int fontNum; // for TrueType collections |
| GooString *substituteName; |
| |
| SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, |
| GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA); |
| ~SysFontInfo(); |
| SysFontInfo(const SysFontInfo &) = delete; |
| SysFontInfo& operator=(const SysFontInfo&) = delete; |
| bool match(SysFontInfo *fi); |
| bool match(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA); |
| bool match(GooString *nameA, bool boldA, bool italicA); |
| }; |
| |
| SysFontInfo::SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, |
| GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA) { |
| name = nameA; |
| bold = boldA; |
| italic = italicA; |
| oblique = obliqueA; |
| fixedWidth = fixedWidthA; |
| path = pathA; |
| type = typeA; |
| fontNum = fontNumA; |
| substituteName = substituteNameA; |
| } |
| |
| SysFontInfo::~SysFontInfo() { |
| delete name; |
| delete path; |
| delete substituteName; |
| } |
| |
| bool SysFontInfo::match(SysFontInfo *fi) { |
| return !strcasecmp(name->c_str(), fi->name->c_str()) && |
| bold == fi->bold && italic == fi->italic && oblique == fi->oblique && fixedWidth == fi->fixedWidth; |
| } |
| |
| bool SysFontInfo::match(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA) { |
| return !strcasecmp(name->c_str(), nameA->c_str()) && |
| bold == boldA && italic == italicA && oblique == obliqueA && fixedWidth == fixedWidthA; |
| } |
| |
| bool SysFontInfo::match(GooString *nameA, bool boldA, bool italicA) { |
| return !strcasecmp(name->c_str(), nameA->c_str()) && |
| bold == boldA && italic == italicA; |
| } |
| |
| //------------------------------------------------------------------------ |
| // SysFontList |
| //------------------------------------------------------------------------ |
| |
| class SysFontList { |
| public: |
| |
| SysFontList(); |
| ~SysFontList(); |
| SysFontList(const SysFontList &) = delete; |
| SysFontList& operator=(const SysFontList &) = delete; |
| SysFontInfo *find(const GooString *name, bool isFixedWidth, bool exact); |
| |
| #ifdef _WIN32 |
| void scanWindowsFonts(GooString *winFontDir); |
| #endif |
| #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
| void addFcFont(SysFontInfo *si) {fonts->push_back(si);} |
| #endif |
| private: |
| |
| #ifdef _WIN32 |
| SysFontInfo *makeWindowsFont(const char *name, int fontNum, |
| const char *path); |
| #endif |
| |
| std::vector<SysFontInfo*> *fonts; |
| }; |
| |
| SysFontList::SysFontList() { |
| fonts = new std::vector<SysFontInfo*>(); |
| } |
| |
| SysFontList::~SysFontList() { |
| for (auto entry : *fonts) { |
| delete entry; |
| } |
| delete fonts; |
| } |
| |
| SysFontInfo *SysFontList::find(const GooString *name, bool fixedWidth, bool exact) { |
| GooString *name2; |
| bool bold, italic, oblique; |
| SysFontInfo *fi; |
| int n; |
| |
| name2 = name->copy(); |
| |
| // remove space, comma, dash chars |
| { |
| int i = 0; |
| while (i < name2->getLength()) { |
| const char c = name2->getChar(i); |
| if (c == ' ' || c == ',' || c == '-') { |
| name2->del(i); |
| } else { |
| ++i; |
| } |
| } |
| n = name2->getLength(); |
| } |
| |
| // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.) |
| if (n > 2 && !strcmp(name2->c_str() + n - 2, "MT")) { |
| name2->del(n - 2, 2); |
| n -= 2; |
| } |
| |
| // look for "Regular" |
| if (n > 7 && !strcmp(name2->c_str() + n - 7, "Regular")) { |
| name2->del(n - 7, 7); |
| n -= 7; |
| } |
| |
| // look for "Italic" |
| if (n > 6 && !strcmp(name2->c_str() + n - 6, "Italic")) { |
| name2->del(n - 6, 6); |
| italic = true; |
| n -= 6; |
| } else { |
| italic = false; |
| } |
| |
| // look for "Oblique" |
| if (n > 6 && !strcmp(name2->c_str() + n - 7, "Oblique")) { |
| name2->del(n - 7, 7); |
| oblique = true; |
| n -= 6; |
| } else { |
| oblique = false; |
| } |
| |
| // look for "Bold" |
| if (n > 4 && !strcmp(name2->c_str() + n - 4, "Bold")) { |
| name2->del(n - 4, 4); |
| bold = true; |
| n -= 4; |
| } else { |
| bold = false; |
| } |
| |
| // remove trailing "MT" (FooMT-Bold, etc.) |
| if (n > 2 && !strcmp(name2->c_str() + n - 2, "MT")) { |
| name2->del(n - 2, 2); |
| n -= 2; |
| } |
| |
| // remove trailing "PS" |
| if (n > 2 && !strcmp(name2->c_str() + n - 2, "PS")) { |
| name2->del(n - 2, 2); |
| n -= 2; |
| } |
| |
| // remove trailing "IdentityH" |
| if (n > 9 && !strcmp(name2->c_str() + n - 9, "IdentityH")) { |
| name2->del(n - 9, 9); |
| n -= 9; |
| } |
| |
| // search for the font |
| fi = nullptr; |
| for (std::size_t i = 0; i < fonts->size(); ++i) { |
| fi = (*fonts)[i]; |
| if (fi->match(name2, bold, italic, oblique, fixedWidth)) { |
| break; |
| } |
| fi = nullptr; |
| } |
| if (!fi && !exact && bold) { |
| // try ignoring the bold flag |
| for (std::size_t i = 0; i < fonts->size(); ++i) { |
| fi = (*fonts)[i]; |
| if (fi->match(name2, false, italic)) { |
| break; |
| } |
| fi = nullptr; |
| } |
| } |
| if (!fi && !exact && (bold || italic)) { |
| // try ignoring the bold and italic flags |
| for (std::size_t i = 0; i < fonts->size(); ++i) { |
| fi = (*fonts)[i]; |
| if (fi->match(name2, false, false)) { |
| break; |
| } |
| fi = nullptr; |
| } |
| } |
| |
| delete name2; |
| return fi; |
| } |
| |
| |
| #define globalParamsLocker() std::unique_lock<std::recursive_mutex> locker(mutex) |
| #define unicodeMapCacheLocker() std::unique_lock<std::recursive_mutex> locker(unicodeMapCacheMutex) |
| #define cMapCacheLocker() std::unique_lock<std::recursive_mutex> locker(cMapCacheMutex) |
| |
| //------------------------------------------------------------------------ |
| // parsing |
| //------------------------------------------------------------------------ |
| |
| GlobalParams::GlobalParams(const char *customPopplerDataDir) |
| : popplerDataDir(customPopplerDataDir) |
| { |
| initBuiltinFontTables(); |
| |
| // scan the encoding in reverse because we want the lowest-numbered |
| // index for each char name ('space' is encoded twice) |
| macRomanReverseMap = new NameToCharCode(); |
| for (int i = 255; i >= 0; --i) { |
| if (macRomanEncoding[i]) { |
| macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i); |
| } |
| } |
| |
| nameToUnicodeZapfDingbats = new NameToCharCode(); |
| nameToUnicodeText = new NameToCharCode(); |
| toUnicodeDirs = new std::vector<GooString*>(); |
| sysFonts = new SysFontList(); |
| psExpandSmaller = false; |
| psShrinkLarger = true; |
| psLevel = psLevel2; |
| textEncoding = new GooString("UTF-8"); |
| #if defined(_WIN32) |
| textEOL = eolDOS; |
| #else |
| textEOL = eolUnix; |
| #endif |
| textPageBreaks = true; |
| enableFreeType = true; |
| overprintPreview = false; |
| printCommands = false; |
| profileCommands = false; |
| errQuiet = false; |
| |
| cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize); |
| unicodeToUnicodeCache = |
| new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize); |
| unicodeMapCache = new UnicodeMapCache(); |
| cMapCache = new CMapCache(); |
| |
| baseFontsInitialized = false; |
| |
| // set up the initial nameToUnicode tables |
| for (int i = 0; nameToUnicodeZapfDingbatsTab[i].name; ++i) { |
| nameToUnicodeZapfDingbats->add(nameToUnicodeZapfDingbatsTab[i].name, nameToUnicodeZapfDingbatsTab[i].u); |
| } |
| |
| for (int i = 0; nameToUnicodeTextTab[i].name; ++i) { |
| nameToUnicodeText->add(nameToUnicodeTextTab[i].name, nameToUnicodeTextTab[i].u); |
| } |
| |
| // set up the residentUnicodeMaps table |
| residentUnicodeMaps.reserve(6); |
| UnicodeMap map = {"Latin1", false, latin1UnicodeMapRanges, latin1UnicodeMapLen}; |
| residentUnicodeMaps.emplace(map.getEncodingName()->toStr(), std::move(map)); |
| map = {"ASCII7", false, ascii7UnicodeMapRanges, ascii7UnicodeMapLen}; |
| residentUnicodeMaps.emplace(map.getEncodingName()->toStr(), std::move(map)); |
| map = {"Symbol", false, symbolUnicodeMapRanges, symbolUnicodeMapLen}; |
| residentUnicodeMaps.emplace(map.getEncodingName()->toStr(), std::move(map)); |
| map = {"ZapfDingbats", false, zapfDingbatsUnicodeMapRanges, zapfDingbatsUnicodeMapLen}; |
| residentUnicodeMaps.emplace(map.getEncodingName()->toStr(), std::move(map)); |
| map = {"UTF-8", true, &mapUTF8}; |
| residentUnicodeMaps.emplace(map.getEncodingName()->toStr(), std::move(map)); |
| map = {"UTF-16", true, &mapUTF16}; |
| residentUnicodeMaps.emplace(map.getEncodingName()->toStr(), std::move(map)); |
| |
| scanEncodingDirs(); |
| } |
| |
| void GlobalParams::scanEncodingDirs() { |
| GDir *dir; |
| GDirEntry *entry; |
| const char *dataRoot = popplerDataDir ? popplerDataDir : POPPLER_DATADIR; |
| |
| // allocate buffer large enough to append "/nameToUnicode" |
| size_t bufSize = strlen(dataRoot) + strlen("/nameToUnicode") + 1; |
| char *dataPathBuffer = new char[bufSize]; |
| |
| snprintf(dataPathBuffer, bufSize, "%s/nameToUnicode", dataRoot); |
| dir = new GDir(dataPathBuffer, true); |
| while (entry = dir->getNextEntry(), entry != nullptr) { |
| if (!entry->isDir()) { |
| parseNameToUnicode(entry->getFullPath()); |
| } |
| delete entry; |
| } |
| delete dir; |
| |
| snprintf(dataPathBuffer, bufSize, "%s/cidToUnicode", dataRoot); |
| dir = new GDir(dataPathBuffer, false); |
| while (entry = dir->getNextEntry(), entry != nullptr) { |
| addCIDToUnicode(entry->getName(), entry->getFullPath()); |
| delete entry; |
| } |
| delete dir; |
| |
| snprintf(dataPathBuffer, bufSize, "%s/unicodeMap", dataRoot); |
| dir = new GDir(dataPathBuffer, false); |
| while (entry = dir->getNextEntry(), entry != nullptr) { |
| addUnicodeMap(entry->getName(), entry->getFullPath()); |
| delete entry; |
| } |
| delete dir; |
| |
| snprintf(dataPathBuffer, bufSize, "%s/cMap", dataRoot); |
| dir = new GDir(dataPathBuffer, false); |
| while (entry = dir->getNextEntry(), entry != nullptr) { |
| addCMapDir(entry->getName(), entry->getFullPath()); |
| toUnicodeDirs->push_back(entry->getFullPath()->copy()); |
| delete entry; |
| } |
| delete dir; |
| |
| delete[] dataPathBuffer; |
| } |
| |
| void GlobalParams::parseNameToUnicode(const GooString *name) { |
| char *tok1, *tok2; |
| FILE *f; |
| char buf[256]; |
| int line; |
| Unicode u; |
| char *tokptr; |
| |
| if (!(f = openFile(name->c_str(), "r"))) { |
| error(errIO, -1, "Couldn't open 'nameToUnicode' file '{0:t}'", |
| name); |
| return; |
| } |
| line = 1; |
| while (getLine(buf, sizeof(buf), f)) { |
| tok1 = strtok_r(buf, " \t\r\n", &tokptr); |
| tok2 = strtok_r(nullptr, " \t\r\n", &tokptr); |
| if (tok1 && tok2) { |
| sscanf(tok1, "%x", &u); |
| nameToUnicodeText->add(tok2, u); |
| } else { |
| error(errConfig, -1, "Bad line in 'nameToUnicode' file ({0:t}:{1:d})", |
| name, line); |
| } |
| ++line; |
| } |
| fclose(f); |
| } |
| |
| void GlobalParams::addCIDToUnicode(const GooString *collection, const GooString *fileName) { |
| cidToUnicodes[collection->toStr()] = fileName->toStr(); |
| } |
| |
| void GlobalParams::addUnicodeMap(const GooString *encodingName, const GooString *fileName) { |
| unicodeMaps[encodingName->toStr()] = fileName->toStr(); |
| } |
| |
| void GlobalParams::addCMapDir(const GooString *collection, const GooString *dir) { |
| cMapDirs.emplace(collection->toStr(), dir->toStr()); |
| } |
| |
| bool GlobalParams::parseYesNo2(const char *token, bool *flag) { |
| if (!strcmp(token, "yes")) { |
| *flag = true; |
| } else if (!strcmp(token, "no")) { |
| *flag = false; |
| } else { |
| return false; |
| } |
| return true; |
| } |
| |
| GlobalParams::~GlobalParams() { |
| freeBuiltinFontTables(); |
| |
| delete macRomanReverseMap; |
| |
| delete nameToUnicodeZapfDingbats; |
| delete nameToUnicodeText; |
| for (auto entry : *toUnicodeDirs) { |
| delete entry; |
| } |
| delete toUnicodeDirs; |
| delete sysFonts; |
| delete textEncoding; |
| |
| delete cidToUnicodeCache; |
| delete unicodeToUnicodeCache; |
| delete unicodeMapCache; |
| delete cMapCache; |
| } |
| |
| //------------------------------------------------------------------------ |
| // accessors |
| //------------------------------------------------------------------------ |
| |
| CharCode GlobalParams::getMacRomanCharCode(char *charName) { |
| // no need to lock - macRomanReverseMap is constant |
| return macRomanReverseMap->lookup(charName); |
| } |
| |
| Unicode GlobalParams::mapNameToUnicodeAll(const char *charName) { |
| // no need to lock - nameToUnicodeZapfDingbats and nameToUnicodeText are constant |
| Unicode u = nameToUnicodeZapfDingbats->lookup(charName); |
| if (!u) |
| u = nameToUnicodeText->lookup(charName); |
| return u; |
| } |
| |
| Unicode GlobalParams::mapNameToUnicodeText(const char *charName) { |
| // no need to lock - nameToUnicodeText is constant |
| return nameToUnicodeText->lookup(charName); |
| } |
| |
| UnicodeMap *GlobalParams::getResidentUnicodeMap(const GooString *encodingName) { |
| UnicodeMap *map = nullptr; |
| |
| globalParamsLocker(); |
| const auto unicodeMap = residentUnicodeMaps.find(encodingName->toStr()); |
| if (unicodeMap != residentUnicodeMaps.end()) { |
| map = &unicodeMap->second; |
| map->incRefCnt(); |
| } |
| |
| return map; |
| } |
| |
| FILE *GlobalParams::getUnicodeMapFile(const GooString *encodingName) { |
| FILE *file = nullptr; |
| |
| globalParamsLocker(); |
| const auto unicodeMap = unicodeMaps.find(encodingName->toStr()); |
| if (unicodeMap != unicodeMaps.end()) { |
| file = openFile(unicodeMap->second.c_str(), "r"); |
| } |
| |
| return file; |
| } |
| |
| FILE *GlobalParams::findCMapFile(const GooString *collection, const GooString *cMapName) { |
| FILE *file = nullptr; |
| |
| globalParamsLocker(); |
| const auto collectionCMapDirs = cMapDirs.equal_range(collection->toStr()); |
| for (auto cMapDir = collectionCMapDirs.first; cMapDir != collectionCMapDirs.second; ++cMapDir) { |
| auto* const path = new GooString(cMapDir->second); |
| appendToPath(path, cMapName->c_str()); |
| file = openFile(path->c_str(), "r"); |
| delete path; |
| if (file) { |
| break; |
| } |
| } |
| |
| return file; |
| } |
| |
| FILE *GlobalParams::findToUnicodeFile(const GooString *name) { |
| GooString *dir, *fileName; |
| FILE *f; |
| |
| globalParamsLocker(); |
| for (std::size_t i = 0; i < toUnicodeDirs->size(); ++i) { |
| dir = (*toUnicodeDirs)[i]; |
| fileName = appendToPath(dir->copy(), name->c_str()); |
| f = openFile(fileName->c_str(), "r"); |
| delete fileName; |
| if (f) { |
| return f; |
| } |
| } |
| return nullptr; |
| } |
| |
| #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
| static bool findModifier(const char *name, const char *modifier, const char **start) |
| { |
| const char *match; |
| |
| if (name == nullptr) |
| return false; |
| |
| match = strstr(name, modifier); |
| if (match) { |
| if (*start == nullptr || match < *start) |
| *start = match; |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| static const char *getFontLang(GfxFont *font) |
| { |
| const char *lang; |
| |
| // find the language we want the font to support |
| if (font->isCIDFont()) |
| { |
| const GooString *collection = ((GfxCIDFont *)font)->getCollection(); |
| if (collection) |
| { |
| if (strcmp(collection->c_str(), "Adobe-GB1") == 0) |
| lang = "zh-cn"; // Simplified Chinese |
| else if (strcmp(collection->c_str(), "Adobe-CNS1") == 0) |
| lang = "zh-tw"; // Traditional Chinese |
| else if (strcmp(collection->c_str(), "Adobe-Japan1") == 0) |
| lang = "ja"; // Japanese |
| else if (strcmp(collection->c_str(), "Adobe-Japan2") == 0) |
| lang = "ja"; // Japanese |
| else if (strcmp(collection->c_str(), "Adobe-Korea1") == 0) |
| lang = "ko"; // Korean |
| else if (strcmp(collection->c_str(), "Adobe-UCS") == 0) |
| lang = "xx"; |
| else if (strcmp(collection->c_str(), "Adobe-Identity") == 0) |
| lang = "xx"; |
| else |
| { |
| error(errUnimplemented, -1, "Unknown CID font collection, please report to poppler bugzilla."); |
| lang = "xx"; |
| } |
| } |
| else lang = "xx"; |
| } |
| else lang = "xx"; |
| return lang; |
| } |
| |
| static FcPattern *buildFcPattern(GfxFont *font, const GooString *base14Name) |
| { |
| int weight = -1, |
| slant = -1, |
| width = -1, |
| spacing = -1; |
| const char *family; |
| const char *start; |
| FcPattern *p; |
| |
| // this is all heuristics will be overwritten if font had proper info |
| char *fontName = strdup(((base14Name == nullptr) ? font->getName() : base14Name)->c_str()); |
| |
| const char *modifiers = strchr (fontName, ','); |
| if (modifiers == nullptr) |
| modifiers = strchr (fontName, '-'); |
| |
| // remove the - from the names, for some reason, Fontconfig does not |
| // understand "MS-Mincho" but does with "MS Mincho" |
| const int len = strlen(fontName); |
| for (int i = 0; i < len; i++) |
| fontName[i] = (fontName[i] == '-' ? ' ' : fontName[i]); |
| |
| start = nullptr; |
| findModifier(modifiers, "Regular", &start); |
| findModifier(modifiers, "Roman", &start); |
| |
| if (findModifier(modifiers, "Oblique", &start)) |
| slant = FC_SLANT_OBLIQUE; |
| if (findModifier(modifiers, "Italic", &start)) |
| slant = FC_SLANT_ITALIC; |
| if (findModifier(modifiers, "Bold", &start)) |
| weight = FC_WEIGHT_BOLD; |
| if (findModifier(modifiers, "Light", &start)) |
| weight = FC_WEIGHT_LIGHT; |
| if (findModifier(modifiers, "Medium", &start)) |
| weight = FC_WEIGHT_MEDIUM; |
| if (findModifier(modifiers, "Condensed", &start)) |
| width = FC_WIDTH_CONDENSED; |
| |
| if (start) { |
| // There have been "modifiers" in the name, crop them to obtain |
| // the family name |
| family = strndup(fontName, modifiers - fontName); |
| free(fontName); |
| fontName = nullptr; |
| } |
| else { |
| family = fontName; |
| fontName = nullptr; |
| } |
| |
| // use font flags |
| if (font->isFixedWidth()) |
| spacing = FC_MONO; |
| if (font->isBold()) |
| weight = FC_WEIGHT_BOLD; |
| if (font->isItalic()) |
| slant = FC_SLANT_ITALIC; |
| |
| bool freeFamily = true; |
| // if the FontDescriptor specified a family name use it |
| if (font->getFamily()) { |
| free((char*)family); |
| family = font->getFamily()->c_str(); |
| freeFamily = false; |
| } |
| |
| // if the FontDescriptor specified a weight use it |
| switch (font -> getWeight()) |
| { |
| case GfxFont::W100: weight = FC_WEIGHT_EXTRALIGHT; break; |
| case GfxFont::W200: weight = FC_WEIGHT_LIGHT; break; |
| case GfxFont::W300: weight = FC_WEIGHT_BOOK; break; |
| case GfxFont::W400: weight = FC_WEIGHT_NORMAL; break; |
| case GfxFont::W500: weight = FC_WEIGHT_MEDIUM; break; |
| case GfxFont::W600: weight = FC_WEIGHT_DEMIBOLD; break; |
| case GfxFont::W700: weight = FC_WEIGHT_BOLD; break; |
| case GfxFont::W800: weight = FC_WEIGHT_EXTRABOLD; break; |
| case GfxFont::W900: weight = FC_WEIGHT_BLACK; break; |
| default: break; |
| } |
| |
| // if the FontDescriptor specified a width use it |
| switch (font -> getStretch()) |
| { |
| case GfxFont::UltraCondensed: width = FC_WIDTH_ULTRACONDENSED; break; |
| case GfxFont::ExtraCondensed: width = FC_WIDTH_EXTRACONDENSED; break; |
| case GfxFont::Condensed: width = FC_WIDTH_CONDENSED; break; |
| case GfxFont::SemiCondensed: width = FC_WIDTH_SEMICONDENSED; break; |
| case GfxFont::Normal: width = FC_WIDTH_NORMAL; break; |
| case GfxFont::SemiExpanded: width = FC_WIDTH_SEMIEXPANDED; break; |
| case GfxFont::Expanded: width = FC_WIDTH_EXPANDED; break; |
| case GfxFont::ExtraExpanded: width = FC_WIDTH_EXTRAEXPANDED; break; |
| case GfxFont::UltraExpanded: width = FC_WIDTH_ULTRAEXPANDED; break; |
| default: break; |
| } |
| |
| const char *lang = getFontLang(font); |
| |
| p = FcPatternBuild(nullptr, |
| FC_FAMILY, FcTypeString, family, |
| FC_LANG, FcTypeString, lang, |
| NULL); |
| if (slant != -1) FcPatternAddInteger(p, FC_SLANT, slant); |
| if (weight != -1) FcPatternAddInteger(p, FC_WEIGHT, weight); |
| if (width != -1) FcPatternAddInteger(p, FC_WIDTH, width); |
| if (spacing != -1) FcPatternAddInteger(p, FC_SPACING, spacing); |
| |
| if (freeFamily) |
| free((char*)family); |
| return p; |
| } |
| #endif |
| |
| GooString *GlobalParams::findFontFile(const GooString *fontName) { |
| GooString *path = nullptr; |
| |
| setupBaseFonts(nullptr); |
| globalParamsLocker(); |
| const auto fontFile = fontFiles.find(fontName->toStr()); |
| if (fontFile != fontFiles.end()) { |
| path = new GooString(fontFile->second); |
| } |
| |
| return path; |
| } |
| |
| /* if you can't or don't want to use Fontconfig, you need to implement |
| this function for your platform. For Windows, it's in GlobalParamsWin.cc |
| */ |
| #ifdef WITH_FONTCONFIGURATION_FONTCONFIG |
| // not needed for fontconfig |
| void GlobalParams::setupBaseFonts(char *) { |
| } |
| |
| GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, GfxFont *font) { |
| SysFontType type; |
| int fontNum; |
| |
| return findSystemFontFile(font, &type, &fontNum, nullptr, base14Name); |
| } |
| |
| GooString *GlobalParams::findSystemFontFile(GfxFont *font, |
| SysFontType *type, |
| int *fontNum, GooString *substituteFontName, const GooString *base14Name) { |
| SysFontInfo *fi = nullptr; |
| FcPattern *p=nullptr; |
| GooString *path = nullptr; |
| const GooString *fontName = font->getName(); |
| GooString substituteName; |
| if (!fontName) return nullptr; |
| |
| globalParamsLocker(); |
| |
| if ((fi = sysFonts->find(fontName, font->isFixedWidth(), true))) { |
| path = fi->path->copy(); |
| *type = fi->type; |
| *fontNum = fi->fontNum; |
| substituteName.Set(fi->substituteName->c_str()); |
| } else { |
| FcChar8* s; |
| char * ext; |
| FcResult res; |
| FcFontSet *set; |
| int i; |
| FcLangSet *lb = nullptr; |
| p = buildFcPattern(font, base14Name); |
| |
| if (!p) |
| goto fin; |
| FcConfigSubstitute(nullptr, p, FcMatchPattern); |
| FcDefaultSubstitute(p); |
| set = FcFontSort(nullptr, p, FcFalse, nullptr, &res); |
| if (!set) |
| goto fin; |
| |
| // find the language we want the font to support |
| const char *lang = getFontLang(font); |
| if (strcmp(lang,"xx") != 0) { |
| lb = FcLangSetCreate(); |
| FcLangSetAdd(lb,(FcChar8 *)lang); |
| } |
| |
| /* |
| scan twice. |
| first: fonts support the language |
| second: all fonts (fall back) |
| */ |
| while (fi == nullptr) |
| { |
| for (i = 0; i < set->nfont; ++i) |
| { |
| res = FcPatternGetString(set->fonts[i], FC_FILE, 0, &s); |
| if (res != FcResultMatch || !s) |
| continue; |
| if (lb != nullptr) { |
| FcLangSet *l; |
| res = FcPatternGetLangSet(set->fonts[i], FC_LANG, 0, &l); |
| if (res != FcResultMatch || !FcLangSetContains(l,lb)) { |
| continue; |
| } |
| } |
| FcChar8* s2; |
| res = FcPatternGetString(set->fonts[i], FC_FULLNAME, 0, &s2); |
| if (res == FcResultMatch && s2) { |
| substituteName.Set((char*)s2); |
| } else { |
| // fontconfig does not extract fullname for some fonts |
| // create the fullname from family and style |
| res = FcPatternGetString(set->fonts[i], FC_FAMILY, 0, &s2); |
| if (res == FcResultMatch && s2) { |
| substituteName.Set((char*)s2); |
| res = FcPatternGetString(set->fonts[i], FC_STYLE, 0, &s2); |
| if (res == FcResultMatch && s2) { |
| GooString *style = new GooString((char*)s2); |
| if (style->cmp("Regular") != 0) { |
| substituteName.append(" "); |
| substituteName.append(style); |
| } |
| delete style; |
| } |
| } |
| } |
| ext = strrchr((char*)s,'.'); |
| if (!ext) |
| continue; |
| if (!strncasecmp(ext,".ttf",4) || !strncasecmp(ext, ".ttc", 4) || !strncasecmp(ext, ".otf", 4)) |
| { |
| int weight, slant; |
| bool bold = font->isBold(); |
| bool italic = font->isItalic(); |
| bool oblique = false; |
| FcPatternGetInteger(set->fonts[i], FC_WEIGHT, 0, &weight); |
| FcPatternGetInteger(set->fonts[i], FC_SLANT, 0, &slant); |
| if (weight == FC_WEIGHT_DEMIBOLD || weight == FC_WEIGHT_BOLD |
| || weight == FC_WEIGHT_EXTRABOLD || weight == FC_WEIGHT_BLACK) |
| { |
| bold = true; |
| } |
| if (slant == FC_SLANT_ITALIC) |
| italic = true; |
| if (slant == FC_SLANT_OBLIQUE) |
| oblique = true; |
| *fontNum = 0; |
| *type = (!strncasecmp(ext,".ttc",4)) ? sysFontTTC : sysFontTTF; |
| FcPatternGetInteger(set->fonts[i], FC_INDEX, 0, fontNum); |
| fi = new SysFontInfo(fontName->copy(), bold, italic, oblique, font->isFixedWidth(), |
| new GooString((char*)s), *type, *fontNum, substituteName.copy()); |
| sysFonts->addFcFont(fi); |
| path = new GooString((char*)s); |
| } |
| else if (!strncasecmp(ext,".pfa",4) || !strncasecmp(ext,".pfb",4)) |
| { |
| int weight, slant; |
| bool bold = font->isBold(); |
| bool italic = font->isItalic(); |
| bool oblique = false; |
| FcPatternGetInteger(set->fonts[i], FC_WEIGHT, 0, &weight); |
| FcPatternGetInteger(set->fonts[i], FC_SLANT, 0, &slant); |
| if (weight == FC_WEIGHT_DEMIBOLD || weight == FC_WEIGHT_BOLD |
| || weight == FC_WEIGHT_EXTRABOLD || weight == FC_WEIGHT_BLACK) |
| { |
| bold = true; |
| } |
| if (slant == FC_SLANT_ITALIC) |
| italic = true; |
| if (slant == FC_SLANT_OBLIQUE) |
| oblique = true; |
| *fontNum = 0; |
| *type = (!strncasecmp(ext,".pfa",4)) ? sysFontPFA : sysFontPFB; |
| FcPatternGetInteger(set->fonts[i], FC_INDEX, 0, fontNum); |
| fi = new SysFontInfo(fontName->copy(), bold, italic, oblique, font->isFixedWidth(), |
| new GooString((char*)s), *type, *fontNum, substituteName.copy()); |
| sysFonts->addFcFont(fi); |
| path = new GooString((char*)s); |
| } |
| else |
| continue; |
| break; |
| } |
| if (lb != nullptr) { |
| FcLangSetDestroy(lb); |
| lb = nullptr; |
| } else { |
| /* scan all fonts of the list */ |
| break; |
| } |
| } |
| FcFontSetDestroy(set); |
| } |
| if (path == nullptr && (fi = sysFonts->find(fontName, font->isFixedWidth(), false))) { |
| path = fi->path->copy(); |
| *type = fi->type; |
| *fontNum = fi->fontNum; |
| } |
| if (substituteFontName) { |
| substituteFontName->Set(substituteName.c_str()); |
| } |
| fin: |
| if (p) |
| FcPatternDestroy(p); |
| |
| return path; |
| } |
| |
| #elif WITH_FONTCONFIGURATION_WIN32 |
| #include "GlobalParamsWin.cc" |
| |
| GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, GfxFont *font) { |
| return findFontFile(base14Name); |
| } |
| #else |
| GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, GfxFont *font) { |
| return findFontFile(base14Name); |
| } |
| |
| static struct { |
| const char *name; |
| const char *t1FileName; |
| const char *ttFileName; |
| } displayFontTab[] = { |
| {"Courier", "n022003l.pfb", "cour.ttf"}, |
| {"Courier-Bold", "n022004l.pfb", "courbd.ttf"}, |
| {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf"}, |
| {"Courier-Oblique", "n022023l.pfb", "couri.ttf"}, |
| {"Helvetica", "n019003l.pfb", "arial.ttf"}, |
| {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf"}, |
| {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"}, |
| {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf"}, |
| {"Symbol", "s050000l.pfb", nullptr}, |
| {"Times-Bold", "n021004l.pfb", "timesbd.ttf"}, |
| {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf"}, |
| {"Times-Italic", "n021023l.pfb", "timesi.ttf"}, |
| {"Times-Roman", "n021003l.pfb", "times.ttf"}, |
| {"ZapfDingbats", "d050000l.pfb", nullptr}, |
| {nullptr, nullptr, nullptr} |
| }; |
| |
| static const char *displayFontDirs[] = { |
| "/usr/share/ghostscript/fonts", |
| "/usr/local/share/ghostscript/fonts", |
| "/usr/share/fonts/default/Type1", |
| "/usr/share/fonts/default/ghostscript", |
| "/usr/share/fonts/type1/gsfonts", |
| nullptr |
| }; |
| |
| void GlobalParams::setupBaseFonts(char *dir) { |
| GooString *fontName; |
| GooString *fileName; |
| FILE *f; |
| int i, j; |
| |
| for (i = 0; displayFontTab[i].name; ++i) { |
| if (fontFiles.count(displayFontTab[i].name) > 0) { |
| continue; |
| } |
| fontName = new GooString(displayFontTab[i].name); |
| fileName = nullptr; |
| if (dir) { |
| fileName = appendToPath(new GooString(dir), displayFontTab[i].t1FileName); |
| if ((f = openFile(fileName->c_str(), "rb"))) { |
| fclose(f); |
| } else { |
| delete fileName; |
| fileName = nullptr; |
| } |
| } |
| for (j = 0; !fileName && displayFontDirs[j]; ++j) { |
| fileName = appendToPath(new GooString(displayFontDirs[j]), |
| displayFontTab[i].t1FileName); |
| if ((f = openFile(fileName->c_str(), "rb"))) { |
| fclose(f); |
| } else { |
| delete fileName; |
| fileName = nullptr; |
| } |
| } |
| if (!fileName) { |
| error(errConfig, -1, "No display font for '{0:s}'", |
| displayFontTab[i].name); |
| delete fontName; |
| continue; |
| } |
| addFontFile(fontName, fileName); |
| } |
| |
| } |
| |
| GooString *GlobalParams::findSystemFontFile(GfxFont *font, |
| SysFontType *type, |
| int *fontNum, GooString * /*substituteFontName*/, |
| const GooString * /*base14Name*/) { |
| SysFontInfo *fi; |
| GooString *path; |
| |
| const GooString *fontName = font->getName(); |
| if (!fontName) return nullptr; |
| |
| path = nullptr; |
| globalParamsLocker(); |
| if ((fi = sysFonts->find(fontName, font->isFixedWidth(), false))) { |
| path = fi->path->copy(); |
| *type = fi->type; |
| *fontNum = fi->fontNum; |
| } |
| |
| return path; |
| } |
| #endif |
| |
| bool GlobalParams::getPSExpandSmaller() { |
| globalParamsLocker(); |
| return psExpandSmaller; |
| } |
| |
| bool GlobalParams::getPSShrinkLarger() { |
| globalParamsLocker(); |
| return psShrinkLarger; |
| } |
| |
| PSLevel GlobalParams::getPSLevel() { |
| globalParamsLocker(); |
| return psLevel; |
| } |
| |
| GooString *GlobalParams::getTextEncodingName() { |
| globalParamsLocker(); |
| return textEncoding->copy(); |
| } |
| |
| EndOfLineKind GlobalParams::getTextEOL() { |
| globalParamsLocker(); |
| return textEOL; |
| } |
| |
| bool GlobalParams::getTextPageBreaks() { |
| globalParamsLocker(); |
| return textPageBreaks; |
| } |
| |
| bool GlobalParams::getEnableFreeType() { |
| globalParamsLocker(); |
| return enableFreeType; |
| } |
| |
| bool GlobalParams::getPrintCommands() { |
| globalParamsLocker(); |
| return printCommands; |
| } |
| |
| bool GlobalParams::getProfileCommands() { |
| globalParamsLocker(); |
| return profileCommands; |
| } |
| |
| bool GlobalParams::getErrQuiet() { |
| // no locking -- this function may get called from inside a locked |
| // section |
| return errQuiet; |
| } |
| |
| CharCodeToUnicode *GlobalParams::getCIDToUnicode(GooString *collection) { |
| CharCodeToUnicode *ctu; |
| |
| globalParamsLocker(); |
| if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) { |
| const auto cidToUnicode = cidToUnicodes.find(collection->toStr()); |
| if (cidToUnicode != cidToUnicodes.end()) { |
| if((ctu = CharCodeToUnicode::parseCIDToUnicode(cidToUnicode->second.c_str(), collection))) { |
| cidToUnicodeCache->add(ctu); |
| } |
| } |
| } |
| |
| return ctu; |
| } |
| |
| UnicodeMap *GlobalParams::getUnicodeMap(GooString *encodingName) { |
| return getUnicodeMap2(encodingName); |
| } |
| |
| UnicodeMap *GlobalParams::getUnicodeMap2(GooString *encodingName) { |
| UnicodeMap *map; |
| |
| if (!(map = getResidentUnicodeMap(encodingName))) { |
| unicodeMapCacheLocker(); |
| map = unicodeMapCache->getUnicodeMap(encodingName); |
| } |
| |
| return map; |
| } |
| |
| CMap *GlobalParams::getCMap(const GooString *collection, GooString *cMapName, Stream *stream) { |
| cMapCacheLocker(); |
| return cMapCache->getCMap(collection, cMapName, stream); |
| } |
| |
| UnicodeMap *GlobalParams::getTextEncoding() { |
| return getUnicodeMap2(textEncoding); |
| } |
| |
| std::vector<GooString*> *GlobalParams::getEncodingNames() |
| { |
| auto* const result = new std::vector<GooString*>; |
| for (const auto& unicodeMap : residentUnicodeMaps) { |
| result->push_back(new GooString(unicodeMap.first)); |
| } |
| for (const auto& unicodeMap : unicodeMaps) { |
| result->push_back(new GooString(unicodeMap.first)); |
| } |
| return result; |
| } |
| |
| //------------------------------------------------------------------------ |
| // functions to set parameters |
| //------------------------------------------------------------------------ |
| |
| void GlobalParams::addFontFile(GooString *fontName, GooString *path) { |
| globalParamsLocker(); |
| fontFiles[fontName->toStr()] = path->toStr(); |
| } |
| |
| void GlobalParams::setPSExpandSmaller(bool expand) { |
| globalParamsLocker(); |
| psExpandSmaller = expand; |
| } |
| |
| void GlobalParams::setPSShrinkLarger(bool shrink) { |
| globalParamsLocker(); |
| psShrinkLarger = shrink; |
| } |
| |
| void GlobalParams::setPSLevel(PSLevel level) { |
| globalParamsLocker(); |
| psLevel = level; |
| } |
| |
| void GlobalParams::setTextEncoding(char *encodingName) { |
| globalParamsLocker(); |
| delete textEncoding; |
| textEncoding = new GooString(encodingName); |
| } |
| |
| bool GlobalParams::setTextEOL(char *s) { |
| globalParamsLocker(); |
| if (!strcmp(s, "unix")) { |
| textEOL = eolUnix; |
| } else if (!strcmp(s, "dos")) { |
| textEOL = eolDOS; |
| } else if (!strcmp(s, "mac")) { |
| textEOL = eolMac; |
| } else { |
| return false; |
| } |
| return true; |
| } |
| |
| void GlobalParams::setTextPageBreaks(bool pageBreaks) { |
| globalParamsLocker(); |
| textPageBreaks = pageBreaks; |
| } |
| |
| bool GlobalParams::setEnableFreeType(char *s) { |
| globalParamsLocker(); |
| return parseYesNo2(s, &enableFreeType); |
| } |
| |
| void GlobalParams::setOverprintPreview(bool overprintPreviewA) { |
| globalParamsLocker(); |
| overprintPreview = overprintPreviewA; |
| } |
| |
| void GlobalParams::setPrintCommands(bool printCommandsA) { |
| globalParamsLocker(); |
| printCommands = printCommandsA; |
| } |
| |
| void GlobalParams::setProfileCommands(bool profileCommandsA) { |
| globalParamsLocker(); |
| profileCommands = profileCommandsA; |
| } |
| |
| void GlobalParams::setErrQuiet(bool errQuietA) { |
| globalParamsLocker(); |
| errQuiet = errQuietA; |
| } |