1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "pdf/pdfium/pdfium_font_linux.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "base/i18n/encoding_detection.h"
11 #include "base/i18n/icu_string_conversions.h"
12 #include "base/logging.h"
13 #include "base/numerics/ranges.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_util.h"
16 #include "ppapi/cpp/instance.h"
17 #include "ppapi/cpp/module.h"
18 #include "ppapi/cpp/private/pdf.h"
19 #include "ppapi/cpp/trusted/browser_font_trusted.h"
20 #include "third_party/pdfium/public/fpdf_sysfontinfo.h"
21
22 namespace chrome_pdf {
23
24 namespace {
25
26 PP_Instance g_last_instance_id;
27
WeightToBrowserFontTrustedWeight(int weight)28 PP_BrowserFont_Trusted_Weight WeightToBrowserFontTrustedWeight(int weight) {
29 static_assert(PP_BROWSERFONT_TRUSTED_WEIGHT_100 == 0,
30 "PP_BrowserFont_Trusted_Weight min");
31 static_assert(PP_BROWSERFONT_TRUSTED_WEIGHT_900 == 8,
32 "PP_BrowserFont_Trusted_Weight max");
33 constexpr int kMinimumWeight = 100;
34 constexpr int kMaximumWeight = 900;
35 int normalized_weight =
36 base::ClampToRange(weight, kMinimumWeight, kMaximumWeight);
37 normalized_weight = (normalized_weight / 100) - 1;
38 return static_cast<PP_BrowserFont_Trusted_Weight>(normalized_weight);
39 }
40
41 // This list is for CPWL_FontMap::GetDefaultFontByCharset().
42 // We pretend to have these font natively and let the browser (or underlying
43 // fontconfig) pick the proper font on the system.
EnumFonts(FPDF_SYSFONTINFO * sysfontinfo,void * mapper)44 void EnumFonts(FPDF_SYSFONTINFO* sysfontinfo, void* mapper) {
45 FPDF_AddInstalledFont(mapper, "Arial", FXFONT_DEFAULT_CHARSET);
46
47 const FPDF_CharsetFontMap* font_map = FPDF_GetDefaultTTFMap();
48 for (; font_map->charset != -1; ++font_map) {
49 FPDF_AddInstalledFont(mapper, font_map->fontname, font_map->charset);
50 }
51 }
52
MapFont(FPDF_SYSFONTINFO *,int weight,int italic,int charset,int pitch_family,const char * face,int * exact)53 void* MapFont(FPDF_SYSFONTINFO*,
54 int weight,
55 int italic,
56 int charset,
57 int pitch_family,
58 const char* face,
59 int* exact) {
60 // Do not attempt to map fonts if PPAPI is not initialized (for Privet local
61 // printing).
62 // TODO(noamsml): Real font substitution (http://crbug.com/391978)
63 if (!pp::Module::Get())
64 return nullptr;
65
66 pp::BrowserFontDescription description;
67
68 // Pretend the system does not have the Symbol font to force a fallback to
69 // the built in Symbol font in CFX_FontMapper::FindSubstFont().
70 if (strcmp(face, "Symbol") == 0)
71 return nullptr;
72
73 if (pitch_family & FXFONT_FF_FIXEDPITCH) {
74 description.set_family(PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE);
75 } else if (pitch_family & FXFONT_FF_ROMAN) {
76 description.set_family(PP_BROWSERFONT_TRUSTED_FAMILY_SERIF);
77 }
78
79 static const struct {
80 const char* pdf_name;
81 const char* face;
82 bool bold;
83 bool italic;
84 } kPdfFontSubstitutions[] = {
85 {"Courier", "Courier New", false, false},
86 {"Courier-Bold", "Courier New", true, false},
87 {"Courier-BoldOblique", "Courier New", true, true},
88 {"Courier-Oblique", "Courier New", false, true},
89 {"Helvetica", "Arial", false, false},
90 {"Helvetica-Bold", "Arial", true, false},
91 {"Helvetica-BoldOblique", "Arial", true, true},
92 {"Helvetica-Oblique", "Arial", false, true},
93 {"Times-Roman", "Times New Roman", false, false},
94 {"Times-Bold", "Times New Roman", true, false},
95 {"Times-BoldItalic", "Times New Roman", true, true},
96 {"Times-Italic", "Times New Roman", false, true},
97
98 // MS P?(Mincho|Gothic) are the most notable fonts in Japanese PDF files
99 // without embedding the glyphs. Sometimes the font names are encoded
100 // in Japanese Windows's locale (CP932/Shift_JIS) without space.
101 // Most Linux systems don't have the exact font, but for outsourcing
102 // fontconfig to find substitutable font in the system, we pass ASCII
103 // font names to it.
104 {"MS-PGothic", "MS PGothic", false, false},
105 {"MS-Gothic", "MS Gothic", false, false},
106 {"MS-PMincho", "MS PMincho", false, false},
107 {"MS-Mincho", "MS Mincho", false, false},
108 // MS PGothic in Shift_JIS encoding.
109 {"\x82\x6C\x82\x72\x82\x6F\x83\x53\x83\x56\x83\x62\x83\x4E", "MS PGothic",
110 false, false},
111 // MS Gothic in Shift_JIS encoding.
112 {"\x82\x6C\x82\x72\x83\x53\x83\x56\x83\x62\x83\x4E", "MS Gothic", false,
113 false},
114 // MS PMincho in Shift_JIS encoding.
115 {"\x82\x6C\x82\x72\x82\x6F\x96\xBE\x92\xA9", "MS PMincho", false, false},
116 // MS Mincho in Shift_JIS encoding.
117 {"\x82\x6C\x82\x72\x96\xBE\x92\xA9", "MS Mincho", false, false},
118 };
119
120 // Similar logic exists in PDFium's CFX_FolderFontInfo::FindFont().
121 if (charset == FXFONT_ANSI_CHARSET && (pitch_family & FXFONT_FF_FIXEDPITCH))
122 face = "Courier New";
123
124 // Map from the standard PDF fonts to TrueType font names.
125 size_t i;
126 for (i = 0; i < base::size(kPdfFontSubstitutions); ++i) {
127 if (strcmp(face, kPdfFontSubstitutions[i].pdf_name) == 0) {
128 description.set_face(kPdfFontSubstitutions[i].face);
129 if (kPdfFontSubstitutions[i].bold)
130 description.set_weight(PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD);
131 if (kPdfFontSubstitutions[i].italic)
132 description.set_italic(true);
133 break;
134 }
135 }
136
137 if (i == base::size(kPdfFontSubstitutions)) {
138 // Convert to UTF-8 before calling set_face().
139 std::string face_utf8;
140 if (base::IsStringUTF8(face)) {
141 face_utf8 = face;
142 } else {
143 std::string encoding;
144 if (base::DetectEncoding(face, &encoding)) {
145 // ConvertToUtf8AndNormalize() clears |face_utf8| on failure.
146 base::ConvertToUtf8AndNormalize(face, encoding, &face_utf8);
147 }
148 }
149
150 if (face_utf8.empty())
151 return nullptr;
152
153 description.set_face(face_utf8);
154 description.set_weight(WeightToBrowserFontTrustedWeight(weight));
155 description.set_italic(italic > 0);
156 }
157
158 if (!pp::PDF::IsAvailable()) {
159 NOTREACHED();
160 return nullptr;
161 }
162
163 PP_Resource font_resource = pp::PDF::GetFontFileWithFallback(
164 pp::InstanceHandle(g_last_instance_id),
165 &description.pp_font_description(),
166 static_cast<PP_PrivateFontCharset>(charset));
167 long res_id = font_resource;
168 return reinterpret_cast<void*>(res_id);
169 }
170
GetFontData(FPDF_SYSFONTINFO *,void * font_id,unsigned int table,unsigned char * buffer,unsigned long buf_size)171 unsigned long GetFontData(FPDF_SYSFONTINFO*,
172 void* font_id,
173 unsigned int table,
174 unsigned char* buffer,
175 unsigned long buf_size) {
176 if (!pp::PDF::IsAvailable()) {
177 NOTREACHED();
178 return 0;
179 }
180
181 uint32_t size = buf_size;
182 long res_id = reinterpret_cast<long>(font_id);
183 if (!pp::PDF::GetFontTableForPrivateFontFile(res_id, table, buffer, &size))
184 return 0;
185 return size;
186 }
187
DeleteFont(FPDF_SYSFONTINFO *,void * font_id)188 void DeleteFont(FPDF_SYSFONTINFO*, void* font_id) {
189 long res_id = reinterpret_cast<long>(font_id);
190 pp::Module::Get()->core()->ReleaseResource(res_id);
191 }
192
193 FPDF_SYSFONTINFO g_font_info = {1, 0, EnumFonts, MapFont, 0,
194 GetFontData, 0, 0, DeleteFont};
195
196 } // namespace
197
InitializeLinuxFontMapper()198 void InitializeLinuxFontMapper() {
199 FPDF_SetSystemFontInfo(&g_font_info);
200 }
201
SetLastInstance(pp::Instance * last_instance)202 void SetLastInstance(pp::Instance* last_instance) {
203 if (last_instance)
204 g_last_instance_id = last_instance->pp_instance();
205 }
206
207 } // namespace chrome_pdf
208