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