1 // Copyright 2016 PDFium 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfapi/page/cpdf_textobject.h"
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "core/fpdfapi/font/cpdf_cidfont.h"
13 #include "core/fpdfapi/font/cpdf_font.h"
14 #include "third_party/base/ptr_util.h"
15 #include "third_party/base/stl_util.h"
16
CPDF_TextObjectItem()17 CPDF_TextObjectItem::CPDF_TextObjectItem() : m_CharCode(0) {}
18
19 CPDF_TextObjectItem::~CPDF_TextObjectItem() = default;
20
CPDF_TextObject()21 CPDF_TextObject::CPDF_TextObject() {}
22
~CPDF_TextObject()23 CPDF_TextObject::~CPDF_TextObject() {
24 // Move m_CharCodes to a local variable so it will be captured in crash dumps,
25 // to help with investigating crbug.com/782215.
26 auto char_codes_copy = std::move(m_CharCodes);
27 }
28
CountItems() const29 size_t CPDF_TextObject::CountItems() const {
30 return m_CharCodes.size();
31 }
32
GetItemInfo(size_t index,CPDF_TextObjectItem * pInfo) const33 void CPDF_TextObject::GetItemInfo(size_t index,
34 CPDF_TextObjectItem* pInfo) const {
35 pInfo->m_CharCode = m_CharCodes[index];
36 pInfo->m_Origin = CFX_PointF(index > 0 ? m_CharPos[index - 1] : 0, 0);
37 if (pInfo->m_CharCode == CPDF_Font::kInvalidCharCode)
38 return;
39
40 CPDF_Font* pFont = m_TextState.GetFont();
41 if (!pFont->IsCIDFont())
42 return;
43 if (!pFont->AsCIDFont()->IsVertWriting())
44 return;
45
46 uint16_t CID = pFont->AsCIDFont()->CIDFromCharCode(pInfo->m_CharCode);
47 pInfo->m_Origin = CFX_PointF(0, pInfo->m_Origin.x);
48
49 short vx;
50 short vy;
51 pFont->AsCIDFont()->GetVertOrigin(CID, vx, vy);
52
53 float fontsize = m_TextState.GetFontSize();
54 pInfo->m_Origin.x -= fontsize * vx / 1000;
55 pInfo->m_Origin.y -= fontsize * vy / 1000;
56 }
57
CountChars() const58 size_t CPDF_TextObject::CountChars() const {
59 size_t count = 0;
60 for (uint32_t charcode : m_CharCodes) {
61 if (charcode != CPDF_Font::kInvalidCharCode)
62 ++count;
63 }
64 return count;
65 }
66
GetCharInfo(size_t index,uint32_t * charcode,float * kerning) const67 void CPDF_TextObject::GetCharInfo(size_t index,
68 uint32_t* charcode,
69 float* kerning) const {
70 size_t count = 0;
71 for (size_t i = 0; i < m_CharCodes.size(); ++i) {
72 if (m_CharCodes[i] == CPDF_Font::kInvalidCharCode)
73 continue;
74 if (count++ != index)
75 continue;
76 *charcode = m_CharCodes[i];
77 if (i == m_CharCodes.size() - 1 ||
78 m_CharCodes[i + 1] != CPDF_Font::kInvalidCharCode) {
79 *kerning = 0;
80 } else {
81 *kerning = m_CharPos[i];
82 }
83 return;
84 }
85 }
86
GetCharInfo(size_t index,CPDF_TextObjectItem * pInfo) const87 void CPDF_TextObject::GetCharInfo(size_t index,
88 CPDF_TextObjectItem* pInfo) const {
89 size_t count = 0;
90 for (size_t i = 0; i < m_CharCodes.size(); ++i) {
91 uint32_t charcode = m_CharCodes[i];
92 if (charcode == CPDF_Font::kInvalidCharCode)
93 continue;
94 if (count++ != index)
95 continue;
96 GetItemInfo(i, pInfo);
97 break;
98 }
99 }
100
Clone() const101 std::unique_ptr<CPDF_TextObject> CPDF_TextObject::Clone() const {
102 auto obj = pdfium::MakeUnique<CPDF_TextObject>();
103 obj->CopyData(this);
104 obj->m_CharCodes = m_CharCodes;
105 obj->m_CharPos = m_CharPos;
106 obj->m_Pos = m_Pos;
107 return obj;
108 }
109
GetType() const110 CPDF_PageObject::Type CPDF_TextObject::GetType() const {
111 return TEXT;
112 }
113
Transform(const CFX_Matrix & matrix)114 void CPDF_TextObject::Transform(const CFX_Matrix& matrix) {
115 CFX_Matrix text_matrix = GetTextMatrix();
116 text_matrix.Concat(matrix);
117
118 float* pTextMatrix = m_TextState.GetMutableMatrix();
119 pTextMatrix[0] = text_matrix.a;
120 pTextMatrix[1] = text_matrix.c;
121 pTextMatrix[2] = text_matrix.b;
122 pTextMatrix[3] = text_matrix.d;
123 m_Pos = CFX_PointF(text_matrix.e, text_matrix.f);
124 CalcPositionData(0);
125 SetDirty(true);
126 }
127
IsText() const128 bool CPDF_TextObject::IsText() const {
129 return true;
130 }
131
AsText()132 CPDF_TextObject* CPDF_TextObject::AsText() {
133 return this;
134 }
135
AsText() const136 const CPDF_TextObject* CPDF_TextObject::AsText() const {
137 return this;
138 }
139
GetTextMatrix() const140 CFX_Matrix CPDF_TextObject::GetTextMatrix() const {
141 const float* pTextMatrix = m_TextState.GetMatrix();
142 return CFX_Matrix(pTextMatrix[0], pTextMatrix[2], pTextMatrix[1],
143 pTextMatrix[3], m_Pos.x, m_Pos.y);
144 }
145
SetSegments(const ByteString * pStrs,const float * pKerning,int nsegs)146 void CPDF_TextObject::SetSegments(const ByteString* pStrs,
147 const float* pKerning,
148 int nsegs) {
149 m_CharCodes.clear();
150 m_CharPos.clear();
151 CPDF_Font* pFont = m_TextState.GetFont();
152 int nChars = 0;
153 for (int i = 0; i < nsegs; ++i)
154 nChars += pFont->CountChar(pStrs[i].c_str(), pStrs[i].GetLength());
155 nChars += nsegs - 1;
156 m_CharCodes.resize(nChars);
157 m_CharPos.resize(nChars - 1);
158 int index = 0;
159 for (int i = 0; i < nsegs; ++i) {
160 const char* segment = pStrs[i].c_str();
161 int len = pStrs[i].GetLength();
162 int offset = 0;
163 while (offset < len)
164 m_CharCodes[index++] = pFont->GetNextChar(segment, len, offset);
165 if (i != nsegs - 1) {
166 m_CharPos[index - 1] = pKerning[i];
167 m_CharCodes[index++] = CPDF_Font::kInvalidCharCode;
168 }
169 }
170 }
171
SetText(const ByteString & str)172 void CPDF_TextObject::SetText(const ByteString& str) {
173 SetSegments(&str, nullptr, 1);
174 RecalcPositionData();
175 SetDirty(true);
176 }
177
GetCharWidth(uint32_t charcode) const178 float CPDF_TextObject::GetCharWidth(uint32_t charcode) const {
179 float fontsize = m_TextState.GetFontSize() / 1000;
180 CPDF_Font* pFont = m_TextState.GetFont();
181 bool bVertWriting = false;
182 CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
183 if (pCIDFont)
184 bVertWriting = pCIDFont->IsVertWriting();
185 if (!bVertWriting)
186 return pFont->GetCharWidthF(charcode) * fontsize;
187
188 uint16_t CID = pCIDFont->CIDFromCharCode(charcode);
189 return pCIDFont->GetVertWidth(CID) * fontsize;
190 }
191
GetFont() const192 CPDF_Font* CPDF_TextObject::GetFont() const {
193 return m_TextState.GetFont();
194 }
195
GetFontSize() const196 float CPDF_TextObject::GetFontSize() const {
197 return m_TextState.GetFontSize();
198 }
199
CalcPositionData(float horz_scale)200 CFX_PointF CPDF_TextObject::CalcPositionData(float horz_scale) {
201 float curpos = 0;
202 float min_x = 10000 * 1.0f;
203 float max_x = -10000 * 1.0f;
204 float min_y = 10000 * 1.0f;
205 float max_y = -10000 * 1.0f;
206 CPDF_Font* pFont = m_TextState.GetFont();
207 bool bVertWriting = false;
208 CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
209 if (pCIDFont)
210 bVertWriting = pCIDFont->IsVertWriting();
211
212 float fontsize = m_TextState.GetFontSize();
213 for (size_t i = 0; i < m_CharCodes.size(); ++i) {
214 uint32_t charcode = m_CharCodes[i];
215 if (i > 0) {
216 if (charcode == CPDF_Font::kInvalidCharCode) {
217 curpos -= (m_CharPos[i - 1] * fontsize) / 1000;
218 continue;
219 }
220 m_CharPos[i - 1] = curpos;
221 }
222
223 FX_RECT char_rect = pFont->GetCharBBox(charcode);
224 float charwidth;
225 if (!bVertWriting) {
226 min_y = std::min(
227 min_y, static_cast<float>(std::min(char_rect.top, char_rect.bottom)));
228 max_y = std::max(
229 max_y, static_cast<float>(std::max(char_rect.top, char_rect.bottom)));
230 float char_left = curpos + char_rect.left * fontsize / 1000;
231 float char_right = curpos + char_rect.right * fontsize / 1000;
232 min_x = std::min(min_x, std::min(char_left, char_right));
233 max_x = std::max(max_x, std::max(char_left, char_right));
234 charwidth = pFont->GetCharWidthF(charcode) * fontsize / 1000;
235 } else {
236 uint16_t CID = pCIDFont->CIDFromCharCode(charcode);
237 short vx;
238 short vy;
239 pCIDFont->GetVertOrigin(CID, vx, vy);
240 char_rect.left -= vx;
241 char_rect.right -= vx;
242 char_rect.top -= vy;
243 char_rect.bottom -= vy;
244 min_x = std::min(
245 min_x, static_cast<float>(std::min(char_rect.left, char_rect.right)));
246 max_x = std::max(
247 max_x, static_cast<float>(std::max(char_rect.left, char_rect.right)));
248 float char_top = curpos + char_rect.top * fontsize / 1000;
249 float char_bottom = curpos + char_rect.bottom * fontsize / 1000;
250 min_y = std::min(min_y, std::min(char_top, char_bottom));
251 max_y = std::max(max_y, std::max(char_top, char_bottom));
252 charwidth = pCIDFont->GetVertWidth(CID) * fontsize / 1000;
253 }
254 curpos += charwidth;
255 if (charcode == ' ' && (!pCIDFont || pCIDFont->GetCharSize(' ') == 1))
256 curpos += m_TextState.GetWordSpace();
257
258 curpos += m_TextState.GetCharSpace();
259 }
260
261 CFX_PointF ret;
262 if (bVertWriting) {
263 ret.y = curpos;
264 min_x = min_x * fontsize / 1000;
265 max_x = max_x * fontsize / 1000;
266 } else {
267 ret.x = curpos * horz_scale;
268 min_y = min_y * fontsize / 1000;
269 max_y = max_y * fontsize / 1000;
270 }
271 std::tie(m_Left, m_Right, m_Top, m_Bottom) =
272 GetTextMatrix().TransformRect(min_x, max_x, max_y, min_y);
273
274 if (!TextRenderingModeIsStrokeMode(m_TextState.GetTextMode()))
275 return ret;
276
277 float half_width = m_GraphState.GetLineWidth() / 2;
278 m_Left -= half_width;
279 m_Right += half_width;
280 m_Top += half_width;
281 m_Bottom -= half_width;
282
283 return ret;
284 }
285
SetPosition(float x,float y)286 void CPDF_TextObject::SetPosition(float x, float y) {
287 float dx = x - m_Pos.x;
288 float dy = y - m_Pos.y;
289 m_Pos.x = x;
290 m_Pos.y = y;
291 m_Left += dx;
292 m_Right += dx;
293 m_Top += dy;
294 m_Bottom += dy;
295 }
296
RecalcPositionData()297 void CPDF_TextObject::RecalcPositionData() {
298 CalcPositionData(1);
299 }
300