diff --git a/Pdf4QtLib/sources/pdffont.cpp b/Pdf4QtLib/sources/pdffont.cpp index 35b9dc8..6823ff2 100644 --- a/Pdf4QtLib/sources/pdffont.cpp +++ b/Pdf4QtLib/sources/pdffont.cpp @@ -49,6 +49,49 @@ namespace pdf { + +enum class ECjkDefaultFontType +{ + Invalid, + AdobeGB, + AdobeCNS, + AdobeJapan, + AdobeKorea +}; + +struct PDF_Default_CJK_Font +{ + ECjkDefaultFontType type = ECjkDefaultFontType::Invalid; + bool isSerif = false; + const char* name = nullptr; +}; + +static constexpr std::array S_DEFAULT_CJK_FONTS = +{ + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "KaiTi_GB2312" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "Song" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, false, "Heiti" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "SimFang" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "FangSong" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, false, "SimHei" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "SimSun" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "SimKai" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "KaiTi" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "SimLi" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeGB, true, "SimLiU" }, + + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeCNS, true, "Ming" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeCNS, false, "Fangti" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeCNS, true, "MingLiU" }, + + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeJapan, false, "Gothic" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeJapan, true, "Mincho" }, + + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeKorea, false, "Gulim" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeKorea, false, "Dotum" }, + PDF_Default_CJK_Font{ ECjkDefaultFontType::AdobeKorea, true, "Batang" }, +}; + /// Storage class for system fonts class PDFSystemFontInfoStorage { @@ -59,11 +102,21 @@ public: /// Loads font from descriptor /// \param descriptor Descriptor describing the font - QByteArray loadFont(const FontDescriptor* descriptor, StandardFontType standardFontType, PDFRenderErrorReporter* reporter) const; + QByteArray loadFont(const CIDSystemInfo* cidSystemInfo, + const FontDescriptor* descriptor, + StandardFontType standardFontType, + PDFRenderErrorReporter* reporter) const; private: explicit PDFSystemFontInfoStorage(); + /// Loads font from descriptor + /// \param descriptor Descriptor describing the font + QByteArray loadFontImpl(const FontDescriptor* descriptor, + QString fontName, + StandardFontType standardFontType, + PDFRenderErrorReporter* reporter) const; + #ifdef Q_OS_UNIX static void checkFontConfigError(FcBool result); #endif @@ -90,6 +143,7 @@ private: { PDFSystemFontInfoStorage* storage = nullptr; HDC hdc = nullptr; + std::set usedFonts; }; std::vector m_fontInfos; @@ -102,9 +156,11 @@ const PDFSystemFontInfoStorage* PDFSystemFontInfoStorage::getInstance() return &instance; } -QByteArray PDFSystemFontInfoStorage::loadFont(const FontDescriptor* descriptor, StandardFontType standardFontType, PDFRenderErrorReporter* reporter) const +QByteArray PDFSystemFontInfoStorage::loadFont(const CIDSystemInfo* cidSystemInfo, + const FontDescriptor* descriptor, + StandardFontType standardFontType, + PDFRenderErrorReporter* reporter) const { - QByteArray result; QString fontName; // Exact match font face name @@ -151,7 +207,61 @@ QByteArray PDFSystemFontInfoStorage::loadFont(const FontDescriptor* descriptor, } } + QByteArray fontData = loadFontImpl(descriptor, fontName, standardFontType, reporter); + + if (fontData.isEmpty() && cidSystemInfo->registry == "Adobe") + { + // Try to load CJK font + ECjkDefaultFontType cjkDefaultFontType = ECjkDefaultFontType::Invalid; + + if (cidSystemInfo->ordering == "GB1") + { + cjkDefaultFontType = ECjkDefaultFontType::AdobeGB; + } + else if (cidSystemInfo->ordering == "CNS1") + { + cjkDefaultFontType = ECjkDefaultFontType::AdobeCNS; + } + else if (cidSystemInfo->ordering == "Japan1") + { + cjkDefaultFontType = ECjkDefaultFontType::AdobeJapan; + } + else if (cidSystemInfo->ordering == "Korea1") + { + cjkDefaultFontType = ECjkDefaultFontType::AdobeKorea; + } + + if (cjkDefaultFontType != ECjkDefaultFontType::Invalid) + { + for (const PDF_Default_CJK_Font& defaultCjkFont : S_DEFAULT_CJK_FONTS) + { + if (defaultCjkFont.type == cjkDefaultFontType && + defaultCjkFont.isSerif == descriptor->isSerif()) + { + fontData = loadFontImpl(descriptor, defaultCjkFont.name, StandardFontType::Invalid, reporter); + + if (!fontData.isEmpty()) + { + return fontData; + } + } + } + } + } + + return fontData; +} + +QByteArray PDFSystemFontInfoStorage::loadFontImpl(const FontDescriptor* descriptor, + QString fontName, + StandardFontType standardFontType, + PDFRenderErrorReporter* reporter) const +{ + QByteArray result; + #if defined(Q_OS_WIN) + + Q_UNUSED(standardFontType); HDC hdc = GetDC(NULL); const BYTE lfItalic = (descriptor->italicAngle != 0.0 ? TRUE : FALSE); if (!fontName.isEmpty()) @@ -318,7 +428,7 @@ QByteArray PDFSystemFontInfoStorage::loadFont(const FontDescriptor* descriptor, if (result.isEmpty() && standardFontType == StandardFontType::Invalid) { reporter->reportRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("Inexact font substitution: font %1 replaced by standard font Times New Roman.").arg(fontName)); - result = loadFont(descriptor, StandardFontType::TimesRoman, reporter); + result = loadFontImpl(descriptor, StandardFontType::TimesRoman, reporter); } return result; @@ -346,7 +456,7 @@ PDFSystemFontInfoStorage::PDFSystemFontInfoStorage() #ifdef Q_OS_WIN int PDFSystemFontInfoStorage::enumerateFontProc(const LOGFONT* font, const TEXTMETRIC* textMetrics, DWORD fontType, LPARAM lParam) { - if ((fontType & TRUETYPE_FONTTYPE) && (font->lfCharSet == ANSI_CHARSET)) + if ((fontType & TRUETYPE_FONTTYPE)) { CallbackInfo* callbackInfo = reinterpret_cast(lParam); @@ -355,6 +465,16 @@ int PDFSystemFontInfoStorage::enumerateFontProc(const LOGFONT* font, const TEXTM fontInfo.textMetric = *textMetrics; fontInfo.faceName = QString::fromWCharArray(font->lfFaceName); fontInfo.faceNameAdjusted = getFontPostscriptName(fontInfo.faceName); + + if (callbackInfo->usedFonts.count(fontInfo.faceName)) + { + return TRUE; + } + else + { + callbackInfo->usedFonts.insert(fontInfo.faceName); + } + callbackInfo->storage->m_fontInfos.push_back(qMove(fontInfo)); // For debug purposes only! @@ -410,7 +530,8 @@ QString PDFSystemFontInfoStorage::getFontPostscriptName(QString fontName) return fontName.remove(QChar(' ')).remove(QChar('-')).remove(QChar(',')).trimmed(); } -PDFFont::PDFFont(FontDescriptor fontDescriptor) : +PDFFont::PDFFont(CIDSystemInfo CIDSystemInfo, FontDescriptor fontDescriptor) : + m_CIDSystemInfo(qMove(CIDSystemInfo)), m_fontDescriptor(qMove(fontDescriptor)) { @@ -1030,7 +1151,7 @@ PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font, } const PDFSystemFontInfoStorage* fontStorage = PDFSystemFontInfoStorage::getInstance(); - impl->m_systemFontData = fontStorage->loadFont(descriptor, standardFontType, reporter); + impl->m_systemFontData = fontStorage->loadFont(font->getCIDSystemInfo(), descriptor, standardFontType, reporter); if (impl->m_systemFontData.isEmpty()) { @@ -1111,6 +1232,21 @@ FontDescriptor PDFFont::readFontDescriptor(const PDFObject& fontDescriptorObject return fontDescriptor; } +CIDSystemInfo PDFFont::readCIDSystemInfo(const PDFObject& cidSystemInfoObject, const PDFDocument* document) +{ + CIDSystemInfo cidSystemInfo; + + if (const PDFDictionary* cidSystemInfoDictionary = document->getDictionaryFromObject(cidSystemInfoObject)) + { + PDFDocumentDataLoaderDecorator cidSystemInfoLoader(document); + cidSystemInfo.registry = cidSystemInfoLoader.readStringFromDictionary(cidSystemInfoDictionary, "Registry"); + cidSystemInfo.ordering = cidSystemInfoLoader.readStringFromDictionary(cidSystemInfoDictionary, "Ordering"); + cidSystemInfo.supplement = cidSystemInfoLoader.readIntegerFromDictionary(cidSystemInfoDictionary, "Supplement", 0); + } + + return cidSystemInfo; +} + PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* document) { const PDFObject& dereferencedFontDictionary = document->getObject(object); @@ -1166,6 +1302,10 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d const PDFObject& fontDescriptorObject = document->getObject(fontDictionary->get("FontDescriptor")); FontDescriptor fontDescriptor = readFontDescriptor(fontDescriptorObject, document); + // Read CID System Info + const PDFObject& cidSystemInfoObject = document->getObject(fontDictionary->get("CIDSystemInfo")); + CIDSystemInfo cidSystemInfo = readCIDSystemInfo(cidSystemInfoObject, document); + // Read Font Encoding // The font encoding for the simple font is determined by this algorithm: // 1) Try to use Encoding dictionary to determine base encoding @@ -1515,6 +1655,9 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d const PDFObject& fontDescriptorObjectForCompositeFont = document->getObject(descendantFontDictionary->get("FontDescriptor")); fontDescriptor = readFontDescriptor(fontDescriptorObjectForCompositeFont, document); + const PDFObject& cidSystemInfoObjectForCompositeFont = document->getObject(descendantFontDictionary->get("CIDSystemInfo")); + cidSystemInfo = readCIDSystemInfo(cidSystemInfoObjectForCompositeFont, document); + QByteArray cidToGidMapping; const PDFObject& cidToGidMappingObject = document->getObject(descendantFontDictionary->get("CIDToGIDMap")); if (cidToGidMappingObject.isStream()) @@ -1583,7 +1726,7 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d toUnicodeCMap = PDFFontCMap::createFromData(decodedStream); } - return PDFFontPointer(new PDFType0Font(qMove(fontDescriptor), qMove(cmap), qMove(toUnicodeCMap), qMove(cidToGidMapper), defaultWidth, qMove(advances))); + return PDFFontPointer(new PDFType0Font(qMove(cidSystemInfo), qMove(fontDescriptor), qMove(cmap), qMove(toUnicodeCMap), qMove(cidToGidMapper), defaultWidth, qMove(advances))); } case FontType::Type3: @@ -1687,10 +1830,10 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d { case FontType::Type1: case FontType::MMType1: - return PDFFontPointer(new PDFType1Font(fontType, qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont, glyphIndexArray)); + return PDFFontPointer(new PDFType1Font(fontType, qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, standardFont, glyphIndexArray)); case FontType::TrueType: - return PDFFontPointer(new PDFTrueTypeFont(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, glyphIndexArray)); + return PDFFontPointer(new PDFTrueTypeFont(qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encoding, simpleFontEncodingTable, glyphIndexArray)); default: { @@ -1702,7 +1845,8 @@ PDFFontPointer PDFFont::createFont(const PDFObject& object, const PDFDocument* d return PDFFontPointer(); } -PDFSimpleFont::PDFSimpleFont(FontDescriptor fontDescriptor, +PDFSimpleFont::PDFSimpleFont(CIDSystemInfo cidSystemInfo, + FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, PDFInteger firstChar, @@ -1711,7 +1855,7 @@ PDFSimpleFont::PDFSimpleFont(FontDescriptor fontDescriptor, PDFEncoding::Encoding encodingType, encoding::EncodingTable encoding, GlyphIndices glyphIndices) : - PDFFont(qMove(fontDescriptor)), + PDFFont(qMove(cidSystemInfo), qMove(fontDescriptor)), m_name(qMove(name)), m_baseFont(qMove(baseFont)), m_firstChar(firstChar), @@ -1795,6 +1939,7 @@ void PDFSimpleFont::dumpFontToTreeItem(QTreeWidgetItem* item) const } PDFType1Font::PDFType1Font(FontType fontType, + CIDSystemInfo cidSystemInfo, FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, @@ -1805,7 +1950,7 @@ PDFType1Font::PDFType1Font(FontType fontType, encoding::EncodingTable encoding, StandardFontType standardFontType, GlyphIndices glyphIndices) : - PDFSimpleFont(qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding, glyphIndices), + PDFSimpleFont(qMove(cidSystemInfo), qMove(fontDescriptor), qMove(name), qMove(baseFont), firstChar, lastChar, qMove(widths), encodingType, encoding, glyphIndices), m_fontType(fontType), m_standardFontType(standardFontType) { @@ -2436,7 +2581,7 @@ PDFType3Font::PDFType3Font(FontDescriptor fontDescriptor, std::vector&& widths, const PDFObject& resources, PDFFontCMap toUnicode) : - PDFFont(qMove(fontDescriptor)), + PDFFont(CIDSystemInfo(), qMove(fontDescriptor)), m_firstCharacterIndex(firstCharacterIndex), m_lastCharacterIndex(lastCharacterIndex), m_fontMatrix(fontMatrix), diff --git a/Pdf4QtLib/sources/pdffont.h b/Pdf4QtLib/sources/pdffont.h index 386ea02..7215c7b 100644 --- a/Pdf4QtLib/sources/pdffont.h +++ b/Pdf4QtLib/sources/pdffont.h @@ -171,6 +171,13 @@ static constexpr PDFEncoding::Encoding getEncodingForStandardFont(StandardFontTy } } +struct PDF4QTLIBSHARED_EXPORT CIDSystemInfo +{ + QByteArray registry; + QByteArray ordering; + int supplement = 0; +}; + struct PDF4QTLIBSHARED_EXPORT FontDescriptor { bool isEmbedded() const { return !fontFile.isEmpty() || !fontFile2.isEmpty() || !fontFile3.isEmpty(); } @@ -178,6 +185,16 @@ struct PDF4QTLIBSHARED_EXPORT FontDescriptor /// Returns embedded font data, or nullptr, if font is not embedded const QByteArray* getEmbeddedFontData() const; + bool isFixedPitch() const { return flags & 1 << 0; } + bool isSerif() const { return flags & 1 << 1; } + bool isSymbolic() const { return flags & 1 << 2; } + bool isScript() const { return flags & 1 << 3; } + bool isNonSymbolic() const { return flags & 1 << 5; } + bool isItalic() const { return flags & 1 << 6; } + bool isAllCap() const { return flags & 1 << 16; } + bool isSmallCap() const { return flags & 1 << 17; } + bool isForceBold() const { return flags & 1 << 18; } + QByteArray fontName; QByteArray fontFamily; QFont::Stretch fontStretch = QFont::AnyStretch; @@ -267,7 +284,7 @@ private: class PDF4QTLIBSHARED_EXPORT PDFFont { public: - explicit PDFFont(FontDescriptor fontDescriptor); + explicit PDFFont(CIDSystemInfo CIDSystemInfo, FontDescriptor fontDescriptor); virtual ~PDFFont() = default; /// Returns the font type @@ -282,6 +299,9 @@ public: /// Returns font descriptor const FontDescriptor* getFontDescriptor() const { return &m_fontDescriptor; } + /// Returns CID system info + const CIDSystemInfo* getCIDSystemInfo() const { return &m_CIDSystemInfo; } + /// Adds information about the font into tree item virtual void dumpFontToTreeItem(QTreeWidgetItem* item) const { Q_UNUSED(item); } @@ -295,7 +315,13 @@ public: /// \param document Document static FontDescriptor readFontDescriptor(const PDFObject& fontDescriptorObject, const PDFDocument* document); + /// Tries to read CID SystemInfo from the object + /// \param cidSystemInfoObject CID System Info dictionary + /// \param document Document + static CIDSystemInfo readCIDSystemInfo(const PDFObject& cidSystemInfoObject, const PDFDocument* document); + protected: + CIDSystemInfo m_CIDSystemInfo; FontDescriptor m_fontDescriptor; }; @@ -306,7 +332,8 @@ class PDFSimpleFont : public PDFFont using BaseClass = PDFFont; public: - explicit PDFSimpleFont(FontDescriptor fontDescriptor, + explicit PDFSimpleFont(CIDSystemInfo cidSystemInfo, + FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, PDFInteger firstChar, @@ -343,6 +370,7 @@ class PDFType1Font : public PDFSimpleFont public: explicit PDFType1Font(FontType fontType, + CIDSystemInfo cidSystemInfo, FontDescriptor fontDescriptor, QByteArray name, QByteArray baseFont, @@ -602,8 +630,8 @@ private: class PDFType0Font : public PDFFont { public: - explicit inline PDFType0Font(FontDescriptor fontDescriptor, PDFFontCMap cmap, PDFFontCMap toUnicode, PDFCIDtoGIDMapper mapper, PDFReal defaultAdvance, std::unordered_map advances) : - PDFFont(qMove(fontDescriptor)), + explicit inline PDFType0Font(CIDSystemInfo cidSystemInfo, FontDescriptor fontDescriptor, PDFFontCMap cmap, PDFFontCMap toUnicode, PDFCIDtoGIDMapper mapper, PDFReal defaultAdvance, std::unordered_map advances) : + PDFFont(qMove(cidSystemInfo), qMove(fontDescriptor)), m_cmap(qMove(cmap)), m_toUnicode(qMove(toUnicode)), m_mapper(qMove(mapper)),