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