diff options
Diffstat (limited to 'xfa/fxfa/parser/cxfa_node.cpp')
-rw-r--r-- | xfa/fxfa/parser/cxfa_node.cpp | 2443 |
1 files changed, 2436 insertions, 7 deletions
diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp index ba882a2b9d..988d1e2ee5 100644 --- a/xfa/fxfa/parser/cxfa_node.cpp +++ b/xfa/fxfa/parser/cxfa_node.cpp @@ -6,6 +6,7 @@ #include "xfa/fxfa/parser/cxfa_node.h" +#include <algorithm> #include <map> #include <memory> #include <set> @@ -20,26 +21,35 @@ #include "core/fxcrt/xml/cfx_xmlelement.h" #include "core/fxcrt/xml/cfx_xmlnode.h" #include "core/fxcrt/xml/cfx_xmltext.h" +#include "core/fxge/dib/cfx_dibitmap.h" #include "fxjs/cfxjse_engine.h" #include "fxjs/cfxjse_value.h" #include "fxjs/xfa/cjx_node.h" #include "third_party/base/logging.h" #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" +#include "xfa/fde/cfde_textout.h" +#include "xfa/fgas/font/cfgas_fontmgr.h" #include "xfa/fxfa/cxfa_eventparam.h" #include "xfa/fxfa/cxfa_ffapp.h" #include "xfa/fxfa/cxfa_ffdocview.h" #include "xfa/fxfa/cxfa_ffnotify.h" #include "xfa/fxfa/cxfa_ffwidget.h" +#include "xfa/fxfa/cxfa_fontmgr.h" +#include "xfa/fxfa/cxfa_textprovider.h" #include "xfa/fxfa/parser/cxfa_arraynodelist.h" #include "xfa/fxfa/parser/cxfa_attachnodelist.h" #include "xfa/fxfa/parser/cxfa_bind.h" #include "xfa/fxfa/parser/cxfa_border.h" #include "xfa/fxfa/parser/cxfa_calculate.h" #include "xfa/fxfa/parser/cxfa_caption.h" +#include "xfa/fxfa/parser/cxfa_comb.h" +#include "xfa/fxfa/parser/cxfa_decimal.h" #include "xfa/fxfa/parser/cxfa_document.h" #include "xfa/fxfa/parser/cxfa_event.h" #include "xfa/fxfa/parser/cxfa_font.h" +#include "xfa/fxfa/parser/cxfa_format.h" +#include "xfa/fxfa/parser/cxfa_image.h" #include "xfa/fxfa/parser/cxfa_items.h" #include "xfa/fxfa/parser/cxfa_keep.h" #include "xfa/fxfa/parser/cxfa_layoutprocessor.h" @@ -49,6 +59,7 @@ #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h" #include "xfa/fxfa/parser/cxfa_occur.h" #include "xfa/fxfa/parser/cxfa_para.h" +#include "xfa/fxfa/parser/cxfa_picture.h" #include "xfa/fxfa/parser/cxfa_simple_parser.h" #include "xfa/fxfa/parser/cxfa_stroke.h" #include "xfa/fxfa/parser/cxfa_subform.h" @@ -59,10 +70,301 @@ #include "xfa/fxfa/parser/xfa_basic_data.h" #include "xfa/fxfa/parser/xfa_utils.h" +class CXFA_WidgetLayoutData { + public: + CXFA_WidgetLayoutData() : m_fWidgetHeight(-1) {} + virtual ~CXFA_WidgetLayoutData() {} + + float m_fWidgetHeight; +}; + namespace { constexpr uint8_t kMaxExecuteRecursion = 2; +constexpr uint8_t g_inv_base64[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, +}; + +uint8_t* XFA_RemoveBase64Whitespace(const uint8_t* pStr, int32_t iLen) { + uint8_t* pCP; + int32_t i = 0, j = 0; + if (iLen == 0) { + iLen = strlen((char*)pStr); + } + pCP = FX_Alloc(uint8_t, iLen + 1); + for (; i < iLen; i++) { + if ((pStr[i] & 128) == 0) { + if (g_inv_base64[pStr[i]] != 0xFF || pStr[i] == '=') { + pCP[j++] = pStr[i]; + } + } + } + pCP[j] = '\0'; + return pCP; +} + +int32_t XFA_Base64Decode(const char* pStr, uint8_t* pOutBuffer) { + if (!pStr) { + return 0; + } + uint8_t* pBuffer = + XFA_RemoveBase64Whitespace((uint8_t*)pStr, strlen((char*)pStr)); + if (!pBuffer) { + return 0; + } + int32_t iLen = strlen((char*)pBuffer); + int32_t i = 0, j = 0; + uint32_t dwLimb = 0; + for (; i + 3 < iLen; i += 4) { + if (pBuffer[i] == '=' || pBuffer[i + 1] == '=' || pBuffer[i + 2] == '=' || + pBuffer[i + 3] == '=') { + if (pBuffer[i] == '=' || pBuffer[i + 1] == '=') { + break; + } + if (pBuffer[i + 2] == '=') { + dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 6) | + ((uint32_t)g_inv_base64[pBuffer[i + 1]]); + pOutBuffer[j] = (uint8_t)(dwLimb >> 4) & 0xFF; + j++; + } else { + dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 12) | + ((uint32_t)g_inv_base64[pBuffer[i + 1]] << 6) | + ((uint32_t)g_inv_base64[pBuffer[i + 2]]); + pOutBuffer[j] = (uint8_t)(dwLimb >> 10) & 0xFF; + pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 2) & 0xFF; + j += 2; + } + } else { + dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 18) | + ((uint32_t)g_inv_base64[pBuffer[i + 1]] << 12) | + ((uint32_t)g_inv_base64[pBuffer[i + 2]] << 6) | + ((uint32_t)g_inv_base64[pBuffer[i + 3]]); + pOutBuffer[j] = (uint8_t)(dwLimb >> 16) & 0xff; + pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 8) & 0xff; + pOutBuffer[j + 2] = (uint8_t)(dwLimb)&0xff; + j += 3; + } + } + FX_Free(pBuffer); + return j; +} + +FXCODEC_IMAGE_TYPE XFA_GetImageType(const WideString& wsType) { + WideString wsContentType(wsType); + wsContentType.MakeLower(); + if (wsContentType == L"image/jpg") + return FXCODEC_IMAGE_JPG; + if (wsContentType == L"image/png") + return FXCODEC_IMAGE_PNG; + if (wsContentType == L"image/gif") + return FXCODEC_IMAGE_GIF; + if (wsContentType == L"image/bmp") + return FXCODEC_IMAGE_BMP; + if (wsContentType == L"image/tif") + return FXCODEC_IMAGE_TIF; + return FXCODEC_IMAGE_UNKNOWN; +} + +RetainPtr<CFX_DIBitmap> XFA_LoadImageData(CXFA_FFDoc* pDoc, + CXFA_Image* pImage, + bool& bNameImage, + int32_t& iImageXDpi, + int32_t& iImageYDpi) { + WideString wsHref = pImage->GetHref(); + WideString wsImage = pImage->GetContent(); + if (wsHref.IsEmpty() && wsImage.IsEmpty()) + return nullptr; + + FXCODEC_IMAGE_TYPE type = XFA_GetImageType(pImage->GetContentType()); + ByteString bsContent; + uint8_t* pImageBuffer = nullptr; + RetainPtr<IFX_SeekableReadStream> pImageFileRead; + if (wsImage.GetLength() > 0) { + XFA_AttributeEnum iEncoding = pImage->GetTransferEncoding(); + if (iEncoding == XFA_AttributeEnum::Base64) { + ByteString bsData = wsImage.UTF8Encode(); + int32_t iLength = bsData.GetLength(); + pImageBuffer = FX_Alloc(uint8_t, iLength); + int32_t iRead = XFA_Base64Decode(bsData.c_str(), pImageBuffer); + if (iRead > 0) { + pImageFileRead = + pdfium::MakeRetain<CFX_MemoryStream>(pImageBuffer, iRead, false); + } + } else { + bsContent = ByteString::FromUnicode(wsImage); + pImageFileRead = pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(bsContent.raw_str()), bsContent.GetLength(), + false); + } + } else { + WideString wsURL = wsHref; + if (wsURL.Left(7) != L"http://" && wsURL.Left(6) != L"ftp://") { + RetainPtr<CFX_DIBitmap> pBitmap = + pDoc->GetPDFNamedImage(wsURL.AsStringView(), iImageXDpi, iImageYDpi); + if (pBitmap) { + bNameImage = true; + return pBitmap; + } + } + pImageFileRead = pDoc->GetDocEnvironment()->OpenLinkedFile(pDoc, wsURL); + } + if (!pImageFileRead) { + FX_Free(pImageBuffer); + return nullptr; + } + bNameImage = false; + RetainPtr<CFX_DIBitmap> pBitmap = + XFA_LoadImageFromBuffer(pImageFileRead, type, iImageXDpi, iImageYDpi); + FX_Free(pImageBuffer); + return pBitmap; +} + +class CXFA_TextLayoutData : public CXFA_WidgetLayoutData { + public: + CXFA_TextLayoutData() {} + ~CXFA_TextLayoutData() override {} + + CXFA_TextLayout* GetTextLayout() const { return m_pTextLayout.get(); } + CXFA_TextProvider* GetTextProvider() const { return m_pTextProvider.get(); } + + void LoadText(CXFA_FFDoc* doc, CXFA_Node* pNode) { + if (m_pTextLayout) + return; + + m_pTextProvider = + pdfium::MakeUnique<CXFA_TextProvider>(pNode, XFA_TEXTPROVIDERTYPE_Text); + m_pTextLayout = + pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pTextProvider.get()); + } + + private: + std::unique_ptr<CXFA_TextLayout> m_pTextLayout; + std::unique_ptr<CXFA_TextProvider> m_pTextProvider; +}; + +class CXFA_ImageLayoutData : public CXFA_WidgetLayoutData { + public: + CXFA_ImageLayoutData() + : m_bNamedImage(false), m_iImageXDpi(0), m_iImageYDpi(0) {} + + ~CXFA_ImageLayoutData() override {} + + bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) { + if (m_pDIBitmap) + return true; + + CXFA_Value* value = pNode->GetFormValueIfExists(); + if (!value) + return false; + + CXFA_Image* image = value->GetImageIfExists(); + if (!image) + return false; + + pNode->SetImageImage(XFA_LoadImageData(doc, image, m_bNamedImage, + m_iImageXDpi, m_iImageYDpi)); + return !!m_pDIBitmap; + } + + RetainPtr<CFX_DIBitmap> m_pDIBitmap; + bool m_bNamedImage; + int32_t m_iImageXDpi; + int32_t m_iImageYDpi; +}; + +class CXFA_FieldLayoutData : public CXFA_WidgetLayoutData { + public: + CXFA_FieldLayoutData() {} + ~CXFA_FieldLayoutData() override {} + + bool LoadCaption(CXFA_FFDoc* doc, CXFA_Node* pNode) { + if (m_pCapTextLayout) + return true; + CXFA_Caption* caption = pNode->GetCaptionIfExists(); + if (!caption || caption->IsHidden()) + return false; + + m_pCapTextProvider = pdfium::MakeUnique<CXFA_TextProvider>( + pNode, XFA_TEXTPROVIDERTYPE_Caption); + m_pCapTextLayout = + pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pCapTextProvider.get()); + return true; + } + + std::unique_ptr<CXFA_TextLayout> m_pCapTextLayout; + std::unique_ptr<CXFA_TextProvider> m_pCapTextProvider; + std::unique_ptr<CFDE_TextOut> m_pTextOut; + std::vector<float> m_FieldSplitArray; +}; + +class CXFA_TextEditData : public CXFA_FieldLayoutData {}; + +class CXFA_ImageEditData : public CXFA_FieldLayoutData { + public: + CXFA_ImageEditData() + : m_bNamedImage(false), m_iImageXDpi(0), m_iImageYDpi(0) {} + + ~CXFA_ImageEditData() override {} + + bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) { + if (m_pDIBitmap) + return true; + + CXFA_Value* value = pNode->GetFormValueIfExists(); + if (!value) + return false; + + CXFA_Image* image = value->GetImageIfExists(); + if (!image) + return false; + + pNode->SetImageEditImage(XFA_LoadImageData(doc, image, m_bNamedImage, + m_iImageXDpi, m_iImageYDpi)); + return !!m_pDIBitmap; + } + + RetainPtr<CFX_DIBitmap> m_pDIBitmap; + bool m_bNamedImage; + int32_t m_iImageXDpi; + int32_t m_iImageYDpi; +}; + +bool SplitDateTime(const WideString& wsDateTime, + WideString& wsDate, + WideString& wsTime) { + wsDate = L""; + wsTime = L""; + if (wsDateTime.IsEmpty()) + return false; + + auto nSplitIndex = wsDateTime.Find('T'); + if (!nSplitIndex.has_value()) + nSplitIndex = wsDateTime.Find(' '); + if (!nSplitIndex.has_value()) + return false; + + wsDate = wsDateTime.Left(nSplitIndex.value()); + if (!wsDate.IsEmpty()) { + if (!std::any_of(wsDate.begin(), wsDate.end(), std::iswdigit)) + return false; + } + wsTime = wsDateTime.Right(wsDateTime.GetLength() - nSplitIndex.value() - 1); + if (!wsTime.IsEmpty()) { + if (!std::any_of(wsTime.begin(), wsTime.end(), std::iswdigit)) + return false; + } + return true; +} + std::vector<CXFA_Node*> NodesSortedByDocumentIdx( const std::set<CXFA_Node*>& rgNodeSet) { if (rgNodeSet.empty()) @@ -360,7 +662,8 @@ CXFA_Node::CXFA_Node(CXFA_Document* pDoc, m_ePacket(ePacket), m_uNodeFlags(XFA_NodeFlag_None), m_dwNameHash(0), - m_pAuxNode(nullptr) { + m_pAuxNode(nullptr), + m_pNode(this) { ASSERT(m_pDocument); } @@ -735,7 +1038,7 @@ CXFA_Node* CXFA_Node::GetContainerNode() { return nullptr; if (eType == XFA_Element::Field) { - CXFA_WidgetAcc* pFieldWidgetAcc = GetWidgetAcc(); + CXFA_Node* pFieldWidgetAcc = GetWidgetAcc(); if (pFieldWidgetAcc && pFieldWidgetAcc->IsChoiceListMultiSelect()) return nullptr; @@ -1829,11 +2132,6 @@ CXFA_Node* CXFA_Node::GetExclGroupIfExists() { return pExcl; } -void CXFA_Node::SetWidgetReady() { - acc_ = pdfium::MakeUnique<CXFA_WidgetAcc>(AsNode()); - is_widget_ready_ = true; -} - int32_t CXFA_Node::ProcessEvent(CXFA_FFDocView* docView, XFA_AttributeEnum iActivity, CXFA_EventParam* pEventParam) { @@ -2457,3 +2755,2134 @@ std::vector<CXFA_Event*> CXFA_Node::GetEventByActivity( } return events; } + +void CXFA_Node::ResetData() { + WideString wsValue; + switch (m_pNode->GetUIType()) { + case XFA_Element::ImageEdit: { + CXFA_Value* imageValue = m_pNode->GetDefaultValueIfExists(); + CXFA_Image* image = imageValue ? imageValue->GetImageIfExists() : nullptr; + WideString wsContentType, wsHref; + if (image) { + wsValue = image->GetContent(); + wsContentType = image->GetContentType(); + wsHref = image->GetHref(); + } + SetImageEdit(wsContentType, wsHref, wsValue); + break; + } + case XFA_Element::ExclGroup: { + CXFA_Node* pNextChild = m_pNode->GetFirstContainerChild(); + while (pNextChild) { + CXFA_Node* pChild = pNextChild; + if (!pChild->IsWidgetReady()) + continue; + + CXFA_Node* pAcc = pChild->GetWidgetAcc(); + bool done = false; + if (wsValue.IsEmpty()) { + CXFA_Value* defValue = pAcc->GetNode()->GetDefaultValueIfExists(); + if (defValue) { + wsValue = defValue->GetChildValueContent(); + SetValue(XFA_VALUEPICTURE_Raw, wsValue); + pAcc->SetValue(XFA_VALUEPICTURE_Raw, wsValue); + done = true; + } + } + if (!done) { + CXFA_Items* pItems = + pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItems) + continue; + + WideString itemText; + if (pItems->CountChildren(XFA_Element::Unknown, false) > 1) { + itemText = + pItems->GetChild<CXFA_Node>(1, XFA_Element::Unknown, false) + ->JSObject() + ->GetContent(false); + } + pAcc->SetValue(XFA_VALUEPICTURE_Raw, itemText); + } + pNextChild = pChild->GetNextContainerSibling(); + } + break; + } + case XFA_Element::ChoiceList: + ClearAllSelections(); + default: { + CXFA_Value* defValue = m_pNode->GetDefaultValueIfExists(); + if (defValue) + wsValue = defValue->GetChildValueContent(); + + SetValue(XFA_VALUEPICTURE_Raw, wsValue); + break; + } + } +} + +void CXFA_Node::SetImageEdit(const WideString& wsContentType, + const WideString& wsHref, + const WideString& wsData) { + CXFA_Value* formValue = m_pNode->GetFormValueIfExists(); + CXFA_Image* image = formValue ? formValue->GetImageIfExists() : nullptr; + if (image) { + image->SetContentType(WideString(wsContentType)); + image->SetHref(wsHref); + } + + m_pNode->JSObject()->SetContent(wsData, GetFormatDataValue(wsData), true, + false, true); + + CXFA_Node* pBind = m_pNode->GetBindData(); + if (!pBind) { + if (image) + image->SetTransferEncoding(XFA_AttributeEnum::Base64); + return; + } + pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false, + false); + CXFA_Node* pHrefNode = pBind->GetFirstChild(); + if (pHrefNode) { + pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref, false, false); + } else { + CFX_XMLNode* pXMLNode = pBind->GetXMLMappingNode(); + ASSERT(pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element); + static_cast<CFX_XMLElement*>(pXMLNode)->SetString(L"href", wsHref); + } +} + +CXFA_FFWidget* CXFA_Node::GetNextWidget(CXFA_FFWidget* pWidget) { + return static_cast<CXFA_FFWidget*>(pWidget->GetNext()); +} + +void CXFA_Node::UpdateUIDisplay(CXFA_FFDocView* docView, + CXFA_FFWidget* pExcept) { + CXFA_FFWidget* pWidget = docView->GetWidgetForNode(m_pNode); + for (; pWidget; pWidget = GetNextWidget(pWidget)) { + if (pWidget == pExcept || !pWidget->IsLoaded() || + (m_pNode->GetUIType() != XFA_Element::CheckButton && + pWidget->IsFocused())) { + continue; + } + pWidget->UpdateFWLData(); + pWidget->AddInvalidateRect(); + } +} + +void CXFA_Node::CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF& szCap) { + CXFA_Caption* caption = m_pNode->GetCaptionIfExists(); + if (!caption || !caption->IsVisible()) + return; + + LoadCaption(doc); + + XFA_Element eUIType = m_pNode->GetUIType(); + XFA_AttributeEnum iCapPlacement = caption->GetPlacementType(); + float fCapReserve = caption->GetReserve(); + const bool bVert = iCapPlacement == XFA_AttributeEnum::Top || + iCapPlacement == XFA_AttributeEnum::Bottom; + const bool bReserveExit = fCapReserve > 0.01; + CXFA_TextLayout* pCapTextLayout = + static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get()) + ->m_pCapTextLayout.get(); + if (pCapTextLayout) { + if (!bVert && eUIType != XFA_Element::Button) + szCap.width = fCapReserve; + + CFX_SizeF minSize; + szCap = pCapTextLayout->CalcSize(minSize, szCap); + if (bReserveExit) + bVert ? szCap.height = fCapReserve : szCap.width = fCapReserve; + } else { + float fFontSize = 10.0f; + CXFA_Font* font = caption->GetFontIfExists(); + if (font) { + fFontSize = font->GetFontSize(); + } else { + CXFA_Font* widgetfont = m_pNode->GetFontIfExists(); + if (widgetfont) + fFontSize = widgetfont->GetFontSize(); + } + + if (bVert) { + szCap.height = fCapReserve > 0 ? fCapReserve : fFontSize; + } else { + szCap.width = fCapReserve > 0 ? fCapReserve : 0; + szCap.height = fFontSize; + } + } + + CXFA_Margin* captionMargin = caption->GetMarginIfExists(); + if (!captionMargin) + return; + + float fLeftInset = captionMargin->GetLeftInset(); + float fTopInset = captionMargin->GetTopInset(); + float fRightInset = captionMargin->GetRightInset(); + float fBottomInset = captionMargin->GetBottomInset(); + if (bReserveExit) { + bVert ? (szCap.width += fLeftInset + fRightInset) + : (szCap.height += fTopInset + fBottomInset); + } else { + szCap.width += fLeftInset + fRightInset; + szCap.height += fTopInset + fBottomInset; + } +} + +bool CXFA_Node::CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + CFX_SizeF szCap; + CalcCaptionSize(doc, szCap); + + CFX_RectF rtUIMargin = m_pNode->GetUIMargin(); + size.width += rtUIMargin.left + rtUIMargin.width; + size.height += rtUIMargin.top + rtUIMargin.height; + if (szCap.width > 0 && szCap.height > 0) { + CXFA_Caption* caption = m_pNode->GetCaptionIfExists(); + XFA_AttributeEnum placement = caption ? caption->GetPlacementType() + : CXFA_Caption::kDefaultPlacementType; + switch (placement) { + case XFA_AttributeEnum::Left: + case XFA_AttributeEnum::Right: + case XFA_AttributeEnum::Inline: { + size.width += szCap.width; + size.height = std::max(size.height, szCap.height); + } break; + case XFA_AttributeEnum::Top: + case XFA_AttributeEnum::Bottom: { + size.height += szCap.height; + size.width = std::max(size.width, szCap.width); + } + default: + break; + } + } + return CalculateWidgetAutoSize(size); +} + +bool CXFA_Node::CalculateWidgetAutoSize(CFX_SizeF& size) { + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) { + size.width += margin->GetLeftInset() + margin->GetRightInset(); + size.height += margin->GetTopInset() + margin->GetBottomInset(); + } + + CXFA_Para* para = m_pNode->GetParaIfExists(); + if (para) + size.width += para->GetMarginLeft() + para->GetTextIndent(); + + Optional<float> width = m_pNode->TryWidth(); + if (width) { + size.width = *width; + } else { + Optional<float> min = m_pNode->TryMinWidth(); + if (min) + size.width = std::max(size.width, *min); + + Optional<float> max = m_pNode->TryMaxWidth(); + if (max && *max > 0) + size.width = std::min(size.width, *max); + } + + Optional<float> height = m_pNode->TryHeight(); + if (height) { + size.height = *height; + } else { + Optional<float> min = m_pNode->TryMinHeight(); + if (min) + size.height = std::max(size.height, *min); + + Optional<float> max = m_pNode->TryMaxHeight(); + if (max && *max > 0) + size.height = std::min(size.height, *max); + } + return true; +} + +void CXFA_Node::CalculateTextContentSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + float fFontSize = m_pNode->GetFontSize(); + WideString wsText = GetValue(XFA_VALUEPICTURE_Display); + if (wsText.IsEmpty()) { + size.height += fFontSize; + return; + } + + wchar_t wcEnter = '\n'; + wchar_t wsLast = wsText[wsText.GetLength() - 1]; + if (wsLast == wcEnter) + wsText = wsText + wcEnter; + + CXFA_FieldLayoutData* layoutData = + static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get()); + if (!layoutData->m_pTextOut) { + layoutData->m_pTextOut = pdfium::MakeUnique<CFDE_TextOut>(); + CFDE_TextOut* pTextOut = layoutData->m_pTextOut.get(); + pTextOut->SetFont(GetFDEFont(doc)); + pTextOut->SetFontSize(fFontSize); + pTextOut->SetLineBreakTolerance(fFontSize * 0.2f); + pTextOut->SetLineSpace(m_pNode->GetLineHeight()); + + FDE_TextStyle dwStyles; + dwStyles.last_line_height_ = true; + if (m_pNode->GetUIType() == XFA_Element::TextEdit && IsMultiLine()) + dwStyles.line_wrap_ = true; + + pTextOut->SetStyles(dwStyles); + } + layoutData->m_pTextOut->CalcLogicSize(wsText, size); +} + +bool CXFA_Node::CalculateTextEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + if (size.width > 0) { + CFX_SizeF szOrz = size; + CFX_SizeF szCap; + CalcCaptionSize(doc, szCap); + bool bCapExit = szCap.width > 0.01 && szCap.height > 0.01; + XFA_AttributeEnum iCapPlacement = XFA_AttributeEnum::Unknown; + if (bCapExit) { + CXFA_Caption* caption = m_pNode->GetCaptionIfExists(); + iCapPlacement = caption ? caption->GetPlacementType() + : CXFA_Caption::kDefaultPlacementType; + switch (iCapPlacement) { + case XFA_AttributeEnum::Left: + case XFA_AttributeEnum::Right: + case XFA_AttributeEnum::Inline: { + size.width -= szCap.width; + } + default: + break; + } + } + CFX_RectF rtUIMargin = m_pNode->GetUIMargin(); + size.width -= rtUIMargin.left + rtUIMargin.width; + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) + size.width -= margin->GetLeftInset() + margin->GetRightInset(); + + CalculateTextContentSize(doc, size); + size.height += rtUIMargin.top + rtUIMargin.height; + if (bCapExit) { + switch (iCapPlacement) { + case XFA_AttributeEnum::Left: + case XFA_AttributeEnum::Right: + case XFA_AttributeEnum::Inline: { + size.height = std::max(size.height, szCap.height); + } break; + case XFA_AttributeEnum::Top: + case XFA_AttributeEnum::Bottom: { + size.height += szCap.height; + } + default: + break; + } + } + size.width = szOrz.width; + return CalculateWidgetAutoSize(size); + } + CalculateTextContentSize(doc, size); + return CalculateFieldAutoSize(doc, size); +} + +bool CXFA_Node::CalculateCheckButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + float fCheckSize = GetCheckButtonSize(); + size = CFX_SizeF(fCheckSize, fCheckSize); + return CalculateFieldAutoSize(doc, size); +} + +bool CXFA_Node::CalculatePushButtonAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + CalcCaptionSize(doc, size); + return CalculateWidgetAutoSize(size); +} + +CFX_SizeF CXFA_Node::CalculateImageSize(float img_width, + float img_height, + float dpi_x, + float dpi_y) { + CFX_RectF rtImage(0, 0, XFA_UnitPx2Pt(img_width, dpi_x), + XFA_UnitPx2Pt(img_height, dpi_y)); + + CFX_RectF rtFit; + Optional<float> width = m_pNode->TryWidth(); + if (width) { + rtFit.width = *width; + GetWidthWithoutMargin(rtFit.width); + } else { + rtFit.width = rtImage.width; + } + + Optional<float> height = m_pNode->TryHeight(); + if (height) { + rtFit.height = *height; + GetHeightWithoutMargin(rtFit.height); + } else { + rtFit.height = rtImage.height; + } + + return rtFit.Size(); +} + +bool CXFA_Node::CalculateImageAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + if (!GetImageImage()) + LoadImageImage(doc); + + size.clear(); + RetainPtr<CFX_DIBitmap> pBitmap = GetImageImage(); + if (!pBitmap) + return CalculateWidgetAutoSize(size); + + int32_t iImageXDpi = 0; + int32_t iImageYDpi = 0; + GetImageDpi(iImageXDpi, iImageYDpi); + + size = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(), + iImageXDpi, iImageYDpi); + return CalculateWidgetAutoSize(size); +} + +bool CXFA_Node::CalculateImageEditAutoSize(CXFA_FFDoc* doc, CFX_SizeF& size) { + if (!GetImageEditImage()) + LoadImageEditImage(doc); + + size.clear(); + RetainPtr<CFX_DIBitmap> pBitmap = GetImageEditImage(); + if (!pBitmap) + return CalculateFieldAutoSize(doc, size); + + int32_t iImageXDpi = 0; + int32_t iImageYDpi = 0; + GetImageEditDpi(iImageXDpi, iImageYDpi); + + size = CalculateImageSize(pBitmap->GetWidth(), pBitmap->GetHeight(), + iImageXDpi, iImageYDpi); + return CalculateFieldAutoSize(doc, size); +} + +bool CXFA_Node::LoadImageImage(CXFA_FFDoc* doc) { + InitLayoutData(); + return static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get()) + ->LoadImageData(doc, this); +} + +bool CXFA_Node::LoadImageEditImage(CXFA_FFDoc* doc) { + InitLayoutData(); + return static_cast<CXFA_ImageEditData*>(m_pLayoutData.get()) + ->LoadImageData(doc, this); +} + +void CXFA_Node::GetImageDpi(int32_t& iImageXDpi, int32_t& iImageYDpi) { + CXFA_ImageLayoutData* pData = + static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get()); + iImageXDpi = pData->m_iImageXDpi; + iImageYDpi = pData->m_iImageYDpi; +} + +void CXFA_Node::GetImageEditDpi(int32_t& iImageXDpi, int32_t& iImageYDpi) { + CXFA_ImageEditData* pData = + static_cast<CXFA_ImageEditData*>(m_pLayoutData.get()); + iImageXDpi = pData->m_iImageXDpi; + iImageYDpi = pData->m_iImageYDpi; +} + +void CXFA_Node::LoadText(CXFA_FFDoc* doc) { + InitLayoutData(); + static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())->LoadText(doc, this); +} + +float CXFA_Node::CalculateWidgetAutoWidth(float fWidthCalc) { + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) + fWidthCalc += margin->GetLeftInset() + margin->GetRightInset(); + + Optional<float> min = m_pNode->TryMinWidth(); + if (min) + fWidthCalc = std::max(fWidthCalc, *min); + + Optional<float> max = m_pNode->TryMaxWidth(); + if (max && *max > 0) + fWidthCalc = std::min(fWidthCalc, *max); + + return fWidthCalc; +} + +float CXFA_Node::GetWidthWithoutMargin(float fWidthCalc) { + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) + fWidthCalc -= margin->GetLeftInset() + margin->GetRightInset(); + return fWidthCalc; +} + +float CXFA_Node::CalculateWidgetAutoHeight(float fHeightCalc) { + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) + fHeightCalc += margin->GetTopInset() + margin->GetBottomInset(); + + Optional<float> min = m_pNode->TryMinHeight(); + if (min) + fHeightCalc = std::max(fHeightCalc, *min); + + Optional<float> max = m_pNode->TryMaxHeight(); + if (max && *max > 0) + fHeightCalc = std::min(fHeightCalc, *max); + + return fHeightCalc; +} + +float CXFA_Node::GetHeightWithoutMargin(float fHeightCalc) { + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) + fHeightCalc -= margin->GetTopInset() + margin->GetBottomInset(); + return fHeightCalc; +} + +void CXFA_Node::StartWidgetLayout(CXFA_FFDoc* doc, + float& fCalcWidth, + float& fCalcHeight) { + InitLayoutData(); + + XFA_Element eUIType = m_pNode->GetUIType(); + if (eUIType == XFA_Element::Text) { + m_pLayoutData->m_fWidgetHeight = m_pNode->TryHeight().value_or(-1); + StartTextLayout(doc, fCalcWidth, fCalcHeight); + return; + } + if (fCalcWidth > 0 && fCalcHeight > 0) + return; + + m_pLayoutData->m_fWidgetHeight = -1; + float fWidth = 0; + if (fCalcWidth > 0 && fCalcHeight < 0) { + Optional<float> height = m_pNode->TryHeight(); + if (height) + fCalcHeight = *height; + else + CalculateAccWidthAndHeight(doc, eUIType, fCalcWidth, fCalcHeight); + + m_pLayoutData->m_fWidgetHeight = fCalcHeight; + return; + } + if (fCalcWidth < 0 && fCalcHeight < 0) { + Optional<float> height; + Optional<float> width = m_pNode->TryWidth(); + if (width) { + fWidth = *width; + + height = m_pNode->TryHeight(); + if (height) + fCalcHeight = *height; + } + if (!width || !height) + CalculateAccWidthAndHeight(doc, eUIType, fWidth, fCalcHeight); + + fCalcWidth = fWidth; + } + m_pLayoutData->m_fWidgetHeight = fCalcHeight; +} + +void CXFA_Node::CalculateAccWidthAndHeight(CXFA_FFDoc* doc, + XFA_Element eUIType, + float& fWidth, + float& fCalcHeight) { + CFX_SizeF sz(fWidth, m_pLayoutData->m_fWidgetHeight); + switch (eUIType) { + case XFA_Element::Barcode: + case XFA_Element::ChoiceList: + case XFA_Element::Signature: + CalculateFieldAutoSize(doc, sz); + break; + case XFA_Element::ImageEdit: + CalculateImageEditAutoSize(doc, sz); + break; + case XFA_Element::Button: + CalculatePushButtonAutoSize(doc, sz); + break; + case XFA_Element::CheckButton: + CalculateCheckButtonAutoSize(doc, sz); + break; + case XFA_Element::DateTimeEdit: + case XFA_Element::NumericEdit: + case XFA_Element::PasswordEdit: + case XFA_Element::TextEdit: + CalculateTextEditAutoSize(doc, sz); + break; + case XFA_Element::Image: + CalculateImageAutoSize(doc, sz); + break; + case XFA_Element::Arc: + case XFA_Element::Line: + case XFA_Element::Rectangle: + case XFA_Element::Subform: + case XFA_Element::ExclGroup: + CalculateWidgetAutoSize(sz); + break; + default: + break; + } + fWidth = sz.width; + m_pLayoutData->m_fWidgetHeight = sz.height; + fCalcHeight = sz.height; +} + +bool CXFA_Node::FindSplitPos(CXFA_FFDocView* docView, + int32_t iBlockIndex, + float& fCalcHeight) { + XFA_Element eUIType = m_pNode->GetUIType(); + if (eUIType == XFA_Element::Subform) + return false; + + if (eUIType != XFA_Element::Text && eUIType != XFA_Element::TextEdit && + eUIType != XFA_Element::NumericEdit && + eUIType != XFA_Element::PasswordEdit) { + fCalcHeight = 0; + return true; + } + + float fTopInset = 0; + float fBottomInset = 0; + if (iBlockIndex == 0) { + CXFA_Margin* margin = m_pNode->GetMarginIfExists(); + if (margin) { + fTopInset = margin->GetTopInset(); + fBottomInset = margin->GetBottomInset(); + } + + CFX_RectF rtUIMargin = m_pNode->GetUIMargin(); + fTopInset += rtUIMargin.top; + fBottomInset += rtUIMargin.width; + } + if (eUIType == XFA_Element::Text) { + float fHeight = fCalcHeight; + if (iBlockIndex == 0) { + fCalcHeight = fCalcHeight - fTopInset; + if (fCalcHeight < 0) + fCalcHeight = 0; + } + + CXFA_TextLayout* pTextLayout = + static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())->GetTextLayout(); + fCalcHeight = + pTextLayout->DoLayout(iBlockIndex, fCalcHeight, fCalcHeight, + m_pLayoutData->m_fWidgetHeight - fTopInset); + if (fCalcHeight != 0) { + if (iBlockIndex == 0) + fCalcHeight = fCalcHeight + fTopInset; + if (fabs(fHeight - fCalcHeight) < XFA_FLOAT_PERCISION) + return false; + } + return true; + } + XFA_AttributeEnum iCapPlacement = XFA_AttributeEnum::Unknown; + float fCapReserve = 0; + if (iBlockIndex == 0) { + CXFA_Caption* caption = m_pNode->GetCaptionIfExists(); + if (caption && !caption->IsHidden()) { + iCapPlacement = caption->GetPlacementType(); + fCapReserve = caption->GetReserve(); + } + if (iCapPlacement == XFA_AttributeEnum::Top && + fCalcHeight < fCapReserve + fTopInset) { + fCalcHeight = 0; + return true; + } + if (iCapPlacement == XFA_AttributeEnum::Bottom && + m_pLayoutData->m_fWidgetHeight - fCapReserve - fBottomInset) { + fCalcHeight = 0; + return true; + } + if (iCapPlacement != XFA_AttributeEnum::Top) + fCapReserve = 0; + } + CXFA_FieldLayoutData* pFieldData = + static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get()); + int32_t iLinesCount = 0; + float fHeight = m_pLayoutData->m_fWidgetHeight; + if (GetValue(XFA_VALUEPICTURE_Display).IsEmpty()) { + iLinesCount = 1; + } else { + if (!pFieldData->m_pTextOut) { + // TODO(dsinclair): Inline fWidth when the 2nd param of + // CalculateAccWidthAndHeight isn't a ref-param. + float fWidth = m_pNode->TryWidth().value_or(0); + CalculateAccWidthAndHeight(docView->GetDoc(), eUIType, fWidth, fHeight); + } + iLinesCount = pFieldData->m_pTextOut->GetTotalLines(); + } + std::vector<float>* pFieldArray = &pFieldData->m_FieldSplitArray; + int32_t iFieldSplitCount = pdfium::CollectionSize<int32_t>(*pFieldArray); + for (int32_t i = 0; i < iBlockIndex * 3; i += 3) { + iLinesCount -= (int32_t)(*pFieldArray)[i + 1]; + fHeight -= (*pFieldArray)[i + 2]; + } + if (iLinesCount == 0) + return false; + + float fLineHeight = m_pNode->GetLineHeight(); + float fFontSize = m_pNode->GetFontSize(); + float fTextHeight = iLinesCount * fLineHeight - fLineHeight + fFontSize; + float fSpaceAbove = 0; + float fStartOffset = 0; + if (fHeight > 0.1f && iBlockIndex == 0) { + fStartOffset = fTopInset; + fHeight -= (fTopInset + fBottomInset); + CXFA_Para* para = m_pNode->GetParaIfExists(); + if (para) { + fSpaceAbove = para->GetSpaceAbove(); + float fSpaceBelow = para->GetSpaceBelow(); + fHeight -= (fSpaceAbove + fSpaceBelow); + switch (para->GetVerticalAlign()) { + case XFA_AttributeEnum::Top: + fStartOffset += fSpaceAbove; + break; + case XFA_AttributeEnum::Middle: + fStartOffset += ((fHeight - fTextHeight) / 2 + fSpaceAbove); + break; + case XFA_AttributeEnum::Bottom: + fStartOffset += (fHeight - fTextHeight + fSpaceAbove); + break; + default: + NOTREACHED(); + break; + } + } + if (fStartOffset < 0.1f) + fStartOffset = 0; + } + for (int32_t i = iBlockIndex - 1; iBlockIndex > 0 && i < iBlockIndex; i++) { + fStartOffset = (*pFieldArray)[i * 3] - (*pFieldArray)[i * 3 + 2]; + if (fStartOffset < 0.1f) + fStartOffset = 0; + } + if (iFieldSplitCount / 3 == (iBlockIndex + 1)) + (*pFieldArray)[0] = fStartOffset; + else + pFieldArray->push_back(fStartOffset); + + XFA_VERSION version = docView->GetDoc()->GetXFADoc()->GetCurVersionMode(); + bool bCanSplitNoContent = false; + XFA_AttributeEnum eLayoutMode = GetNode() + ->GetParent() + ->JSObject() + ->TryEnum(XFA_Attribute::Layout, true) + .value_or(XFA_AttributeEnum::Position); + if ((eLayoutMode == XFA_AttributeEnum::Position || + eLayoutMode == XFA_AttributeEnum::Tb || + eLayoutMode == XFA_AttributeEnum::Row || + eLayoutMode == XFA_AttributeEnum::Table) && + version > XFA_VERSION_208) { + bCanSplitNoContent = true; + } + if ((eLayoutMode == XFA_AttributeEnum::Tb || + eLayoutMode == XFA_AttributeEnum::Row || + eLayoutMode == XFA_AttributeEnum::Table) && + version <= XFA_VERSION_208) { + if (fStartOffset < fCalcHeight) { + bCanSplitNoContent = true; + } else { + fCalcHeight = 0; + return true; + } + } + if (bCanSplitNoContent) { + if ((fCalcHeight - fTopInset - fSpaceAbove < fLineHeight)) { + fCalcHeight = 0; + return true; + } + if (fStartOffset + XFA_FLOAT_PERCISION >= fCalcHeight) { + if (iFieldSplitCount / 3 == (iBlockIndex + 1)) { + (*pFieldArray)[iBlockIndex * 3 + 1] = 0; + (*pFieldArray)[iBlockIndex * 3 + 2] = fCalcHeight; + } else { + pFieldArray->push_back(0); + pFieldArray->push_back(fCalcHeight); + } + return false; + } + if (fCalcHeight - fStartOffset < fLineHeight) { + fCalcHeight = fStartOffset; + if (iFieldSplitCount / 3 == (iBlockIndex + 1)) { + (*pFieldArray)[iBlockIndex * 3 + 1] = 0; + (*pFieldArray)[iBlockIndex * 3 + 2] = fCalcHeight; + } else { + pFieldArray->push_back(0); + pFieldArray->push_back(fCalcHeight); + } + return true; + } + float fTextNum = + fCalcHeight + XFA_FLOAT_PERCISION - fCapReserve - fStartOffset; + int32_t iLineNum = + (int32_t)((fTextNum + (fLineHeight - fFontSize)) / fLineHeight); + if (iLineNum >= iLinesCount) { + if (fCalcHeight - fStartOffset - fTextHeight >= fFontSize) { + if (iFieldSplitCount / 3 == (iBlockIndex + 1)) { + (*pFieldArray)[iBlockIndex * 3 + 1] = (float)iLinesCount; + (*pFieldArray)[iBlockIndex * 3 + 2] = fCalcHeight; + } else { + pFieldArray->push_back((float)iLinesCount); + pFieldArray->push_back(fCalcHeight); + } + return false; + } + if (fHeight - fStartOffset - fTextHeight < fFontSize) { + iLineNum -= 1; + if (iLineNum == 0) { + fCalcHeight = 0; + return true; + } + } else { + iLineNum = (int32_t)(fTextNum / fLineHeight); + } + } + if (iLineNum > 0) { + float fSplitHeight = iLineNum * fLineHeight + fCapReserve + fStartOffset; + if (iFieldSplitCount / 3 == (iBlockIndex + 1)) { + (*pFieldArray)[iBlockIndex * 3 + 1] = (float)iLineNum; + (*pFieldArray)[iBlockIndex * 3 + 2] = fSplitHeight; + } else { + pFieldArray->push_back((float)iLineNum); + pFieldArray->push_back(fSplitHeight); + } + if (fabs(fSplitHeight - fCalcHeight) < XFA_FLOAT_PERCISION) + return false; + + fCalcHeight = fSplitHeight; + return true; + } + } + fCalcHeight = 0; + return true; +} + +void CXFA_Node::InitLayoutData() { + if (m_pLayoutData) + return; + + switch (m_pNode->GetUIType()) { + case XFA_Element::Text: + m_pLayoutData = pdfium::MakeUnique<CXFA_TextLayoutData>(); + return; + case XFA_Element::TextEdit: + m_pLayoutData = pdfium::MakeUnique<CXFA_TextEditData>(); + return; + case XFA_Element::Image: + m_pLayoutData = pdfium::MakeUnique<CXFA_ImageLayoutData>(); + return; + case XFA_Element::ImageEdit: + m_pLayoutData = pdfium::MakeUnique<CXFA_ImageEditData>(); + return; + default: + break; + } + if (m_pNode && m_pNode->GetElementType() == XFA_Element::Field) { + m_pLayoutData = pdfium::MakeUnique<CXFA_FieldLayoutData>(); + return; + } + m_pLayoutData = pdfium::MakeUnique<CXFA_WidgetLayoutData>(); +} + +void CXFA_Node::StartTextLayout(CXFA_FFDoc* doc, + float& fCalcWidth, + float& fCalcHeight) { + LoadText(doc); + + CXFA_TextLayout* pTextLayout = + static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get())->GetTextLayout(); + float fTextHeight = 0; + if (fCalcWidth > 0 && fCalcHeight > 0) { + float fWidth = GetWidthWithoutMargin(fCalcWidth); + pTextLayout->StartLayout(fWidth); + fTextHeight = fCalcHeight; + fTextHeight = GetHeightWithoutMargin(fTextHeight); + pTextLayout->DoLayout(0, fTextHeight, -1, fTextHeight); + return; + } + if (fCalcWidth > 0 && fCalcHeight < 0) { + float fWidth = GetWidthWithoutMargin(fCalcWidth); + pTextLayout->StartLayout(fWidth); + } + + if (fCalcWidth < 0 && fCalcHeight < 0) { + Optional<float> width = m_pNode->TryWidth(); + if (width) { + pTextLayout->StartLayout(GetWidthWithoutMargin(*width)); + fCalcWidth = *width; + } else { + float fMaxWidth = CalculateWidgetAutoWidth(pTextLayout->StartLayout(-1)); + pTextLayout->StartLayout(GetWidthWithoutMargin(fMaxWidth)); + fCalcWidth = fMaxWidth; + } + } + + if (m_pLayoutData->m_fWidgetHeight < 0) { + m_pLayoutData->m_fWidgetHeight = pTextLayout->GetLayoutHeight(); + m_pLayoutData->m_fWidgetHeight = + CalculateWidgetAutoHeight(m_pLayoutData->m_fWidgetHeight); + } + fTextHeight = m_pLayoutData->m_fWidgetHeight; + fTextHeight = GetHeightWithoutMargin(fTextHeight); + pTextLayout->DoLayout(0, fTextHeight, -1, fTextHeight); + fCalcHeight = m_pLayoutData->m_fWidgetHeight; +} + +bool CXFA_Node::LoadCaption(CXFA_FFDoc* doc) { + InitLayoutData(); + return static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get()) + ->LoadCaption(doc, this); +} + +CXFA_TextLayout* CXFA_Node::GetCaptionTextLayout() { + return m_pLayoutData ? static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get()) + ->m_pCapTextLayout.get() + : nullptr; +} + +CXFA_TextLayout* CXFA_Node::GetTextLayout() { + return m_pLayoutData ? static_cast<CXFA_TextLayoutData*>(m_pLayoutData.get()) + ->GetTextLayout() + : nullptr; +} + +RetainPtr<CFX_DIBitmap> CXFA_Node::GetImageImage() { + return m_pLayoutData ? static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get()) + ->m_pDIBitmap + : nullptr; +} + +RetainPtr<CFX_DIBitmap> CXFA_Node::GetImageEditImage() { + return m_pLayoutData ? static_cast<CXFA_ImageEditData*>(m_pLayoutData.get()) + ->m_pDIBitmap + : nullptr; +} + +void CXFA_Node::SetImageImage(const RetainPtr<CFX_DIBitmap>& newImage) { + CXFA_ImageLayoutData* pData = + static_cast<CXFA_ImageLayoutData*>(m_pLayoutData.get()); + if (pData->m_pDIBitmap != newImage) + pData->m_pDIBitmap = newImage; +} + +void CXFA_Node::SetImageEditImage(const RetainPtr<CFX_DIBitmap>& newImage) { + CXFA_ImageEditData* pData = + static_cast<CXFA_ImageEditData*>(m_pLayoutData.get()); + if (pData->m_pDIBitmap != newImage) + pData->m_pDIBitmap = newImage; +} + +RetainPtr<CFGAS_GEFont> CXFA_Node::GetFDEFont(CXFA_FFDoc* doc) { + WideString wsFontName = L"Courier"; + uint32_t dwFontStyle = 0; + CXFA_Font* font = m_pNode->GetFontIfExists(); + if (font) { + if (font->IsBold()) + dwFontStyle |= FXFONT_BOLD; + if (font->IsItalic()) + dwFontStyle |= FXFONT_ITALIC; + + wsFontName = font->GetTypeface(); + } + return doc->GetApp()->GetXFAFontMgr()->GetFont(doc, wsFontName.AsStringView(), + dwFontStyle); +} + +bool CXFA_Node::IsOpenAccess() const { + return m_pNode && m_pNode->IsOpenAccess(); +} + +XFA_AttributeEnum CXFA_Node::GetButtonHighlight() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) + return pUIChild->JSObject()->GetEnum(XFA_Attribute::Highlight); + return XFA_AttributeEnum::Inverted; +} + +bool CXFA_Node::HasButtonRollover() const { + CXFA_Items* pItems = + m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItems) + return false; + + for (CXFA_Node* pText = pItems->GetFirstChild(); pText; + pText = pText->GetNextSibling()) { + if (pText->JSObject()->GetCData(XFA_Attribute::Name) == L"rollover") + return !pText->JSObject()->GetContent(false).IsEmpty(); + } + return false; +} + +bool CXFA_Node::HasButtonDown() const { + CXFA_Items* pItems = + m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItems) + return false; + + for (CXFA_Node* pText = pItems->GetFirstChild(); pText; + pText = pText->GetNextSibling()) { + if (pText->JSObject()->GetCData(XFA_Attribute::Name) == L"down") + return !pText->JSObject()->GetContent(false).IsEmpty(); + } + return false; +} + +bool CXFA_Node::IsCheckButtonRound() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) + return pUIChild->JSObject()->GetEnum(XFA_Attribute::Shape) == + XFA_AttributeEnum::Round; + return false; +} + +XFA_AttributeEnum CXFA_Node::GetCheckButtonMark() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) + return pUIChild->JSObject()->GetEnum(XFA_Attribute::Mark); + return XFA_AttributeEnum::Default; +} + +bool CXFA_Node::IsRadioButton() { + CXFA_Node* pParent = m_pNode->GetParent(); + return pParent && pParent->GetElementType() == XFA_Element::ExclGroup; +} + +float CXFA_Node::GetCheckButtonSize() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) { + return pUIChild->JSObject() + ->GetMeasure(XFA_Attribute::Size) + .ToUnit(XFA_Unit::Pt); + } + return CXFA_Measurement(10, XFA_Unit::Pt).ToUnit(XFA_Unit::Pt); +} + +bool CXFA_Node::IsAllowNeutral() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + return pUIChild && + pUIChild->JSObject()->GetBoolean(XFA_Attribute::AllowNeutral); +} + +XFA_CHECKSTATE CXFA_Node::GetCheckState() { + WideString wsValue = m_pNode->GetRawValue(); + if (wsValue.IsEmpty()) + return XFA_CHECKSTATE_Off; + + auto* pItems = m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItems) + return XFA_CHECKSTATE_Off; + + CXFA_Node* pText = pItems->GetFirstChild(); + int32_t i = 0; + while (pText) { + Optional<WideString> wsContent = pText->JSObject()->TryContent(false, true); + if (wsContent && *wsContent == wsValue) + return static_cast<XFA_CHECKSTATE>(i); + + i++; + pText = pText->GetNextSibling(); + } + return XFA_CHECKSTATE_Off; +} + +void CXFA_Node::SetCheckState(XFA_CHECKSTATE eCheckState, bool bNotify) { + CXFA_Node* node = m_pNode->GetExclGroupIfExists(); + if (!node) { + CXFA_Items* pItems = + m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItems) + return; + + int32_t i = -1; + CXFA_Node* pText = pItems->GetFirstChild(); + WideString wsContent; + while (pText) { + i++; + if (i == eCheckState) { + wsContent = pText->JSObject()->GetContent(false); + break; + } + pText = pText->GetNextSibling(); + } + m_pNode->SyncValue(wsContent, bNotify); + + return; + } + + WideString wsValue; + if (eCheckState != XFA_CHECKSTATE_Off) { + if (CXFA_Items* pItems = + m_pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false)) { + CXFA_Node* pText = pItems->GetFirstChild(); + if (pText) + wsValue = pText->JSObject()->GetContent(false); + } + } + CXFA_Node* pChild = node->GetFirstChild(); + for (; pChild; pChild = pChild->GetNextSibling()) { + if (pChild->GetElementType() != XFA_Element::Field) + continue; + + CXFA_Items* pItem = + pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItem) + continue; + + CXFA_Node* pItemchild = pItem->GetFirstChild(); + if (!pItemchild) + continue; + + WideString text = pItemchild->JSObject()->GetContent(false); + WideString wsChildValue = text; + if (wsValue != text) { + pItemchild = pItemchild->GetNextSibling(); + if (pItemchild) + wsChildValue = pItemchild->JSObject()->GetContent(false); + else + wsChildValue.clear(); + } + pChild->SyncValue(wsChildValue, bNotify); + } + node->SyncValue(wsValue, bNotify); +} + +CXFA_Node* CXFA_Node::GetSelectedMember() { + CXFA_Node* pSelectedMember = nullptr; + WideString wsState = m_pNode->GetRawValue(); + if (wsState.IsEmpty()) + return pSelectedMember; + + for (CXFA_Node* pNode = ToNode(m_pNode->GetFirstChild()); pNode; + pNode = pNode->GetNextSibling()) { + if (pNode->GetCheckState() == XFA_CHECKSTATE_On) { + pSelectedMember = pNode; + break; + } + } + return pSelectedMember; +} + +CXFA_Node* CXFA_Node::SetSelectedMember(const WideStringView& wsName, + bool bNotify) { + uint32_t nameHash = FX_HashCode_GetW(wsName, false); + for (CXFA_Node* pNode = ToNode(m_pNode->GetFirstChild()); pNode; + pNode = pNode->GetNextSibling()) { + if (pNode->GetNameHash() == nameHash) { + pNode->SetCheckState(XFA_CHECKSTATE_On, bNotify); + return pNode; + } + } + return nullptr; +} + +void CXFA_Node::SetSelectedMemberByValue(const WideStringView& wsValue, + bool bNotify, + bool bScriptModify, + bool bSyncData) { + WideString wsExclGroup; + for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode; + pNode = pNode->GetNextSibling()) { + if (pNode->GetElementType() != XFA_Element::Field) + continue; + + CXFA_Items* pItem = + pNode->GetChild<CXFA_Items>(0, XFA_Element::Items, false); + if (!pItem) + continue; + + CXFA_Node* pItemchild = pItem->GetFirstChild(); + if (!pItemchild) + continue; + + WideString wsChildValue = pItemchild->JSObject()->GetContent(false); + if (wsValue != wsChildValue) { + pItemchild = pItemchild->GetNextSibling(); + if (pItemchild) + wsChildValue = pItemchild->JSObject()->GetContent(false); + else + wsChildValue.clear(); + } else { + wsExclGroup = wsValue; + } + pNode->JSObject()->SetContent(wsChildValue, wsChildValue, bNotify, + bScriptModify, false); + } + m_pNode->JSObject()->SetContent(wsExclGroup, wsExclGroup, bNotify, + bScriptModify, bSyncData); +} + +CXFA_Node* CXFA_Node::GetExclGroupFirstMember() { + CXFA_Node* pExcl = GetNode(); + if (!pExcl) + return nullptr; + + CXFA_Node* pNode = pExcl->GetFirstChild(); + while (pNode) { + if (pNode->GetElementType() == XFA_Element::Field) + return pNode; + + pNode = pNode->GetNextSibling(); + } + return nullptr; +} +CXFA_Node* CXFA_Node::GetExclGroupNextMember(CXFA_Node* pNode) { + if (!pNode) + return nullptr; + + CXFA_Node* pNodeField = pNode->GetNextSibling(); + while (pNodeField) { + if (pNodeField->GetElementType() == XFA_Element::Field) + return pNodeField; + + pNodeField = pNodeField->GetNextSibling(); + } + return nullptr; +} + +bool CXFA_Node::IsChoiceListCommitOnSelect() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) { + return pUIChild->JSObject()->GetEnum(XFA_Attribute::CommitOn) == + XFA_AttributeEnum::Select; + } + return true; +} + +bool CXFA_Node::IsChoiceListAllowTextEntry() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::TextEntry); +} + +bool CXFA_Node::IsChoiceListMultiSelect() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) { + return pUIChild->JSObject()->GetEnum(XFA_Attribute::Open) == + XFA_AttributeEnum::MultiSelect; + } + return false; +} + +bool CXFA_Node::IsListBox() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (!pUIChild) + return false; + + XFA_AttributeEnum attr = pUIChild->JSObject()->GetEnum(XFA_Attribute::Open); + return attr == XFA_AttributeEnum::Always || + attr == XFA_AttributeEnum::MultiSelect; +} + +int32_t CXFA_Node::CountChoiceListItems(bool bSaveValue) { + std::vector<CXFA_Node*> pItems; + int32_t iCount = 0; + for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode; + pNode = pNode->GetNextSibling()) { + if (pNode->GetElementType() != XFA_Element::Items) + continue; + iCount++; + pItems.push_back(pNode); + if (iCount == 2) + break; + } + if (iCount == 0) + return 0; + + CXFA_Node* pItem = pItems[0]; + if (iCount > 1) { + bool bItemOneHasSave = + pItems[0]->JSObject()->GetBoolean(XFA_Attribute::Save); + bool bItemTwoHasSave = + pItems[1]->JSObject()->GetBoolean(XFA_Attribute::Save); + if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave) + pItem = pItems[1]; + } + return pItem->CountChildren(XFA_Element::Unknown, false); +} + +Optional<WideString> CXFA_Node::GetChoiceListItem(int32_t nIndex, + bool bSaveValue) { + std::vector<CXFA_Node*> pItemsArray; + int32_t iCount = 0; + for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode; + pNode = pNode->GetNextSibling()) { + if (pNode->GetElementType() != XFA_Element::Items) + continue; + + ++iCount; + pItemsArray.push_back(pNode); + if (iCount == 2) + break; + } + if (iCount == 0) + return {}; + + CXFA_Node* pItems = pItemsArray[0]; + if (iCount > 1) { + bool bItemOneHasSave = + pItemsArray[0]->JSObject()->GetBoolean(XFA_Attribute::Save); + bool bItemTwoHasSave = + pItemsArray[1]->JSObject()->GetBoolean(XFA_Attribute::Save); + if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave) + pItems = pItemsArray[1]; + } + if (!pItems) + return {}; + + CXFA_Node* pItem = + pItems->GetChild<CXFA_Node>(nIndex, XFA_Element::Unknown, false); + if (pItem) + return {pItem->JSObject()->GetContent(false)}; + return {}; +} + +std::vector<WideString> CXFA_Node::GetChoiceListItems(bool bSaveValue) { + std::vector<CXFA_Node*> items; + for (CXFA_Node* pNode = m_pNode->GetFirstChild(); pNode && items.size() < 2; + pNode = pNode->GetNextSibling()) { + if (pNode->GetElementType() == XFA_Element::Items) + items.push_back(pNode); + } + if (items.empty()) + return std::vector<WideString>(); + + CXFA_Node* pItem = items.front(); + if (items.size() > 1) { + bool bItemOneHasSave = + items[0]->JSObject()->GetBoolean(XFA_Attribute::Save); + bool bItemTwoHasSave = + items[1]->JSObject()->GetBoolean(XFA_Attribute::Save); + if (bItemOneHasSave != bItemTwoHasSave && bSaveValue == bItemTwoHasSave) + pItem = items[1]; + } + + std::vector<WideString> wsTextArray; + for (CXFA_Node* pNode = pItem->GetFirstChild(); pNode; + pNode = pNode->GetNextSibling()) { + wsTextArray.emplace_back(pNode->JSObject()->GetContent(false)); + } + return wsTextArray; +} + +int32_t CXFA_Node::CountSelectedItems() { + std::vector<WideString> wsValueArray = GetSelectedItemsValue(); + if (IsListBox() || !IsChoiceListAllowTextEntry()) + return pdfium::CollectionSize<int32_t>(wsValueArray); + + int32_t iSelected = 0; + std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true); + for (const auto& value : wsValueArray) { + if (pdfium::ContainsValue(wsSaveTextArray, value)) + iSelected++; + } + return iSelected; +} + +int32_t CXFA_Node::GetSelectedItem(int32_t nIndex) { + std::vector<WideString> wsValueArray = GetSelectedItemsValue(); + if (!pdfium::IndexInBounds(wsValueArray, nIndex)) + return -1; + + std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true); + auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(), + wsValueArray[nIndex]); + return it != wsSaveTextArray.end() ? it - wsSaveTextArray.begin() : -1; +} + +std::vector<int32_t> CXFA_Node::GetSelectedItems() { + std::vector<int32_t> iSelArray; + std::vector<WideString> wsValueArray = GetSelectedItemsValue(); + std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true); + for (const auto& value : wsValueArray) { + auto it = std::find(wsSaveTextArray.begin(), wsSaveTextArray.end(), value); + if (it != wsSaveTextArray.end()) + iSelArray.push_back(it - wsSaveTextArray.begin()); + } + return iSelArray; +} + +std::vector<WideString> CXFA_Node::GetSelectedItemsValue() { + std::vector<WideString> wsSelTextArray; + WideString wsValue = m_pNode->GetRawValue(); + if (IsChoiceListMultiSelect()) { + if (!wsValue.IsEmpty()) { + size_t iStart = 0; + size_t iLength = wsValue.GetLength(); + auto iEnd = wsValue.Find(L'\n', iStart); + iEnd = (!iEnd.has_value()) ? iLength : iEnd; + while (iEnd >= iStart) { + wsSelTextArray.push_back(wsValue.Mid(iStart, iEnd.value() - iStart)); + iStart = iEnd.value() + 1; + if (iStart >= iLength) + break; + iEnd = wsValue.Find(L'\n', iStart); + if (!iEnd.has_value()) + wsSelTextArray.push_back(wsValue.Mid(iStart, iLength - iStart)); + } + } + } else { + wsSelTextArray.push_back(wsValue); + } + return wsSelTextArray; +} + +bool CXFA_Node::GetItemState(int32_t nIndex) { + std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true); + return pdfium::IndexInBounds(wsSaveTextArray, nIndex) && + pdfium::ContainsValue(GetSelectedItemsValue(), + wsSaveTextArray[nIndex]); +} + +void CXFA_Node::SetItemState(int32_t nIndex, + bool bSelected, + bool bNotify, + bool bScriptModify, + bool bSyncData) { + std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true); + if (!pdfium::IndexInBounds(wsSaveTextArray, nIndex)) + return; + + int32_t iSel = -1; + std::vector<WideString> wsValueArray = GetSelectedItemsValue(); + auto it = std::find(wsValueArray.begin(), wsValueArray.end(), + wsSaveTextArray[nIndex]); + if (it != wsValueArray.end()) + iSel = it - wsValueArray.begin(); + + if (IsChoiceListMultiSelect()) { + if (bSelected) { + if (iSel < 0) { + WideString wsValue = m_pNode->GetRawValue(); + if (!wsValue.IsEmpty()) { + wsValue += L"\n"; + } + wsValue += wsSaveTextArray[nIndex]; + m_pNode->JSObject()->SetContent(wsValue, wsValue, bNotify, + bScriptModify, bSyncData); + } + } else if (iSel >= 0) { + std::vector<int32_t> iSelArray = GetSelectedItems(); + auto it = std::find(iSelArray.begin(), iSelArray.end(), nIndex); + if (it != iSelArray.end()) + iSelArray.erase(it); + SetSelectedItems(iSelArray, bNotify, bScriptModify, bSyncData); + } + } else { + if (bSelected) { + if (iSel < 0) { + WideString wsSaveText = wsSaveTextArray[nIndex]; + m_pNode->JSObject()->SetContent(wsSaveText, + GetFormatDataValue(wsSaveText), bNotify, + bScriptModify, bSyncData); + } + } else if (iSel >= 0) { + m_pNode->JSObject()->SetContent(WideString(), WideString(), bNotify, + bScriptModify, bSyncData); + } + } +} + +void CXFA_Node::SetSelectedItems(const std::vector<int32_t>& iSelArray, + bool bNotify, + bool bScriptModify, + bool bSyncData) { + WideString wsValue; + int32_t iSize = pdfium::CollectionSize<int32_t>(iSelArray); + if (iSize >= 1) { + std::vector<WideString> wsSaveTextArray = GetChoiceListItems(true); + WideString wsItemValue; + for (int32_t i = 0; i < iSize; i++) { + wsItemValue = (iSize == 1) ? wsSaveTextArray[iSelArray[i]] + : wsSaveTextArray[iSelArray[i]] + L"\n"; + wsValue += wsItemValue; + } + } + WideString wsFormat(wsValue); + if (!IsChoiceListMultiSelect()) + wsFormat = GetFormatDataValue(wsValue); + + m_pNode->JSObject()->SetContent(wsValue, wsFormat, bNotify, bScriptModify, + bSyncData); +} + +void CXFA_Node::ClearAllSelections() { + CXFA_Node* pBind = m_pNode->GetBindData(); + if (!pBind || !IsChoiceListMultiSelect()) { + m_pNode->SyncValue(WideString(), false); + return; + } + + while (CXFA_Node* pChildNode = pBind->GetFirstChild()) + pBind->RemoveChild(pChildNode, true); +} + +void CXFA_Node::InsertItem(const WideString& wsLabel, + const WideString& wsValue, + bool bNotify) { + int32_t nIndex = -1; + WideString wsNewValue(wsValue); + if (wsNewValue.IsEmpty()) + wsNewValue = wsLabel; + + std::vector<CXFA_Node*> listitems; + for (CXFA_Node* pItem = m_pNode->GetFirstChild(); pItem; + pItem = pItem->GetNextSibling()) { + if (pItem->GetElementType() == XFA_Element::Items) + listitems.push_back(pItem); + } + if (listitems.empty()) { + CXFA_Node* pItems = m_pNode->CreateSamePacketNode(XFA_Element::Items); + m_pNode->InsertChild(-1, pItems); + InsertListTextItem(pItems, wsLabel, nIndex); + CXFA_Node* pSaveItems = m_pNode->CreateSamePacketNode(XFA_Element::Items); + m_pNode->InsertChild(-1, pSaveItems); + pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false); + InsertListTextItem(pSaveItems, wsNewValue, nIndex); + } else if (listitems.size() > 1) { + for (int32_t i = 0; i < 2; i++) { + CXFA_Node* pNode = listitems[i]; + bool bHasSave = pNode->JSObject()->GetBoolean(XFA_Attribute::Save); + if (bHasSave) + InsertListTextItem(pNode, wsNewValue, nIndex); + else + InsertListTextItem(pNode, wsLabel, nIndex); + } + } else { + CXFA_Node* pNode = listitems[0]; + pNode->JSObject()->SetBoolean(XFA_Attribute::Save, false, false); + pNode->JSObject()->SetEnum(XFA_Attribute::Presence, + XFA_AttributeEnum::Visible, false); + CXFA_Node* pSaveItems = m_pNode->CreateSamePacketNode(XFA_Element::Items); + m_pNode->InsertChild(-1, pSaveItems); + pSaveItems->JSObject()->SetBoolean(XFA_Attribute::Save, true, false); + pSaveItems->JSObject()->SetEnum(XFA_Attribute::Presence, + XFA_AttributeEnum::Hidden, false); + CXFA_Node* pListNode = pNode->GetFirstChild(); + int32_t i = 0; + while (pListNode) { + InsertListTextItem(pSaveItems, pListNode->JSObject()->GetContent(false), + i); + ++i; + + pListNode = pListNode->GetNextSibling(); + } + InsertListTextItem(pNode, wsLabel, nIndex); + InsertListTextItem(pSaveItems, wsNewValue, nIndex); + } + if (!bNotify) + return; + + m_pNode->GetDocument()->GetNotify()->OnWidgetListItemAdded( + m_pNode, wsLabel.c_str(), wsValue.c_str(), nIndex); +} + +void CXFA_Node::GetItemLabel(const WideStringView& wsValue, + WideString& wsLabel) { + int32_t iCount = 0; + std::vector<CXFA_Node*> listitems; + CXFA_Node* pItems = m_pNode->GetFirstChild(); + for (; pItems; pItems = pItems->GetNextSibling()) { + if (pItems->GetElementType() != XFA_Element::Items) + continue; + iCount++; + listitems.push_back(pItems); + } + + if (iCount <= 1) { + wsLabel = wsValue; + return; + } + + CXFA_Node* pLabelItems = listitems[0]; + bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save); + CXFA_Node* pSaveItems = nullptr; + if (bSave) { + pSaveItems = pLabelItems; + pLabelItems = listitems[1]; + } else { + pSaveItems = listitems[1]; + } + iCount = 0; + + int32_t iSearch = -1; + for (CXFA_Node* pChildItem = pSaveItems->GetFirstChild(); pChildItem; + pChildItem = pChildItem->GetNextSibling()) { + if (pChildItem->JSObject()->GetContent(false) == wsValue) { + iSearch = iCount; + break; + } + iCount++; + } + if (iSearch < 0) + return; + + CXFA_Node* pText = + pLabelItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false); + if (pText) + wsLabel = pText->JSObject()->GetContent(false); +} + +WideString CXFA_Node::GetItemValue(const WideStringView& wsLabel) { + int32_t iCount = 0; + std::vector<CXFA_Node*> listitems; + for (CXFA_Node* pItems = m_pNode->GetFirstChild(); pItems; + pItems = pItems->GetNextSibling()) { + if (pItems->GetElementType() != XFA_Element::Items) + continue; + iCount++; + listitems.push_back(pItems); + } + if (iCount <= 1) + return WideString(wsLabel); + + CXFA_Node* pLabelItems = listitems[0]; + bool bSave = pLabelItems->JSObject()->GetBoolean(XFA_Attribute::Save); + CXFA_Node* pSaveItems = nullptr; + if (bSave) { + pSaveItems = pLabelItems; + pLabelItems = listitems[1]; + } else { + pSaveItems = listitems[1]; + } + iCount = 0; + + int32_t iSearch = -1; + WideString wsContent; + CXFA_Node* pChildItem = pLabelItems->GetFirstChild(); + for (; pChildItem; pChildItem = pChildItem->GetNextSibling()) { + if (pChildItem->JSObject()->GetContent(false) == wsLabel) { + iSearch = iCount; + break; + } + iCount++; + } + if (iSearch < 0) + return L""; + + CXFA_Node* pText = + pSaveItems->GetChild<CXFA_Node>(iSearch, XFA_Element::Unknown, false); + return pText ? pText->JSObject()->GetContent(false) : L""; +} + +bool CXFA_Node::DeleteItem(int32_t nIndex, bool bNotify, bool bScriptModify) { + bool bSetValue = false; + CXFA_Node* pItems = m_pNode->GetFirstChild(); + for (; pItems; pItems = pItems->GetNextSibling()) { + if (pItems->GetElementType() != XFA_Element::Items) + continue; + + if (nIndex < 0) { + while (CXFA_Node* pNode = pItems->GetFirstChild()) { + pItems->RemoveChild(pNode, true); + } + } else { + if (!bSetValue && pItems->JSObject()->GetBoolean(XFA_Attribute::Save)) { + SetItemState(nIndex, false, true, bScriptModify, true); + bSetValue = true; + } + int32_t i = 0; + CXFA_Node* pNode = pItems->GetFirstChild(); + while (pNode) { + if (i == nIndex) { + pItems->RemoveChild(pNode, true); + break; + } + i++; + pNode = pNode->GetNextSibling(); + } + } + } + if (bNotify) + m_pNode->GetDocument()->GetNotify()->OnWidgetListItemRemoved(m_pNode, + nIndex); + return true; +} + +bool CXFA_Node::IsHorizontalScrollPolicyOff() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) { + return pUIChild->JSObject()->GetEnum(XFA_Attribute::HScrollPolicy) == + XFA_AttributeEnum::Off; + } + return false; +} + +bool CXFA_Node::IsVerticalScrollPolicyOff() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (pUIChild) { + return pUIChild->JSObject()->GetEnum(XFA_Attribute::VScrollPolicy) == + XFA_AttributeEnum::Off; + } + return false; +} + +Optional<int32_t> CXFA_Node::GetNumberOfCells() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + if (!pUIChild) + return {}; + if (CXFA_Comb* pNode = + pUIChild->GetChild<CXFA_Comb>(0, XFA_Element::Comb, false)) + return {pNode->JSObject()->GetInteger(XFA_Attribute::NumberOfCells)}; + return {}; +} + +WideString CXFA_Node::GetPasswordChar() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + return pUIChild ? pUIChild->JSObject()->GetCData(XFA_Attribute::PasswordChar) + : L"*"; +} + +bool CXFA_Node::IsMultiLine() { + CXFA_Node* pUIChild = m_pNode->GetUIChild(); + return pUIChild && pUIChild->JSObject()->GetBoolean(XFA_Attribute::MultiLine); +} + +std::pair<XFA_Element, int32_t> CXFA_Node::GetMaxChars() { + if (CXFA_Value* pNode = + m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false)) { + if (CXFA_Node* pChild = pNode->GetFirstChild()) { + switch (pChild->GetElementType()) { + case XFA_Element::Text: + return {XFA_Element::Text, + pChild->JSObject()->GetInteger(XFA_Attribute::MaxChars)}; + case XFA_Element::ExData: { + int32_t iMax = + pChild->JSObject()->GetInteger(XFA_Attribute::MaxLength); + return {XFA_Element::ExData, iMax < 0 ? 0 : iMax}; + } + default: + break; + } + } + } + return {XFA_Element::Unknown, 0}; +} + +int32_t CXFA_Node::GetFracDigits() { + CXFA_Value* pNode = + m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false); + if (!pNode) + return -1; + + CXFA_Decimal* pChild = + pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false); + if (!pChild) + return -1; + + return pChild->JSObject() + ->TryInteger(XFA_Attribute::FracDigits, true) + .value_or(-1); +} + +int32_t CXFA_Node::GetLeadDigits() { + CXFA_Value* pNode = + m_pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false); + if (!pNode) + return -1; + + CXFA_Decimal* pChild = + pNode->GetChild<CXFA_Decimal>(0, XFA_Element::Decimal, false); + if (!pChild) + return -1; + + return pChild->JSObject() + ->TryInteger(XFA_Attribute::LeadDigits, true) + .value_or(-1); +} + +bool CXFA_Node::SetValue(XFA_VALUEPICTURE eValueType, + const WideString& wsValue) { + if (wsValue.IsEmpty()) { + m_pNode->SyncValue(wsValue, true); + return true; + } + + m_pNode->SetPreNull(m_pNode->IsNull()); + m_pNode->SetIsNull(false); + + WideString wsNewText(wsValue); + WideString wsPicture = GetPictureContent(eValueType); + bool bValidate = true; + bool bSyncData = false; + CXFA_Node* pNode = m_pNode->GetUIChild(); + if (!pNode) + return true; + + XFA_Element eType = pNode->GetElementType(); + if (!wsPicture.IsEmpty()) { + CXFA_LocaleMgr* pLocalMgr = m_pNode->GetDocument()->GetLocalMgr(); + IFX_Locale* pLocale = m_pNode->GetLocale(); + CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode()); + bValidate = + widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture); + if (bValidate) { + widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsNewText, + wsPicture, pLocale, pLocalMgr); + wsNewText = widgetValue.GetValue(); + if (eType == XFA_Element::NumericEdit) + wsNewText = NumericLimit(wsNewText, GetLeadDigits(), GetFracDigits()); + + bSyncData = true; + } + } else { + if (eType == XFA_Element::NumericEdit) { + if (wsNewText != L"0") + wsNewText = NumericLimit(wsNewText, GetLeadDigits(), GetFracDigits()); + + bSyncData = true; + } + } + if (eType != XFA_Element::NumericEdit || bSyncData) + m_pNode->SyncValue(wsNewText, true); + + return bValidate; +} + +WideString CXFA_Node::GetPictureContent(XFA_VALUEPICTURE ePicture) { + if (ePicture == XFA_VALUEPICTURE_Raw) + return L""; + + CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode()); + switch (ePicture) { + case XFA_VALUEPICTURE_Display: { + if (CXFA_Format* pFormat = + m_pNode->GetChild<CXFA_Format>(0, XFA_Element::Format, false)) { + if (CXFA_Picture* pPicture = pFormat->GetChild<CXFA_Picture>( + 0, XFA_Element::Picture, false)) { + Optional<WideString> picture = + pPicture->JSObject()->TryContent(false, true); + if (picture) + return *picture; + } + } + + IFX_Locale* pLocale = m_pNode->GetLocale(); + if (!pLocale) + return L""; + + uint32_t dwType = widgetValue.GetType(); + switch (dwType) { + case XFA_VT_DATE: + return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium); + case XFA_VT_TIME: + return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium); + case XFA_VT_DATETIME: + return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium) + + L"T" + + pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Medium); + case XFA_VT_DECIMAL: + case XFA_VT_FLOAT: + default: + return L""; + } + } + case XFA_VALUEPICTURE_Edit: { + CXFA_Ui* pUI = m_pNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false); + if (pUI) { + if (CXFA_Picture* pPicture = + pUI->GetChild<CXFA_Picture>(0, XFA_Element::Picture, false)) { + Optional<WideString> picture = + pPicture->JSObject()->TryContent(false, true); + if (picture) + return *picture; + } + } + + IFX_Locale* pLocale = m_pNode->GetLocale(); + if (!pLocale) + return L""; + + uint32_t dwType = widgetValue.GetType(); + switch (dwType) { + case XFA_VT_DATE: + return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short); + case XFA_VT_TIME: + return pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short); + case XFA_VT_DATETIME: + return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Short) + + L"T" + + pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Short); + default: + return L""; + } + } + case XFA_VALUEPICTURE_DataBind: { + CXFA_Bind* bind = m_pNode->GetBindIfExists(); + if (bind) + return bind->GetPicture(); + break; + } + default: + break; + } + return L""; +} + +WideString CXFA_Node::GetValue(XFA_VALUEPICTURE eValueType) { + WideString wsValue = m_pNode->JSObject()->GetContent(false); + + if (eValueType == XFA_VALUEPICTURE_Display) + GetItemLabel(wsValue.AsStringView(), wsValue); + + WideString wsPicture = GetPictureContent(eValueType); + CXFA_Node* pNode = m_pNode->GetUIChild(); + if (!pNode) + return wsValue; + + switch (m_pNode->GetUIChild()->GetElementType()) { + case XFA_Element::ChoiceList: { + if (eValueType == XFA_VALUEPICTURE_Display) { + int32_t iSelItemIndex = GetSelectedItem(0); + if (iSelItemIndex >= 0) { + wsValue = GetChoiceListItem(iSelItemIndex, false).value_or(L""); + wsPicture.clear(); + } + } + } break; + case XFA_Element::NumericEdit: + if (eValueType != XFA_VALUEPICTURE_Raw && wsPicture.IsEmpty()) { + IFX_Locale* pLocale = m_pNode->GetLocale(); + if (eValueType == XFA_VALUEPICTURE_Display && pLocale) + wsValue = FormatNumStr(NormalizeNumStr(wsValue), pLocale); + } + break; + default: + break; + } + if (wsPicture.IsEmpty()) + return wsValue; + + if (IFX_Locale* pLocale = m_pNode->GetLocale()) { + CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode()); + CXFA_LocaleMgr* pLocalMgr = m_pNode->GetDocument()->GetLocalMgr(); + switch (widgetValue.GetType()) { + case XFA_VT_DATE: { + WideString wsDate, wsTime; + if (SplitDateTime(wsValue, wsDate, wsTime)) { + CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocalMgr); + if (date.FormatPatterns(wsValue, wsPicture, pLocale, eValueType)) + return wsValue; + } + break; + } + case XFA_VT_TIME: { + WideString wsDate, wsTime; + if (SplitDateTime(wsValue, wsDate, wsTime)) { + CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocalMgr); + if (time.FormatPatterns(wsValue, wsPicture, pLocale, eValueType)) + return wsValue; + } + break; + } + default: + break; + } + widgetValue.FormatPatterns(wsValue, wsPicture, pLocale, eValueType); + } + return wsValue; +} + +WideString CXFA_Node::GetNormalizeDataValue(const WideString& wsValue) { + if (wsValue.IsEmpty()) + return L""; + + WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind); + if (wsPicture.IsEmpty()) + return wsValue; + + ASSERT(GetNode()); + CXFA_LocaleMgr* pLocalMgr = GetNode()->GetDocument()->GetLocalMgr(); + IFX_Locale* pLocale = m_pNode->GetLocale(); + CXFA_LocaleValue widgetValue = XFA_GetLocaleValue(GetNode()); + if (widgetValue.ValidateValue(wsValue, wsPicture, pLocale, &wsPicture)) { + widgetValue = CXFA_LocaleValue(widgetValue.GetType(), wsValue, wsPicture, + pLocale, pLocalMgr); + return widgetValue.GetValue(); + } + return wsValue; +} + +WideString CXFA_Node::GetFormatDataValue(const WideString& wsValue) { + if (wsValue.IsEmpty()) + return L""; + + WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind); + if (wsPicture.IsEmpty()) + return wsValue; + + WideString wsFormattedValue = wsValue; + if (IFX_Locale* pLocale = m_pNode->GetLocale()) { + ASSERT(GetNode()); + CXFA_Value* pNodeValue = + GetNode()->GetChild<CXFA_Value>(0, XFA_Element::Value, false); + if (!pNodeValue) + return wsValue; + + CXFA_Node* pValueChild = pNodeValue->GetFirstChild(); + if (!pValueChild) + return wsValue; + + int32_t iVTType = XFA_VT_NULL; + switch (pValueChild->GetElementType()) { + case XFA_Element::Decimal: + iVTType = XFA_VT_DECIMAL; + break; + case XFA_Element::Float: + iVTType = XFA_VT_FLOAT; + break; + case XFA_Element::Date: + iVTType = XFA_VT_DATE; + break; + case XFA_Element::Time: + iVTType = XFA_VT_TIME; + break; + case XFA_Element::DateTime: + iVTType = XFA_VT_DATETIME; + break; + case XFA_Element::Boolean: + iVTType = XFA_VT_BOOLEAN; + break; + case XFA_Element::Integer: + iVTType = XFA_VT_INTEGER; + break; + case XFA_Element::Text: + iVTType = XFA_VT_TEXT; + break; + default: + iVTType = XFA_VT_NULL; + break; + } + CXFA_LocaleMgr* pLocalMgr = GetNode()->GetDocument()->GetLocalMgr(); + CXFA_LocaleValue widgetValue(iVTType, wsValue, pLocalMgr); + switch (widgetValue.GetType()) { + case XFA_VT_DATE: { + WideString wsDate, wsTime; + if (SplitDateTime(wsValue, wsDate, wsTime)) { + CXFA_LocaleValue date(XFA_VT_DATE, wsDate, pLocalMgr); + if (date.FormatPatterns(wsFormattedValue, wsPicture, pLocale, + XFA_VALUEPICTURE_DataBind)) { + return wsFormattedValue; + } + } + break; + } + case XFA_VT_TIME: { + WideString wsDate, wsTime; + if (SplitDateTime(wsValue, wsDate, wsTime)) { + CXFA_LocaleValue time(XFA_VT_TIME, wsTime, pLocalMgr); + if (time.FormatPatterns(wsFormattedValue, wsPicture, pLocale, + XFA_VALUEPICTURE_DataBind)) { + return wsFormattedValue; + } + } + break; + } + default: + break; + } + widgetValue.FormatPatterns(wsFormattedValue, wsPicture, pLocale, + XFA_VALUEPICTURE_DataBind); + } + return wsFormattedValue; +} + +WideString CXFA_Node::NormalizeNumStr(const WideString& wsValue) { + if (wsValue.IsEmpty()) + return L""; + + WideString wsOutput = wsValue; + wsOutput.TrimLeft('0'); + + if (!wsOutput.IsEmpty() && wsOutput.Contains('.') && GetFracDigits() != -1) { + wsOutput.TrimRight(L"0"); + wsOutput.TrimRight(L"."); + } + if (wsOutput.IsEmpty() || wsOutput[0] == '.') + wsOutput.InsertAtFront('0'); + + return wsOutput; +} + +WideString CXFA_Node::FormatNumStr(const WideString& wsValue, + IFX_Locale* pLocale) { + if (wsValue.IsEmpty()) + return L""; + + WideString wsSrcNum = wsValue; + WideString wsGroupSymbol = + pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping); + bool bNeg = false; + if (wsSrcNum[0] == '-') { + bNeg = true; + wsSrcNum.Delete(0, 1); + } + + auto dot_index = wsSrcNum.Find('.'); + dot_index = !dot_index.has_value() ? wsSrcNum.GetLength() : dot_index; + + if (dot_index.value() < 1) + return L""; + + size_t nPos = dot_index.value() % 3; + WideString wsOutput; + for (size_t i = 0; i < dot_index.value(); i++) { + if (i % 3 == nPos && i != 0) + wsOutput += wsGroupSymbol; + + wsOutput += wsSrcNum[i]; + } + if (dot_index.value() < wsSrcNum.GetLength()) { + wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal); + wsOutput += wsSrcNum.Right(wsSrcNum.GetLength() - dot_index.value() - 1); + } + if (bNeg) + return pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + wsOutput; + + return wsOutput; +} + +void CXFA_Node::InsertListTextItem(CXFA_Node* pItems, + const WideString& wsText, + int32_t nIndex) { + CXFA_Node* pText = pItems->CreateSamePacketNode(XFA_Element::Text); + pItems->InsertChild(nIndex, pText); + pText->JSObject()->SetContent(wsText, wsText, false, false, false); +} + +WideString CXFA_Node::NumericLimit(const WideString& wsValue, + int32_t iLead, + int32_t iTread) const { + if ((iLead == -1) && (iTread == -1)) + return wsValue; + + WideString wsRet; + int32_t iLead_ = 0, iTread_ = -1; + int32_t iCount = wsValue.GetLength(); + if (iCount == 0) + return wsValue; + + int32_t i = 0; + if (wsValue[i] == L'-') { + wsRet += L'-'; + i++; + } + for (; i < iCount; i++) { + wchar_t wc = wsValue[i]; + if (FXSYS_isDecimalDigit(wc)) { + if (iLead >= 0) { + iLead_++; + if (iLead_ > iLead) + return L"0"; + } else if (iTread_ >= 0) { + iTread_++; + if (iTread_ > iTread) { + if (iTread != -1) { + CFX_Decimal wsDeci = CFX_Decimal(wsValue.AsStringView()); + wsDeci.SetScale(iTread); + wsRet = wsDeci; + } + return wsRet; + } + } + } else if (wc == L'.') { + iTread_ = 0; + iLead = -1; + } + wsRet += wc; + } + return wsRet; +} |