Skip to content

Commit 60daf83

Browse files
authored
PDF CMap (#348)
1 parent 76bee27 commit 60daf83

16 files changed

Lines changed: 366 additions & 55 deletions

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ find_package(cryptopp REQUIRED)
4747
find_package(nlohmann_json REQUIRED)
4848
find_package(vincentlaucsb-csv-parser REQUIRED)
4949
find_package(uchardet REQUIRED)
50+
find_package(utf8cpp REQUIRED)
5051

5152
configure_file("src/odr/internal/project_info.cpp.in" "src/odr/internal/project_info.cpp")
5253

@@ -146,6 +147,8 @@ add_library(odr
146147
"src/odr/internal/ooxml/ooxml_meta.cpp"
147148
"src/odr/internal/ooxml/ooxml_util.cpp"
148149

150+
"src/odr/internal/pdf/pdf_cmap.cpp"
151+
"src/odr/internal/pdf/pdf_cmap_parser.cpp"
149152
"src/odr/internal/pdf/pdf_document.cpp"
150153
"src/odr/internal/pdf/pdf_document_element.cpp"
151154
"src/odr/internal/pdf/pdf_document_parser.cpp"
@@ -164,6 +167,7 @@ add_library(odr
164167
"src/odr/internal/text/text_file.cpp"
165168
"src/odr/internal/text/text_util.cpp"
166169

170+
"src/odr/internal/util/byte_util.cpp"
167171
"src/odr/internal/util/file_util.cpp"
168172
"src/odr/internal/util/hash_util.cpp"
169173
"src/odr/internal/util/odr_meta_util.cpp"
@@ -191,6 +195,7 @@ target_link_libraries(odr
191195
nlohmann_json::nlohmann_json
192196
vincentlaucsb-csv-parser::vincentlaucsb-csv-parser
193197
uchardet::uchardet
198+
utf8cpp::utf8cpp
194199
)
195200

196201
add_subdirectory("cli")

conanfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class OpenDocumentCoreConan(ConanFile):
2222
exports_sources = ["cli/*", "cmake/*", "src/*", "CMakeLists.txt"]
2323

2424
requires = ["pugixml/1.14", "cryptopp/8.8.0", "miniz/3.0.2", "nlohmann_json/3.11.3",
25-
"vincentlaucsb-csv-parser/2.1.3", "uchardet/0.0.7"]
25+
"vincentlaucsb-csv-parser/2.1.3", "uchardet/0.0.7", "utfcpp/4.0.4"]
2626
build_requires = ["gtest/1.14.0"]
2727
generators = "cmake_paths", "cmake_find_package"
2828

src/odr/internal/pdf/pdf_cmap.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <odr/internal/pdf/pdf_cmap.hpp>
2+
3+
#include <odr/internal/util/map_util.hpp>
4+
5+
#include <utf8cpp/utf8/cpp17.h>
6+
7+
namespace odr::internal::pdf {
8+
9+
CMap::CMap() = default;
10+
11+
void CMap::map_bfchar(char glyph, char16_t unicode) {
12+
m_bfchar[glyph] = unicode;
13+
}
14+
15+
char16_t CMap::translate_glyph(char glyph) const {
16+
return util::map::lookup_default(m_bfchar, glyph, glyph);
17+
}
18+
19+
std::string CMap::translate_string(const std::string &glyphs) const {
20+
std::u16string result;
21+
22+
for (char glyph : glyphs) {
23+
result += translate_glyph(glyph);
24+
}
25+
26+
return utf8::utf16to8(result);
27+
}
28+
29+
} // namespace odr::internal::pdf

src/odr/internal/pdf/pdf_cmap.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef ODR_INTERNAL_PDF_CMAP_HPP
2+
#define ODR_INTERNAL_PDF_CMAP_HPP
3+
4+
#include <string>
5+
#include <unordered_map>
6+
7+
namespace odr::internal::pdf {
8+
9+
class CMap {
10+
public:
11+
CMap();
12+
13+
void map_bfchar(char glyph, char16_t unicode);
14+
15+
char16_t translate_glyph(char glyph) const;
16+
std::string translate_string(const std::string &glyphs) const;
17+
18+
private:
19+
std::unordered_map<char, char16_t> m_bfchar;
20+
};
21+
22+
} // namespace odr::internal::pdf
23+
24+
#endif // ODR_INTERNAL_PDF_CMAP_HPP
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include <odr/internal/pdf/pdf_cmap_parser.hpp>
2+
3+
#include <odr/internal/pdf/pdf_cmap.hpp>
4+
#include <odr/internal/util/byte_util.hpp>
5+
6+
#include <iostream>
7+
8+
namespace odr::internal::pdf {
9+
10+
using char_type = std::streambuf::char_type;
11+
using int_type = std::streambuf::int_type;
12+
static constexpr int_type eof = std::streambuf::traits_type::eof();
13+
14+
CMapParser::CMapParser(std::istream &in) : m_parser(in) {}
15+
16+
std::istream &CMapParser::in() const { return m_parser.in(); }
17+
18+
std::streambuf &CMapParser::sb() const { return m_parser.sb(); }
19+
20+
const ObjectParser &CMapParser::parser() const { return m_parser; }
21+
22+
std::variant<Object, std::string> CMapParser::read_token() const {
23+
if (m_parser.peek_number()) {
24+
return std::visit([](auto n) { return Object(n); },
25+
m_parser.read_integer_or_real());
26+
}
27+
if (m_parser.peek_string()) {
28+
return std::visit([](auto s) { return Object(std::move(s)); },
29+
m_parser.read_string());
30+
}
31+
if (m_parser.peek_name()) {
32+
return m_parser.read_name();
33+
}
34+
if (m_parser.peek_dictionary()) {
35+
return m_parser.read_dictionary();
36+
}
37+
38+
std::string token;
39+
while (true) {
40+
int_type c = sb().sgetc();
41+
if (c == eof) {
42+
in().setstate(std::ios::eofbit);
43+
return token;
44+
}
45+
if (ObjectParser::is_whitespace(c)) {
46+
return token;
47+
}
48+
sb().sbumpc();
49+
token += (char_type)c;
50+
}
51+
}
52+
53+
void CMapParser::read_codespacerange(std::uint32_t n, CMap &cmap) const {
54+
m_parser.skip_whitespace();
55+
for (std::uint32_t i = 0; i < n; ++i) {
56+
auto from_glyph = m_parser.read_object();
57+
m_parser.skip_whitespace();
58+
auto to_glyph = m_parser.read_object();
59+
m_parser.skip_whitespace();
60+
61+
// TODO
62+
}
63+
}
64+
65+
void CMapParser::read_bfchar(std::uint32_t n, CMap &cmap) const {
66+
m_parser.skip_whitespace();
67+
for (std::uint32_t i = 0; i < n; ++i) {
68+
std::string glyph = m_parser.read_object().as_string();
69+
m_parser.skip_whitespace();
70+
std::string unicode = m_parser.read_object().as_string();
71+
m_parser.skip_whitespace();
72+
73+
util::reverse_bytes(reinterpret_cast<char16_t *>(unicode.data()),
74+
(std::size_t)unicode.size() / 2);
75+
std::u16string_view unicode16(
76+
reinterpret_cast<const char16_t *>(unicode.data()), unicode.size() / 2);
77+
78+
if (glyph.length() != 1) {
79+
throw std::runtime_error("unexpected glyph length");
80+
}
81+
if (unicode16.length() != 1) {
82+
throw std::runtime_error("unexpected unicode length");
83+
}
84+
85+
cmap.map_bfchar(glyph[0], unicode16[0]);
86+
}
87+
}
88+
89+
void CMapParser::read_bfrange(std::uint32_t n, CMap &cmap) const {
90+
m_parser.skip_whitespace();
91+
for (std::uint32_t i = 0; i < n; ++i) {
92+
auto from_glyph = m_parser.read_object();
93+
m_parser.skip_whitespace();
94+
auto to_glyph = m_parser.read_object();
95+
m_parser.skip_whitespace();
96+
auto unicode = m_parser.read_object();
97+
m_parser.skip_whitespace();
98+
99+
// TODO
100+
}
101+
}
102+
103+
CMap CMapParser::parse_cmap() const {
104+
CMap cmap;
105+
106+
std::uint32_t last_int{};
107+
108+
m_parser.skip_whitespace();
109+
while (true) {
110+
Token token = read_token();
111+
if (in().eof()) {
112+
break;
113+
}
114+
m_parser.skip_whitespace();
115+
116+
if (std::holds_alternative<Object>(token)) {
117+
const Object &object = std::get<Object>(token);
118+
if (object.is_integer()) {
119+
last_int = object.as_integer();
120+
}
121+
} else if (std::holds_alternative<std::string>(token)) {
122+
const std::string &command = std::get<std::string>(token);
123+
if (command == "begincodespacerange") {
124+
read_codespacerange(last_int, cmap);
125+
} else if (command == "beginbfchar") {
126+
read_bfchar(last_int, cmap);
127+
} else if (command == "beginbfrange") {
128+
read_bfrange(last_int, cmap);
129+
}
130+
}
131+
}
132+
133+
return cmap;
134+
}
135+
136+
} // namespace odr::internal::pdf
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef ODR_INTERNAL_PDF_CMAP_PARSER_HPP
2+
#define ODR_INTERNAL_PDF_CMAP_PARSER_HPP
3+
4+
#include <odr/internal/pdf/pdf_object.hpp>
5+
#include <odr/internal/pdf/pdf_object_parser.hpp>
6+
7+
#include <iosfwd>
8+
#include <variant>
9+
10+
namespace odr::internal::pdf {
11+
12+
class CMap;
13+
14+
class CMapParser {
15+
public:
16+
using Token = std::variant<Object, std::string>;
17+
18+
explicit CMapParser(std::istream &);
19+
20+
std::istream &in() const;
21+
std::streambuf &sb() const;
22+
const ObjectParser &parser() const;
23+
24+
CMap parse_cmap() const;
25+
26+
private:
27+
ObjectParser m_parser;
28+
29+
Token read_token() const;
30+
31+
void read_codespacerange(std::uint32_t n, CMap &) const;
32+
void read_bfchar(std::uint32_t n, CMap &) const;
33+
void read_bfrange(std::uint32_t n, CMap &) const;
34+
};
35+
36+
} // namespace odr::internal::pdf
37+
38+
#endif // ODR_INTERNAL_PDF_CMAP_PARSER_HPP

src/odr/internal/pdf/pdf_document_element.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef ODR_INTERNAL_PDF_DOCUMENT_ELEMENT_HPP
22
#define ODR_INTERNAL_PDF_DOCUMENT_ELEMENT_HPP
33

4+
#include <odr/internal/pdf/pdf_cmap.hpp>
45
#include <odr/internal/pdf/pdf_object.hpp>
56

67
#include <unordered_map>
@@ -57,7 +58,9 @@ struct Resources : Element {
5758
std::unordered_map<std::string, Font *> font;
5859
};
5960

60-
struct Font : Element {};
61+
struct Font : Element {
62+
CMap cmap;
63+
};
6164

6265
} // namespace odr::internal::pdf
6366

src/odr/internal/pdf/pdf_document_parser.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#include <odr/internal/pdf/pdf_document_parser.hpp>
22

3+
#include <odr/internal/crypto/crypto_util.hpp>
4+
#include <odr/internal/pdf/pdf_cmap_parser.hpp>
35
#include <odr/internal/pdf/pdf_document.hpp>
46
#include <odr/internal/pdf/pdf_document_element.hpp>
57
#include <odr/internal/pdf/pdf_file_parser.hpp>
68

9+
#include <sstream>
10+
711
namespace odr::internal::pdf {
812
namespace {
913

@@ -22,6 +26,16 @@ pdf::Font *parse_font(DocumentParser &parser, const ObjectReference &reference,
2226
font->object_reference = reference;
2327
font->object = dictionary;
2428

29+
if (dictionary.has_key("ToUnicode")) {
30+
auto to_unicode_obj =
31+
parser.read_object(dictionary["ToUnicode"].as_reference());
32+
std::string stream = parser.read_object_stream(to_unicode_obj);
33+
std::string inflate = crypto::util::zlib_inflate(stream);
34+
std::istringstream ss(inflate);
35+
CMapParser cmap_parser(ss);
36+
font->cmap = cmap_parser.parse_cmap();
37+
}
38+
2539
return font;
2640
}
2741

src/odr/internal/pdf/pdf_graphics_operator.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,10 @@ enum class GraphicsOperatorType {
188188
set_text_matrix,
189189
text_next_line,
190190

191-
show_string,
192-
next_line_show_string,
193-
set_spacing_next_line_show,
194-
show_string_manual_spacing,
191+
show_text,
192+
show_text_manual_spacing,
193+
show_text_next_line,
194+
show_text_next_line_set_spacing,
195195

196196
set_stroke_color_space,
197197
set_stroke_color,

src/odr/internal/pdf/pdf_graphics_operator_parser.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <odr/internal/pdf/pdf_graphics_operator_parser.hpp>
22

33
#include <odr/internal/pdf/pdf_graphics_operator.hpp>
4+
#include <odr/internal/util/map_util.hpp>
45

56
#include <unordered_map>
67

@@ -67,10 +68,10 @@ GraphicsOperatorType operator_name_to_type(const std::string &name) {
6768
{"Tm", GraphicsOperatorType::set_text_matrix},
6869
{"T*", GraphicsOperatorType::text_next_line},
6970

70-
{"Tj", GraphicsOperatorType::show_string},
71-
{"'", GraphicsOperatorType::next_line_show_string},
72-
{"\"", GraphicsOperatorType::set_spacing_next_line_show},
73-
{"TJ", GraphicsOperatorType::show_string_manual_spacing},
71+
{"Tj", GraphicsOperatorType::show_text},
72+
{"TJ", GraphicsOperatorType::show_text_manual_spacing},
73+
{"'", GraphicsOperatorType::show_text_next_line},
74+
{"\"", GraphicsOperatorType::show_text_next_line_set_spacing},
7475

7576
{"CS", GraphicsOperatorType::set_stroke_color_space},
7677
{"SC", GraphicsOperatorType::set_stroke_color},
@@ -99,11 +100,8 @@ GraphicsOperatorType operator_name_to_type(const std::string &name) {
99100
{"EX", GraphicsOperatorType::end_compat_sec},
100101
};
101102

102-
if (auto it = mapping.find(name); it != std::end(mapping)) {
103-
return it->second;
104-
}
105-
106-
return GraphicsOperatorType::unknown;
103+
return util::map::lookup_default(mapping, name,
104+
GraphicsOperatorType::unknown);
107105
}
108106

109107
} // namespace

0 commit comments

Comments
 (0)