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