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 16 CPDF_TextObjectItem::CPDF_TextObjectItem() : m_CharCode(0) {} 17 18 CPDF_TextObjectItem::~CPDF_TextObjectItem() = default; 19 20 CPDF_TextObject::CPDF_TextObject() {} 21 22 CPDF_TextObject::~CPDF_TextObject() {} 23 24 int CPDF_TextObject::CountItems() const { 25 return pdfium::CollectionSize<int>(m_CharCodes); 26 } 27 28 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 52 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 61 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 81 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 94 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 103 CPDF_PageObject::Type CPDF_TextObject::GetType() const { 104 return TEXT; 105 } 106 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 120 bool CPDF_TextObject::IsText() const { 121 return true; 122 } 123 124 CPDF_TextObject* CPDF_TextObject::AsText() { 125 return this; 126 } 127 128 const CPDF_TextObject* CPDF_TextObject::AsText() const { 129 return this; 130 } 131 132 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 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 164 void CPDF_TextObject::SetText(const CFX_ByteString& str) { 165 SetSegments(&str, nullptr, 1); 166 RecalcPositionData(); 167 } 168 169 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 183 CPDF_Font* CPDF_TextObject::GetFont() const { 184 return m_TextState.GetFont(); 185 } 186 187 FX_FLOAT CPDF_TextObject::GetFontSize() const { 188 return m_TextState.GetFontSize(); 189 } 190 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 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 292 void CPDF_TextObject::RecalcPositionData() { 293 CalcPositionData(1); 294 } 295