| // Copyright 2014 The PDFium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "core/fpdfdoc/cpdf_bafontmap.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "constants/annotation_common.h" | |
| #include "constants/catalog.h" | |
| #include "core/fpdfapi/font/cpdf_font.h" | |
| #include "core/fpdfapi/font/cpdf_fontencoding.h" | |
| #include "core/fpdfapi/page/cpdf_docpagedata.h" | |
| #include "core/fpdfapi/page/cpdf_page.h" | |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" | |
| #include "core/fpdfapi/parser/cpdf_document.h" | |
| #include "core/fpdfapi/parser/cpdf_parser.h" | |
| #include "core/fpdfapi/parser/cpdf_reference.h" | |
| #include "core/fpdfapi/parser/cpdf_stream.h" | |
| #include "core/fpdfapi/parser/fpdf_parser_utility.h" | |
| #include "core/fpdfdoc/cpdf_defaultappearance.h" | |
| #include "core/fpdfdoc/cpdf_formfield.h" | |
| #include "core/fpdfdoc/ipvt_fontmap.h" | |
| #include "core/fxcrt/fx_codepage.h" | |
| #include "core/fxcrt/stl_util.h" | |
| #include "core/fxge/cfx_fontmapper.h" | |
| #include "core/fxge/cfx_fontmgr.h" | |
| #include "core/fxge/cfx_gemodule.h" | |
| #include "core/fxge/cfx_standardfont.h" | |
| namespace { | |
| bool FindNativeTrueTypeFont(ByteStringView sFontFaceName) { | |
| CFX_FontMgr* font_mgr = CFX_GEModule::Get()->GetFontMgr(); | |
| CFX_FontMapper* font_mapper = font_mgr->GetBuiltinMapper(); | |
| font_mapper->LoadInstalledFonts(); | |
| return font_mapper->HasInstalledFont(sFontFaceName) || | |
| font_mapper->HasLocalizedFont(sFontFaceName); | |
| } | |
| RetainPtr<CPDF_Font> AddNativeTrueTypeFontToPDF(CPDF_Document* doc, | |
| ByteString sFontFaceName, | |
| FX_Charset nCharset) { | |
| if (!doc) { | |
| return nullptr; | |
| } | |
| auto pFXFont = std::make_unique<CFX_Font>(); | |
| pFXFont->LoadSubstFace(sFontFaceName, true, 0, 0, 0, | |
| FX_GetCodePageFromCharset(nCharset), false); | |
| auto* pDocPageData = CPDF_DocPageData::FromDocument(doc); | |
| return pDocPageData->AddFont(std::move(pFXFont), nCharset); | |
| } | |
| ByteString EncodeFontAlias(ByteString sFontName, FX_Charset nCharset) { | |
| sFontName.Remove(' '); | |
| sFontName += ByteString::Format("_%02X", nCharset); | |
| return sFontName; | |
| } | |
| } // namespace | |
| CPDF_BAFontMap::Data::Data() = default; | |
| CPDF_BAFontMap::Data::~Data() = default; | |
| CPDF_BAFontMap::CPDF_BAFontMap(CPDF_Document* document, | |
| RetainPtr<CPDF_Dictionary> pAnnotDict, | |
| const ByteString& sAPType) | |
| : document_(document), | |
| annot_dict_(std::move(pAnnotDict)), | |
| ap_type_(sAPType) { | |
| FX_Charset nCharset = FX_Charset::kDefault; | |
| default_font_ = GetAnnotDefaultFont(&default_font_name_); | |
| if (default_font_) { | |
| auto maybe_charset = default_font_->GetSubstFontCharset(); | |
| if (maybe_charset.has_value()) { | |
| nCharset = maybe_charset.value(); | |
| } else if (default_font_name_ == "Wingdings" || | |
| default_font_name_ == "Wingdings2" || | |
| default_font_name_ == "Wingdings3" || | |
| default_font_name_ == "Webdings") { | |
| nCharset = FX_Charset::kSymbol; | |
| } else { | |
| nCharset = FX_Charset::kANSI; | |
| } | |
| AddFontData(default_font_, default_font_name_, nCharset); | |
| AddFontToAnnotDict(default_font_, default_font_name_); | |
| } | |
| if (nCharset != FX_Charset::kANSI) { | |
| GetFontIndex(CFX_Font::kDefaultAnsiFontName, FX_Charset::kANSI, false); | |
| } | |
| } | |
| CPDF_BAFontMap::~CPDF_BAFontMap() = default; | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::GetPDFFont(int32_t nFontIndex) { | |
| if (fxcrt::IndexInBounds(data_, nFontIndex)) { | |
| return data_[nFontIndex]->font; | |
| } | |
| return nullptr; | |
| } | |
| ByteString CPDF_BAFontMap::GetPDFFontAlias(int32_t nFontIndex) { | |
| if (fxcrt::IndexInBounds(data_, nFontIndex)) { | |
| return data_[nFontIndex]->sFontName; | |
| } | |
| return ByteString(); | |
| } | |
| int32_t CPDF_BAFontMap::GetWordFontIndex(uint16_t word, | |
| FX_Charset nCharset, | |
| int32_t nFontIndex) { | |
| if (nFontIndex > 0) { | |
| if (KnowWord(nFontIndex, word)) { | |
| return nFontIndex; | |
| } | |
| } else { | |
| if (!data_.empty()) { | |
| const Data* pData = data_.front().get(); | |
| if (nCharset == FX_Charset::kDefault || | |
| pData->nCharset == FX_Charset::kSymbol || | |
| nCharset == pData->nCharset) { | |
| if (KnowWord(0, word)) { | |
| return 0; | |
| } | |
| } | |
| } | |
| } | |
| int32_t nNewFontIndex = | |
| GetFontIndex(GetCachedNativeFontName(nCharset), nCharset, true); | |
| if (nNewFontIndex >= 0) { | |
| if (KnowWord(nNewFontIndex, word)) { | |
| return nNewFontIndex; | |
| } | |
| } | |
| nNewFontIndex = GetFontIndex(CFX_Font::kUniversalDefaultFontName, | |
| FX_Charset::kDefault, false); | |
| if (nNewFontIndex >= 0) { | |
| if (KnowWord(nNewFontIndex, word)) { | |
| return nNewFontIndex; | |
| } | |
| } | |
| return -1; | |
| } | |
| int32_t CPDF_BAFontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) { | |
| if (!fxcrt::IndexInBounds(data_, nFontIndex)) { | |
| return -1; | |
| } | |
| Data* pData = data_[nFontIndex].get(); | |
| if (!pData->font) { | |
| return -1; | |
| } | |
| if (pData->font->IsUnicodeCompatible()) { | |
| return pData->font->CharCodeFromUnicode(word); | |
| } | |
| return word < 0xFF ? word : -1; | |
| } | |
| FX_Charset CPDF_BAFontMap::CharSetFromUnicode(uint16_t word, | |
| FX_Charset nOldCharset) { | |
| // to avoid CJK Font to show ASCII | |
| if (word < 0x7F) { | |
| return FX_Charset::kANSI; | |
| } | |
| // follow the old charset | |
| if (nOldCharset != FX_Charset::kDefault) { | |
| return nOldCharset; | |
| } | |
| return CFX_Font::GetCharSetFromUnicode(word); | |
| } | |
| FX_Charset CPDF_BAFontMap::GetNativeCharset() { | |
| return FX_GetCharsetFromCodePage(FX_GetACP()); | |
| } | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::FindFontSameCharset(ByteString* sFontAlias, | |
| FX_Charset nCharset) { | |
| if (annot_dict_->GetNameFor(pdfium::annotation::kSubtype) != "Widget") { | |
| return nullptr; | |
| } | |
| const CPDF_Dictionary* pRootDict = document_->GetRoot(); | |
| if (!pRootDict) { | |
| return nullptr; | |
| } | |
| RetainPtr<const CPDF_Dictionary> pAcroFormDict = | |
| pRootDict->GetDictFor(pdfium::catalog::kAcroForm); | |
| if (!pAcroFormDict) { | |
| return nullptr; | |
| } | |
| RetainPtr<const CPDF_Dictionary> pDRDict = pAcroFormDict->GetDictFor("DR"); | |
| if (!pDRDict) { | |
| return nullptr; | |
| } | |
| return FindResFontSameCharset(pDRDict.Get(), sFontAlias, nCharset); | |
| } | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::FindResFontSameCharset( | |
| const CPDF_Dictionary* pResDict, | |
| ByteString* sFontAlias, | |
| FX_Charset nCharset) { | |
| if (!pResDict) { | |
| return nullptr; | |
| } | |
| RetainPtr<const CPDF_Dictionary> fonts = pResDict->GetDictFor("Font"); | |
| if (!fonts) { | |
| return nullptr; | |
| } | |
| RetainPtr<CPDF_Font> pFind; | |
| CPDF_DictionaryLocker locker(fonts); | |
| for (const auto& it : locker) { | |
| const ByteString& csKey = it.first; | |
| RetainPtr<CPDF_Dictionary> pElement = | |
| ToDictionary(it.second->GetMutableDirect()); | |
| if (!ValidateDictType(pElement.Get(), "Font")) { | |
| continue; | |
| } | |
| auto* pData = CPDF_DocPageData::FromDocument(document_); | |
| RetainPtr<CPDF_Font> font = pData->GetFont(std::move(pElement)); | |
| if (!font) { | |
| continue; | |
| } | |
| auto maybe_charset = font->GetSubstFontCharset(); | |
| if (maybe_charset.has_value() && maybe_charset.value() == nCharset) { | |
| *sFontAlias = csKey; | |
| pFind = std::move(font); | |
| } | |
| } | |
| return pFind; | |
| } | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::GetAnnotDefaultFont(ByteString* sAlias) { | |
| RetainPtr<CPDF_Dictionary> pAcroFormDict; | |
| const bool bWidget = | |
| (annot_dict_->GetNameFor(pdfium::annotation::kSubtype) == "Widget"); | |
| if (bWidget) { | |
| RetainPtr<CPDF_Dictionary> pRootDict = document_->GetMutableRoot(); | |
| if (pRootDict) { | |
| pAcroFormDict = pRootDict->GetMutableDictFor(pdfium::catalog::kAcroForm); | |
| } | |
| } | |
| ByteString sDA; | |
| RetainPtr<const CPDF_Object> pObj = | |
| CPDF_FormField::GetFieldAttrForDict(annot_dict_.Get(), "DA"); | |
| if (pObj) { | |
| sDA = pObj->GetString(); | |
| } | |
| if (bWidget) { | |
| if (sDA.IsEmpty()) { | |
| pObj = CPDF_FormField::GetFieldAttrForDict(pAcroFormDict.Get(), "DA"); | |
| sDA = pObj ? pObj->GetString() : ByteString(); | |
| } | |
| } | |
| if (sDA.IsEmpty()) { | |
| return nullptr; | |
| } | |
| CPDF_DefaultAppearance appearance(sDA); | |
| auto maybe_font_name_and_size = appearance.GetFont(); | |
| if (maybe_font_name_and_size.has_value()) { | |
| *sAlias = maybe_font_name_and_size.value().name; | |
| } else { | |
| sAlias->clear(); | |
| } | |
| RetainPtr<CPDF_Dictionary> font_dict; | |
| if (RetainPtr<CPDF_Dictionary> pAPDict = | |
| annot_dict_->GetMutableDictFor(pdfium::annotation::kAP)) { | |
| if (RetainPtr<CPDF_Dictionary> pNormalDict = | |
| pAPDict->GetMutableDictFor("N")) { | |
| if (RetainPtr<CPDF_Dictionary> pNormalResDict = | |
| pNormalDict->GetMutableDictFor("Resources")) { | |
| if (RetainPtr<CPDF_Dictionary> pResFontDict = | |
| pNormalResDict->GetMutableDictFor("Font")) { | |
| font_dict = pResFontDict->GetMutableDictFor(sAlias->AsStringView()); | |
| } | |
| } | |
| } | |
| } | |
| if (bWidget && !font_dict && pAcroFormDict) { | |
| if (RetainPtr<CPDF_Dictionary> pDRDict = | |
| pAcroFormDict->GetMutableDictFor("DR")) { | |
| if (RetainPtr<CPDF_Dictionary> pDRFontDict = | |
| pDRDict->GetMutableDictFor("Font")) { | |
| font_dict = pDRFontDict->GetMutableDictFor(sAlias->AsStringView()); | |
| } | |
| } | |
| } | |
| if (!font_dict) { | |
| return nullptr; | |
| } | |
| return CPDF_DocPageData::FromDocument(document_)->GetFont(font_dict); | |
| } | |
| void CPDF_BAFontMap::AddFontToAnnotDict(const RetainPtr<CPDF_Font>& font, | |
| const ByteString& sAlias) { | |
| if (!font) { | |
| return; | |
| } | |
| RetainPtr<CPDF_Dictionary> pAPDict = | |
| annot_dict_->GetOrCreateDictFor(pdfium::annotation::kAP); | |
| // to avoid checkbox and radiobutton | |
| if (ToDictionary(pAPDict->GetObjectFor(ap_type_.AsStringView()))) { | |
| return; | |
| } | |
| RetainPtr<CPDF_Stream> stream = | |
| pAPDict->GetMutableStreamFor(ap_type_.AsStringView()); | |
| if (!stream) { | |
| stream = | |
| document_->NewIndirect<CPDF_Stream>(document_->New<CPDF_Dictionary>()); | |
| pAPDict->SetNewFor<CPDF_Reference>(ap_type_, document_, | |
| stream->GetObjNum()); | |
| } | |
| RetainPtr<CPDF_Dictionary> pStreamResList = | |
| stream->GetMutableDict()->GetOrCreateDictFor("Resources"); | |
| RetainPtr<CPDF_Dictionary> pStreamResFontList = | |
| pStreamResList->GetMutableDictFor("Font"); | |
| if (!pStreamResFontList) { | |
| pStreamResFontList = document_->NewIndirect<CPDF_Dictionary>(); | |
| pStreamResList->SetNewFor<CPDF_Reference>("Font", document_, | |
| pStreamResFontList->GetObjNum()); | |
| } | |
| if (!pStreamResFontList->KeyExist(sAlias.AsStringView())) { | |
| RetainPtr<const CPDF_Dictionary> font_dict = font->GetFontDict(); | |
| RetainPtr<CPDF_Object> pObject = font_dict->IsInline() | |
| ? font_dict->Clone() | |
| : font_dict->MakeReference(document_); | |
| pStreamResFontList->SetFor(sAlias, std::move(pObject)); | |
| } | |
| } | |
| bool CPDF_BAFontMap::KnowWord(int32_t nFontIndex, uint16_t word) { | |
| return fxcrt::IndexInBounds(data_, nFontIndex) && | |
| CharCodeFromUnicode(nFontIndex, word) >= 0; | |
| } | |
| int32_t CPDF_BAFontMap::GetFontIndex(const ByteString& sFontName, | |
| FX_Charset nCharset, | |
| bool bFind) { | |
| int32_t nFontIndex = FindFont(EncodeFontAlias(sFontName, nCharset), nCharset); | |
| if (nFontIndex >= 0) { | |
| return nFontIndex; | |
| } | |
| ByteString sAlias; | |
| RetainPtr<CPDF_Font> font = | |
| bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr; | |
| if (!font) { | |
| font = AddFontToDocument(sFontName, nCharset); | |
| sAlias = EncodeFontAlias(sFontName, nCharset); | |
| } | |
| AddFontToAnnotDict(font, sAlias); | |
| return AddFontData(font, sAlias, nCharset); | |
| } | |
| int32_t CPDF_BAFontMap::AddFontData(const RetainPtr<CPDF_Font>& font, | |
| const ByteString& sFontAlias, | |
| FX_Charset nCharset) { | |
| auto pNewData = std::make_unique<Data>(); | |
| pNewData->font = font; | |
| pNewData->sFontName = sFontAlias; | |
| pNewData->nCharset = nCharset; | |
| data_.push_back(std::move(pNewData)); | |
| return fxcrt::CollectionSize<int32_t>(data_) - 1; | |
| } | |
| int32_t CPDF_BAFontMap::FindFont(const ByteString& sFontName, | |
| FX_Charset nCharset) { | |
| int32_t i = 0; | |
| for (const auto& pData : data_) { | |
| if ((nCharset == FX_Charset::kDefault || nCharset == pData->nCharset) && | |
| (sFontName.IsEmpty() || pData->sFontName == sFontName)) { | |
| return i; | |
| } | |
| ++i; | |
| } | |
| return -1; | |
| } | |
| ByteString CPDF_BAFontMap::GetNativeFontName(FX_Charset nCharset) { | |
| if (nCharset == FX_Charset::kDefault) { | |
| nCharset = GetNativeCharset(); | |
| } | |
| ByteString sFontName = CFX_Font::GetDefaultFontNameByCharset(nCharset); | |
| if (!FindNativeTrueTypeFont(sFontName.AsStringView())) { | |
| return ByteString(); | |
| } | |
| return sFontName; | |
| } | |
| ByteString CPDF_BAFontMap::GetCachedNativeFontName(FX_Charset nCharset) { | |
| for (const auto& pData : native_font_) { | |
| if (pData && pData->nCharset == nCharset) { | |
| return pData->sFontName; | |
| } | |
| } | |
| ByteString sNew = GetNativeFontName(nCharset); | |
| if (sNew.IsEmpty()) { | |
| return ByteString(); | |
| } | |
| auto pNewData = std::make_unique<Native>(); | |
| pNewData->nCharset = nCharset; | |
| pNewData->sFontName = sNew; | |
| native_font_.push_back(std::move(pNewData)); | |
| return sNew; | |
| } | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::AddFontToDocument(ByteString sFontName, | |
| FX_Charset nCharset) { | |
| if (CFX_StandardFont::IsStandardFontName(sFontName)) { | |
| return AddStandardFont(sFontName); | |
| } | |
| return AddSystemFont(sFontName, nCharset); | |
| } | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::AddStandardFont(ByteString sFontName) { | |
| auto* pPageData = CPDF_DocPageData::FromDocument(document_); | |
| if (sFontName == "ZapfDingbats") { | |
| return pPageData->AddStandardFont(sFontName, nullptr); | |
| } | |
| static const CPDF_FontEncoding fe(FontEncoding::kWinAnsi); | |
| return pPageData->AddStandardFont(sFontName, &fe); | |
| } | |
| RetainPtr<CPDF_Font> CPDF_BAFontMap::AddSystemFont(ByteString sFontName, | |
| FX_Charset nCharset) { | |
| if (sFontName.IsEmpty()) { | |
| sFontName = GetNativeFontName(nCharset); | |
| } | |
| if (nCharset == FX_Charset::kDefault) { | |
| nCharset = GetNativeCharset(); | |
| } | |
| return AddNativeTrueTypeFontToPDF(document_, sFontName, nCharset); | |
| } |