| // 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/fpdfapi/font/cpdf_cmapparser.h" |
| |
| #include <ctype.h> |
| |
| #include <iterator> |
| |
| #include "core/fpdfapi/cmaps/fpdf_cmaps.h" |
| #include "core/fpdfapi/parser/cpdf_array.h" |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" |
| #include "core/fpdfapi/parser/cpdf_simple_parser.h" |
| #include "core/fxcrt/fx_extension.h" |
| #include "core/fxcrt/fx_safe_types.h" |
| #include "core/fxge/freetype/fx_freetype.h" |
| #include "third_party/base/check.h" |
| |
| namespace { |
| |
| ByteStringView CMap_GetString(ByteStringView word) { |
| if (word.GetLength() <= 2) |
| return ByteStringView(); |
| return word.Last(word.GetLength() - 2); |
| } |
| |
| } // namespace |
| |
| CPDF_CMapParser::CPDF_CMapParser(CPDF_CMap* pCMap) : m_pCMap(pCMap) {} |
| |
| CPDF_CMapParser::~CPDF_CMapParser() { |
| m_pCMap->SetAdditionalMappings(std::move(m_AdditionalCharcodeToCIDMappings)); |
| m_pCMap->SetMixedFourByteLeadingRanges(std::move(m_Ranges)); |
| } |
| |
| void CPDF_CMapParser::ParseWord(ByteStringView word) { |
| DCHECK(!word.IsEmpty()); |
| |
| if (word == "begincidchar") { |
| m_Status = kProcessingCidChar; |
| m_CodeSeq = 0; |
| } else if (word == "begincidrange") { |
| m_Status = kProcessingCidRange; |
| m_CodeSeq = 0; |
| } else if (word == "endcidrange" || word == "endcidchar") { |
| m_Status = kStart; |
| } else if (word == "/WMode") { |
| m_Status = kProcessingWMode; |
| } else if (word == "/Registry") { |
| m_Status = kProcessingRegistry; |
| } else if (word == "/Ordering") { |
| m_Status = kProcessingOrdering; |
| } else if (word == "/Supplement") { |
| m_Status = kProcessingSupplement; |
| } else if (word == "begincodespacerange") { |
| m_Status = kProcessingCodeSpaceRange; |
| m_CodeSeq = 0; |
| } else if (word == "usecmap") { |
| } else if (m_Status == kProcessingCidChar) { |
| HandleCid(word); |
| } else if (m_Status == kProcessingCidRange) { |
| HandleCid(word); |
| } else if (m_Status == kProcessingRegistry) { |
| m_Status = kStart; |
| } else if (m_Status == kProcessingOrdering) { |
| m_pCMap->SetCharset(CharsetFromOrdering(CMap_GetString(word))); |
| m_Status = kStart; |
| } else if (m_Status == kProcessingSupplement) { |
| m_Status = kStart; |
| } else if (m_Status == kProcessingWMode) { |
| m_pCMap->SetVertical(GetCode(word) != 0); |
| m_Status = kStart; |
| } else if (m_Status == kProcessingCodeSpaceRange) { |
| HandleCodeSpaceRange(word); |
| } |
| m_LastWord = word; |
| } |
| |
| void CPDF_CMapParser::HandleCid(ByteStringView word) { |
| DCHECK(m_Status == kProcessingCidChar || m_Status == kProcessingCidRange); |
| bool bChar = m_Status == kProcessingCidChar; |
| |
| m_CodePoints[m_CodeSeq] = GetCode(word); |
| m_CodeSeq++; |
| int nRequiredCodePoints = bChar ? 2 : 3; |
| if (m_CodeSeq < nRequiredCodePoints) |
| return; |
| |
| uint32_t StartCode = m_CodePoints[0]; |
| uint32_t EndCode; |
| uint16_t StartCID; |
| if (bChar) { |
| EndCode = StartCode; |
| StartCID = static_cast<uint16_t>(m_CodePoints[1]); |
| } else { |
| EndCode = m_CodePoints[1]; |
| StartCID = static_cast<uint16_t>(m_CodePoints[2]); |
| } |
| if (EndCode < CPDF_CMap::kDirectMapTableSize) { |
| for (uint32_t code = StartCode; code <= EndCode; code++) { |
| m_pCMap->SetDirectCharcodeToCIDTable( |
| code, static_cast<uint16_t>(StartCID + code - StartCode)); |
| } |
| } else { |
| m_AdditionalCharcodeToCIDMappings.push_back({StartCode, EndCode, StartCID}); |
| } |
| m_CodeSeq = 0; |
| } |
| |
| void CPDF_CMapParser::HandleCodeSpaceRange(ByteStringView word) { |
| if (word != "endcodespacerange") { |
| if (word.IsEmpty() || word[0] != '<') |
| return; |
| |
| if (m_CodeSeq % 2) { |
| absl::optional<CPDF_CMap::CodeRange> range = |
| GetCodeRange(m_LastWord.AsStringView(), word); |
| if (range.has_value()) |
| m_PendingRanges.push_back(range.value()); |
| } |
| m_CodeSeq++; |
| return; |
| } |
| |
| size_t nSegs = m_Ranges.size() + m_PendingRanges.size(); |
| if (nSegs == 1) { |
| const auto& first_range = |
| !m_Ranges.empty() ? m_Ranges[0] : m_PendingRanges[0]; |
| m_pCMap->SetCodingScheme(first_range.m_CharSize == 2 ? CPDF_CMap::TwoBytes |
| : CPDF_CMap::OneByte); |
| } else if (nSegs > 1) { |
| m_pCMap->SetCodingScheme(CPDF_CMap::MixedFourBytes); |
| m_Ranges.reserve(nSegs); |
| std::move(m_PendingRanges.begin(), m_PendingRanges.end(), |
| std::back_inserter(m_Ranges)); |
| m_PendingRanges.clear(); |
| } |
| m_Status = kStart; |
| } |
| |
| // static |
| uint32_t CPDF_CMapParser::GetCode(ByteStringView word) { |
| if (word.IsEmpty()) |
| return 0; |
| |
| FX_SAFE_UINT32 num = 0; |
| if (word[0] == '<') { |
| for (size_t i = 1; i < word.GetLength() && isxdigit(word[i]); ++i) { |
| num = num * 16 + FXSYS_HexCharToInt(word[i]); |
| if (!num.IsValid()) |
| return 0; |
| } |
| return num.ValueOrDie(); |
| } |
| |
| for (size_t i = 0; i < word.GetLength() && isdigit(word[i]); ++i) { |
| num = num * 10 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(word[i])); |
| if (!num.IsValid()) |
| return 0; |
| } |
| return num.ValueOrDie(); |
| } |
| |
| // static |
| absl::optional<CPDF_CMap::CodeRange> CPDF_CMapParser::GetCodeRange( |
| ByteStringView first, |
| ByteStringView second) { |
| if (first.IsEmpty() || first[0] != '<') |
| return absl::nullopt; |
| |
| size_t i; |
| for (i = 1; i < first.GetLength(); ++i) { |
| if (first[i] == '>') |
| break; |
| } |
| size_t char_size = (i - 1) / 2; |
| if (char_size > 4) |
| return absl::nullopt; |
| |
| CPDF_CMap::CodeRange range; |
| range.m_CharSize = char_size; |
| for (i = 0; i < range.m_CharSize; ++i) { |
| uint8_t digit1 = first[i * 2 + 1]; |
| uint8_t digit2 = first[i * 2 + 2]; |
| range.m_Lower[i] = |
| FXSYS_HexCharToInt(digit1) * 16 + FXSYS_HexCharToInt(digit2); |
| } |
| |
| size_t size = second.GetLength(); |
| for (i = 0; i < range.m_CharSize; ++i) { |
| size_t i1 = i * 2 + 1; |
| size_t i2 = i1 + 1; |
| uint8_t digit1 = i1 < size ? second[i1] : '0'; |
| uint8_t digit2 = i2 < size ? second[i2] : '0'; |
| range.m_Upper[i] = |
| FXSYS_HexCharToInt(digit1) * 16 + FXSYS_HexCharToInt(digit2); |
| } |
| return range; |
| } |
| |
| // static |
| CIDSet CPDF_CMapParser::CharsetFromOrdering(ByteStringView ordering) { |
| static const char* const kCharsetNames[CIDSET_NUM_SETS] = { |
| nullptr, "GB1", "CNS1", "Japan1", "Korea1", "UCS"}; |
| static_assert(std::size(kCharsetNames) == CIDSET_NUM_SETS, |
| "Too many CID sets"); |
| |
| for (size_t charset = 1; charset < std::size(kCharsetNames); ++charset) { |
| if (ordering == kCharsetNames[charset]) |
| return static_cast<CIDSet>(charset); |
| } |
| return CIDSET_UNKNOWN; |
| } |