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/fpdfdoc/cpdf_annot.h" 8 9 #include <utility> 10 11 #include "core/fpdfapi/page/cpdf_form.h" 12 #include "core/fpdfapi/page/cpdf_page.h" 13 #include "core/fpdfapi/parser/cpdf_array.h" 14 #include "core/fpdfapi/parser/cpdf_boolean.h" 15 #include "core/fpdfapi/parser/cpdf_document.h" 16 #include "core/fpdfapi/render/cpdf_rendercontext.h" 17 #include "core/fpdfapi/render/cpdf_renderoptions.h" 18 #include "core/fpdfdoc/cpvt_generateap.h" 19 #include "core/fxcrt/fx_memory.h" 20 #include "core/fxge/cfx_graphstatedata.h" 21 #include "core/fxge/cfx_pathdata.h" 22 #include "core/fxge/cfx_renderdevice.h" 23 #include "third_party/base/ptr_util.h" 24 25 namespace { 26 27 char kPDFiumKey_HasGeneratedAP[] = "PDFIUM_HasGeneratedAP"; 28 29 bool IsTextMarkupAnnotation(CPDF_Annot::Subtype type) { 30 return type == CPDF_Annot::Subtype::HIGHLIGHT || 31 type == CPDF_Annot::Subtype::SQUIGGLY || 32 type == CPDF_Annot::Subtype::STRIKEOUT || 33 type == CPDF_Annot::Subtype::UNDERLINE; 34 } 35 36 bool ShouldGenerateAPForAnnotation(CPDF_Dictionary* pAnnotDict) { 37 // If AP dictionary exists, we use the appearance defined in the 38 // existing AP dictionary. 39 if (pAnnotDict->KeyExist("AP")) 40 return false; 41 42 return !CPDF_Annot::IsAnnotationHidden(pAnnotDict); 43 } 44 45 CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage, 46 CPDF_Annot* pAnnot, 47 CPDF_Annot::AppearanceMode mode, 48 const CFX_Matrix* pUser2Device, 49 CFX_Matrix* matrix) { 50 CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode); 51 if (!pForm) 52 return nullptr; 53 54 CFX_FloatRect form_bbox = pForm->m_pFormDict->GetRectFor("BBox"); 55 CFX_Matrix form_matrix = pForm->m_pFormDict->GetMatrixFor("Matrix"); 56 form_matrix.TransformRect(form_bbox); 57 matrix->MatchRect(pAnnot->GetRect(), form_bbox); 58 matrix->Concat(*pUser2Device); 59 return pForm; 60 } 61 62 } // namespace 63 64 CPDF_Annot::CPDF_Annot(std::unique_ptr<CPDF_Dictionary> pDict, 65 CPDF_Document* pDocument) 66 : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) { 67 Init(); 68 } 69 70 CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument) 71 : m_pAnnotDict(pDict), m_pDocument(pDocument) { 72 Init(); 73 } 74 75 CPDF_Annot::~CPDF_Annot() { 76 ClearCachedAP(); 77 } 78 79 void CPDF_Annot::Init() { 80 m_nSubtype = StringToAnnotSubtype(m_pAnnotDict->GetStringFor("Subtype")); 81 m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype); 82 m_bHasGeneratedAP = m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP); 83 GenerateAPIfNeeded(); 84 } 85 86 void CPDF_Annot::GenerateAPIfNeeded() { 87 if (!ShouldGenerateAPForAnnotation(m_pAnnotDict.Get())) 88 return; 89 90 CPDF_Dictionary* pDict = m_pAnnotDict.Get(); 91 bool result = false; 92 if (m_nSubtype == CPDF_Annot::Subtype::CIRCLE) 93 result = CPVT_GenerateAP::GenerateCircleAP(m_pDocument, pDict); 94 else if (m_nSubtype == CPDF_Annot::Subtype::HIGHLIGHT) 95 result = CPVT_GenerateAP::GenerateHighlightAP(m_pDocument, pDict); 96 else if (m_nSubtype == CPDF_Annot::Subtype::INK) 97 result = CPVT_GenerateAP::GenerateInkAP(m_pDocument, pDict); 98 else if (m_nSubtype == CPDF_Annot::Subtype::POPUP) 99 result = CPVT_GenerateAP::GeneratePopupAP(m_pDocument, pDict); 100 else if (m_nSubtype == CPDF_Annot::Subtype::SQUARE) 101 result = CPVT_GenerateAP::GenerateSquareAP(m_pDocument, pDict); 102 else if (m_nSubtype == CPDF_Annot::Subtype::SQUIGGLY) 103 result = CPVT_GenerateAP::GenerateSquigglyAP(m_pDocument, pDict); 104 else if (m_nSubtype == CPDF_Annot::Subtype::STRIKEOUT) 105 result = CPVT_GenerateAP::GenerateStrikeOutAP(m_pDocument, pDict); 106 else if (m_nSubtype == CPDF_Annot::Subtype::TEXT) 107 result = CPVT_GenerateAP::GenerateTextAP(m_pDocument, pDict); 108 else if (m_nSubtype == CPDF_Annot::Subtype::UNDERLINE) 109 result = CPVT_GenerateAP::GenerateUnderlineAP(m_pDocument, pDict); 110 111 if (result) { 112 m_pAnnotDict->SetNewFor<CPDF_Boolean>(kPDFiumKey_HasGeneratedAP, result); 113 m_bHasGeneratedAP = result; 114 } 115 } 116 117 bool CPDF_Annot::ShouldDrawAnnotation() { 118 if (IsAnnotationHidden(m_pAnnotDict.Get())) 119 return false; 120 121 if (m_nSubtype == CPDF_Annot::Subtype::POPUP && !m_bOpenState) 122 return false; 123 124 return true; 125 } 126 127 void CPDF_Annot::ClearCachedAP() { 128 m_APMap.clear(); 129 } 130 131 CPDF_Annot::Subtype CPDF_Annot::GetSubtype() const { 132 return m_nSubtype; 133 } 134 135 CFX_FloatRect CPDF_Annot::RectForDrawing() const { 136 if (!m_pAnnotDict) 137 return CFX_FloatRect(); 138 139 bool bShouldUseQuadPointsCoords = 140 m_bIsTextMarkupAnnotation && m_bHasGeneratedAP; 141 if (bShouldUseQuadPointsCoords) 142 return RectFromQuadPoints(m_pAnnotDict.Get()); 143 144 return m_pAnnotDict->GetRectFor("Rect"); 145 } 146 147 CFX_FloatRect CPDF_Annot::GetRect() const { 148 if (!m_pAnnotDict) 149 return CFX_FloatRect(); 150 151 CFX_FloatRect rect = RectForDrawing(); 152 rect.Normalize(); 153 return rect; 154 } 155 156 uint32_t CPDF_Annot::GetFlags() const { 157 return m_pAnnotDict->GetIntegerFor("F"); 158 } 159 160 CPDF_Stream* FPDFDOC_GetAnnotAP(CPDF_Dictionary* pAnnotDict, 161 CPDF_Annot::AppearanceMode mode) { 162 CPDF_Dictionary* pAP = pAnnotDict->GetDictFor("AP"); 163 if (!pAP) { 164 return nullptr; 165 } 166 const FX_CHAR* ap_entry = "N"; 167 if (mode == CPDF_Annot::Down) 168 ap_entry = "D"; 169 else if (mode == CPDF_Annot::Rollover) 170 ap_entry = "R"; 171 if (!pAP->KeyExist(ap_entry)) 172 ap_entry = "N"; 173 174 CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry); 175 if (!psub) 176 return nullptr; 177 if (CPDF_Stream* pStream = psub->AsStream()) 178 return pStream; 179 180 if (CPDF_Dictionary* pDict = psub->AsDictionary()) { 181 CFX_ByteString as = pAnnotDict->GetStringFor("AS"); 182 if (as.IsEmpty()) { 183 CFX_ByteString value = pAnnotDict->GetStringFor("V"); 184 if (value.IsEmpty()) { 185 CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent"); 186 value = pParentDict ? pParentDict->GetStringFor("V") : CFX_ByteString(); 187 } 188 if (value.IsEmpty() || !pDict->KeyExist(value)) 189 as = "Off"; 190 else 191 as = value; 192 } 193 return pDict->GetStreamFor(as); 194 } 195 return nullptr; 196 } 197 198 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) { 199 CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pAnnotDict.Get(), mode); 200 if (!pStream) 201 return nullptr; 202 203 auto it = m_APMap.find(pStream); 204 if (it != m_APMap.end()) 205 return it->second.get(); 206 207 auto pNewForm = 208 pdfium::MakeUnique<CPDF_Form>(m_pDocument, pPage->m_pResources, pStream); 209 pNewForm->ParseContent(nullptr, nullptr, nullptr); 210 211 CPDF_Form* pResult = pNewForm.get(); 212 m_APMap[pStream] = std::move(pNewForm); 213 return pResult; 214 } 215 216 // Static. 217 CFX_FloatRect CPDF_Annot::RectFromQuadPoints(CPDF_Dictionary* pAnnotDict) { 218 CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); 219 if (!pArray) 220 return CFX_FloatRect(); 221 222 // QuadPoints are defined with 4 pairs of numbers 223 // ([ pair0, pair1, pair2, pair3 ]), where 224 // pair0 = top_left 225 // pair1 = top_right 226 // pair2 = bottom_left 227 // pair3 = bottom_right 228 // 229 // On the other hand, /Rect is define as 2 pairs [pair0, pair1] where: 230 // pair0 = bottom_left 231 // pair1 = top_right. 232 return CFX_FloatRect(pArray->GetNumberAt(4), pArray->GetNumberAt(5), 233 pArray->GetNumberAt(2), pArray->GetNumberAt(3)); 234 } 235 236 // Static. 237 bool CPDF_Annot::IsAnnotationHidden(CPDF_Dictionary* pAnnotDict) { 238 return !!(pAnnotDict->GetIntegerFor("F") & ANNOTFLAG_HIDDEN); 239 } 240 241 // Static. 242 CPDF_Annot::Subtype CPDF_Annot::StringToAnnotSubtype( 243 const CFX_ByteString& sSubtype) { 244 if (sSubtype == "Text") 245 return CPDF_Annot::Subtype::TEXT; 246 if (sSubtype == "Link") 247 return CPDF_Annot::Subtype::LINK; 248 if (sSubtype == "FreeText") 249 return CPDF_Annot::Subtype::FREETEXT; 250 if (sSubtype == "Line") 251 return CPDF_Annot::Subtype::LINE; 252 if (sSubtype == "Square") 253 return CPDF_Annot::Subtype::SQUARE; 254 if (sSubtype == "Circle") 255 return CPDF_Annot::Subtype::CIRCLE; 256 if (sSubtype == "Polygon") 257 return CPDF_Annot::Subtype::POLYGON; 258 if (sSubtype == "PolyLine") 259 return CPDF_Annot::Subtype::POLYLINE; 260 if (sSubtype == "Highlight") 261 return CPDF_Annot::Subtype::HIGHLIGHT; 262 if (sSubtype == "Underline") 263 return CPDF_Annot::Subtype::UNDERLINE; 264 if (sSubtype == "Squiggly") 265 return CPDF_Annot::Subtype::SQUIGGLY; 266 if (sSubtype == "StrikeOut") 267 return CPDF_Annot::Subtype::STRIKEOUT; 268 if (sSubtype == "Stamp") 269 return CPDF_Annot::Subtype::STAMP; 270 if (sSubtype == "Caret") 271 return CPDF_Annot::Subtype::CARET; 272 if (sSubtype == "Ink") 273 return CPDF_Annot::Subtype::INK; 274 if (sSubtype == "Popup") 275 return CPDF_Annot::Subtype::POPUP; 276 if (sSubtype == "FileAttachment") 277 return CPDF_Annot::Subtype::FILEATTACHMENT; 278 if (sSubtype == "Sound") 279 return CPDF_Annot::Subtype::SOUND; 280 if (sSubtype == "Movie") 281 return CPDF_Annot::Subtype::MOVIE; 282 if (sSubtype == "Widget") 283 return CPDF_Annot::Subtype::WIDGET; 284 if (sSubtype == "Screen") 285 return CPDF_Annot::Subtype::SCREEN; 286 if (sSubtype == "PrinterMark") 287 return CPDF_Annot::Subtype::PRINTERMARK; 288 if (sSubtype == "TrapNet") 289 return CPDF_Annot::Subtype::TRAPNET; 290 if (sSubtype == "Watermark") 291 return CPDF_Annot::Subtype::WATERMARK; 292 if (sSubtype == "3D") 293 return CPDF_Annot::Subtype::THREED; 294 if (sSubtype == "RichMedia") 295 return CPDF_Annot::Subtype::RICHMEDIA; 296 if (sSubtype == "XFAWidget") 297 return CPDF_Annot::Subtype::XFAWIDGET; 298 return CPDF_Annot::Subtype::UNKNOWN; 299 } 300 301 // Static. 302 CFX_ByteString CPDF_Annot::AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype) { 303 if (nSubtype == CPDF_Annot::Subtype::TEXT) 304 return "Text"; 305 if (nSubtype == CPDF_Annot::Subtype::LINK) 306 return "Link"; 307 if (nSubtype == CPDF_Annot::Subtype::FREETEXT) 308 return "FreeText"; 309 if (nSubtype == CPDF_Annot::Subtype::LINE) 310 return "Line"; 311 if (nSubtype == CPDF_Annot::Subtype::SQUARE) 312 return "Square"; 313 if (nSubtype == CPDF_Annot::Subtype::CIRCLE) 314 return "Circle"; 315 if (nSubtype == CPDF_Annot::Subtype::POLYGON) 316 return "Polygon"; 317 if (nSubtype == CPDF_Annot::Subtype::POLYLINE) 318 return "PolyLine"; 319 if (nSubtype == CPDF_Annot::Subtype::HIGHLIGHT) 320 return "Highlight"; 321 if (nSubtype == CPDF_Annot::Subtype::UNDERLINE) 322 return "Underline"; 323 if (nSubtype == CPDF_Annot::Subtype::SQUIGGLY) 324 return "Squiggly"; 325 if (nSubtype == CPDF_Annot::Subtype::STRIKEOUT) 326 return "StrikeOut"; 327 if (nSubtype == CPDF_Annot::Subtype::STAMP) 328 return "Stamp"; 329 if (nSubtype == CPDF_Annot::Subtype::CARET) 330 return "Caret"; 331 if (nSubtype == CPDF_Annot::Subtype::INK) 332 return "Ink"; 333 if (nSubtype == CPDF_Annot::Subtype::POPUP) 334 return "Popup"; 335 if (nSubtype == CPDF_Annot::Subtype::FILEATTACHMENT) 336 return "FileAttachment"; 337 if (nSubtype == CPDF_Annot::Subtype::SOUND) 338 return "Sound"; 339 if (nSubtype == CPDF_Annot::Subtype::MOVIE) 340 return "Movie"; 341 if (nSubtype == CPDF_Annot::Subtype::WIDGET) 342 return "Widget"; 343 if (nSubtype == CPDF_Annot::Subtype::SCREEN) 344 return "Screen"; 345 if (nSubtype == CPDF_Annot::Subtype::PRINTERMARK) 346 return "PrinterMark"; 347 if (nSubtype == CPDF_Annot::Subtype::TRAPNET) 348 return "TrapNet"; 349 if (nSubtype == CPDF_Annot::Subtype::WATERMARK) 350 return "Watermark"; 351 if (nSubtype == CPDF_Annot::Subtype::THREED) 352 return "3D"; 353 if (nSubtype == CPDF_Annot::Subtype::RICHMEDIA) 354 return "RichMedia"; 355 if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET) 356 return "XFAWidget"; 357 return ""; 358 } 359 360 bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage, 361 CFX_RenderDevice* pDevice, 362 const CFX_Matrix* pUser2Device, 363 AppearanceMode mode, 364 const CPDF_RenderOptions* pOptions) { 365 if (!ShouldDrawAnnotation()) 366 return false; 367 368 // It might happen that by the time this annotation instance was created, 369 // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided 370 // to not "generate" its AP. 371 // If for a reason the object is no longer hidden, but still does not have 372 // its "AP" generated, generate it now. 373 GenerateAPIfNeeded(); 374 375 CFX_Matrix matrix; 376 CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix); 377 if (!pForm) 378 return false; 379 380 CPDF_RenderContext context(pPage); 381 context.AppendLayer(pForm, &matrix); 382 context.Render(pDevice, pOptions, nullptr); 383 return true; 384 } 385 386 bool CPDF_Annot::DrawInContext(const CPDF_Page* pPage, 387 CPDF_RenderContext* pContext, 388 const CFX_Matrix* pUser2Device, 389 AppearanceMode mode) { 390 if (!ShouldDrawAnnotation()) 391 return false; 392 393 // It might happen that by the time this annotation instance was created, 394 // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided 395 // to not "generate" its AP. 396 // If for a reason the object is no longer hidden, but still does not have 397 // its "AP" generated, generate it now. 398 GenerateAPIfNeeded(); 399 400 CFX_Matrix matrix; 401 CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix); 402 if (!pForm) 403 return false; 404 405 pContext->AppendLayer(pForm, &matrix); 406 return true; 407 } 408 409 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, 410 const CFX_Matrix* pUser2Device, 411 const CPDF_RenderOptions* pOptions) { 412 if (GetSubtype() == CPDF_Annot::Subtype::POPUP) 413 return; 414 415 uint32_t annot_flags = GetFlags(); 416 if (annot_flags & ANNOTFLAG_HIDDEN) { 417 return; 418 } 419 bool bPrinting = pDevice->GetDeviceClass() == FXDC_PRINTER || 420 (pOptions && (pOptions->m_Flags & RENDER_PRINTPREVIEW)); 421 if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) { 422 return; 423 } 424 if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) { 425 return; 426 } 427 CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS"); 428 char style_char; 429 FX_FLOAT width; 430 CPDF_Array* pDashArray = nullptr; 431 if (!pBS) { 432 CPDF_Array* pBorderArray = m_pAnnotDict->GetArrayFor("Border"); 433 style_char = 'S'; 434 if (pBorderArray) { 435 width = pBorderArray->GetNumberAt(2); 436 if (pBorderArray->GetCount() == 4) { 437 pDashArray = pBorderArray->GetArrayAt(3); 438 if (!pDashArray) { 439 return; 440 } 441 size_t nLen = pDashArray->GetCount(); 442 size_t i = 0; 443 for (; i < nLen; ++i) { 444 CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i); 445 if (pObj && pObj->GetInteger()) { 446 break; 447 } 448 } 449 if (i == nLen) { 450 return; 451 } 452 style_char = 'D'; 453 } 454 } else { 455 width = 1; 456 } 457 } else { 458 CFX_ByteString style = pBS->GetStringFor("S"); 459 pDashArray = pBS->GetArrayFor("D"); 460 style_char = style[1]; 461 width = pBS->GetNumberFor("W"); 462 } 463 if (width <= 0) { 464 return; 465 } 466 CPDF_Array* pColor = m_pAnnotDict->GetArrayFor("C"); 467 uint32_t argb = 0xff000000; 468 if (pColor) { 469 int R = (int32_t)(pColor->GetNumberAt(0) * 255); 470 int G = (int32_t)(pColor->GetNumberAt(1) * 255); 471 int B = (int32_t)(pColor->GetNumberAt(2) * 255); 472 argb = ArgbEncode(0xff, R, G, B); 473 } 474 CFX_GraphStateData graph_state; 475 graph_state.m_LineWidth = width; 476 if (style_char == 'D') { 477 if (pDashArray) { 478 size_t dash_count = pDashArray->GetCount(); 479 if (dash_count % 2) { 480 dash_count++; 481 } 482 graph_state.m_DashArray = FX_Alloc(FX_FLOAT, dash_count); 483 graph_state.m_DashCount = dash_count; 484 size_t i; 485 for (i = 0; i < pDashArray->GetCount(); ++i) { 486 graph_state.m_DashArray[i] = pDashArray->GetNumberAt(i); 487 } 488 if (i < dash_count) { 489 graph_state.m_DashArray[i] = graph_state.m_DashArray[i - 1]; 490 } 491 } else { 492 graph_state.m_DashArray = FX_Alloc(FX_FLOAT, 2); 493 graph_state.m_DashCount = 2; 494 graph_state.m_DashArray[0] = graph_state.m_DashArray[1] = 3 * 1.0f; 495 } 496 } 497 CFX_FloatRect rect = GetRect(); 498 CFX_PathData path; 499 width /= 2; 500 path.AppendRect(rect.left + width, rect.bottom + width, rect.right - width, 501 rect.top - width); 502 int fill_type = 0; 503 if (pOptions && (pOptions->m_Flags & RENDER_NOPATHSMOOTH)) 504 fill_type |= FXFILL_NOPATHSMOOTH; 505 506 pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type); 507 } 508