|  | // Copyright 2014 The PDFium Authors | 
|  | // 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 "xfa/fxfa/cxfa_ffwidget.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "core/fxcodec/fx_codec.h" | 
|  | #include "core/fxcodec/progressive_decoder.h" | 
|  | #include "core/fxcrt/maybe_owned.h" | 
|  | #include "core/fxge/cfx_fillrenderoptions.h" | 
|  | #include "core/fxge/cfx_path.h" | 
|  | #include "core/fxge/cfx_renderdevice.h" | 
|  | #include "core/fxge/dib/cfx_dibitmap.h" | 
|  | #include "third_party/base/check.h" | 
|  | #include "xfa/fgas/graphics/cfgas_gegraphics.h" | 
|  | #include "xfa/fwl/fwl_widgethit.h" | 
|  | #include "xfa/fxfa/cxfa_eventparam.h" | 
|  | #include "xfa/fxfa/cxfa_ffapp.h" | 
|  | #include "xfa/fxfa/cxfa_ffdoc.h" | 
|  | #include "xfa/fxfa/cxfa_ffdocview.h" | 
|  | #include "xfa/fxfa/cxfa_ffpageview.h" | 
|  | #include "xfa/fxfa/cxfa_ffwidgethandler.h" | 
|  | #include "xfa/fxfa/cxfa_imagerenderer.h" | 
|  | #include "xfa/fxfa/layout/cxfa_layoutprocessor.h" | 
|  | #include "xfa/fxfa/parser/cxfa_border.h" | 
|  | #include "xfa/fxfa/parser/cxfa_box.h" | 
|  | #include "xfa/fxfa/parser/cxfa_edge.h" | 
|  | #include "xfa/fxfa/parser/cxfa_image.h" | 
|  | #include "xfa/fxfa/parser/cxfa_margin.h" | 
|  | #include "xfa/fxfa/parser/cxfa_node.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | FXDIB_Format XFA_GetDIBFormat(FXCODEC_IMAGE_TYPE type, | 
|  | int32_t iComponents, | 
|  | int32_t iBitsPerComponent) { | 
|  | FXDIB_Format dibFormat = FXDIB_Format::kArgb; | 
|  | switch (type) { | 
|  | case FXCODEC_IMAGE_JPG: | 
|  | #ifdef PDF_ENABLE_XFA_BMP | 
|  | case FXCODEC_IMAGE_BMP: | 
|  | #endif  // PDF_ENABLE_XFA_BMP | 
|  | #ifdef PDF_ENABLE_XFA_TIFF | 
|  | case FXCODEC_IMAGE_TIFF: | 
|  | #endif  // PDF_ENABLE_XFA_TIFF | 
|  | { | 
|  | dibFormat = FXDIB_Format::kRgb32; | 
|  | int32_t bpp = iComponents * iBitsPerComponent; | 
|  | if (bpp <= 24) { | 
|  | dibFormat = FXDIB_Format::kRgb; | 
|  | } | 
|  | } break; | 
|  | #ifdef PDF_ENABLE_XFA_PNG | 
|  | case FXCODEC_IMAGE_PNG: | 
|  | #endif  // PDF_ENABLE_XFA_PNG | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return dibFormat; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void XFA_DrawImage(CFGAS_GEGraphics* pGS, | 
|  | const CFX_RectF& rtImage, | 
|  | const CFX_Matrix& matrix, | 
|  | RetainPtr<CFX_DIBitmap> pDIBitmap, | 
|  | XFA_AttributeValue iAspect, | 
|  | const CFX_Size& dpi, | 
|  | XFA_AttributeValue iHorzAlign, | 
|  | XFA_AttributeValue iVertAlign) { | 
|  | if (rtImage.IsEmpty()) | 
|  | return; | 
|  |  | 
|  | if (!pDIBitmap || pDIBitmap->GetBuffer().empty()) | 
|  | return; | 
|  |  | 
|  | CFX_RectF rtFit(rtImage.TopLeft(), | 
|  | XFA_UnitPx2Pt(pDIBitmap->GetWidth(), dpi.width), | 
|  | XFA_UnitPx2Pt(pDIBitmap->GetHeight(), dpi.height)); | 
|  | switch (iAspect) { | 
|  | case XFA_AttributeValue::Fit: { | 
|  | float f1 = rtImage.height / rtFit.height; | 
|  | float f2 = rtImage.width / rtFit.width; | 
|  | f1 = std::min(f1, f2); | 
|  | rtFit.height = rtFit.height * f1; | 
|  | rtFit.width = rtFit.width * f1; | 
|  | break; | 
|  | } | 
|  | case XFA_AttributeValue::Height: { | 
|  | float f1 = rtImage.height / rtFit.height; | 
|  | rtFit.height = rtImage.height; | 
|  | rtFit.width = f1 * rtFit.width; | 
|  | break; | 
|  | } | 
|  | case XFA_AttributeValue::None: | 
|  | rtFit.height = rtImage.height; | 
|  | rtFit.width = rtImage.width; | 
|  | break; | 
|  | case XFA_AttributeValue::Width: { | 
|  | float f1 = rtImage.width / rtFit.width; | 
|  | rtFit.width = rtImage.width; | 
|  | rtFit.height = rtFit.height * f1; | 
|  | break; | 
|  | } | 
|  | case XFA_AttributeValue::Actual: | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (iHorzAlign == XFA_AttributeValue::Center) | 
|  | rtFit.left += (rtImage.width - rtFit.width) / 2; | 
|  | else if (iHorzAlign == XFA_AttributeValue::Right) | 
|  | rtFit.left = rtImage.right() - rtFit.width; | 
|  |  | 
|  | if (iVertAlign == XFA_AttributeValue::Middle) | 
|  | rtFit.top += (rtImage.height - rtFit.height) / 2; | 
|  | else if (iVertAlign == XFA_AttributeValue::Bottom) | 
|  | rtFit.top = rtImage.bottom() - rtImage.height; | 
|  |  | 
|  | CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice(); | 
|  | CFX_RenderDevice::StateRestorer restorer(pRenderDevice); | 
|  | CFX_Path path; | 
|  | path.AppendRect(rtImage.left, rtImage.bottom(), rtImage.right(), rtImage.top); | 
|  | pRenderDevice->SetClip_PathFill(path, &matrix, | 
|  | CFX_FillRenderOptions::WindingOptions()); | 
|  |  | 
|  | CFX_Matrix mtImage(1, 0, 0, -1, 0, 1); | 
|  | mtImage.Concat( | 
|  | CFX_Matrix(rtFit.width, 0, 0, rtFit.height, rtFit.left, rtFit.top)); | 
|  | mtImage.Concat(matrix); | 
|  |  | 
|  | CXFA_ImageRenderer imageRender(pRenderDevice, std::move(pDIBitmap), mtImage); | 
|  | if (!imageRender.Start()) | 
|  | return; | 
|  |  | 
|  | while (imageRender.Continue()) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | RetainPtr<CFX_DIBitmap> XFA_LoadImageFromBuffer( | 
|  | RetainPtr<IFX_SeekableReadStream> pImageFileRead, | 
|  | FXCODEC_IMAGE_TYPE type, | 
|  | int32_t& iImageXDpi, | 
|  | int32_t& iImageYDpi) { | 
|  | auto pProgressiveDecoder = std::make_unique<ProgressiveDecoder>(); | 
|  |  | 
|  | CFX_DIBAttribute dibAttr; | 
|  | pProgressiveDecoder->LoadImageInfo(std::move(pImageFileRead), type, &dibAttr, | 
|  | false); | 
|  | switch (dibAttr.m_wDPIUnit) { | 
|  | case CFX_DIBAttribute::kResUnitCentimeter: | 
|  | dibAttr.m_nXDPI = static_cast<int32_t>(dibAttr.m_nXDPI * 2.54f); | 
|  | dibAttr.m_nYDPI = static_cast<int32_t>(dibAttr.m_nYDPI * 2.54f); | 
|  | break; | 
|  | case CFX_DIBAttribute::kResUnitMeter: | 
|  | dibAttr.m_nXDPI = | 
|  | static_cast<int32_t>(dibAttr.m_nXDPI / (float)100 * 2.54f); | 
|  | dibAttr.m_nYDPI = | 
|  | static_cast<int32_t>(dibAttr.m_nYDPI / (float)100 * 2.54f); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | iImageXDpi = dibAttr.m_nXDPI > 1 ? dibAttr.m_nXDPI : (96); | 
|  | iImageYDpi = dibAttr.m_nYDPI > 1 ? dibAttr.m_nYDPI : (96); | 
|  | if (pProgressiveDecoder->GetWidth() <= 0 || | 
|  | pProgressiveDecoder->GetHeight() <= 0) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | type = pProgressiveDecoder->GetType(); | 
|  | int32_t iComponents = pProgressiveDecoder->GetNumComponents(); | 
|  | int32_t iBpc = pProgressiveDecoder->GetBPC(); | 
|  | FXDIB_Format dibFormat = XFA_GetDIBFormat(type, iComponents, iBpc); | 
|  | RetainPtr<CFX_DIBitmap> pBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); | 
|  | pBitmap->Create(pProgressiveDecoder->GetWidth(), | 
|  | pProgressiveDecoder->GetHeight(), dibFormat); | 
|  | pBitmap->Clear(0xffffffff); | 
|  |  | 
|  | size_t nFrames; | 
|  | FXCODEC_STATUS status; | 
|  | std::tie(status, nFrames) = pProgressiveDecoder->GetFrames(); | 
|  | if (status != FXCODEC_STATUS::kDecodeReady || nFrames == 0) | 
|  | return nullptr; | 
|  |  | 
|  | status = pProgressiveDecoder->StartDecode(pBitmap, 0, 0, pBitmap->GetWidth(), | 
|  | pBitmap->GetHeight()); | 
|  | if (status == FXCODEC_STATUS::kError) | 
|  | return nullptr; | 
|  |  | 
|  | while (status == FXCODEC_STATUS::kDecodeToBeContinued) { | 
|  | status = pProgressiveDecoder->ContinueDecode(); | 
|  | if (status == FXCODEC_STATUS::kError) | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return pBitmap; | 
|  | } | 
|  |  | 
|  | void XFA_RectWithoutMargin(CFX_RectF* rt, const CXFA_Margin* margin) { | 
|  | if (!margin) | 
|  | return; | 
|  |  | 
|  | rt->Deflate(margin->GetLeftInset(), margin->GetTopInset(), | 
|  | margin->GetRightInset(), margin->GetBottomInset()); | 
|  | } | 
|  |  | 
|  | // static | 
|  | CXFA_FFWidget* CXFA_FFWidget::FromLayoutItem(CXFA_LayoutItem* pLayoutItem) { | 
|  | if (!pLayoutItem->GetFormNode()->HasCreatedUIWidget()) | 
|  | return nullptr; | 
|  |  | 
|  | return GetFFWidget(ToContentLayoutItem(pLayoutItem)); | 
|  | } | 
|  |  | 
|  | CXFA_FFWidget::CXFA_FFWidget(CXFA_Node* node) : m_pNode(node) {} | 
|  |  | 
|  | CXFA_FFWidget::~CXFA_FFWidget() = default; | 
|  |  | 
|  | void CXFA_FFWidget::Trace(cppgc::Visitor* visitor) const { | 
|  | visitor->Trace(m_pLayoutItem); | 
|  | visitor->Trace(m_pDocView); | 
|  | visitor->Trace(m_pPageView); | 
|  | visitor->Trace(m_pNode); | 
|  | } | 
|  |  | 
|  | CFWL_App* CXFA_FFWidget::GetFWLApp() const { | 
|  | return GetPageView()->GetDocView()->GetDoc()->GetApp()->GetFWLApp(); | 
|  | } | 
|  |  | 
|  | CXFA_FFWidget* CXFA_FFWidget::GetNextFFWidget() const { | 
|  | return GetFFWidget(GetLayoutItem()->GetNext()); | 
|  | } | 
|  |  | 
|  | const CFX_RectF& CXFA_FFWidget::GetWidgetRect() const { | 
|  | if (!GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kRectCached)) | 
|  | RecacheWidgetRect(); | 
|  | return m_WidgetRect; | 
|  | } | 
|  |  | 
|  | const CFX_RectF& CXFA_FFWidget::RecacheWidgetRect() const { | 
|  | GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kRectCached); | 
|  | m_WidgetRect = GetLayoutItem()->GetAbsoluteRect(); | 
|  | return m_WidgetRect; | 
|  | } | 
|  |  | 
|  | CFX_RectF CXFA_FFWidget::GetRectWithoutRotate() { | 
|  | CFX_RectF rtWidget = GetWidgetRect(); | 
|  | float fValue = 0; | 
|  | switch (m_pNode->GetRotate()) { | 
|  | case 90: | 
|  | rtWidget.top = rtWidget.bottom(); | 
|  | fValue = rtWidget.width; | 
|  | rtWidget.width = rtWidget.height; | 
|  | rtWidget.height = fValue; | 
|  | break; | 
|  | case 180: | 
|  | rtWidget.left = rtWidget.right(); | 
|  | rtWidget.top = rtWidget.bottom(); | 
|  | break; | 
|  | case 270: | 
|  | rtWidget.left = rtWidget.right(); | 
|  | fValue = rtWidget.width; | 
|  | rtWidget.width = rtWidget.height; | 
|  | rtWidget.height = fValue; | 
|  | break; | 
|  | } | 
|  | return rtWidget; | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::ModifyStatus(Mask<XFA_WidgetStatus> dwAdded, | 
|  | Mask<XFA_WidgetStatus> dwRemoved) { | 
|  | GetLayoutItem()->ClearStatusBits(dwRemoved); | 
|  | GetLayoutItem()->SetStatusBits(dwAdded); | 
|  | } | 
|  |  | 
|  | CXFA_FFField* CXFA_FFWidget::AsField() { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | CFX_RectF CXFA_FFWidget::GetBBox(FocusOption focus) { | 
|  | if (focus == kDrawFocus || !m_pPageView) | 
|  | return CFX_RectF(); | 
|  | return m_pPageView->GetPageViewRect(); | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::RenderWidget(CFGAS_GEGraphics* pGS, | 
|  | const CFX_Matrix& matrix, | 
|  | HighlightOption highlight) { | 
|  | if (!HasVisibleStatus()) | 
|  | return; | 
|  |  | 
|  | CXFA_Border* border = m_pNode->GetBorderIfExists(); | 
|  | if (!border) | 
|  | return; | 
|  |  | 
|  | CFX_RectF rtBorder = GetRectWithoutRotate(); | 
|  | CXFA_Margin* margin = border->GetMarginIfExists(); | 
|  | XFA_RectWithoutMargin(&rtBorder, margin); | 
|  | rtBorder.Normalize(); | 
|  | DrawBorder(pGS, border, rtBorder, matrix); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::IsLoaded() { | 
|  | return !!m_pPageView; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::LoadWidget() { | 
|  | PerformLayout(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::PerformLayout() { | 
|  | RecacheWidgetRect(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::UpdateFWLData() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::UpdateWidgetProperty() {} | 
|  |  | 
|  | bool CXFA_FFWidget::HasEventUnderHandler(XFA_EVENTTYPE eEventType, | 
|  | CXFA_FFWidgetHandler* pHandler) { | 
|  | CXFA_Node* pNode = GetNode(); | 
|  | return pNode->IsWidgetReady() && pHandler->HasEvent(pNode, eEventType); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::ProcessEventUnderHandler(CXFA_EventParam* params, | 
|  | CXFA_FFWidgetHandler* pHandler) { | 
|  | CXFA_Node* pNode = GetNode(); | 
|  | if (!pNode->IsWidgetReady()) | 
|  | return false; | 
|  |  | 
|  | return pHandler->ProcessEvent(pNode, params) == XFA_EventError::kSuccess; | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::DrawBorder(CFGAS_GEGraphics* pGS, | 
|  | CXFA_Box* box, | 
|  | const CFX_RectF& rtBorder, | 
|  | const CFX_Matrix& matrix) { | 
|  | if (box) | 
|  | box->Draw(pGS, rtBorder, matrix, false); | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::DrawBorderWithFlag(CFGAS_GEGraphics* pGS, | 
|  | CXFA_Box* box, | 
|  | const CFX_RectF& rtBorder, | 
|  | const CFX_Matrix& matrix, | 
|  | bool forceRound) { | 
|  | if (box) | 
|  | box->Draw(pGS, rtBorder, matrix, forceRound); | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::InvalidateRect() { | 
|  | CFX_RectF rtWidget = GetBBox(kDoNotDrawFocus); | 
|  | rtWidget.Inflate(2, 2); | 
|  | m_pDocView->InvalidateRect(m_pPageView.Get(), rtWidget); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnMouseEnter() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnMouseExit() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::AcceptsFocusOnButtonDown( | 
|  | Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point, | 
|  | CFWL_MessageMouse::MouseCommand command) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnLButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnMouseWheel(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point, | 
|  | const CFX_Vector& delta) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnRButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnRButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnRButtonDblClk(Mask<XFA_FWL_KeyFlag> dwFlags, | 
|  | const CFX_PointF& point) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnSetFocus(CXFA_FFWidget* pOldWidget) { | 
|  | CXFA_FFWidget* pParent = GetFFWidget(ToContentLayoutItem(GetParent())); | 
|  | if (pParent && !pParent->IsAncestorOf(pOldWidget)) { | 
|  | if (!pParent->OnSetFocus(pOldWidget)) | 
|  | return false; | 
|  | } | 
|  | GetLayoutItem()->SetStatusBits(XFA_WidgetStatus::kFocused); | 
|  |  | 
|  | CXFA_EventParam eParam; | 
|  | eParam.m_eType = XFA_EVENT_Enter; | 
|  | m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Enter, &eParam); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnKillFocus(CXFA_FFWidget* pNewWidget) { | 
|  | GetLayoutItem()->ClearStatusBits(XFA_WidgetStatus::kFocused); | 
|  | EventKillFocus(); | 
|  | if (!pNewWidget) | 
|  | return true; | 
|  |  | 
|  | CXFA_FFWidget* pParent = GetFFWidget(ToContentLayoutItem(GetParent())); | 
|  | if (pParent && !pParent->IsAncestorOf(pNewWidget)) { | 
|  | if (!pParent->OnKillFocus(pNewWidget)) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnKeyDown(XFA_FWL_VKEYCODE dwKeyCode, | 
|  | Mask<XFA_FWL_KeyFlag> dwFlags) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::OnChar(uint32_t dwChar, Mask<XFA_FWL_KeyFlag> dwFlags) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | FWL_WidgetHit CXFA_FFWidget::HitTest(const CFX_PointF& point) { | 
|  | return FWL_WidgetHit::Unknown; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanUndo() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanRedo() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanCopy() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanCut() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanPaste() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanSelectAll() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanDelete() { | 
|  | return CanCut(); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::CanDeSelect() { | 
|  | return CanCopy(); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::Undo() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::Redo() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | absl::optional<WideString> CXFA_FFWidget::Copy() { | 
|  | return absl::nullopt; | 
|  | } | 
|  |  | 
|  | absl::optional<WideString> CXFA_FFWidget::Cut() { | 
|  | return absl::nullopt; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::Paste(const WideString& wsPaste) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::SelectAll() {} | 
|  |  | 
|  | void CXFA_FFWidget::Delete() {} | 
|  |  | 
|  | void CXFA_FFWidget::DeSelect() {} | 
|  |  | 
|  | WideString CXFA_FFWidget::GetText() { | 
|  | return WideString(); | 
|  | } | 
|  |  | 
|  | FormFieldType CXFA_FFWidget::GetFormFieldType() { | 
|  | return FormFieldType::kXFA; | 
|  | } | 
|  |  | 
|  | CFX_PointF CXFA_FFWidget::Rotate2Normal(const CFX_PointF& point) { | 
|  | CFX_Matrix mt = GetRotateMatrix(); | 
|  | if (mt.IsIdentity()) | 
|  | return point; | 
|  |  | 
|  | return mt.GetInverse().Transform(point); | 
|  | } | 
|  |  | 
|  | CFX_Matrix CXFA_FFWidget::GetRotateMatrix() { | 
|  | int32_t iRotate = m_pNode->GetRotate(); | 
|  | if (!iRotate) | 
|  | return CFX_Matrix(); | 
|  |  | 
|  | CFX_RectF rcWidget = GetRectWithoutRotate(); | 
|  | CFX_Matrix mt; | 
|  | switch (iRotate) { | 
|  | case 90: | 
|  | mt.a = 0; | 
|  | mt.b = -1; | 
|  | mt.c = 1; | 
|  | mt.d = 0; | 
|  | mt.e = rcWidget.left - rcWidget.top; | 
|  | mt.f = rcWidget.left + rcWidget.top; | 
|  | break; | 
|  | case 180: | 
|  | mt.a = -1; | 
|  | mt.b = 0; | 
|  | mt.c = 0; | 
|  | mt.d = -1; | 
|  | mt.e = rcWidget.left * 2; | 
|  | mt.f = rcWidget.top * 2; | 
|  | break; | 
|  | case 270: | 
|  | mt.a = 0; | 
|  | mt.b = 1; | 
|  | mt.c = -1; | 
|  | mt.d = 0; | 
|  | mt.e = rcWidget.left + rcWidget.top; | 
|  | mt.f = rcWidget.top - rcWidget.left; | 
|  | break; | 
|  | } | 
|  | return mt; | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::DisplayCaret(bool bVisible, const CFX_RectF* pRtAnchor) { | 
|  | GetDoc()->DisplayCaret(this, bVisible, pRtAnchor); | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::GetBorderColorAndThickness(FX_ARGB* cr, float* fWidth) { | 
|  | DCHECK(GetNode()->IsWidgetReady()); | 
|  | CXFA_Border* borderUI = GetNode()->GetUIBorder(); | 
|  | if (!borderUI) | 
|  | return; | 
|  |  | 
|  | CXFA_Edge* edge = borderUI->GetEdgeIfExists(0); | 
|  | if (!edge) | 
|  | return; | 
|  |  | 
|  | *cr = edge->GetColor(); | 
|  | *fWidth = edge->GetThickness(); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::IsLayoutRectEmpty() { | 
|  | CFX_RectF rtLayout = GetRectWithoutRotate(); | 
|  | return rtLayout.width < 0.1f && rtLayout.height < 0.1f; | 
|  | } | 
|  |  | 
|  | CXFA_LayoutItem* CXFA_FFWidget::GetParent() { | 
|  | CXFA_Node* pParentNode = m_pNode->GetParent(); | 
|  | if (!pParentNode) | 
|  | return nullptr; | 
|  |  | 
|  | CXFA_LayoutProcessor* layout = GetDocView()->GetLayoutProcessor(); | 
|  | return layout->GetLayoutItem(pParentNode); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::IsAncestorOf(CXFA_FFWidget* pWidget) { | 
|  | if (!pWidget) | 
|  | return false; | 
|  |  | 
|  | CXFA_Node* pChildNode = pWidget->GetNode(); | 
|  | while (pChildNode) { | 
|  | if (pChildNode == m_pNode) | 
|  | return true; | 
|  |  | 
|  | pChildNode = pChildNode->GetParent(); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::PtInActiveRect(const CFX_PointF& point) { | 
|  | return GetWidgetRect().Contains(point); | 
|  | } | 
|  |  | 
|  | CXFA_FFDoc* CXFA_FFWidget::GetDoc() { | 
|  | return m_pDocView->GetDoc(); | 
|  | } | 
|  |  | 
|  | CXFA_FFApp* CXFA_FFWidget::GetApp() { | 
|  | return GetDoc()->GetApp(); | 
|  | } | 
|  |  | 
|  | CXFA_FFApp::CallbackIface* CXFA_FFWidget::GetAppProvider() { | 
|  | return GetApp()->GetAppProvider(); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::HasVisibleStatus() const { | 
|  | return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kVisible); | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::EventKillFocus() { | 
|  | CXFA_ContentLayoutItem* pItem = GetLayoutItem(); | 
|  | if (pItem->TestStatusBits(XFA_WidgetStatus::kAccess)) { | 
|  | pItem->ClearStatusBits(XFA_WidgetStatus::kAccess); | 
|  | return; | 
|  | } | 
|  | CXFA_EventParam eParam; | 
|  | eParam.m_eType = XFA_EVENT_Exit; | 
|  | m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Exit, &eParam); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFWidget::IsButtonDown() { | 
|  | return GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kButtonDown); | 
|  | } | 
|  |  | 
|  | void CXFA_FFWidget::SetButtonDown(bool bSet) { | 
|  | CXFA_ContentLayoutItem* pItem = GetLayoutItem(); | 
|  | if (bSet) | 
|  | pItem->SetStatusBits(XFA_WidgetStatus::kButtonDown); | 
|  | else | 
|  | pItem->ClearStatusBits(XFA_WidgetStatus::kButtonDown); | 
|  | } |