|  | // Copyright 2016 PDFium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | 
|  |  | 
|  | #include "core/fpdfdoc/cpdf_annotlist.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "core/fpdfapi/page/cpdf_page.h" | 
|  | #include "core/fpdfapi/parser/cpdf_array.h" | 
|  | #include "core/fpdfapi/parser/cpdf_document.h" | 
|  | #include "core/fpdfapi/parser/cpdf_name.h" | 
|  | #include "core/fpdfapi/parser/cpdf_number.h" | 
|  | #include "core/fpdfapi/parser/cpdf_reference.h" | 
|  | #include "core/fpdfapi/parser/cpdf_string.h" | 
|  | #include "core/fpdfapi/render/cpdf_renderoptions.h" | 
|  | #include "core/fpdfdoc/cpdf_annot.h" | 
|  | #include "core/fpdfdoc/cpdf_interform.h" | 
|  | #include "core/fpdfdoc/cpdf_occontext.h" | 
|  | #include "core/fpdfdoc/cpvt_generateap.h" | 
|  | #include "core/fxge/cfx_renderdevice.h" | 
|  | #include "third_party/base/ptr_util.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | std::unique_ptr<CPDF_Annot> CreatePopupAnnot(CPDF_Annot* pAnnot, | 
|  | CPDF_Document* pDocument, | 
|  | CPDF_Page* pPage) { | 
|  | CPDF_Dictionary* pParentDict = pAnnot->GetAnnotDict(); | 
|  | if (!pParentDict) | 
|  | return nullptr; | 
|  |  | 
|  | // TODO(jaepark): We shouldn't strip BOM for some strings and not for others. | 
|  | // See pdfium:593. | 
|  | WideString sContents = pParentDict->GetUnicodeTextFor("Contents"); | 
|  | if (sContents.IsEmpty()) | 
|  | return nullptr; | 
|  |  | 
|  | auto pAnnotDict = | 
|  | pdfium::MakeUnique<CPDF_Dictionary>(pDocument->GetByteStringPool()); | 
|  | pAnnotDict->SetNewFor<CPDF_Name>("Type", "Annot"); | 
|  | pAnnotDict->SetNewFor<CPDF_Name>("Subtype", "Popup"); | 
|  | pAnnotDict->SetNewFor<CPDF_String>("T", pParentDict->GetStringFor("T"), | 
|  | false); | 
|  | pAnnotDict->SetNewFor<CPDF_String>("Contents", sContents.UTF8Encode(), false); | 
|  |  | 
|  | CFX_FloatRect rect = pParentDict->GetRectFor("Rect"); | 
|  | rect.Normalize(); | 
|  | CFX_FloatRect popupRect(0, 0, 200, 200); | 
|  | // Note that if the popup can set its own dimensions, then we will need to | 
|  | // make sure that it isn't larger than the page size. | 
|  | if (rect.left + popupRect.Width() > pPage->GetPageWidth() && | 
|  | rect.bottom - popupRect.Height() < 0) { | 
|  | // If the annotation is on the bottom-right corner of the page, then place | 
|  | // the popup above and to the left of the annotation. | 
|  | popupRect.Translate(rect.right - popupRect.Width(), rect.top); | 
|  | } else { | 
|  | // Place the popup below and to the right of the annotation without getting | 
|  | // clipped by page edges. | 
|  | popupRect.Translate( | 
|  | std::min(rect.left, pPage->GetPageWidth() - popupRect.Width()), | 
|  | std::max(rect.bottom - popupRect.Height(), 0.f)); | 
|  | } | 
|  |  | 
|  | pAnnotDict->SetRectFor("Rect", popupRect); | 
|  | pAnnotDict->SetNewFor<CPDF_Number>("F", 0); | 
|  |  | 
|  | auto pPopupAnnot = | 
|  | pdfium::MakeUnique<CPDF_Annot>(std::move(pAnnotDict), pDocument); | 
|  | pAnnot->SetPopupAnnot(pPopupAnnot.get()); | 
|  | return pPopupAnnot; | 
|  | } | 
|  |  | 
|  | void GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { | 
|  | if (!pAnnotDict || pAnnotDict->GetStringFor("Subtype") != "Widget") | 
|  | return; | 
|  |  | 
|  | CPDF_Object* pFieldTypeObj = FPDF_GetFieldAttr(pAnnotDict, "FT"); | 
|  | if (!pFieldTypeObj) | 
|  | return; | 
|  |  | 
|  | ByteString field_type = pFieldTypeObj->GetString(); | 
|  | if (field_type == "Tx") { | 
|  | CPVT_GenerateAP::GenerateFormAP(CPVT_GenerateAP::kTextField, pDoc, | 
|  | pAnnotDict); | 
|  | return; | 
|  | } | 
|  |  | 
|  | CPDF_Object* pFieldFlagsObj = FPDF_GetFieldAttr(pAnnotDict, "Ff"); | 
|  | uint32_t flags = pFieldFlagsObj ? pFieldFlagsObj->GetInteger() : 0; | 
|  | if (field_type == "Ch") { | 
|  | CPVT_GenerateAP::GenerateFormAP((flags & (1 << 17)) | 
|  | ? CPVT_GenerateAP::kComboBox | 
|  | : CPVT_GenerateAP::kListBox, | 
|  | pDoc, pAnnotDict); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (field_type != "Btn") | 
|  | return; | 
|  | if (flags & (1 << 16)) | 
|  | return; | 
|  | if (pAnnotDict->KeyExist("AS")) | 
|  | return; | 
|  |  | 
|  | CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent"); | 
|  | if (!pParentDict || !pParentDict->KeyExist("AS")) | 
|  | return; | 
|  |  | 
|  | pAnnotDict->SetNewFor<CPDF_String>("AS", pParentDict->GetStringFor("AS"), | 
|  | false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage) | 
|  | : m_pDocument(pPage->m_pDocument.Get()) { | 
|  | if (!pPage->m_pFormDict) | 
|  | return; | 
|  |  | 
|  | CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots"); | 
|  | if (!pAnnots) | 
|  | return; | 
|  |  | 
|  | const CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); | 
|  | CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); | 
|  | bool bRegenerateAP = pAcroForm && pAcroForm->GetBooleanFor("NeedAppearances"); | 
|  | for (size_t i = 0; i < pAnnots->GetCount(); ++i) { | 
|  | CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i)); | 
|  | if (!pDict) | 
|  | continue; | 
|  | const ByteString subtype = pDict->GetStringFor("Subtype"); | 
|  | if (subtype == "Popup") { | 
|  | // Skip creating Popup annotations in the PDF document since PDFium | 
|  | // provides its own Popup annotations. | 
|  | continue; | 
|  | } | 
|  | pAnnots->ConvertToIndirectObjectAt(i, m_pDocument); | 
|  | m_AnnotList.push_back(pdfium::MakeUnique<CPDF_Annot>(pDict, m_pDocument)); | 
|  | if (bRegenerateAP && subtype == "Widget" && | 
|  | CPDF_InterForm::IsUpdateAPEnabled() && !pDict->GetDictFor("AP")) { | 
|  | GenerateAP(m_pDocument, pDict); | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t nAnnotListSize = m_AnnotList.size(); | 
|  | for (size_t i = 0; i < nAnnotListSize; ++i) { | 
|  | std::unique_ptr<CPDF_Annot> pPopupAnnot( | 
|  | CreatePopupAnnot(m_AnnotList[i].get(), m_pDocument, pPage)); | 
|  | if (pPopupAnnot) | 
|  | m_AnnotList.push_back(std::move(pPopupAnnot)); | 
|  | } | 
|  | } | 
|  |  | 
|  | CPDF_AnnotList::~CPDF_AnnotList() {} | 
|  |  | 
|  | void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage, | 
|  | CFX_RenderDevice* pDevice, | 
|  | CPDF_RenderContext* pContext, | 
|  | bool bPrinting, | 
|  | const CFX_Matrix* pMatrix, | 
|  | bool bWidgetPass, | 
|  | CPDF_RenderOptions* pOptions, | 
|  | FX_RECT* clip_rect) { | 
|  | for (const auto& pAnnot : m_AnnotList) { | 
|  | bool bWidget = pAnnot->GetSubtype() == CPDF_Annot::Subtype::WIDGET; | 
|  | if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget)) | 
|  | continue; | 
|  |  | 
|  | uint32_t annot_flags = pAnnot->GetFlags(); | 
|  | if (annot_flags & ANNOTFLAG_HIDDEN) | 
|  | continue; | 
|  |  | 
|  | if (bPrinting && (annot_flags & ANNOTFLAG_PRINT) == 0) | 
|  | continue; | 
|  |  | 
|  | if (!bPrinting && (annot_flags & ANNOTFLAG_NOVIEW)) | 
|  | continue; | 
|  |  | 
|  | if (pOptions) { | 
|  | CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); | 
|  | if (pOptions->GetOCContext() && pAnnotDict && | 
|  | !pOptions->GetOCContext()->CheckOCGVisible( | 
|  | pAnnotDict->GetDictFor("OC"))) { | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | CFX_Matrix matrix = *pMatrix; | 
|  | if (clip_rect) { | 
|  | FX_RECT annot_rect = | 
|  | matrix.TransformRect(pAnnot->GetRect()).GetOuterRect(); | 
|  | annot_rect.Intersect(*clip_rect); | 
|  | if (annot_rect.IsEmpty()) | 
|  | continue; | 
|  | } | 
|  | if (pContext) { | 
|  | pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal); | 
|  | } else if (!pAnnot->DrawAppearance(pPage, pDevice, matrix, | 
|  | CPDF_Annot::Normal, pOptions)) { | 
|  | pAnnot->DrawBorder(pDevice, &matrix, pOptions); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage, | 
|  | CFX_RenderDevice* pDevice, | 
|  | CPDF_RenderContext* pContext, | 
|  | bool bPrinting, | 
|  | const CFX_Matrix* pUser2Device, | 
|  | uint32_t dwAnnotFlags, | 
|  | CPDF_RenderOptions* pOptions, | 
|  | FX_RECT* pClipRect) { | 
|  | if (dwAnnotFlags & ANNOTFLAG_INVISIBLE) { | 
|  | DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, false, | 
|  | pOptions, pClipRect); | 
|  | } | 
|  | if (dwAnnotFlags & ANNOTFLAG_HIDDEN) { | 
|  | DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, true, | 
|  | pOptions, pClipRect); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage, | 
|  | CPDF_RenderContext* pContext, | 
|  | bool bPrinting, | 
|  | const CFX_Matrix* pMatrix, | 
|  | bool bShowWidget, | 
|  | CPDF_RenderOptions* pOptions) { | 
|  | uint32_t dwAnnotFlags = bShowWidget ? ANNOTFLAG_INVISIBLE | ANNOTFLAG_HIDDEN | 
|  | : ANNOTFLAG_INVISIBLE; | 
|  | DisplayAnnots(pPage, nullptr, pContext, bPrinting, pMatrix, dwAnnotFlags, | 
|  | pOptions, nullptr); | 
|  | } |