| // Copyright 2014 PDFium Authors. All rights reserved. |
| // 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/fpdfapi/page/cpdf_docpagedata.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "build/build_config.h" |
| #include "core/fpdfapi/font/cpdf_type1font.h" |
| #include "core/fpdfapi/page/cpdf_form.h" |
| #include "core/fpdfapi/page/cpdf_iccprofile.h" |
| #include "core/fpdfapi/page/cpdf_image.h" |
| #include "core/fpdfapi/page/cpdf_pagemodule.h" |
| #include "core/fpdfapi/page/cpdf_pattern.h" |
| #include "core/fpdfapi/page/cpdf_shadingpattern.h" |
| #include "core/fpdfapi/page/cpdf_tilingpattern.h" |
| #include "core/fpdfapi/parser/cpdf_array.h" |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" |
| #include "core/fpdfapi/parser/cpdf_name.h" |
| #include "core/fpdfapi/parser/cpdf_number.h" |
| #include "core/fpdfapi/parser/cpdf_reference.h" |
| #include "core/fpdfapi/parser/cpdf_stream.h" |
| #include "core/fpdfapi/parser/cpdf_stream_acc.h" |
| #include "core/fpdfapi/parser/cpdf_string.h" |
| #include "core/fxcrt/fx_codepage.h" |
| #include "core/fxcrt/fx_memory.h" |
| #include "core/fxcrt/fx_safe_types.h" |
| #include "core/fxcrt/scoped_set_insertion.h" |
| #include "core/fxge/cfx_font.h" |
| #include "core/fxge/cfx_fontmapper.h" |
| #include "core/fxge/cfx_substfont.h" |
| #include "core/fxge/cfx_unicodeencoding.h" |
| #include "core/fxge/fx_font.h" |
| #include "third_party/base/check.h" |
| #include "third_party/base/stl_util.h" |
| |
| namespace { |
| |
| void InsertWidthArrayImpl(std::vector<int> widths, CPDF_Array* pWidthArray) { |
| size_t i; |
| for (i = 1; i < widths.size(); i++) { |
| if (widths[i] != widths[0]) |
| break; |
| } |
| if (i == widths.size()) { |
| int first = pWidthArray->GetIntegerAt(pWidthArray->size() - 1); |
| pWidthArray->AppendNew<CPDF_Number>(first + |
| static_cast<int>(widths.size()) - 1); |
| pWidthArray->AppendNew<CPDF_Number>(widths[0]); |
| return; |
| } |
| CPDF_Array* pWidthArray1 = pWidthArray->AppendNew<CPDF_Array>(); |
| for (int w : widths) |
| pWidthArray1->AppendNew<CPDF_Number>(w); |
| } |
| |
| #if defined(OS_WIN) |
| void InsertWidthArray(HDC hDC, int start, int end, CPDF_Array* pWidthArray) { |
| std::vector<int> widths(end - start + 1); |
| GetCharWidth(hDC, start, end, widths.data()); |
| InsertWidthArrayImpl(std::move(widths), pWidthArray); |
| } |
| |
| ByteString GetPSNameFromTT(HDC hDC) { |
| ByteString result; |
| DWORD size = ::GetFontData(hDC, 'eman', 0, nullptr, 0); |
| if (size != GDI_ERROR) { |
| LPBYTE buffer = FX_Alloc(BYTE, size); |
| ::GetFontData(hDC, 'eman', 0, buffer, size); |
| result = GetNameFromTT({buffer, size}, 6); |
| FX_Free(buffer); |
| } |
| return result; |
| } |
| #endif // defined(OS_WIN) |
| |
| void InsertWidthArray1(CFX_Font* pFont, |
| CFX_UnicodeEncoding* pEncoding, |
| wchar_t start, |
| wchar_t end, |
| CPDF_Array* pWidthArray) { |
| std::vector<int> widths(end - start + 1); |
| for (size_t i = 0; i < widths.size(); ++i) { |
| int glyph_index = pEncoding->GlyphFromCharCode(start + i); |
| widths[i] = pFont->GetGlyphWidth(glyph_index); |
| } |
| InsertWidthArrayImpl(std::move(widths), pWidthArray); |
| } |
| |
| int CalculateFlags(bool bold, |
| bool italic, |
| bool fixedPitch, |
| bool serif, |
| bool script, |
| bool symbolic) { |
| int flags = 0; |
| if (bold) |
| flags |= FXFONT_FORCE_BOLD; |
| if (italic) |
| flags |= FXFONT_ITALIC; |
| if (fixedPitch) |
| flags |= FXFONT_FIXED_PITCH; |
| if (serif) |
| flags |= FXFONT_SERIF; |
| if (script) |
| flags |= FXFONT_SCRIPT; |
| if (symbolic) |
| flags |= FXFONT_SYMBOLIC; |
| else |
| flags |= FXFONT_NONSYMBOLIC; |
| return flags; |
| } |
| |
| void ProcessNonbCJK(CPDF_Dictionary* pBaseDict, |
| bool bold, |
| bool italic, |
| ByteString basefont, |
| RetainPtr<CPDF_Array> pWidths) { |
| if (bold && italic) |
| basefont += ",BoldItalic"; |
| else if (bold) |
| basefont += ",Bold"; |
| else if (italic) |
| basefont += ",Italic"; |
| pBaseDict->SetNewFor<CPDF_Name>("Subtype", "TrueType"); |
| pBaseDict->SetNewFor<CPDF_Name>("BaseFont", basefont); |
| pBaseDict->SetNewFor<CPDF_Number>("FirstChar", 32); |
| pBaseDict->SetNewFor<CPDF_Number>("LastChar", 255); |
| pBaseDict->SetFor("Widths", pWidths); |
| } |
| |
| RetainPtr<CPDF_Dictionary> CalculateFontDesc(CPDF_Document* pDoc, |
| ByteString basefont, |
| int flags, |
| int italicangle, |
| int ascend, |
| int descend, |
| RetainPtr<CPDF_Array> bbox, |
| int32_t stemV) { |
| auto pFontDesc = pDoc->New<CPDF_Dictionary>(); |
| pFontDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor"); |
| pFontDesc->SetNewFor<CPDF_Name>("FontName", basefont); |
| pFontDesc->SetNewFor<CPDF_Number>("Flags", flags); |
| pFontDesc->SetFor("FontBBox", bbox); |
| pFontDesc->SetNewFor<CPDF_Number>("ItalicAngle", italicangle); |
| pFontDesc->SetNewFor<CPDF_Number>("Ascent", ascend); |
| pFontDesc->SetNewFor<CPDF_Number>("Descent", descend); |
| pFontDesc->SetNewFor<CPDF_Number>("StemV", stemV); |
| return pFontDesc; |
| } |
| |
| } // namespace |
| |
| // static |
| CPDF_DocPageData* CPDF_DocPageData::FromDocument(const CPDF_Document* pDoc) { |
| return static_cast<CPDF_DocPageData*>(pDoc->GetPageData()); |
| } |
| |
| CPDF_DocPageData::CPDF_DocPageData() = default; |
| |
| CPDF_DocPageData::~CPDF_DocPageData() { |
| for (auto& it : m_FontMap) { |
| if (it.second) |
| it.second->WillBeDestroyed(); |
| } |
| } |
| |
| void CPDF_DocPageData::ClearStockFont() { |
| CPDF_PageModule::GetInstance()->ClearStockFont(GetDocument()); |
| } |
| |
| RetainPtr<CPDF_Font> CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) { |
| if (!pFontDict) |
| return nullptr; |
| |
| auto it = m_FontMap.find(pFontDict); |
| if (it != m_FontMap.end() && it->second) |
| return pdfium::WrapRetain(it->second.Get()); |
| |
| RetainPtr<CPDF_Font> pFont = |
| CPDF_Font::Create(GetDocument(), pFontDict, this); |
| if (!pFont) |
| return nullptr; |
| |
| m_FontMap[pFontDict].Reset(pFont.Get()); |
| return pFont; |
| } |
| |
| RetainPtr<CPDF_Font> CPDF_DocPageData::GetStandardFont( |
| const ByteString& fontName, |
| const CPDF_FontEncoding* pEncoding) { |
| if (fontName.IsEmpty()) |
| return nullptr; |
| |
| for (auto& it : m_FontMap) { |
| CPDF_Font* pFont = it.second.Get(); |
| if (!pFont) |
| continue; |
| if (pFont->GetBaseFontName() != fontName) |
| continue; |
| if (pFont->IsEmbedded()) |
| continue; |
| if (!pFont->IsType1Font()) |
| continue; |
| if (pFont->GetFontDict()->KeyExist("Widths")) |
| continue; |
| |
| CPDF_Type1Font* pT1Font = pFont->AsType1Font(); |
| if (pEncoding && !pT1Font->GetEncoding()->IsIdentical(pEncoding)) |
| continue; |
| |
| return pdfium::WrapRetain(pFont); |
| } |
| |
| CPDF_Dictionary* pDict = GetDocument()->NewIndirect<CPDF_Dictionary>(); |
| pDict->SetNewFor<CPDF_Name>("Type", "Font"); |
| pDict->SetNewFor<CPDF_Name>("Subtype", "Type1"); |
| pDict->SetNewFor<CPDF_Name>("BaseFont", fontName); |
| if (pEncoding) { |
| pDict->SetFor("Encoding", |
| pEncoding->Realize(GetDocument()->GetByteStringPool())); |
| } |
| |
| // Note: NULL FormFactoryIface OK since known Type1 font from above. |
| RetainPtr<CPDF_Font> pFont = CPDF_Font::Create(GetDocument(), pDict, nullptr); |
| if (!pFont) |
| return nullptr; |
| |
| m_FontMap[pDict].Reset(pFont.Get()); |
| return pFont; |
| } |
| |
| RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpace( |
| const CPDF_Object* pCSObj, |
| const CPDF_Dictionary* pResources) { |
| std::set<const CPDF_Object*> visited; |
| return GetColorSpaceGuarded(pCSObj, pResources, &visited); |
| } |
| |
| RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpaceGuarded( |
| const CPDF_Object* pCSObj, |
| const CPDF_Dictionary* pResources, |
| std::set<const CPDF_Object*>* pVisited) { |
| std::set<const CPDF_Object*> visitedLocal; |
| return GetColorSpaceInternal(pCSObj, pResources, pVisited, &visitedLocal); |
| } |
| |
| RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpaceInternal( |
| const CPDF_Object* pCSObj, |
| const CPDF_Dictionary* pResources, |
| std::set<const CPDF_Object*>* pVisited, |
| std::set<const CPDF_Object*>* pVisitedInternal) { |
| if (!pCSObj) |
| return nullptr; |
| |
| if (pdfium::Contains(*pVisitedInternal, pCSObj)) |
| return nullptr; |
| |
| ScopedSetInsertion<const CPDF_Object*> insertion(pVisitedInternal, pCSObj); |
| |
| if (pCSObj->IsName()) { |
| ByteString name = pCSObj->GetString(); |
| RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::ColorspaceFromName(name); |
| if (!pCS && pResources) { |
| const CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace"); |
| if (pList) { |
| return GetColorSpaceInternal(pList->GetDirectObjectFor(name), nullptr, |
| pVisited, pVisitedInternal); |
| } |
| } |
| if (!pCS || !pResources) |
| return pCS; |
| |
| const CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace"); |
| if (!pColorSpaces) |
| return pCS; |
| |
| const CPDF_Object* pDefaultCS = nullptr; |
| switch (pCS->GetFamily()) { |
| case CPDF_ColorSpace::Family::kDeviceRGB: |
| pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultRGB"); |
| break; |
| case CPDF_ColorSpace::Family::kDeviceGray: |
| pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultGray"); |
| break; |
| case CPDF_ColorSpace::Family::kDeviceCMYK: |
| pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultCMYK"); |
| break; |
| default: |
| break; |
| } |
| if (!pDefaultCS) |
| return pCS; |
| |
| return GetColorSpaceInternal(pDefaultCS, nullptr, pVisited, |
| pVisitedInternal); |
| } |
| |
| const CPDF_Array* pArray = pCSObj->AsArray(); |
| if (!pArray || pArray->IsEmpty()) |
| return nullptr; |
| |
| if (pArray->size() == 1) { |
| return GetColorSpaceInternal(pArray->GetDirectObjectAt(0), pResources, |
| pVisited, pVisitedInternal); |
| } |
| |
| auto it = m_ColorSpaceMap.find(pCSObj); |
| if (it != m_ColorSpaceMap.end() && it->second) |
| return pdfium::WrapRetain(it->second.Get()); |
| |
| RetainPtr<CPDF_ColorSpace> pCS = |
| CPDF_ColorSpace::Load(GetDocument(), pArray, pVisited); |
| if (!pCS) |
| return nullptr; |
| |
| m_ColorSpaceMap[pCSObj].Reset(pCS.Get()); |
| return pCS; |
| } |
| |
| RetainPtr<CPDF_Pattern> CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj, |
| const CFX_Matrix& matrix) { |
| if (!pPatternObj) |
| return nullptr; |
| |
| auto it = m_PatternMap.find(pPatternObj); |
| if (it != m_PatternMap.end() && it->second) |
| return pdfium::WrapRetain(it->second.Get()); |
| |
| CPDF_Dictionary* pDict = pPatternObj->GetDict(); |
| if (!pDict) |
| return nullptr; |
| |
| RetainPtr<CPDF_Pattern> pPattern; |
| int type = pDict->GetIntegerFor("PatternType"); |
| if (type == CPDF_Pattern::kTiling) { |
| pPattern = pdfium::MakeRetain<CPDF_TilingPattern>(GetDocument(), |
| pPatternObj, matrix); |
| } else if (type == CPDF_Pattern::kShading) { |
| pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>( |
| GetDocument(), pPatternObj, false, matrix); |
| } else { |
| return nullptr; |
| } |
| m_PatternMap[pPatternObj].Reset(pPattern.Get()); |
| return pPattern; |
| } |
| |
| RetainPtr<CPDF_ShadingPattern> CPDF_DocPageData::GetShading( |
| CPDF_Object* pPatternObj, |
| const CFX_Matrix& matrix) { |
| if (!pPatternObj) |
| return nullptr; |
| |
| auto it = m_PatternMap.find(pPatternObj); |
| if (it != m_PatternMap.end() && it->second) |
| return pdfium::WrapRetain(it->second->AsShadingPattern()); |
| |
| auto pPattern = pdfium::MakeRetain<CPDF_ShadingPattern>( |
| GetDocument(), pPatternObj, true, matrix); |
| m_PatternMap[pPatternObj].Reset(pPattern.Get()); |
| return pPattern; |
| } |
| |
| RetainPtr<CPDF_Image> CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) { |
| DCHECK(dwStreamObjNum); |
| auto it = m_ImageMap.find(dwStreamObjNum); |
| if (it != m_ImageMap.end()) |
| return it->second; |
| |
| auto pImage = pdfium::MakeRetain<CPDF_Image>(GetDocument(), dwStreamObjNum); |
| m_ImageMap[dwStreamObjNum] = pImage; |
| return pImage; |
| } |
| |
| void CPDF_DocPageData::MaybePurgeImage(uint32_t dwStreamObjNum) { |
| DCHECK(dwStreamObjNum); |
| auto it = m_ImageMap.find(dwStreamObjNum); |
| if (it != m_ImageMap.end() && it->second->HasOneRef()) |
| m_ImageMap.erase(it); |
| } |
| |
| RetainPtr<CPDF_IccProfile> CPDF_DocPageData::GetIccProfile( |
| const CPDF_Stream* pProfileStream) { |
| if (!pProfileStream) |
| return nullptr; |
| |
| auto it = m_IccProfileMap.find(pProfileStream); |
| if (it != m_IccProfileMap.end() && it->second) |
| return pdfium::WrapRetain(it->second.Get()); |
| |
| auto pAccessor = pdfium::MakeRetain<CPDF_StreamAcc>(pProfileStream); |
| pAccessor->LoadAllDataFiltered(); |
| |
| ByteString bsDigest = pAccessor->ComputeDigest(); |
| auto hash_it = m_HashProfileMap.find(bsDigest); |
| if (hash_it != m_HashProfileMap.end()) { |
| auto it_copied_stream = m_IccProfileMap.find(hash_it->second.Get()); |
| if (it_copied_stream != m_IccProfileMap.end() && it_copied_stream->second) |
| return pdfium::WrapRetain(it_copied_stream->second.Get()); |
| } |
| auto pProfile = |
| pdfium::MakeRetain<CPDF_IccProfile>(pProfileStream, pAccessor->GetSpan()); |
| m_IccProfileMap[pProfileStream].Reset(pProfile.Get()); |
| m_HashProfileMap[bsDigest].Reset(pProfileStream); |
| return pProfile; |
| } |
| |
| RetainPtr<CPDF_StreamAcc> CPDF_DocPageData::GetFontFileStreamAcc( |
| const CPDF_Stream* pFontStream) { |
| DCHECK(pFontStream); |
| auto it = m_FontFileMap.find(pFontStream); |
| if (it != m_FontFileMap.end()) |
| return it->second; |
| |
| const CPDF_Dictionary* pFontDict = pFontStream->GetDict(); |
| int32_t len1 = pFontDict->GetIntegerFor("Length1"); |
| int32_t len2 = pFontDict->GetIntegerFor("Length2"); |
| int32_t len3 = pFontDict->GetIntegerFor("Length3"); |
| uint32_t org_size = 0; |
| if (len1 >= 0 && len2 >= 0 && len3 >= 0) { |
| FX_SAFE_UINT32 safe_org_size = len1; |
| safe_org_size += len2; |
| safe_org_size += len3; |
| org_size = safe_org_size.ValueOrDefault(0); |
| } |
| |
| auto pFontAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pFontStream); |
| pFontAcc->LoadAllDataFilteredWithEstimatedSize(org_size); |
| m_FontFileMap[pFontStream] = pFontAcc; |
| return pFontAcc; |
| } |
| |
| void CPDF_DocPageData::MaybePurgeFontFileStreamAcc( |
| const CPDF_Stream* pFontStream) { |
| if (!pFontStream) |
| return; |
| |
| auto it = m_FontFileMap.find(pFontStream); |
| if (it != m_FontFileMap.end() && it->second->HasOneRef()) |
| m_FontFileMap.erase(it); |
| } |
| |
| std::unique_ptr<CPDF_Font::FormIface> CPDF_DocPageData::CreateForm( |
| CPDF_Document* pDocument, |
| CPDF_Dictionary* pPageResources, |
| CPDF_Stream* pFormStream) { |
| return std::make_unique<CPDF_Form>(pDocument, pPageResources, pFormStream); |
| } |
| |
| RetainPtr<CPDF_Font> CPDF_DocPageData::AddStandardFont( |
| const ByteString& fontName, |
| const CPDF_FontEncoding* pEncoding) { |
| ByteString mutable_name(fontName); |
| Optional<CFX_FontMapper::StandardFont> font_id = |
| CFX_FontMapper::GetStandardFontName(&mutable_name); |
| if (!font_id.has_value()) |
| return nullptr; |
| return GetStandardFont(mutable_name, pEncoding); |
| } |
| |
| RetainPtr<CPDF_Font> CPDF_DocPageData::AddFont(std::unique_ptr<CFX_Font> pFont, |
| int charset) { |
| if (!pFont) |
| return nullptr; |
| |
| const bool bCJK = FX_CharSetIsCJK(charset); |
| ByteString basefont = pFont->GetFamilyName(); |
| basefont.Replace(" ", ""); |
| int flags = |
| CalculateFlags(pFont->IsBold(), pFont->IsItalic(), pFont->IsFixedWidth(), |
| false, false, charset == FX_CHARSET_Symbol); |
| |
| CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>(); |
| pBaseDict->SetNewFor<CPDF_Name>("Type", "Font"); |
| auto pEncoding = std::make_unique<CFX_UnicodeEncoding>(pFont.get()); |
| CPDF_Dictionary* pFontDict = pBaseDict; |
| if (!bCJK) { |
| auto pWidths = pdfium::MakeRetain<CPDF_Array>(); |
| for (int charcode = 32; charcode < 128; charcode++) { |
| int glyph_index = pEncoding->GlyphFromCharCode(charcode); |
| int char_width = pFont->GetGlyphWidth(glyph_index); |
| pWidths->AppendNew<CPDF_Number>(char_width); |
| } |
| if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Default || |
| charset == FX_CHARSET_Symbol) { |
| pBaseDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding"); |
| for (int charcode = 128; charcode <= 255; charcode++) { |
| int glyph_index = pEncoding->GlyphFromCharCode(charcode); |
| int char_width = pFont->GetGlyphWidth(glyph_index); |
| pWidths->AppendNew<CPDF_Number>(char_width); |
| } |
| } else { |
| size_t i = CalculateEncodingDict(charset, pBaseDict); |
| if (i < pdfium::size(g_FX_CharsetUnicodes)) { |
| const uint16_t* pUnicodes = g_FX_CharsetUnicodes[i].m_pUnicodes; |
| for (int j = 0; j < 128; j++) { |
| int glyph_index = pEncoding->GlyphFromCharCode(pUnicodes[j]); |
| int char_width = pFont->GetGlyphWidth(glyph_index); |
| pWidths->AppendNew<CPDF_Number>(char_width); |
| } |
| } |
| } |
| ProcessNonbCJK(pBaseDict, pFont->IsBold(), pFont->IsItalic(), basefont, |
| std::move(pWidths)); |
| } else { |
| pFontDict = ProcessbCJK( |
| pBaseDict, charset, basefont, |
| [&pFont, &pEncoding](wchar_t start, wchar_t end, CPDF_Array* widthArr) { |
| InsertWidthArray1(pFont.get(), pEncoding.get(), start, end, widthArr); |
| }); |
| } |
| int italicangle = pFont->GetSubstFontItalicAngle(); |
| FX_RECT bbox = pFont->GetBBox().value_or(FX_RECT()); |
| auto pBBox = pdfium::MakeRetain<CPDF_Array>(); |
| pBBox->AppendNew<CPDF_Number>(bbox.left); |
| pBBox->AppendNew<CPDF_Number>(bbox.bottom); |
| pBBox->AppendNew<CPDF_Number>(bbox.right); |
| pBBox->AppendNew<CPDF_Number>(bbox.top); |
| int32_t nStemV = 0; |
| if (pFont->GetSubstFont()) { |
| nStemV = pFont->GetSubstFont()->m_Weight / 5; |
| } else { |
| static const char stem_chars[] = {'i', 'I', '!', '1'}; |
| const size_t count = pdfium::size(stem_chars); |
| uint32_t glyph = pEncoding->GlyphFromCharCode(stem_chars[0]); |
| nStemV = pFont->GetGlyphWidth(glyph); |
| for (size_t i = 1; i < count; i++) { |
| glyph = pEncoding->GlyphFromCharCode(stem_chars[i]); |
| int width = pFont->GetGlyphWidth(glyph); |
| if (width > 0 && width < nStemV) |
| nStemV = width; |
| } |
| } |
| CPDF_Dictionary* pFontDesc = |
| ToDictionary(GetDocument()->AddIndirectObject(CalculateFontDesc( |
| GetDocument(), basefont, flags, italicangle, pFont->GetAscent(), |
| pFont->GetDescent(), std::move(pBBox), nStemV))); |
| pFontDict->SetNewFor<CPDF_Reference>("FontDescriptor", GetDocument(), |
| pFontDesc->GetObjNum()); |
| return GetFont(pBaseDict); |
| } |
| |
| #if defined(OS_WIN) |
| RetainPtr<CPDF_Font> CPDF_DocPageData::AddWindowsFont(LOGFONTA* pLogFont) { |
| pLogFont->lfHeight = -1000; |
| pLogFont->lfWidth = 0; |
| HGDIOBJ hFont = CreateFontIndirectA(pLogFont); |
| HDC hDC = CreateCompatibleDC(nullptr); |
| hFont = SelectObject(hDC, hFont); |
| int tm_size = GetOutlineTextMetrics(hDC, 0, nullptr); |
| if (tm_size == 0) { |
| hFont = SelectObject(hDC, hFont); |
| DeleteObject(hFont); |
| DeleteDC(hDC); |
| return nullptr; |
| } |
| |
| LPBYTE tm_buf = FX_Alloc(BYTE, tm_size); |
| OUTLINETEXTMETRIC* ptm = reinterpret_cast<OUTLINETEXTMETRIC*>(tm_buf); |
| GetOutlineTextMetrics(hDC, tm_size, ptm); |
| int flags = CalculateFlags(false, pLogFont->lfItalic != 0, |
| (pLogFont->lfPitchAndFamily & 3) == FIXED_PITCH, |
| (pLogFont->lfPitchAndFamily & 0xf8) == FF_ROMAN, |
| (pLogFont->lfPitchAndFamily & 0xf8) == FF_SCRIPT, |
| pLogFont->lfCharSet == FX_CHARSET_Symbol); |
| |
| const bool bCJK = FX_CharSetIsCJK(pLogFont->lfCharSet); |
| ByteString basefont; |
| if (bCJK) |
| basefont = GetPSNameFromTT(hDC); |
| |
| if (basefont.IsEmpty()) |
| basefont = pLogFont->lfFaceName; |
| |
| int italicangle = ptm->otmItalicAngle / 10; |
| int ascend = ptm->otmrcFontBox.top; |
| int descend = ptm->otmrcFontBox.bottom; |
| int capheight = ptm->otmsCapEmHeight; |
| int bbox[4] = {ptm->otmrcFontBox.left, ptm->otmrcFontBox.bottom, |
| ptm->otmrcFontBox.right, ptm->otmrcFontBox.top}; |
| FX_Free(tm_buf); |
| basefont.Replace(" ", ""); |
| CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect<CPDF_Dictionary>(); |
| pBaseDict->SetNewFor<CPDF_Name>("Type", "Font"); |
| CPDF_Dictionary* pFontDict = pBaseDict; |
| if (!bCJK) { |
| if (pLogFont->lfCharSet == FX_CHARSET_ANSI || |
| pLogFont->lfCharSet == FX_CHARSET_Default || |
| pLogFont->lfCharSet == FX_CHARSET_Symbol) { |
| pBaseDict->SetNewFor<CPDF_Name>("Encoding", "WinAnsiEncoding"); |
| } else { |
| CalculateEncodingDict(pLogFont->lfCharSet, pBaseDict); |
| } |
| int char_widths[224]; |
| GetCharWidth(hDC, 32, 255, char_widths); |
| auto pWidths = pdfium::MakeRetain<CPDF_Array>(); |
| for (size_t i = 0; i < 224; i++) |
| pWidths->AppendNew<CPDF_Number>(char_widths[i]); |
| ProcessNonbCJK(pBaseDict, pLogFont->lfWeight > FW_MEDIUM, |
| pLogFont->lfItalic != 0, basefont, std::move(pWidths)); |
| } else { |
| pFontDict = |
| ProcessbCJK(pBaseDict, pLogFont->lfCharSet, basefont, |
| [&hDC](wchar_t start, wchar_t end, CPDF_Array* widthArr) { |
| InsertWidthArray(hDC, start, end, widthArr); |
| }); |
| } |
| auto pBBox = pdfium::MakeRetain<CPDF_Array>(); |
| for (int i = 0; i < 4; i++) |
| pBBox->AppendNew<CPDF_Number>(bbox[i]); |
| RetainPtr<CPDF_Dictionary> pFontDesc = |
| CalculateFontDesc(GetDocument(), basefont, flags, italicangle, ascend, |
| descend, std::move(pBBox), pLogFont->lfWeight / 5); |
| pFontDesc->SetNewFor<CPDF_Number>("CapHeight", capheight); |
| pFontDict->SetFor("FontDescriptor", |
| GetDocument() |
| ->AddIndirectObject(std::move(pFontDesc)) |
| ->MakeReference(GetDocument())); |
| hFont = SelectObject(hDC, hFont); |
| DeleteObject(hFont); |
| DeleteDC(hDC); |
| return GetFont(pBaseDict); |
| } |
| #endif // defined(OS_WIN) |
| |
| size_t CPDF_DocPageData::CalculateEncodingDict(int charset, |
| CPDF_Dictionary* pBaseDict) { |
| size_t i; |
| for (i = 0; i < pdfium::size(g_FX_CharsetUnicodes); ++i) { |
| if (g_FX_CharsetUnicodes[i].m_Charset == charset) |
| break; |
| } |
| if (i == pdfium::size(g_FX_CharsetUnicodes)) |
| return i; |
| |
| CPDF_Dictionary* pEncodingDict = |
| GetDocument()->NewIndirect<CPDF_Dictionary>(); |
| pEncodingDict->SetNewFor<CPDF_Name>("BaseEncoding", "WinAnsiEncoding"); |
| |
| CPDF_Array* pArray = pEncodingDict->SetNewFor<CPDF_Array>("Differences"); |
| pArray->AppendNew<CPDF_Number>(128); |
| |
| const uint16_t* pUnicodes = g_FX_CharsetUnicodes[i].m_pUnicodes; |
| for (int j = 0; j < 128; j++) { |
| ByteString name = PDF_AdobeNameFromUnicode(pUnicodes[j]); |
| pArray->AppendNew<CPDF_Name>(name.IsEmpty() ? ".notdef" : name); |
| } |
| pBaseDict->SetNewFor<CPDF_Reference>("Encoding", GetDocument(), |
| pEncodingDict->GetObjNum()); |
| return i; |
| } |
| |
| CPDF_Dictionary* CPDF_DocPageData::ProcessbCJK( |
| CPDF_Dictionary* pBaseDict, |
| int charset, |
| ByteString basefont, |
| std::function<void(wchar_t, wchar_t, CPDF_Array*)> Insert) { |
| CPDF_Dictionary* pFontDict = GetDocument()->NewIndirect<CPDF_Dictionary>(); |
| ByteString cmap; |
| ByteString ordering; |
| int supplement = 0; |
| CPDF_Array* pWidthArray = pFontDict->SetNewFor<CPDF_Array>("W"); |
| switch (charset) { |
| case FX_CHARSET_ChineseTraditional: |
| cmap = "ETenms-B5-H"; |
| ordering = "CNS1"; |
| supplement = 4; |
| pWidthArray->AppendNew<CPDF_Number>(1); |
| Insert(0x20, 0x7e, pWidthArray); |
| break; |
| case FX_CHARSET_ChineseSimplified: |
| cmap = "GBK-EUC-H"; |
| ordering = "GB1"; |
| supplement = 2; |
| pWidthArray->AppendNew<CPDF_Number>(7716); |
| Insert(0x20, 0x20, pWidthArray); |
| pWidthArray->AppendNew<CPDF_Number>(814); |
| Insert(0x21, 0x7e, pWidthArray); |
| break; |
| case FX_CHARSET_Hangul: |
| cmap = "KSCms-UHC-H"; |
| ordering = "Korea1"; |
| supplement = 2; |
| pWidthArray->AppendNew<CPDF_Number>(1); |
| Insert(0x20, 0x7e, pWidthArray); |
| break; |
| case FX_CHARSET_ShiftJIS: |
| cmap = "90ms-RKSJ-H"; |
| ordering = "Japan1"; |
| supplement = 5; |
| pWidthArray->AppendNew<CPDF_Number>(231); |
| Insert(0x20, 0x7d, pWidthArray); |
| pWidthArray->AppendNew<CPDF_Number>(326); |
| Insert(0xa0, 0xa0, pWidthArray); |
| pWidthArray->AppendNew<CPDF_Number>(327); |
| Insert(0xa1, 0xdf, pWidthArray); |
| pWidthArray->AppendNew<CPDF_Number>(631); |
| Insert(0x7e, 0x7e, pWidthArray); |
| break; |
| } |
| pBaseDict->SetNewFor<CPDF_Name>("Subtype", "Type0"); |
| pBaseDict->SetNewFor<CPDF_Name>("BaseFont", basefont); |
| pBaseDict->SetNewFor<CPDF_Name>("Encoding", cmap); |
| pFontDict->SetNewFor<CPDF_Name>("Type", "Font"); |
| pFontDict->SetNewFor<CPDF_Name>("Subtype", "CIDFontType2"); |
| pFontDict->SetNewFor<CPDF_Name>("BaseFont", basefont); |
| |
| CPDF_Dictionary* pCIDSysInfo = |
| pFontDict->SetNewFor<CPDF_Dictionary>("CIDSystemInfo"); |
| pCIDSysInfo->SetNewFor<CPDF_String>("Registry", "Adobe", false); |
| pCIDSysInfo->SetNewFor<CPDF_String>("Ordering", ordering, false); |
| pCIDSysInfo->SetNewFor<CPDF_Number>("Supplement", supplement); |
| |
| CPDF_Array* pArray = pBaseDict->SetNewFor<CPDF_Array>("DescendantFonts"); |
| pArray->AppendNew<CPDF_Reference>(GetDocument(), pFontDict->GetObjNum()); |
| return pFontDict; |
| } |