// 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/font/cpdf_cmapparser.h"
#include <vector>
#include "core/fpdfapi/cmaps/cmap_int.h"
#include "core/fpdfapi/cpdf_modulemgr.h"
#include "core/fpdfapi/page/cpdf_pagemodule.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/fxge/fx_freetype.h"
#include "third_party/base/logging.h"
namespace {
const char* const g_CharsetNames[CIDSET_NUM_SETS] = {nullptr, "GB1", "CNS1",
"Japan1", "Korea1", "UCS"};
CIDSet CIDSetFromSizeT(size_t index) {
if (index >= CIDSET_NUM_SETS) {
NOTREACHED();
return CIDSET_UNKNOWN;
}
return static_cast<CIDSet>(index);
}
ByteStringView CMap_GetString(const ByteStringView& word) {
if (word.GetLength() <= 2)
return ByteStringView();
return word.Right(word.GetLength() - 2);
}
} // namespace
CPDF_CMapParser::CPDF_CMapParser(CPDF_CMap* pCMap)
: m_pCMap(pCMap), m_Status(0), m_CodeSeq(0) {}
CPDF_CMapParser::~CPDF_CMapParser() {}
void CPDF_CMapParser::ParseWord(const ByteStringView& word) {
if (word.IsEmpty()) {
return;
}
if (word == "begincidchar") {
m_Status = 1;
m_CodeSeq = 0;
} else if (word == "begincidrange") {
m_Status = 2;
m_CodeSeq = 0;
} else if (word == "endcidrange" || word == "endcidchar") {
m_Status = 0;
} else if (word == "/WMode") {
m_Status = 6;
} else if (word == "/Registry") {
m_Status = 3;
} else if (word == "/Ordering") {
m_Status = 4;
} else if (word == "/Supplement") {
m_Status = 5;
} else if (word == "begincodespacerange") {
m_Status = 7;
m_CodeSeq = 0;
} else if (word == "usecmap") {
} else if (m_Status == 1 || m_Status == 2) {
m_CodePoints[m_CodeSeq] = GetCode(word);
m_CodeSeq++;
uint32_t StartCode, EndCode;
uint16_t StartCID;
if (m_Status == 1) {
if (m_CodeSeq < 2) {
return;
}
EndCode = StartCode = m_CodePoints[0];
StartCID = (uint16_t)m_CodePoints[1];
} else {
if (m_CodeSeq < 3) {
return;
}
StartCode = m_CodePoints[0];
EndCode = m_CodePoints[1];
StartCID = (uint16_t)m_CodePoints[2];
}
if (EndCode < 0x10000) {
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;
} else if (m_Status == 3) {
m_Status = 0;
} else if (m_Status == 4) {
m_pCMap->SetCharset(CharsetFromOrdering(CMap_GetString(word)));
m_Status = 0;
} else if (m_Status == 5) {
m_Status = 0;
} else if (m_Status == 6) {
m_pCMap->SetVertical(GetCode(word) != 0);
m_Status = 0;
} else if (m_Status == 7) {
if (word == "endcodespacerange") {
size_t nSegs = m_CodeRanges.size();
if (nSegs == 1) {
m_pCMap->SetCodingScheme((m_CodeRanges[0].m_CharSize == 2)
? CPDF_CMap::TwoBytes
: CPDF_CMap::OneByte);
} else if (nSegs > 1) {
m_pCMap->SetCodingScheme(CPDF_CMap::MixedFourBytes);
m_pCMap->SetMixedFourByteLeadingRanges(m_CodeRanges);
}
m_Status = 0;
} else {
if (word.GetLength() == 0 || word[0] != '<') {
return;
}
if (m_CodeSeq % 2) {
CPDF_CMap::CodeRange range;
if (GetCodeRange(range, m_LastWord.AsStringView(), word))
m_CodeRanges.push_back(range);
}
m_CodeSeq++;
}
}
m_LastWord = word;
}
uint32_t CPDF_CMapParser::GetCode(const ByteStringView& word) const {
if (word.IsEmpty())
return 0;
pdfium::base::CheckedNumeric<uint32_t> num = 0;
if (word[0] == '<') {
for (size_t i = 1; i < word.GetLength() && std::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() && std::isdigit(word[i]); ++i) {
num = num * 10 + FXSYS_DecimalCharToInt(static_cast<wchar_t>(word[i]));
if (!num.IsValid())
return 0;
}
return num.ValueOrDie();
}
bool CPDF_CMapParser::GetCodeRange(CPDF_CMap::CodeRange& range,
const ByteStringView& first,
const ByteStringView& second) const {
if (first.GetLength() == 0 || first[0] != '<')
return false;
size_t i;
for (i = 1; i < first.GetLength(); ++i) {
if (first[i] == '>') {
break;
}
}
range.m_CharSize = (i - 1) / 2;
if (range.m_CharSize > 4)
return false;
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) {
uint8_t digit1 = (i * 2 + 1 < size) ? second[i * 2 + 1] : '0';
uint8_t digit2 = (i * 2 + 2 < size) ? second[i * 2 + 2] : '0';
range.m_Upper[i] =
FXSYS_HexCharToInt(digit1) * 16 + FXSYS_HexCharToInt(digit2);
}
return true;
}
// static
CIDSet CPDF_CMapParser::CharsetFromOrdering(const ByteStringView& ordering) {
for (size_t charset = 1; charset < FX_ArraySize(g_CharsetNames); ++charset) {
if (ordering == g_CharsetNames[charset])
return CIDSetFromSizeT(charset);
}
return CIDSET_UNKNOWN;
}