• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "core/fpdfapi/render/charposlist.h"
8 
9 #include "build/build_config.h"
10 #include "core/fpdfapi/font/cpdf_cidfont.h"
11 #include "core/fpdfapi/font/cpdf_font.h"
12 #include "core/fpdfapi/parser/cpdf_stream.h"
13 #include "core/fxge/cfx_fontmapper.h"
14 #include "core/fxge/cfx_substfont.h"
15 #include "core/fxge/text_char_pos.h"
16 
17 namespace {
18 
ShouldUseExistingFont(const CPDF_Font * font,uint32_t glyph_id,bool has_to_unicode)19 bool ShouldUseExistingFont(const CPDF_Font* font,
20                            uint32_t glyph_id,
21                            bool has_to_unicode) {
22   // Check for invalid glyph ID.
23   if (glyph_id == static_cast<uint32_t>(-1))
24     return false;
25 
26   if (!font->IsTrueTypeFont())
27     return true;
28 
29   // For TrueType fonts, a glyph ID of 0 may be invalid.
30   //
31   // When a "ToUnicode" entry exists in the font dictionary, it indicates
32   // a "ToUnicode" mapping file is used to convert from CIDs (which
33   // begins at decimal 0) to Unicode code. (See ToUnicode Mapping File
34   // Tutorial - Adobe
35   // https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/5411.ToUnicode.pdf
36   // and
37   // https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-6)
38   return glyph_id != 0 || has_to_unicode;
39 }
40 
41 // The following is not a perfect solution and can be further improved.
42 // For example, if `subst_font` is "Book" and the `base_font_name` is "Bookman",
43 // this function will return "true" even though the actual font "Bookman"
44 // is not loaded.
45 // An exact string match is not possible here, because `subst_font_name`
46 // will be the same value for different postscript names.
47 // For example: "Times New Roman" as `subst_font_name` for all of these
48 // `base_font_name` values: "TimesNewRoman,Bold", "TimesNewRomanPS-Bold",
49 // "TimesNewRomanBold" and "TimesNewRoman-Bold".
IsActualFontLoaded(const CFX_SubstFont * subst_font,const ByteString & base_font_name)50 bool IsActualFontLoaded(const CFX_SubstFont* subst_font,
51                         const ByteString& base_font_name) {
52   // Skip if we loaded the actual font.
53   // example: TimesNewRoman,Bold -> Times New Roman
54   ByteString subst_font_name = subst_font->m_Family;
55   subst_font_name.Remove(' ');
56   subst_font_name.MakeLower();
57 
58   absl::optional<size_t> find =
59       base_font_name.Find(subst_font_name.AsStringView());
60   return find.has_value() && find.value() == 0;
61 }
62 
ApplyGlyphSpacingHeuristic(const CPDF_Font * font,const CFX_Font * current_font,bool is_vertical_writing)63 bool ApplyGlyphSpacingHeuristic(const CPDF_Font* font,
64                                 const CFX_Font* current_font,
65                                 bool is_vertical_writing) {
66   if (is_vertical_writing || font->IsEmbedded() || !font->HasFontWidths()) {
67     return false;
68   }
69 
70   // Skip if we loaded a standard alternate font.
71   // example: Helvetica -> Arial
72   ByteString base_font_name = font->GetBaseFontName();
73   base_font_name.MakeLower();
74 
75   auto standard_font_name =
76       CFX_FontMapper::GetStandardFontName(&base_font_name);
77   if (standard_font_name.has_value()) {
78     return false;
79   }
80 
81   CFX_SubstFont* subst_font = current_font->GetSubstFont();
82   if (subst_font->IsBuiltInGenericFont()) {
83     return false;
84   }
85 
86   return !IsActualFontLoaded(subst_font, base_font_name);
87 }
88 
89 }  // namespace
90 
GetCharPosList(pdfium::span<const uint32_t> char_codes,pdfium::span<const float> char_pos,CPDF_Font * font,float font_size)91 std::vector<TextCharPos> GetCharPosList(pdfium::span<const uint32_t> char_codes,
92                                         pdfium::span<const float> char_pos,
93                                         CPDF_Font* font,
94                                         float font_size) {
95   std::vector<TextCharPos> results;
96   results.reserve(char_codes.size());
97 
98   CPDF_CIDFont* cid_font = font->AsCIDFont();
99   bool is_vertical_writing = cid_font && cid_font->IsVertWriting();
100   bool has_to_unicode = !!font->GetFontDict()->GetStreamFor("ToUnicode");
101   for (size_t i = 0; i < char_codes.size(); ++i) {
102     uint32_t char_code = char_codes[i];
103     if (char_code == static_cast<uint32_t>(-1))
104       continue;
105 
106     bool is_vertical_glyph = false;
107     results.emplace_back();
108     TextCharPos& text_char_pos = results.back();
109     if (cid_font)
110       text_char_pos.m_bFontStyle = true;
111     WideString unicode = font->UnicodeFromCharCode(char_code);
112     text_char_pos.m_Unicode = !unicode.IsEmpty() ? unicode[0] : char_code;
113     text_char_pos.m_GlyphIndex =
114         font->GlyphFromCharCode(char_code, &is_vertical_glyph);
115     uint32_t glyph_id = text_char_pos.m_GlyphIndex;
116 #if BUILDFLAG(IS_APPLE)
117     text_char_pos.m_ExtGID = font->GlyphFromCharCodeExt(char_code);
118     glyph_id = text_char_pos.m_ExtGID != static_cast<uint32_t>(-1)
119                    ? text_char_pos.m_ExtGID
120                    : text_char_pos.m_GlyphIndex;
121 #endif
122     CFX_Font* current_font;
123     if (ShouldUseExistingFont(font, glyph_id, has_to_unicode)) {
124       current_font = font->GetFont();
125       text_char_pos.m_FallbackFontPosition = -1;
126     } else {
127       int32_t fallback_position = font->FallbackFontFromCharcode(char_code);
128       current_font = font->GetFontFallback(fallback_position);
129       text_char_pos.m_FallbackFontPosition = fallback_position;
130       text_char_pos.m_GlyphIndex =
131           font->FallbackGlyphFromCharcode(fallback_position, char_code);
132 #if BUILDFLAG(IS_APPLE)
133       text_char_pos.m_ExtGID = text_char_pos.m_GlyphIndex;
134 #endif
135     }
136 
137     if (!font->IsEmbedded() && !font->IsCIDFont())
138       text_char_pos.m_FontCharWidth = font->GetCharWidthF(char_code);
139     else
140       text_char_pos.m_FontCharWidth = 0;
141 
142     text_char_pos.m_Origin = CFX_PointF(i > 0 ? char_pos[i - 1] : 0, 0);
143     text_char_pos.m_bGlyphAdjust = false;
144 
145     float scaling_factor = 1.0f;
146     if (ApplyGlyphSpacingHeuristic(font, current_font, is_vertical_writing)) {
147       int pdf_glyph_width = font->GetCharWidthF(char_code);
148       int font_glyph_width =
149           current_font->GetGlyphWidth(text_char_pos.m_GlyphIndex);
150       if (font_glyph_width && pdf_glyph_width > font_glyph_width + 1) {
151         // Move the initial x position by half of the excess (transformed to
152         // text space coordinates).
153         text_char_pos.m_Origin.x +=
154             (pdf_glyph_width - font_glyph_width) * font_size / 2000.0f;
155       } else if (pdf_glyph_width && font_glyph_width &&
156                  pdf_glyph_width < font_glyph_width) {
157         scaling_factor = static_cast<float>(pdf_glyph_width) / font_glyph_width;
158         text_char_pos.m_AdjustMatrix[0] = scaling_factor;
159         text_char_pos.m_AdjustMatrix[1] = 0.0f;
160         text_char_pos.m_AdjustMatrix[2] = 0.0f;
161         text_char_pos.m_AdjustMatrix[3] = 1.0f;
162         text_char_pos.m_bGlyphAdjust = true;
163       }
164     }
165     if (!cid_font)
166       continue;
167 
168     uint16_t cid = cid_font->CIDFromCharCode(char_code);
169     if (is_vertical_writing) {
170       text_char_pos.m_Origin = CFX_PointF(0, text_char_pos.m_Origin.x);
171 
172       CFX_Point16 vertical_origin = cid_font->GetVertOrigin(cid);
173       text_char_pos.m_Origin.x -= font_size * vertical_origin.x / 1000;
174       text_char_pos.m_Origin.y -= font_size * vertical_origin.y / 1000;
175     }
176 
177     const uint8_t* cid_transform = cid_font->GetCIDTransform(cid);
178     if (cid_transform && !is_vertical_glyph) {
179       text_char_pos.m_AdjustMatrix[0] =
180           cid_font->CIDTransformToFloat(cid_transform[0]) * scaling_factor;
181       text_char_pos.m_AdjustMatrix[1] =
182           cid_font->CIDTransformToFloat(cid_transform[1]) * scaling_factor;
183       text_char_pos.m_AdjustMatrix[2] =
184           cid_font->CIDTransformToFloat(cid_transform[2]);
185       text_char_pos.m_AdjustMatrix[3] =
186           cid_font->CIDTransformToFloat(cid_transform[3]);
187       text_char_pos.m_Origin.x +=
188           cid_font->CIDTransformToFloat(cid_transform[4]) * font_size;
189       text_char_pos.m_Origin.y +=
190           cid_font->CIDTransformToFloat(cid_transform[5]) * font_size;
191       text_char_pos.m_bGlyphAdjust = true;
192     }
193   }
194 
195   return results;
196 }
197