| // Copyright 2014 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "xfa/fxfa/parser/cxfa_itemlayoutprocessor.h" | |
| #include <algorithm> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| #include "third_party/base/logging.h" | |
| #include "third_party/base/ptr_util.h" | |
| #include "third_party/base/stl_util.h" | |
| #include "xfa/fxfa/cxfa_ffnotify.h" | |
| #include "xfa/fxfa/parser/cxfa_containerlayoutitem.h" | |
| #include "xfa/fxfa/parser/cxfa_contentlayoutitem.h" | |
| #include "xfa/fxfa/parser/cxfa_document.h" | |
| #include "xfa/fxfa/parser/cxfa_layoutcontext.h" | |
| #include "xfa/fxfa/parser/cxfa_layoutpagemgr.h" | |
| #include "xfa/fxfa/parser/cxfa_localemgr.h" | |
| #include "xfa/fxfa/parser/cxfa_measurement.h" | |
| #include "xfa/fxfa/parser/cxfa_node.h" | |
| #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h" | |
| #include "xfa/fxfa/parser/cxfa_occurdata.h" | |
| #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h" | |
| #include "xfa/fxfa/parser/xfa_utils.h" | |
| namespace { | |
| std::vector<WideString> SeparateStringW(const wchar_t* pStr, | |
| int32_t iStrLen, | |
| wchar_t delimiter) { | |
| std::vector<WideString> ret; | |
| if (!pStr) | |
| return ret; | |
| if (iStrLen < 0) | |
| iStrLen = wcslen(pStr); | |
| const wchar_t* pToken = pStr; | |
| const wchar_t* pEnd = pStr + iStrLen; | |
| while (true) { | |
| if (pStr >= pEnd || delimiter == *pStr) { | |
| ret.push_back(WideString(pToken, pStr - pToken)); | |
| pToken = pStr + 1; | |
| if (pStr >= pEnd) | |
| break; | |
| } | |
| pStr++; | |
| } | |
| return ret; | |
| } | |
| void UpdateWidgetSize(CXFA_ContentLayoutItem* pLayoutItem, | |
| float* fWidth, | |
| float* fHeight) { | |
| CXFA_Node* pNode = pLayoutItem->m_pFormNode; | |
| switch (pNode->GetElementType()) { | |
| case XFA_Element::Subform: | |
| case XFA_Element::Area: | |
| case XFA_Element::ExclGroup: | |
| case XFA_Element::SubformSet: { | |
| if (*fWidth < -XFA_LAYOUT_FLOAT_PERCISION) | |
| *fWidth = pLayoutItem->m_sSize.width; | |
| if (*fHeight < -XFA_LAYOUT_FLOAT_PERCISION) | |
| *fHeight = pLayoutItem->m_sSize.height; | |
| break; | |
| } | |
| case XFA_Element::Draw: | |
| case XFA_Element::Field: { | |
| pNode->GetDocument()->GetNotify()->StartFieldDrawLayout(pNode, *fWidth, | |
| *fHeight); | |
| break; | |
| } | |
| default: | |
| NOTREACHED(); | |
| } | |
| } | |
| CFX_SizeF CalculateContainerSpecifiedSize(CXFA_Node* pFormNode, | |
| bool* bContainerWidthAutoSize, | |
| bool* bContainerHeightAutoSize) { | |
| *bContainerWidthAutoSize = true; | |
| *bContainerHeightAutoSize = true; | |
| XFA_Element eType = pFormNode->GetElementType(); | |
| CFX_SizeF containerSize; | |
| if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup) { | |
| pdfium::Optional<CXFA_Measurement> wValue = | |
| pFormNode->JSNode()->TryMeasure(XFA_Attribute::W, false); | |
| if (wValue && wValue->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) { | |
| containerSize.width = wValue->ToUnit(XFA_Unit::Pt); | |
| *bContainerWidthAutoSize = false; | |
| } | |
| pdfium::Optional<CXFA_Measurement> hValue = | |
| pFormNode->JSNode()->TryMeasure(XFA_Attribute::H, false); | |
| if (hValue && hValue->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) { | |
| containerSize.height = hValue->ToUnit(XFA_Unit::Pt); | |
| *bContainerHeightAutoSize = false; | |
| } | |
| } | |
| if (*bContainerWidthAutoSize && eType == XFA_Element::Subform) { | |
| pdfium::Optional<CXFA_Measurement> maxW = | |
| pFormNode->JSNode()->TryMeasure(XFA_Attribute::MaxW, false); | |
| if (maxW && maxW->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) { | |
| containerSize.width = maxW->ToUnit(XFA_Unit::Pt); | |
| *bContainerWidthAutoSize = false; | |
| } | |
| pdfium::Optional<CXFA_Measurement> maxH = | |
| pFormNode->JSNode()->TryMeasure(XFA_Attribute::MaxH, false); | |
| if (maxH && maxH->GetValue() > XFA_LAYOUT_FLOAT_PERCISION) { | |
| containerSize.height = maxH->ToUnit(XFA_Unit::Pt); | |
| *bContainerHeightAutoSize = false; | |
| } | |
| } | |
| return containerSize; | |
| } | |
| CFX_SizeF CalculateContainerComponentSizeFromContentSize( | |
| CXFA_Node* pFormNode, | |
| bool bContainerWidthAutoSize, | |
| float fContentCalculatedWidth, | |
| bool bContainerHeightAutoSize, | |
| float fContentCalculatedHeight, | |
| const CFX_SizeF& currentContainerSize) { | |
| CFX_SizeF componentSize = currentContainerSize; | |
| CXFA_Node* pMarginNode = pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| if (bContainerWidthAutoSize) { | |
| componentSize.width = fContentCalculatedWidth; | |
| if (pMarginNode) { | |
| pdfium::Optional<CXFA_Measurement> leftInset = | |
| pMarginNode->JSNode()->TryMeasure(XFA_Attribute::LeftInset, false); | |
| if (leftInset) | |
| componentSize.width += leftInset->ToUnit(XFA_Unit::Pt); | |
| pdfium::Optional<CXFA_Measurement> rightInset = | |
| pMarginNode->JSNode()->TryMeasure(XFA_Attribute::RightInset, false); | |
| if (rightInset) | |
| componentSize.width += rightInset->ToUnit(XFA_Unit::Pt); | |
| } | |
| } | |
| if (bContainerHeightAutoSize) { | |
| componentSize.height = fContentCalculatedHeight; | |
| if (pMarginNode) { | |
| pdfium::Optional<CXFA_Measurement> topInset = | |
| pMarginNode->JSNode()->TryMeasure(XFA_Attribute::TopInset, false); | |
| if (topInset) | |
| componentSize.height += topInset->ToUnit(XFA_Unit::Pt); | |
| pdfium::Optional<CXFA_Measurement> bottomInset = | |
| pMarginNode->JSNode()->TryMeasure(XFA_Attribute::BottomInset, false); | |
| if (bottomInset) | |
| componentSize.height += bottomInset->ToUnit(XFA_Unit::Pt); | |
| } | |
| } | |
| return componentSize; | |
| } | |
| void RelocateTableRowCells(CXFA_ContentLayoutItem* pLayoutRow, | |
| const std::vector<float>& rgSpecifiedColumnWidths, | |
| XFA_AttributeEnum eLayout) { | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| pLayoutRow->m_pFormNode, &bContainerWidthAutoSize, | |
| &bContainerHeightAutoSize); | |
| CXFA_Node* pMarginNode = | |
| pLayoutRow->m_pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fTopInset = 0; | |
| float fRightInset = 0; | |
| float fBottomInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::LeftInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fTopInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::TopInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::RightInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fBottomInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::BottomInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| float fContentWidthLimit = | |
| bContainerWidthAutoSize ? FLT_MAX | |
| : containerSize.width - fLeftInset - fRightInset; | |
| float fContentCurrentHeight = | |
| pLayoutRow->m_sSize.height - fTopInset - fBottomInset; | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| float fCurrentColX = 0; | |
| int32_t nCurrentColIdx = 0; | |
| bool bMetWholeRowCell = false; | |
| for (auto* pLayoutChild = | |
| static_cast<CXFA_ContentLayoutItem*>(pLayoutRow->m_pFirstChild); | |
| pLayoutChild; pLayoutChild = static_cast<CXFA_ContentLayoutItem*>( | |
| pLayoutChild->m_pNextSibling)) { | |
| int32_t nOriginalColSpan = | |
| pLayoutChild->m_pFormNode->JSNode()->GetInteger(XFA_Attribute::ColSpan); | |
| int32_t nColSpan = nOriginalColSpan; | |
| float fColSpanWidth = 0; | |
| if (nColSpan == -1 || | |
| nCurrentColIdx + nColSpan > | |
| pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths)) { | |
| nColSpan = pdfium::CollectionSize<int32_t>(rgSpecifiedColumnWidths) - | |
| nCurrentColIdx; | |
| } | |
| for (int32_t i = 0; i < nColSpan; i++) | |
| fColSpanWidth += rgSpecifiedColumnWidths[nCurrentColIdx + i]; | |
| if (nColSpan != nOriginalColSpan) { | |
| fColSpanWidth = | |
| bMetWholeRowCell ? 0 : std::max(fColSpanWidth, | |
| pLayoutChild->m_sSize.height); | |
| } | |
| if (nOriginalColSpan == -1) | |
| bMetWholeRowCell = true; | |
| pLayoutChild->m_sPos = CFX_PointF(fCurrentColX, 0); | |
| pLayoutChild->m_sSize.width = fColSpanWidth; | |
| if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutChild->m_pFormNode)) | |
| continue; | |
| fCurrentColX += fColSpanWidth; | |
| nCurrentColIdx += nColSpan; | |
| float fNewHeight = bContainerHeightAutoSize ? -1 : fContentCurrentHeight; | |
| UpdateWidgetSize(pLayoutChild, &fColSpanWidth, &fNewHeight); | |
| pLayoutChild->m_sSize.height = fNewHeight; | |
| if (bContainerHeightAutoSize) { | |
| fContentCalculatedHeight = | |
| std::max(fContentCalculatedHeight, pLayoutChild->m_sSize.height); | |
| } | |
| } | |
| if (bContainerHeightAutoSize) { | |
| for (CXFA_ContentLayoutItem* pLayoutChild = | |
| (CXFA_ContentLayoutItem*)pLayoutRow->m_pFirstChild; | |
| pLayoutChild; | |
| pLayoutChild = (CXFA_ContentLayoutItem*)pLayoutChild->m_pNextSibling) { | |
| UpdateWidgetSize(pLayoutChild, &pLayoutChild->m_sSize.width, | |
| &fContentCalculatedHeight); | |
| float fOldChildHeight = pLayoutChild->m_sSize.height; | |
| pLayoutChild->m_sSize.height = fContentCalculatedHeight; | |
| CXFA_Node* pParaNode = | |
| pLayoutChild->m_pFormNode->GetFirstChildByClass(XFA_Element::Para); | |
| if (pParaNode && pLayoutChild->m_pFirstChild) { | |
| float fOffHeight = fContentCalculatedHeight - fOldChildHeight; | |
| XFA_AttributeEnum eVType = | |
| pParaNode->JSNode()->GetEnum(XFA_Attribute::VAlign); | |
| switch (eVType) { | |
| case XFA_AttributeEnum::Middle: | |
| fOffHeight = fOffHeight / 2; | |
| break; | |
| case XFA_AttributeEnum::Bottom: | |
| break; | |
| case XFA_AttributeEnum::Top: | |
| default: | |
| fOffHeight = 0; | |
| break; | |
| } | |
| if (fOffHeight > 0) { | |
| for (CXFA_ContentLayoutItem* pInnerLayoutChild = | |
| (CXFA_ContentLayoutItem*)pLayoutChild->m_pFirstChild; | |
| pInnerLayoutChild; | |
| pInnerLayoutChild = | |
| (CXFA_ContentLayoutItem*)pInnerLayoutChild->m_pNextSibling) { | |
| pInnerLayoutChild->m_sPos.y += fOffHeight; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = fCurrentColX; | |
| if (fContentWidthLimit < FLT_MAX && | |
| fContentWidthLimit > fChildSuppliedWidth) { | |
| fChildSuppliedWidth = fContentWidthLimit; | |
| } | |
| fContentCalculatedWidth = | |
| std::max(fContentCalculatedWidth, fChildSuppliedWidth); | |
| } else { | |
| fContentCalculatedWidth = containerSize.width - fLeftInset - fRightInset; | |
| } | |
| if (pLayoutRow->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout) == | |
| XFA_AttributeEnum::Rl_row) { | |
| for (CXFA_ContentLayoutItem* pLayoutChild = | |
| (CXFA_ContentLayoutItem*)pLayoutRow->m_pFirstChild; | |
| pLayoutChild; | |
| pLayoutChild = (CXFA_ContentLayoutItem*)pLayoutChild->m_pNextSibling) { | |
| pLayoutChild->m_sPos.x = fContentCalculatedWidth - | |
| pLayoutChild->m_sPos.x - | |
| pLayoutChild->m_sSize.width; | |
| } | |
| } | |
| pLayoutRow->m_sSize = CalculateContainerComponentSizeFromContentSize( | |
| pLayoutRow->m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth, | |
| bContainerHeightAutoSize, fContentCalculatedHeight, containerSize); | |
| } | |
| void UpdatePendingItemLayout(CXFA_ItemLayoutProcessor* pProcessor, | |
| CXFA_ContentLayoutItem* pLayoutItem) { | |
| XFA_AttributeEnum eLayout = | |
| pLayoutItem->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout); | |
| switch (eLayout) { | |
| case XFA_AttributeEnum::Row: | |
| case XFA_AttributeEnum::Rl_row: | |
| RelocateTableRowCells(pLayoutItem, pProcessor->m_rgSpecifiedColumnWidths, | |
| eLayout); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| void AddTrailerBeforeSplit(CXFA_ItemLayoutProcessor* pProcessor, | |
| float fSplitPos, | |
| CXFA_ContentLayoutItem* pTrailerLayoutItem, | |
| bool bUseInherited) { | |
| if (!pTrailerLayoutItem) | |
| return; | |
| float fHeight = pTrailerLayoutItem->m_sSize.height; | |
| if (bUseInherited) { | |
| float fNewSplitPos = 0; | |
| if (fSplitPos - fHeight > XFA_LAYOUT_FLOAT_PERCISION) | |
| fNewSplitPos = pProcessor->FindSplitPos(fSplitPos - fHeight); | |
| if (fNewSplitPos > XFA_LAYOUT_FLOAT_PERCISION) | |
| pProcessor->SplitLayoutItem(fNewSplitPos); | |
| return; | |
| } | |
| UpdatePendingItemLayout(pProcessor, pTrailerLayoutItem); | |
| CXFA_Node* pMarginNode = | |
| pProcessor->m_pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fTopInset = 0; | |
| float fRightInset = 0; | |
| float fBottomInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::LeftInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fTopInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::TopInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::RightInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fBottomInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::BottomInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| if (!pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem)) { | |
| pTrailerLayoutItem->m_sPos.y = pProcessor->m_fLastRowY; | |
| pTrailerLayoutItem->m_sPos.x = pProcessor->m_fLastRowWidth; | |
| pProcessor->m_pLayoutItem->m_sSize.width += | |
| pTrailerLayoutItem->m_sSize.width; | |
| pProcessor->m_pLayoutItem->AddChild(pTrailerLayoutItem); | |
| return; | |
| } | |
| float fNewSplitPos = 0; | |
| if (fSplitPos - fHeight > XFA_LAYOUT_FLOAT_PERCISION) | |
| fNewSplitPos = pProcessor->FindSplitPos(fSplitPos - fHeight); | |
| if (fNewSplitPos > XFA_LAYOUT_FLOAT_PERCISION) { | |
| pProcessor->SplitLayoutItem(fNewSplitPos); | |
| pTrailerLayoutItem->m_sPos.y = fNewSplitPos - fTopInset - fBottomInset; | |
| } else { | |
| pTrailerLayoutItem->m_sPos.y = fSplitPos - fTopInset - fBottomInset; | |
| } | |
| switch (pTrailerLayoutItem->m_pFormNode->JSNode()->GetEnum( | |
| XFA_Attribute::HAlign)) { | |
| case XFA_AttributeEnum::Right: | |
| pTrailerLayoutItem->m_sPos.x = pProcessor->m_pLayoutItem->m_sSize.width - | |
| fRightInset - | |
| pTrailerLayoutItem->m_sSize.width; | |
| break; | |
| case XFA_AttributeEnum::Center: | |
| pTrailerLayoutItem->m_sPos.x = | |
| (pProcessor->m_pLayoutItem->m_sSize.width - fLeftInset - fRightInset - | |
| pTrailerLayoutItem->m_sSize.width) / | |
| 2; | |
| break; | |
| case XFA_AttributeEnum::Left: | |
| default: | |
| pTrailerLayoutItem->m_sPos.x = fLeftInset; | |
| break; | |
| } | |
| pProcessor->m_pLayoutItem->m_sSize.height += fHeight; | |
| pProcessor->m_pLayoutItem->AddChild(pTrailerLayoutItem); | |
| } | |
| void AddLeaderAfterSplit(CXFA_ItemLayoutProcessor* pProcessor, | |
| CXFA_ContentLayoutItem* pLeaderLayoutItem) { | |
| UpdatePendingItemLayout(pProcessor, pLeaderLayoutItem); | |
| CXFA_Node* pMarginNode = | |
| pProcessor->m_pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fRightInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::LeftInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::RightInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| float fHeight = pLeaderLayoutItem->m_sSize.height; | |
| for (CXFA_ContentLayoutItem* pChildItem = | |
| (CXFA_ContentLayoutItem*)pProcessor->m_pLayoutItem->m_pFirstChild; | |
| pChildItem; | |
| pChildItem = (CXFA_ContentLayoutItem*)pChildItem->m_pNextSibling) { | |
| pChildItem->m_sPos.y += fHeight; | |
| } | |
| pLeaderLayoutItem->m_sPos.y = 0; | |
| switch (pLeaderLayoutItem->m_pFormNode->JSNode()->GetEnum( | |
| XFA_Attribute::HAlign)) { | |
| case XFA_AttributeEnum::Right: | |
| pLeaderLayoutItem->m_sPos.x = pProcessor->m_pLayoutItem->m_sSize.width - | |
| fRightInset - | |
| pLeaderLayoutItem->m_sSize.width; | |
| break; | |
| case XFA_AttributeEnum::Center: | |
| pLeaderLayoutItem->m_sPos.x = | |
| (pProcessor->m_pLayoutItem->m_sSize.width - fLeftInset - fRightInset - | |
| pLeaderLayoutItem->m_sSize.width) / | |
| 2; | |
| break; | |
| case XFA_AttributeEnum::Left: | |
| default: | |
| pLeaderLayoutItem->m_sPos.x = fLeftInset; | |
| break; | |
| } | |
| pProcessor->m_pLayoutItem->m_sSize.height += fHeight; | |
| pProcessor->m_pLayoutItem->AddChild(pLeaderLayoutItem); | |
| } | |
| void AddPendingNode(CXFA_ItemLayoutProcessor* pProcessor, | |
| CXFA_Node* pPendingNode, | |
| bool bBreakPending) { | |
| pProcessor->m_PendingNodes.push_back(pPendingNode); | |
| pProcessor->m_bBreakPending = bBreakPending; | |
| } | |
| float InsertPendingItems(CXFA_ItemLayoutProcessor* pProcessor, | |
| CXFA_Node* pCurChildNode) { | |
| float fTotalHeight = 0; | |
| if (pProcessor->m_PendingNodes.empty()) | |
| return fTotalHeight; | |
| if (!pProcessor->m_pLayoutItem) { | |
| pProcessor->m_pLayoutItem = | |
| pProcessor->CreateContentLayoutItem(pCurChildNode); | |
| pProcessor->m_pLayoutItem->m_sSize.clear(); | |
| } | |
| while (!pProcessor->m_PendingNodes.empty()) { | |
| auto pPendingProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| pProcessor->m_PendingNodes.front(), nullptr); | |
| pProcessor->m_PendingNodes.pop_front(); | |
| pPendingProcessor->DoLayout(false, FLT_MAX, FLT_MAX, nullptr); | |
| CXFA_ContentLayoutItem* pPendingLayoutItem = | |
| pPendingProcessor->HasLayoutItem() | |
| ? pPendingProcessor->ExtractLayoutItem() | |
| : nullptr; | |
| if (pPendingLayoutItem) { | |
| AddLeaderAfterSplit(pProcessor, pPendingLayoutItem); | |
| if (pProcessor->m_bBreakPending) | |
| fTotalHeight += pPendingLayoutItem->m_sSize.height; | |
| } | |
| } | |
| return fTotalHeight; | |
| } | |
| XFA_AttributeEnum GetLayout(CXFA_Node* pFormNode, bool* bRootForceTb) { | |
| *bRootForceTb = false; | |
| pdfium::Optional<XFA_AttributeEnum> layoutMode = | |
| pFormNode->JSNode()->TryEnum(XFA_Attribute::Layout, false); | |
| if (layoutMode) | |
| return *layoutMode; | |
| CXFA_Node* pParentNode = pFormNode->GetNodeItem(XFA_NODEITEM_Parent); | |
| if (pParentNode && pParentNode->GetElementType() == XFA_Element::Form) { | |
| *bRootForceTb = true; | |
| return XFA_AttributeEnum::Tb; | |
| } | |
| return XFA_AttributeEnum::Position; | |
| } | |
| bool ExistContainerKeep(CXFA_Node* pCurNode, bool bPreFind) { | |
| if (!pCurNode || !XFA_ItemLayoutProcessor_IsTakingSpace(pCurNode)) | |
| return false; | |
| XFA_NODEITEM eItemType = XFA_NODEITEM_PrevSibling; | |
| if (!bPreFind) | |
| eItemType = XFA_NODEITEM_NextSibling; | |
| CXFA_Node* pPreContainer = | |
| pCurNode->GetNodeItem(eItemType, XFA_ObjectType::ContainerNode); | |
| if (!pPreContainer) | |
| return false; | |
| CXFA_Node* pKeep = pCurNode->GetFirstChildByClass(XFA_Element::Keep); | |
| if (pKeep) { | |
| XFA_Attribute eKeepType = XFA_Attribute::Previous; | |
| if (!bPreFind) | |
| eKeepType = XFA_Attribute::Next; | |
| pdfium::Optional<XFA_AttributeEnum> previous = | |
| pKeep->JSNode()->TryEnum(eKeepType, false); | |
| if (previous) { | |
| if (*previous == XFA_AttributeEnum::ContentArea || | |
| *previous == XFA_AttributeEnum::PageArea) { | |
| return true; | |
| } | |
| } | |
| } | |
| pKeep = pPreContainer->GetFirstChildByClass(XFA_Element::Keep); | |
| if (!pKeep) | |
| return false; | |
| XFA_Attribute eKeepType = XFA_Attribute::Next; | |
| if (!bPreFind) | |
| eKeepType = XFA_Attribute::Previous; | |
| pdfium::Optional<XFA_AttributeEnum> next = | |
| pKeep->JSNode()->TryEnum(eKeepType, false); | |
| if (!next) | |
| return false; | |
| if (*next == XFA_AttributeEnum::ContentArea || | |
| *next == XFA_AttributeEnum::PageArea) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool FindBreakNode(CXFA_Node* pContainerNode, | |
| CXFA_Node*& pCurActionNode, | |
| XFA_ItemLayoutProcessorStages* nCurStage, | |
| bool bBreakBefore) { | |
| bool bFindRs = false; | |
| for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode; | |
| pBreakNode = pBreakNode->GetNodeItem(XFA_NODEITEM_NextSibling)) { | |
| XFA_Attribute eAttributeType = XFA_Attribute::Before; | |
| if (!bBreakBefore) | |
| eAttributeType = XFA_Attribute::After; | |
| switch (pBreakNode->GetElementType()) { | |
| case XFA_Element::BreakBefore: { | |
| if (bBreakBefore) { | |
| pCurActionNode = pBreakNode; | |
| *nCurStage = XFA_ItemLayoutProcessorStages::BreakBefore; | |
| bFindRs = true; | |
| } | |
| break; | |
| } | |
| case XFA_Element::BreakAfter: { | |
| if (!bBreakBefore) { | |
| pCurActionNode = pBreakNode; | |
| *nCurStage = XFA_ItemLayoutProcessorStages::BreakAfter; | |
| bFindRs = true; | |
| } | |
| break; | |
| } | |
| case XFA_Element::Break: | |
| if (pBreakNode->JSNode()->GetEnum(eAttributeType) != | |
| XFA_AttributeEnum::Auto) { | |
| pCurActionNode = pBreakNode; | |
| *nCurStage = XFA_ItemLayoutProcessorStages::BreakBefore; | |
| if (!bBreakBefore) | |
| *nCurStage = XFA_ItemLayoutProcessorStages::BreakAfter; | |
| bFindRs = true; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| if (bFindRs) | |
| break; | |
| } | |
| return bFindRs; | |
| } | |
| void DeleteLayoutGeneratedNode(CXFA_Node* pGenerateNode) { | |
| CXFA_FFNotify* pNotify = pGenerateNode->GetDocument()->GetNotify(); | |
| CXFA_LayoutProcessor* pDocLayout = | |
| pGenerateNode->GetDocument()->GetDocLayout(); | |
| CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator( | |
| pGenerateNode); | |
| for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; | |
| pNode = sIterator.MoveToNext()) { | |
| CXFA_ContentLayoutItem* pCurLayoutItem = | |
| static_cast<CXFA_ContentLayoutItem*>(pNode->JSNode()->GetLayoutItem()); | |
| CXFA_ContentLayoutItem* pNextLayoutItem = nullptr; | |
| while (pCurLayoutItem) { | |
| pNextLayoutItem = pCurLayoutItem->m_pNext; | |
| pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem); | |
| delete pCurLayoutItem; | |
| pCurLayoutItem = pNextLayoutItem; | |
| } | |
| } | |
| pGenerateNode->GetNodeItem(XFA_NODEITEM_Parent) | |
| ->RemoveChild(pGenerateNode, true); | |
| } | |
| uint8_t HAlignEnumToInt(XFA_AttributeEnum eHAlign) { | |
| switch (eHAlign) { | |
| case XFA_AttributeEnum::Center: | |
| return 1; | |
| case XFA_AttributeEnum::Right: | |
| return 2; | |
| case XFA_AttributeEnum::Left: | |
| default: | |
| return 0; | |
| } | |
| } | |
| XFA_ItemLayoutProcessorResult InsertFlowedItem( | |
| CXFA_ItemLayoutProcessor* pThis, | |
| CXFA_ItemLayoutProcessor* pProcessor, | |
| bool bContainerWidthAutoSize, | |
| bool bContainerHeightAutoSize, | |
| float fContainerHeight, | |
| XFA_AttributeEnum eFlowStrategy, | |
| uint8_t* uCurHAlignState, | |
| std::vector<CXFA_ContentLayoutItem*> (&rgCurLineLayoutItems)[3], | |
| bool bUseBreakControl, | |
| float fAvailHeight, | |
| float fRealHeight, | |
| float fContentWidthLimit, | |
| float* fContentCurRowY, | |
| float* fContentCurRowAvailWidth, | |
| float* fContentCurRowHeight, | |
| bool* bAddedItemInRow, | |
| bool* bForceEndPage, | |
| CXFA_LayoutContext* pLayoutContext, | |
| bool bNewRow) { | |
| bool bTakeSpace = | |
| XFA_ItemLayoutProcessor_IsTakingSpace(pProcessor->m_pFormNode); | |
| uint8_t uHAlign = HAlignEnumToInt( | |
| pThis->m_pCurChildNode->JSNode()->GetEnum(XFA_Attribute::HAlign)); | |
| if (bContainerWidthAutoSize) | |
| uHAlign = 0; | |
| if ((eFlowStrategy != XFA_AttributeEnum::Rl_tb && | |
| uHAlign < *uCurHAlignState) || | |
| (eFlowStrategy == XFA_AttributeEnum::Rl_tb && | |
| uHAlign > *uCurHAlignState)) { | |
| return XFA_ItemLayoutProcessorResult::RowFullBreak; | |
| } | |
| *uCurHAlignState = uHAlign; | |
| bool bIsOwnSplit = | |
| pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None; | |
| bool bUseRealHeight = | |
| bTakeSpace && bContainerHeightAutoSize && bIsOwnSplit && | |
| pProcessor->m_pFormNode->GetNodeItem(XFA_NODEITEM_Parent)->GetIntact() == | |
| XFA_AttributeEnum::None; | |
| bool bIsTransHeight = bTakeSpace; | |
| if (bIsTransHeight && !bIsOwnSplit) { | |
| bool bRootForceTb = false; | |
| XFA_AttributeEnum eLayoutStrategy = | |
| GetLayout(pProcessor->m_pFormNode, &bRootForceTb); | |
| if (eLayoutStrategy == XFA_AttributeEnum::Lr_tb || | |
| eLayoutStrategy == XFA_AttributeEnum::Rl_tb) { | |
| bIsTransHeight = false; | |
| } | |
| } | |
| bool bUseInherited = false; | |
| CXFA_LayoutContext layoutContext; | |
| if (pThis->m_pPageMgr) { | |
| CXFA_Node* pOverflowNode = | |
| pThis->m_pPageMgr->QueryOverflow(pThis->m_pFormNode); | |
| if (pOverflowNode) { | |
| layoutContext.m_pOverflowNode = pOverflowNode; | |
| layoutContext.m_pOverflowProcessor = pThis; | |
| pLayoutContext = &layoutContext; | |
| } | |
| } | |
| XFA_ItemLayoutProcessorResult eRetValue = XFA_ItemLayoutProcessorResult::Done; | |
| if (!bNewRow || | |
| pProcessor->m_ePreProcessRs == XFA_ItemLayoutProcessorResult::Done) { | |
| eRetValue = pProcessor->DoLayout( | |
| bTakeSpace ? bUseBreakControl : false, | |
| bUseRealHeight ? fRealHeight - *fContentCurRowY : FLT_MAX, | |
| bIsTransHeight ? fRealHeight - *fContentCurRowY : FLT_MAX, | |
| pLayoutContext); | |
| pProcessor->m_ePreProcessRs = eRetValue; | |
| } else { | |
| eRetValue = pProcessor->m_ePreProcessRs; | |
| pProcessor->m_ePreProcessRs = XFA_ItemLayoutProcessorResult::Done; | |
| } | |
| if (pProcessor->HasLayoutItem() == false) | |
| return eRetValue; | |
| CFX_SizeF childSize = pProcessor->GetCurrentComponentSize(); | |
| if (bUseRealHeight && fRealHeight < XFA_LAYOUT_FLOAT_PERCISION) { | |
| fRealHeight = FLT_MAX; | |
| fAvailHeight = FLT_MAX; | |
| } | |
| if (bTakeSpace && (childSize.width > | |
| *fContentCurRowAvailWidth + XFA_LAYOUT_FLOAT_PERCISION) && | |
| (fContentWidthLimit - *fContentCurRowAvailWidth > | |
| XFA_LAYOUT_FLOAT_PERCISION)) { | |
| return XFA_ItemLayoutProcessorResult::RowFullBreak; | |
| } | |
| CXFA_Node* pOverflowLeaderNode = nullptr; | |
| CXFA_Node* pOverflowTrailerNode = nullptr; | |
| CXFA_Node* pFormNode = nullptr; | |
| CXFA_ContentLayoutItem* pTrailerLayoutItem = nullptr; | |
| bool bIsAddTrailerHeight = false; | |
| if (pThis->m_pPageMgr && | |
| pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None) { | |
| pFormNode = pThis->m_pPageMgr->QueryOverflow(pProcessor->m_pFormNode); | |
| if (!pFormNode && pLayoutContext && pLayoutContext->m_pOverflowProcessor) { | |
| pFormNode = pLayoutContext->m_pOverflowNode; | |
| bUseInherited = true; | |
| } | |
| if (pThis->m_pPageMgr->ProcessOverflow(pFormNode, pOverflowLeaderNode, | |
| pOverflowTrailerNode, false, | |
| false)) { | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowTrailerNode)) { | |
| if (pOverflowTrailerNode) { | |
| auto pOverflowLeaderProcessor = | |
| pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pOverflowTrailerNode, | |
| nullptr); | |
| pOverflowLeaderProcessor->DoLayout(false, FLT_MAX, FLT_MAX, nullptr); | |
| pTrailerLayoutItem = | |
| pOverflowLeaderProcessor->HasLayoutItem() | |
| ? pOverflowLeaderProcessor->ExtractLayoutItem() | |
| : nullptr; | |
| } | |
| bIsAddTrailerHeight = | |
| bUseInherited | |
| ? pThis->IsAddNewRowForTrailer(pTrailerLayoutItem) | |
| : pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem); | |
| if (bIsAddTrailerHeight) { | |
| childSize.height += pTrailerLayoutItem->m_sSize.height; | |
| bIsAddTrailerHeight = true; | |
| } | |
| } | |
| } | |
| } | |
| if (!bTakeSpace || | |
| *fContentCurRowY + childSize.height <= | |
| fAvailHeight + XFA_LAYOUT_FLOAT_PERCISION || | |
| (!bContainerHeightAutoSize && | |
| pThis->m_fUsedSize + fAvailHeight + XFA_LAYOUT_FLOAT_PERCISION >= | |
| fContainerHeight)) { | |
| if (!bTakeSpace || eRetValue == XFA_ItemLayoutProcessorResult::Done) { | |
| if (pProcessor->m_bUseInheriated) { | |
| if (pTrailerLayoutItem) | |
| AddTrailerBeforeSplit(pProcessor, childSize.height, | |
| pTrailerLayoutItem, false); | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode)) | |
| AddPendingNode(pProcessor, pOverflowLeaderNode, false); | |
| pProcessor->m_bUseInheriated = false; | |
| } else { | |
| if (bIsAddTrailerHeight) | |
| childSize.height -= pTrailerLayoutItem->m_sSize.height; | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| } | |
| CXFA_ContentLayoutItem* pChildLayoutItem = | |
| pProcessor->ExtractLayoutItem(); | |
| if (ExistContainerKeep(pProcessor->m_pFormNode, false) && | |
| pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None) { | |
| pThis->m_arrayKeepItems.push_back(pChildLayoutItem); | |
| } else { | |
| pThis->m_arrayKeepItems.clear(); | |
| } | |
| rgCurLineLayoutItems[uHAlign].push_back(pChildLayoutItem); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = | |
| std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| return XFA_ItemLayoutProcessorResult::Done; | |
| } | |
| if (eRetValue == XFA_ItemLayoutProcessorResult::PageFullBreak) { | |
| if (pProcessor->m_bUseInheriated) { | |
| if (pTrailerLayoutItem) { | |
| AddTrailerBeforeSplit(pProcessor, childSize.height, | |
| pTrailerLayoutItem, false); | |
| } | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode)) | |
| AddPendingNode(pProcessor, pOverflowLeaderNode, false); | |
| pProcessor->m_bUseInheriated = false; | |
| } else { | |
| if (bIsAddTrailerHeight) | |
| childSize.height -= pTrailerLayoutItem->m_sSize.height; | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| } | |
| } | |
| rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height); | |
| return eRetValue; | |
| } | |
| XFA_ItemLayoutProcessorResult eResult; | |
| if (pThis->ProcessKeepForSplit( | |
| pThis, pProcessor, eRetValue, &rgCurLineLayoutItems[uHAlign], | |
| fContentCurRowAvailWidth, fContentCurRowHeight, fContentCurRowY, | |
| bAddedItemInRow, bForceEndPage, &eResult)) { | |
| return eResult; | |
| } | |
| *bForceEndPage = true; | |
| float fSplitPos = pProcessor->FindSplitPos(fAvailHeight - *fContentCurRowY); | |
| if (fSplitPos > XFA_LAYOUT_FLOAT_PERCISION) { | |
| XFA_AttributeEnum eLayout = | |
| pProcessor->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout); | |
| if (eLayout == XFA_AttributeEnum::Tb && | |
| eRetValue == XFA_ItemLayoutProcessorResult::Done) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, pTrailerLayoutItem, | |
| pFormNode); | |
| rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = | |
| std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| return XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| } | |
| CXFA_Node* pTempLeaderNode = nullptr; | |
| CXFA_Node* pTempTrailerNode = nullptr; | |
| if (pThis->m_pPageMgr && !pProcessor->m_bUseInheriated && | |
| eRetValue != XFA_ItemLayoutProcessorResult::PageFullBreak) { | |
| pThis->m_pPageMgr->ProcessOverflow(pFormNode, pTempLeaderNode, | |
| pTempTrailerNode, false, true); | |
| } | |
| if (pTrailerLayoutItem && bIsAddTrailerHeight) { | |
| AddTrailerBeforeSplit(pProcessor, fSplitPos, pTrailerLayoutItem, | |
| bUseInherited); | |
| } else { | |
| pProcessor->SplitLayoutItem(fSplitPos); | |
| } | |
| if (bUseInherited) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, pTrailerLayoutItem, | |
| pFormNode); | |
| pThis->m_bUseInheriated = true; | |
| } else { | |
| CXFA_LayoutItem* firstChild = pProcessor->m_pLayoutItem->m_pFirstChild; | |
| if (firstChild && !firstChild->m_pNextSibling && | |
| firstChild->m_pFormNode->IsLayoutGeneratedNode()) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| } else if (pProcessor->JudgeLeaderOrTrailerForOccur( | |
| pOverflowLeaderNode)) { | |
| AddPendingNode(pProcessor, pOverflowLeaderNode, false); | |
| } | |
| } | |
| if (pProcessor->m_pLayoutItem->m_pNextSibling) { | |
| childSize = pProcessor->GetCurrentComponentSize(); | |
| rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = | |
| std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| } | |
| return XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| } | |
| if (*fContentCurRowY <= XFA_LAYOUT_FLOAT_PERCISION) { | |
| childSize = pProcessor->GetCurrentComponentSize(); | |
| if (pProcessor->m_pPageMgr->GetNextAvailContentHeight(childSize.height)) { | |
| CXFA_Node* pTempLeaderNode = nullptr; | |
| CXFA_Node* pTempTrailerNode = nullptr; | |
| if (pThis->m_pPageMgr) { | |
| if (!pFormNode && pLayoutContext) | |
| pFormNode = pLayoutContext->m_pOverflowProcessor->m_pFormNode; | |
| pThis->m_pPageMgr->ProcessOverflow(pFormNode, pTempLeaderNode, | |
| pTempTrailerNode, false, true); | |
| } | |
| if (bUseInherited) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| pThis->m_bUseInheriated = true; | |
| } | |
| return XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| } | |
| rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| if (eRetValue == XFA_ItemLayoutProcessorResult::Done) | |
| *bForceEndPage = false; | |
| return eRetValue; | |
| } | |
| XFA_AttributeEnum eLayout = | |
| pProcessor->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout); | |
| if (pProcessor->m_pFormNode->GetIntact() == XFA_AttributeEnum::None && | |
| eLayout == XFA_AttributeEnum::Tb) { | |
| if (pThis->m_pPageMgr) { | |
| pThis->m_pPageMgr->ProcessOverflow(pFormNode, pOverflowLeaderNode, | |
| pOverflowTrailerNode, false, true); | |
| } | |
| if (pTrailerLayoutItem) | |
| AddTrailerBeforeSplit(pProcessor, fSplitPos, pTrailerLayoutItem, false); | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode)) | |
| AddPendingNode(pProcessor, pOverflowLeaderNode, false); | |
| return XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| } | |
| if (eRetValue != XFA_ItemLayoutProcessorResult::Done) | |
| return XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| if (!pFormNode && pLayoutContext) | |
| pFormNode = pLayoutContext->m_pOverflowProcessor->m_pFormNode; | |
| if (pThis->m_pPageMgr) { | |
| pThis->m_pPageMgr->ProcessOverflow(pFormNode, pOverflowLeaderNode, | |
| pOverflowTrailerNode, false, true); | |
| } | |
| if (bUseInherited) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| pThis->m_bUseInheriated = true; | |
| } | |
| return XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| } | |
| bool FindLayoutItemSplitPos(CXFA_ContentLayoutItem* pLayoutItem, | |
| float fCurVerticalOffset, | |
| float* fProposedSplitPos, | |
| bool* bAppChange, | |
| bool bCalculateMargin) { | |
| CXFA_Node* pFormNode = pLayoutItem->m_pFormNode; | |
| if (*fProposedSplitPos <= fCurVerticalOffset + XFA_LAYOUT_FLOAT_PERCISION || | |
| *fProposedSplitPos > fCurVerticalOffset + pLayoutItem->m_sSize.height - | |
| XFA_LAYOUT_FLOAT_PERCISION) { | |
| return false; | |
| } | |
| switch (pFormNode->GetIntact()) { | |
| case XFA_AttributeEnum::None: { | |
| bool bAnyChanged = false; | |
| CXFA_Document* pDocument = pFormNode->GetDocument(); | |
| CXFA_FFNotify* pNotify = pDocument->GetNotify(); | |
| float fCurTopMargin = 0, fCurBottomMargin = 0; | |
| CXFA_Node* pMarginNode = | |
| pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| if (pMarginNode && bCalculateMargin) { | |
| fCurTopMargin = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::TopInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fCurBottomMargin = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::BottomInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| bool bChanged = true; | |
| while (bChanged) { | |
| bChanged = false; | |
| { | |
| float fRelSplitPos = *fProposedSplitPos - fCurVerticalOffset; | |
| if (pNotify->FindSplitPos(pFormNode, pLayoutItem->GetIndex(), | |
| fRelSplitPos)) { | |
| bAnyChanged = true; | |
| bChanged = true; | |
| *fProposedSplitPos = fCurVerticalOffset + fRelSplitPos; | |
| *bAppChange = true; | |
| if (*fProposedSplitPos <= | |
| fCurVerticalOffset + XFA_LAYOUT_FLOAT_PERCISION) { | |
| return true; | |
| } | |
| } | |
| } | |
| float fRelSplitPos = *fProposedSplitPos - fCurBottomMargin; | |
| for (CXFA_ContentLayoutItem* pChildItem = | |
| (CXFA_ContentLayoutItem*)pLayoutItem->m_pFirstChild; | |
| pChildItem; | |
| pChildItem = (CXFA_ContentLayoutItem*)pChildItem->m_pNextSibling) { | |
| float fChildOffset = | |
| fCurVerticalOffset + fCurTopMargin + pChildItem->m_sPos.y; | |
| bool bChange = false; | |
| if (FindLayoutItemSplitPos(pChildItem, fChildOffset, &fRelSplitPos, | |
| &bChange, bCalculateMargin)) { | |
| if (fRelSplitPos - fChildOffset < XFA_LAYOUT_FLOAT_PERCISION && | |
| bChange) { | |
| *fProposedSplitPos = fRelSplitPos - fCurTopMargin; | |
| } else { | |
| *fProposedSplitPos = fRelSplitPos + fCurBottomMargin; | |
| } | |
| bAnyChanged = true; | |
| bChanged = true; | |
| if (*fProposedSplitPos <= | |
| fCurVerticalOffset + XFA_LAYOUT_FLOAT_PERCISION) { | |
| return true; | |
| } | |
| if (bAnyChanged) | |
| break; | |
| } | |
| } | |
| } | |
| return bAnyChanged; | |
| } | |
| case XFA_AttributeEnum::ContentArea: | |
| case XFA_AttributeEnum::PageArea: { | |
| *fProposedSplitPos = fCurVerticalOffset; | |
| return true; | |
| } | |
| default: | |
| return false; | |
| } | |
| } | |
| CFX_PointF CalculatePositionedContainerPos(CXFA_Node* pNode, | |
| const CFX_SizeF& size) { | |
| XFA_AttributeEnum eAnchorType = | |
| pNode->JSNode()->GetEnum(XFA_Attribute::AnchorType); | |
| int32_t nAnchorType = 0; | |
| switch (eAnchorType) { | |
| case XFA_AttributeEnum::TopLeft: | |
| nAnchorType = 0; | |
| break; | |
| case XFA_AttributeEnum::TopCenter: | |
| nAnchorType = 1; | |
| break; | |
| case XFA_AttributeEnum::TopRight: | |
| nAnchorType = 2; | |
| break; | |
| case XFA_AttributeEnum::MiddleLeft: | |
| nAnchorType = 3; | |
| break; | |
| case XFA_AttributeEnum::MiddleCenter: | |
| nAnchorType = 4; | |
| break; | |
| case XFA_AttributeEnum::MiddleRight: | |
| nAnchorType = 5; | |
| break; | |
| case XFA_AttributeEnum::BottomLeft: | |
| nAnchorType = 6; | |
| break; | |
| case XFA_AttributeEnum::BottomCenter: | |
| nAnchorType = 7; | |
| break; | |
| case XFA_AttributeEnum::BottomRight: | |
| nAnchorType = 8; | |
| break; | |
| default: | |
| break; | |
| } | |
| static const uint8_t nNextPos[4][9] = {{0, 1, 2, 3, 4, 5, 6, 7, 8}, | |
| {6, 3, 0, 7, 4, 1, 8, 5, 2}, | |
| {8, 7, 6, 5, 4, 3, 2, 1, 0}, | |
| {2, 5, 8, 1, 4, 7, 0, 3, 6}}; | |
| CFX_PointF pos( | |
| pNode->JSNode()->GetMeasure(XFA_Attribute::X).ToUnit(XFA_Unit::Pt), | |
| pNode->JSNode()->GetMeasure(XFA_Attribute::Y).ToUnit(XFA_Unit::Pt)); | |
| int32_t nRotate = | |
| XFA_MapRotation(pNode->JSNode()->GetInteger(XFA_Attribute::Rotate)) / 90; | |
| int32_t nAbsoluteAnchorType = nNextPos[nRotate][nAnchorType]; | |
| switch (nAbsoluteAnchorType / 3) { | |
| case 1: | |
| pos.y -= size.height / 2; | |
| break; | |
| case 2: | |
| pos.y -= size.height; | |
| break; | |
| default: | |
| break; | |
| } | |
| switch (nAbsoluteAnchorType % 3) { | |
| case 1: | |
| pos.x -= size.width / 2; | |
| break; | |
| case 2: | |
| pos.x -= size.width; | |
| break; | |
| default: | |
| break; | |
| } | |
| return pos; | |
| } | |
| } // namespace | |
| CXFA_ItemLayoutProcessor::CXFA_ItemLayoutProcessor(CXFA_Node* pNode, | |
| CXFA_LayoutPageMgr* pPageMgr) | |
| : m_pFormNode(pNode), | |
| m_pLayoutItem(nullptr), | |
| m_pCurChildNode(XFA_LAYOUT_INVALIDNODE), | |
| m_fUsedSize(0), | |
| m_pPageMgr(pPageMgr), | |
| m_bBreakPending(true), | |
| m_fLastRowWidth(0), | |
| m_fLastRowY(0), | |
| m_bUseInheriated(false), | |
| m_ePreProcessRs(XFA_ItemLayoutProcessorResult::Done), | |
| m_bKeepBreakFinish(false), | |
| m_bIsProcessKeep(false), | |
| m_pKeepHeadNode(nullptr), | |
| m_pKeepTailNode(nullptr), | |
| m_pOldLayoutItem(nullptr), | |
| m_pCurChildPreprocessor(nullptr), | |
| m_nCurChildNodeStage(XFA_ItemLayoutProcessorStages::None), | |
| m_fWidthLimite(0), | |
| m_bHasAvailHeight(true) { | |
| ASSERT(m_pFormNode && (m_pFormNode->IsContainerNode() || | |
| m_pFormNode->GetElementType() == XFA_Element::Form)); | |
| m_pOldLayoutItem = static_cast<CXFA_ContentLayoutItem*>( | |
| m_pFormNode->JSNode()->GetLayoutItem()); | |
| } | |
| CXFA_ItemLayoutProcessor::~CXFA_ItemLayoutProcessor() {} | |
| CXFA_ContentLayoutItem* CXFA_ItemLayoutProcessor::CreateContentLayoutItem( | |
| CXFA_Node* pFormNode) { | |
| if (!pFormNode) | |
| return nullptr; | |
| CXFA_ContentLayoutItem* pLayoutItem = nullptr; | |
| if (m_pOldLayoutItem) { | |
| pLayoutItem = m_pOldLayoutItem; | |
| m_pOldLayoutItem = m_pOldLayoutItem->m_pNext; | |
| return pLayoutItem; | |
| } | |
| pLayoutItem = (CXFA_ContentLayoutItem*)pFormNode->GetDocument() | |
| ->GetNotify() | |
| ->OnCreateLayoutItem(pFormNode); | |
| CXFA_ContentLayoutItem* pPrevLayoutItem = | |
| static_cast<CXFA_ContentLayoutItem*>( | |
| pFormNode->JSNode()->GetLayoutItem()); | |
| if (pPrevLayoutItem) { | |
| while (pPrevLayoutItem->m_pNext) | |
| pPrevLayoutItem = pPrevLayoutItem->m_pNext; | |
| pPrevLayoutItem->m_pNext = pLayoutItem; | |
| pLayoutItem->m_pPrev = pPrevLayoutItem; | |
| } else { | |
| pFormNode->JSNode()->SetLayoutItem(pLayoutItem); | |
| } | |
| return pLayoutItem; | |
| } | |
| float CXFA_ItemLayoutProcessor::FindSplitPos(float fProposedSplitPos) { | |
| ASSERT(m_pLayoutItem); | |
| XFA_AttributeEnum eLayout = m_pFormNode->JSNode() | |
| ->TryEnum(XFA_Attribute::Layout, true) | |
| .value_or(XFA_AttributeEnum::Position); | |
| bool bCalculateMargin = eLayout != XFA_AttributeEnum::Position; | |
| while (fProposedSplitPos > XFA_LAYOUT_FLOAT_PERCISION) { | |
| bool bAppChange = false; | |
| if (!FindLayoutItemSplitPos(m_pLayoutItem, 0, &fProposedSplitPos, | |
| &bAppChange, bCalculateMargin)) { | |
| break; | |
| } | |
| } | |
| return fProposedSplitPos; | |
| } | |
| void CXFA_ItemLayoutProcessor::SplitLayoutItem( | |
| CXFA_ContentLayoutItem* pLayoutItem, | |
| CXFA_ContentLayoutItem* pSecondParent, | |
| float fSplitPos) { | |
| float fCurTopMargin = 0, fCurBottomMargin = 0; | |
| XFA_AttributeEnum eLayout = m_pFormNode->JSNode() | |
| ->TryEnum(XFA_Attribute::Layout, true) | |
| .value_or(XFA_AttributeEnum::Position); | |
| bool bCalculateMargin = true; | |
| if (eLayout == XFA_AttributeEnum::Position) | |
| bCalculateMargin = false; | |
| CXFA_Node* pMarginNode = | |
| pLayoutItem->m_pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| if (pMarginNode && bCalculateMargin) { | |
| fCurTopMargin = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::TopInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fCurBottomMargin = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::BottomInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| CXFA_ContentLayoutItem* pSecondLayoutItem = nullptr; | |
| if (m_pCurChildPreprocessor && | |
| m_pCurChildPreprocessor->m_pFormNode == pLayoutItem->m_pFormNode) { | |
| pSecondLayoutItem = m_pCurChildPreprocessor->CreateContentLayoutItem( | |
| pLayoutItem->m_pFormNode); | |
| } else { | |
| pSecondLayoutItem = CreateContentLayoutItem(pLayoutItem->m_pFormNode); | |
| } | |
| pSecondLayoutItem->m_sPos.x = pLayoutItem->m_sPos.x; | |
| pSecondLayoutItem->m_sSize.width = pLayoutItem->m_sSize.width; | |
| pSecondLayoutItem->m_sPos.y = 0; | |
| pSecondLayoutItem->m_sSize.height = pLayoutItem->m_sSize.height - fSplitPos; | |
| pLayoutItem->m_sSize.height -= pSecondLayoutItem->m_sSize.height; | |
| if (pLayoutItem->m_pFirstChild) | |
| pSecondLayoutItem->m_sSize.height += fCurTopMargin; | |
| if (pSecondParent) { | |
| pSecondParent->AddChild(pSecondLayoutItem); | |
| if (fCurTopMargin > 0 && pLayoutItem->m_pFirstChild) { | |
| pSecondParent->m_sSize.height += fCurTopMargin; | |
| CXFA_ContentLayoutItem* pParentItem = | |
| (CXFA_ContentLayoutItem*)pSecondParent->m_pParent; | |
| while (pParentItem) { | |
| pParentItem->m_sSize.height += fCurTopMargin; | |
| pParentItem = (CXFA_ContentLayoutItem*)pParentItem->m_pParent; | |
| } | |
| } | |
| } else { | |
| pSecondLayoutItem->m_pParent = pLayoutItem->m_pParent; | |
| pSecondLayoutItem->m_pNextSibling = pLayoutItem->m_pNextSibling; | |
| pLayoutItem->m_pNextSibling = pSecondLayoutItem; | |
| } | |
| CXFA_ContentLayoutItem* pChildren = | |
| (CXFA_ContentLayoutItem*)pLayoutItem->m_pFirstChild; | |
| pLayoutItem->m_pFirstChild = nullptr; | |
| float lHeightForKeep = 0; | |
| float fAddMarginHeight = 0; | |
| std::vector<CXFA_ContentLayoutItem*> keepLayoutItems; | |
| for (CXFA_ContentLayoutItem *pChildItem = pChildren, *pChildNext = nullptr; | |
| pChildItem; pChildItem = pChildNext) { | |
| pChildNext = (CXFA_ContentLayoutItem*)pChildItem->m_pNextSibling; | |
| pChildItem->m_pNextSibling = nullptr; | |
| if (fSplitPos <= fCurTopMargin + pChildItem->m_sPos.y + fCurBottomMargin + | |
| XFA_LAYOUT_FLOAT_PERCISION) { | |
| if (!ExistContainerKeep(pChildItem->m_pFormNode, true)) { | |
| pChildItem->m_sPos.y -= fSplitPos - fCurBottomMargin; | |
| pChildItem->m_sPos.y += lHeightForKeep; | |
| pChildItem->m_sPos.y += fAddMarginHeight; | |
| pSecondLayoutItem->AddChild(pChildItem); | |
| continue; | |
| } | |
| if (lHeightForKeep < XFA_LAYOUT_FLOAT_PERCISION) { | |
| for (auto* pPreItem : keepLayoutItems) { | |
| pLayoutItem->RemoveChild(pPreItem); | |
| pPreItem->m_sPos.y -= fSplitPos; | |
| if (pPreItem->m_sPos.y < 0) | |
| pPreItem->m_sPos.y = 0; | |
| if (pPreItem->m_sPos.y + pPreItem->m_sSize.height > lHeightForKeep) { | |
| pPreItem->m_sPos.y = lHeightForKeep; | |
| lHeightForKeep += pPreItem->m_sSize.height; | |
| pSecondLayoutItem->m_sSize.height += pPreItem->m_sSize.height; | |
| if (pSecondParent) | |
| pSecondParent->m_sSize.height += pPreItem->m_sSize.height; | |
| } | |
| pSecondLayoutItem->AddChild(pPreItem); | |
| } | |
| } | |
| pChildItem->m_sPos.y -= fSplitPos; | |
| pChildItem->m_sPos.y += lHeightForKeep; | |
| pChildItem->m_sPos.y += fAddMarginHeight; | |
| pSecondLayoutItem->AddChild(pChildItem); | |
| continue; | |
| } | |
| if (fSplitPos + XFA_LAYOUT_FLOAT_PERCISION >= | |
| fCurTopMargin + fCurBottomMargin + pChildItem->m_sPos.y + | |
| pChildItem->m_sSize.height) { | |
| pLayoutItem->AddChild(pChildItem); | |
| if (ExistContainerKeep(pChildItem->m_pFormNode, false)) | |
| keepLayoutItems.push_back(pChildItem); | |
| else | |
| keepLayoutItems.clear(); | |
| continue; | |
| } | |
| float fOldHeight = pSecondLayoutItem->m_sSize.height; | |
| SplitLayoutItem( | |
| pChildItem, pSecondLayoutItem, | |
| fSplitPos - fCurTopMargin - fCurBottomMargin - pChildItem->m_sPos.y); | |
| fAddMarginHeight = pSecondLayoutItem->m_sSize.height - fOldHeight; | |
| pLayoutItem->AddChild(pChildItem); | |
| } | |
| } | |
| void CXFA_ItemLayoutProcessor::SplitLayoutItem(float fSplitPos) { | |
| ASSERT(m_pLayoutItem); | |
| SplitLayoutItem(m_pLayoutItem, nullptr, fSplitPos); | |
| } | |
| CXFA_ContentLayoutItem* CXFA_ItemLayoutProcessor::ExtractLayoutItem() { | |
| CXFA_ContentLayoutItem* pLayoutItem = m_pLayoutItem; | |
| if (pLayoutItem) { | |
| m_pLayoutItem = | |
| static_cast<CXFA_ContentLayoutItem*>(pLayoutItem->m_pNextSibling); | |
| pLayoutItem->m_pNextSibling = nullptr; | |
| } | |
| if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Done || | |
| !ToContentLayoutItem(m_pOldLayoutItem)) { | |
| return pLayoutItem; | |
| } | |
| if (m_pOldLayoutItem->m_pPrev) | |
| m_pOldLayoutItem->m_pPrev->m_pNext = nullptr; | |
| CXFA_FFNotify* pNotify = | |
| m_pOldLayoutItem->m_pFormNode->GetDocument()->GetNotify(); | |
| CXFA_LayoutProcessor* pDocLayout = | |
| m_pOldLayoutItem->m_pFormNode->GetDocument()->GetDocLayout(); | |
| CXFA_ContentLayoutItem* pOldLayoutItem = m_pOldLayoutItem; | |
| while (pOldLayoutItem) { | |
| CXFA_ContentLayoutItem* pNextOldLayoutItem = pOldLayoutItem->m_pNext; | |
| pNotify->OnLayoutItemRemoving(pDocLayout, pOldLayoutItem); | |
| if (pOldLayoutItem->m_pParent) | |
| pOldLayoutItem->m_pParent->RemoveChild(pOldLayoutItem); | |
| delete pOldLayoutItem; | |
| pOldLayoutItem = pNextOldLayoutItem; | |
| } | |
| m_pOldLayoutItem = nullptr; | |
| return pLayoutItem; | |
| } | |
| void CXFA_ItemLayoutProcessor::GotoNextContainerNode( | |
| CXFA_Node*& pCurActionNode, | |
| XFA_ItemLayoutProcessorStages& nCurStage, | |
| CXFA_Node* pParentContainer, | |
| bool bUsePageBreak) { | |
| CXFA_Node* pEntireContainer = pParentContainer; | |
| CXFA_Node* pChildContainer = XFA_LAYOUT_INVALIDNODE; | |
| switch (nCurStage) { | |
| case XFA_ItemLayoutProcessorStages::BreakBefore: | |
| case XFA_ItemLayoutProcessorStages::BreakAfter: { | |
| pChildContainer = pCurActionNode->GetNodeItem(XFA_NODEITEM_Parent); | |
| break; | |
| } | |
| case XFA_ItemLayoutProcessorStages::Keep: | |
| case XFA_ItemLayoutProcessorStages::Container: | |
| pChildContainer = pCurActionNode; | |
| break; | |
| default: | |
| pChildContainer = XFA_LAYOUT_INVALIDNODE; | |
| break; | |
| } | |
| switch (nCurStage) { | |
| case XFA_ItemLayoutProcessorStages::Keep: { | |
| CXFA_Node* pBreakAfterNode = | |
| pChildContainer->GetNodeItem(XFA_NODEITEM_FirstChild); | |
| if (!m_bKeepBreakFinish && | |
| FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage, false)) { | |
| return; | |
| } | |
| goto CheckNextChildContainer; | |
| } | |
| case XFA_ItemLayoutProcessorStages::None: { | |
| pCurActionNode = XFA_LAYOUT_INVALIDNODE; | |
| case XFA_ItemLayoutProcessorStages::BookendLeader: | |
| for (CXFA_Node* pBookendNode = | |
| pCurActionNode == XFA_LAYOUT_INVALIDNODE | |
| ? pEntireContainer->GetNodeItem(XFA_NODEITEM_FirstChild) | |
| : pCurActionNode->GetNodeItem(XFA_NODEITEM_NextSibling); | |
| pBookendNode; pBookendNode = pBookendNode->GetNodeItem( | |
| XFA_NODEITEM_NextSibling)) { | |
| switch (pBookendNode->GetElementType()) { | |
| case XFA_Element::Bookend: | |
| case XFA_Element::Break: | |
| pCurActionNode = pBookendNode; | |
| nCurStage = XFA_ItemLayoutProcessorStages::BookendLeader; | |
| return; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| { | |
| pCurActionNode = XFA_LAYOUT_INVALIDNODE; | |
| case XFA_ItemLayoutProcessorStages::BreakBefore: | |
| if (pCurActionNode != XFA_LAYOUT_INVALIDNODE) { | |
| CXFA_Node* pBreakBeforeNode = | |
| pCurActionNode->GetNodeItem(XFA_NODEITEM_NextSibling); | |
| if (!m_bKeepBreakFinish && | |
| FindBreakNode(pBreakBeforeNode, pCurActionNode, &nCurStage, | |
| true)) { | |
| return; | |
| } | |
| if (m_bIsProcessKeep) { | |
| if (ProcessKeepNodesForBreakBefore(pCurActionNode, nCurStage, | |
| pChildContainer)) { | |
| return; | |
| } | |
| goto CheckNextChildContainer; | |
| } | |
| pCurActionNode = pChildContainer; | |
| nCurStage = XFA_ItemLayoutProcessorStages::Container; | |
| return; | |
| } | |
| goto CheckNextChildContainer; | |
| } | |
| case XFA_ItemLayoutProcessorStages::Container: { | |
| pCurActionNode = XFA_LAYOUT_INVALIDNODE; | |
| case XFA_ItemLayoutProcessorStages::BreakAfter: { | |
| if (pCurActionNode == XFA_LAYOUT_INVALIDNODE) { | |
| CXFA_Node* pBreakAfterNode = | |
| pChildContainer->GetNodeItem(XFA_NODEITEM_FirstChild); | |
| if (!m_bKeepBreakFinish && | |
| FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage, | |
| false)) { | |
| return; | |
| } | |
| } else { | |
| CXFA_Node* pBreakAfterNode = | |
| pCurActionNode->GetNodeItem(XFA_NODEITEM_NextSibling); | |
| if (FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage, | |
| false)) { | |
| return; | |
| } | |
| } | |
| goto CheckNextChildContainer; | |
| } | |
| } | |
| CheckNextChildContainer : { | |
| CXFA_Node* pNextChildContainer = | |
| pChildContainer == XFA_LAYOUT_INVALIDNODE | |
| ? pEntireContainer->GetNodeItem(XFA_NODEITEM_FirstChild, | |
| XFA_ObjectType::ContainerNode) | |
| : pChildContainer->GetNodeItem(XFA_NODEITEM_NextSibling, | |
| XFA_ObjectType::ContainerNode); | |
| while (pNextChildContainer && | |
| pNextChildContainer->IsLayoutGeneratedNode()) { | |
| CXFA_Node* pSaveNode = pNextChildContainer; | |
| pNextChildContainer = pNextChildContainer->GetNodeItem( | |
| XFA_NODEITEM_NextSibling, XFA_ObjectType::ContainerNode); | |
| if (pSaveNode->IsUnusedNode()) | |
| DeleteLayoutGeneratedNode(pSaveNode); | |
| } | |
| if (!pNextChildContainer) | |
| goto NoMoreChildContainer; | |
| bool bLastKeep = false; | |
| if (ProcessKeepNodesForCheckNext(pCurActionNode, nCurStage, | |
| pNextChildContainer, bLastKeep)) { | |
| return; | |
| } | |
| if (!m_bKeepBreakFinish && !bLastKeep && | |
| FindBreakNode( | |
| pNextChildContainer->GetNodeItem(XFA_NODEITEM_FirstChild), | |
| pCurActionNode, &nCurStage, true)) { | |
| return; | |
| } | |
| pCurActionNode = pNextChildContainer; | |
| if (m_bIsProcessKeep) | |
| nCurStage = XFA_ItemLayoutProcessorStages::Keep; | |
| else | |
| nCurStage = XFA_ItemLayoutProcessorStages::Container; | |
| return; | |
| } | |
| NoMoreChildContainer : { | |
| pCurActionNode = XFA_LAYOUT_INVALIDNODE; | |
| case XFA_ItemLayoutProcessorStages::BookendTrailer: | |
| for (CXFA_Node* pBookendNode = | |
| pCurActionNode == XFA_LAYOUT_INVALIDNODE | |
| ? pEntireContainer->GetNodeItem(XFA_NODEITEM_FirstChild) | |
| : pCurActionNode->GetNodeItem(XFA_NODEITEM_NextSibling); | |
| pBookendNode; pBookendNode = pBookendNode->GetNodeItem( | |
| XFA_NODEITEM_NextSibling)) { | |
| switch (pBookendNode->GetElementType()) { | |
| case XFA_Element::Bookend: | |
| case XFA_Element::Break: | |
| pCurActionNode = pBookendNode; | |
| nCurStage = XFA_ItemLayoutProcessorStages::BookendTrailer; | |
| return; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| default: | |
| pCurActionNode = nullptr; | |
| nCurStage = XFA_ItemLayoutProcessorStages::Done; | |
| } | |
| } | |
| bool CXFA_ItemLayoutProcessor::ProcessKeepNodesForCheckNext( | |
| CXFA_Node*& pCurActionNode, | |
| XFA_ItemLayoutProcessorStages& nCurStage, | |
| CXFA_Node*& pNextContainer, | |
| bool& bLastKeepNode) { | |
| const bool bCanSplit = pNextContainer->GetIntact() == XFA_AttributeEnum::None; | |
| bool bNextKeep = false; | |
| if (ExistContainerKeep(pNextContainer, false)) | |
| bNextKeep = true; | |
| if (bNextKeep && !bCanSplit) { | |
| if (!m_bIsProcessKeep && !m_bKeepBreakFinish) { | |
| m_pKeepHeadNode = pNextContainer; | |
| m_bIsProcessKeep = true; | |
| } | |
| return false; | |
| } | |
| if (m_bIsProcessKeep && m_pKeepHeadNode) { | |
| m_pKeepTailNode = pNextContainer; | |
| if (!m_bKeepBreakFinish && | |
| FindBreakNode(pNextContainer->GetNodeItem(XFA_NODEITEM_FirstChild), | |
| pCurActionNode, &nCurStage, true)) { | |
| return true; | |
| } | |
| pNextContainer = m_pKeepHeadNode; | |
| m_bKeepBreakFinish = true; | |
| m_pKeepHeadNode = nullptr; | |
| m_pKeepTailNode = nullptr; | |
| m_bIsProcessKeep = false; | |
| } else { | |
| if (m_bKeepBreakFinish) | |
| bLastKeepNode = true; | |
| m_bKeepBreakFinish = false; | |
| } | |
| return false; | |
| } | |
| bool CXFA_ItemLayoutProcessor::ProcessKeepNodesForBreakBefore( | |
| CXFA_Node*& pCurActionNode, | |
| XFA_ItemLayoutProcessorStages& nCurStage, | |
| CXFA_Node* pContainerNode) { | |
| if (m_pKeepTailNode == pContainerNode) { | |
| pCurActionNode = m_pKeepHeadNode; | |
| m_bKeepBreakFinish = true; | |
| m_pKeepHeadNode = nullptr; | |
| m_pKeepTailNode = nullptr; | |
| m_bIsProcessKeep = false; | |
| nCurStage = XFA_ItemLayoutProcessorStages::Container; | |
| return true; | |
| } | |
| CXFA_Node* pBreakAfterNode = | |
| pContainerNode->GetNodeItem(XFA_NODEITEM_FirstChild); | |
| return FindBreakNode(pBreakAfterNode, pCurActionNode, &nCurStage, false); | |
| } | |
| bool XFA_ItemLayoutProcessor_IsTakingSpace(CXFA_Node* pNode) { | |
| XFA_AttributeEnum ePresence = pNode->JSNode() | |
| ->TryEnum(XFA_Attribute::Presence, true) | |
| .value_or(XFA_AttributeEnum::Visible); | |
| return ePresence == XFA_AttributeEnum::Visible || | |
| ePresence == XFA_AttributeEnum::Invisible; | |
| } | |
| bool CXFA_ItemLayoutProcessor::IncrementRelayoutNode( | |
| CXFA_LayoutProcessor* pLayoutProcessor, | |
| CXFA_Node* pNode, | |
| CXFA_Node* pParentNode) { | |
| return false; | |
| } | |
| void CXFA_ItemLayoutProcessor::DoLayoutPageArea( | |
| CXFA_ContainerLayoutItem* pPageAreaLayoutItem) { | |
| CXFA_Node* pFormNode = pPageAreaLayoutItem->m_pFormNode; | |
| CXFA_Node* pCurChildNode = XFA_LAYOUT_INVALIDNODE; | |
| XFA_ItemLayoutProcessorStages nCurChildNodeStage = | |
| XFA_ItemLayoutProcessorStages::None; | |
| CXFA_LayoutItem* pBeforeItem = nullptr; | |
| for (GotoNextContainerNode(pCurChildNode, nCurChildNodeStage, pFormNode, | |
| false); | |
| pCurChildNode; GotoNextContainerNode(pCurChildNode, nCurChildNodeStage, | |
| pFormNode, false)) { | |
| if (nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Container) | |
| continue; | |
| if (pCurChildNode->GetElementType() == XFA_Element::Variables) | |
| continue; | |
| auto pProcessor = | |
| pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pCurChildNode, nullptr); | |
| pProcessor->DoLayout(false, FLT_MAX, FLT_MAX, nullptr); | |
| if (!pProcessor->HasLayoutItem()) | |
| continue; | |
| pProcessor->SetCurrentComponentPos(CalculatePositionedContainerPos( | |
| pCurChildNode, pProcessor->GetCurrentComponentSize())); | |
| CXFA_LayoutItem* pProcessItem = pProcessor->ExtractLayoutItem(); | |
| if (!pBeforeItem) | |
| pPageAreaLayoutItem->AddHeadChild(pProcessItem); | |
| else | |
| pPageAreaLayoutItem->InsertChild(pBeforeItem, pProcessItem); | |
| pBeforeItem = pProcessItem; | |
| } | |
| pBeforeItem = nullptr; | |
| CXFA_LayoutItem* pLayoutItem = pPageAreaLayoutItem->m_pFirstChild; | |
| while (pLayoutItem) { | |
| if (!pLayoutItem->IsContentLayoutItem() || | |
| pLayoutItem->m_pFormNode->GetElementType() != XFA_Element::Draw) { | |
| pLayoutItem = pLayoutItem->m_pNextSibling; | |
| continue; | |
| } | |
| if (pLayoutItem->m_pFormNode->GetElementType() != XFA_Element::Draw) | |
| continue; | |
| CXFA_LayoutItem* pNextLayoutItem = pLayoutItem->m_pNextSibling; | |
| pPageAreaLayoutItem->RemoveChild(pLayoutItem); | |
| if (!pBeforeItem) | |
| pPageAreaLayoutItem->AddHeadChild(pLayoutItem); | |
| else | |
| pPageAreaLayoutItem->InsertChild(pBeforeItem, pLayoutItem); | |
| pBeforeItem = pLayoutItem; | |
| pLayoutItem = pNextLayoutItem; | |
| } | |
| } | |
| void CXFA_ItemLayoutProcessor::DoLayoutPositionedContainer( | |
| CXFA_LayoutContext* pContext) { | |
| if (m_pLayoutItem) | |
| return; | |
| m_pLayoutItem = CreateContentLayoutItem(m_pFormNode); | |
| bool bIgnoreXY = (m_pFormNode->JSNode() | |
| ->TryEnum(XFA_Attribute::Layout, true) | |
| .value_or(XFA_AttributeEnum::Position) != | |
| XFA_AttributeEnum::Position); | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| m_pFormNode, &bContainerWidthAutoSize, &bContainerHeightAutoSize); | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| float fHiddenContentCalculatedWidth = 0; | |
| float fHiddenContentCalculatedHeight = 0; | |
| if (m_pCurChildNode == XFA_LAYOUT_INVALIDNODE) { | |
| GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, | |
| false); | |
| } | |
| int32_t iColIndex = 0; | |
| for (; m_pCurChildNode; GotoNextContainerNode( | |
| m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, false)) { | |
| if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Container) | |
| continue; | |
| if (m_pCurChildNode->GetElementType() == XFA_Element::Variables) | |
| continue; | |
| auto pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| m_pCurChildNode, m_pPageMgr); | |
| if (pContext && pContext->m_prgSpecifiedColumnWidths) { | |
| int32_t iColSpan = | |
| m_pCurChildNode->JSNode()->GetInteger(XFA_Attribute::ColSpan); | |
| if (iColSpan <= pdfium::CollectionSize<int32_t>( | |
| *pContext->m_prgSpecifiedColumnWidths) - | |
| iColIndex) { | |
| pContext->m_fCurColumnWidth = 0; | |
| pContext->m_bCurColumnWidthAvaiable = true; | |
| if (iColSpan == -1) { | |
| iColSpan = pdfium::CollectionSize<int32_t>( | |
| *pContext->m_prgSpecifiedColumnWidths); | |
| } | |
| for (int32_t i = 0; iColIndex + i < iColSpan; ++i) { | |
| pContext->m_fCurColumnWidth += | |
| (*pContext->m_prgSpecifiedColumnWidths)[iColIndex + i]; | |
| } | |
| if (pContext->m_fCurColumnWidth == 0) | |
| pContext->m_bCurColumnWidthAvaiable = false; | |
| iColIndex += iColSpan >= 0 ? iColSpan : 0; | |
| } | |
| } | |
| pProcessor->DoLayout(false, FLT_MAX, FLT_MAX, pContext); | |
| if (!pProcessor->HasLayoutItem()) | |
| continue; | |
| CFX_SizeF size = pProcessor->GetCurrentComponentSize(); | |
| bool bChangeParentSize = false; | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace(m_pCurChildNode)) | |
| bChangeParentSize = true; | |
| CFX_PointF absolutePos; | |
| if (!bIgnoreXY) | |
| absolutePos = CalculatePositionedContainerPos(m_pCurChildNode, size); | |
| pProcessor->SetCurrentComponentPos(absolutePos); | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = absolutePos.x + size.width; | |
| if (bChangeParentSize) { | |
| fContentCalculatedWidth = | |
| std::max(fContentCalculatedWidth, fChildSuppliedWidth); | |
| } else { | |
| if (fHiddenContentCalculatedWidth < fChildSuppliedWidth && | |
| m_pCurChildNode->GetElementType() != XFA_Element::Subform) { | |
| fHiddenContentCalculatedWidth = fChildSuppliedWidth; | |
| } | |
| } | |
| } | |
| if (bContainerHeightAutoSize) { | |
| float fChildSuppliedHeight = absolutePos.y + size.height; | |
| if (bChangeParentSize) { | |
| fContentCalculatedHeight = | |
| std::max(fContentCalculatedHeight, fChildSuppliedHeight); | |
| } else { | |
| if (fHiddenContentCalculatedHeight < fChildSuppliedHeight && | |
| m_pCurChildNode->GetElementType() != XFA_Element::Subform) { | |
| fHiddenContentCalculatedHeight = fChildSuppliedHeight; | |
| } | |
| } | |
| } | |
| m_pLayoutItem->AddChild(pProcessor->ExtractLayoutItem()); | |
| } | |
| XFA_VERSION eVersion = m_pFormNode->GetDocument()->GetCurVersionMode(); | |
| if (fContentCalculatedWidth == 0 && eVersion < XFA_VERSION_207) | |
| fContentCalculatedWidth = fHiddenContentCalculatedWidth; | |
| if (fContentCalculatedHeight == 0 && eVersion < XFA_VERSION_207) | |
| fContentCalculatedHeight = fHiddenContentCalculatedHeight; | |
| containerSize = CalculateContainerComponentSizeFromContentSize( | |
| m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth, | |
| bContainerHeightAutoSize, fContentCalculatedHeight, containerSize); | |
| SetCurrentComponentSize(containerSize); | |
| } | |
| void CXFA_ItemLayoutProcessor::DoLayoutTableContainer(CXFA_Node* pLayoutNode) { | |
| if (m_pLayoutItem) | |
| return; | |
| if (!pLayoutNode) | |
| pLayoutNode = m_pFormNode; | |
| ASSERT(m_pCurChildNode == XFA_LAYOUT_INVALIDNODE); | |
| m_pLayoutItem = CreateContentLayoutItem(m_pFormNode); | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| m_pFormNode, &bContainerWidthAutoSize, &bContainerHeightAutoSize); | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| CXFA_Node* pMarginNode = | |
| m_pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fRightInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::LeftInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::RightInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| float fContentWidthLimit = | |
| bContainerWidthAutoSize ? FLT_MAX | |
| : containerSize.width - fLeftInset - fRightInset; | |
| WideString wsColumnWidths = | |
| pLayoutNode->JSNode()->GetCData(XFA_Attribute::ColumnWidths); | |
| if (!wsColumnWidths.IsEmpty()) { | |
| auto widths = SeparateStringW(wsColumnWidths.c_str(), | |
| wsColumnWidths.GetLength(), L' '); | |
| for (auto& width : widths) { | |
| width.TrimLeft(L' '); | |
| if (width.IsEmpty()) | |
| continue; | |
| m_rgSpecifiedColumnWidths.push_back( | |
| CXFA_Measurement(width.AsStringView()).ToUnit(XFA_Unit::Pt)); | |
| } | |
| } | |
| int32_t iSpecifiedColumnCount = | |
| pdfium::CollectionSize<int32_t>(m_rgSpecifiedColumnWidths); | |
| CXFA_LayoutContext layoutContext; | |
| layoutContext.m_prgSpecifiedColumnWidths = &m_rgSpecifiedColumnWidths; | |
| CXFA_LayoutContext* pLayoutContext = | |
| iSpecifiedColumnCount > 0 ? &layoutContext : nullptr; | |
| if (m_pCurChildNode == XFA_LAYOUT_INVALIDNODE) { | |
| GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, | |
| false); | |
| } | |
| for (; m_pCurChildNode; GotoNextContainerNode( | |
| m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, false)) { | |
| layoutContext.m_bCurColumnWidthAvaiable = false; | |
| layoutContext.m_fCurColumnWidth = 0; | |
| if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Container) | |
| continue; | |
| auto pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| m_pCurChildNode, m_pPageMgr); | |
| pProcessor->DoLayout(false, FLT_MAX, FLT_MAX, pLayoutContext); | |
| if (!pProcessor->HasLayoutItem()) | |
| continue; | |
| m_pLayoutItem->AddChild(pProcessor->ExtractLayoutItem()); | |
| } | |
| int32_t iRowCount = 0; | |
| int32_t iColCount = 0; | |
| { | |
| std::vector<CXFA_ContentLayoutItem*> rgRowItems; | |
| std::vector<int32_t> rgRowItemsSpan; | |
| std::vector<float> rgRowItemsWidth; | |
| for (auto* pLayoutChild = | |
| static_cast<CXFA_ContentLayoutItem*>(m_pLayoutItem->m_pFirstChild); | |
| pLayoutChild; pLayoutChild = static_cast<CXFA_ContentLayoutItem*>( | |
| pLayoutChild->m_pNextSibling)) { | |
| if (pLayoutChild->m_pFormNode->GetElementType() != XFA_Element::Subform) | |
| continue; | |
| if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutChild->m_pFormNode)) | |
| continue; | |
| XFA_AttributeEnum eLayout = | |
| pLayoutChild->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout); | |
| if (eLayout != XFA_AttributeEnum::Row && | |
| eLayout != XFA_AttributeEnum::Rl_row) { | |
| continue; | |
| } | |
| if (CXFA_ContentLayoutItem* pRowLayoutCell = | |
| (CXFA_ContentLayoutItem*)pLayoutChild->m_pFirstChild) { | |
| rgRowItems.push_back(pRowLayoutCell); | |
| int32_t iColSpan = pRowLayoutCell->m_pFormNode->JSNode()->GetInteger( | |
| XFA_Attribute::ColSpan); | |
| rgRowItemsSpan.push_back(iColSpan); | |
| rgRowItemsWidth.push_back(pRowLayoutCell->m_sSize.width); | |
| } | |
| } | |
| iRowCount = pdfium::CollectionSize<int32_t>(rgRowItems); | |
| iColCount = 0; | |
| bool bMoreColumns = true; | |
| while (bMoreColumns) { | |
| bMoreColumns = false; | |
| bool bAutoCol = false; | |
| for (int32_t i = 0; i < iRowCount; i++) { | |
| while (rgRowItems[i] && (rgRowItemsSpan[i] <= 0 || | |
| !XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgRowItems[i]->m_pFormNode))) { | |
| CXFA_ContentLayoutItem* pNewCell = | |
| (CXFA_ContentLayoutItem*)rgRowItems[i]->m_pNextSibling; | |
| if (rgRowItemsSpan[i] < 0 && XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgRowItems[i]->m_pFormNode)) { | |
| pNewCell = nullptr; | |
| } | |
| rgRowItems[i] = pNewCell; | |
| rgRowItemsSpan[i] = pNewCell | |
| ? pNewCell->m_pFormNode->JSNode()->GetInteger( | |
| XFA_Attribute::ColSpan) | |
| : 0; | |
| rgRowItemsWidth[i] = pNewCell ? pNewCell->m_sSize.width : 0; | |
| } | |
| CXFA_ContentLayoutItem* pCell = rgRowItems[i]; | |
| if (!pCell) | |
| continue; | |
| bMoreColumns = true; | |
| if (rgRowItemsSpan[i] != 1) | |
| continue; | |
| if (iColCount >= iSpecifiedColumnCount) { | |
| int32_t c = iColCount + 1 - pdfium::CollectionSize<int32_t>( | |
| m_rgSpecifiedColumnWidths); | |
| for (int32_t j = 0; j < c; j++) | |
| m_rgSpecifiedColumnWidths.push_back(0); | |
| } | |
| if (m_rgSpecifiedColumnWidths[iColCount] < XFA_LAYOUT_FLOAT_PERCISION) | |
| bAutoCol = true; | |
| if (bAutoCol && | |
| m_rgSpecifiedColumnWidths[iColCount] < rgRowItemsWidth[i]) { | |
| m_rgSpecifiedColumnWidths[iColCount] = rgRowItemsWidth[i]; | |
| } | |
| } | |
| if (!bMoreColumns) | |
| continue; | |
| float fFinalColumnWidth = 0.0f; | |
| if (pdfium::IndexInBounds(m_rgSpecifiedColumnWidths, iColCount)) | |
| fFinalColumnWidth = m_rgSpecifiedColumnWidths[iColCount]; | |
| for (int32_t i = 0; i < iRowCount; ++i) { | |
| if (!rgRowItems[i]) | |
| continue; | |
| --rgRowItemsSpan[i]; | |
| rgRowItemsWidth[i] -= fFinalColumnWidth; | |
| } | |
| ++iColCount; | |
| } | |
| } | |
| float fCurrentRowY = 0; | |
| for (CXFA_ContentLayoutItem* pLayoutChild = | |
| (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild; | |
| pLayoutChild; | |
| pLayoutChild = (CXFA_ContentLayoutItem*)pLayoutChild->m_pNextSibling) { | |
| if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutChild->m_pFormNode)) | |
| continue; | |
| if (pLayoutChild->m_pFormNode->GetElementType() == XFA_Element::Subform) { | |
| XFA_AttributeEnum eSubformLayout = | |
| pLayoutChild->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout); | |
| if (eSubformLayout == XFA_AttributeEnum::Row || | |
| eSubformLayout == XFA_AttributeEnum::Rl_row) { | |
| RelocateTableRowCells(pLayoutChild, m_rgSpecifiedColumnWidths, | |
| eSubformLayout); | |
| } | |
| } | |
| pLayoutChild->m_sPos.y = fCurrentRowY; | |
| if (bContainerWidthAutoSize) { | |
| pLayoutChild->m_sPos.x = 0; | |
| } else { | |
| switch ( | |
| pLayoutChild->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::HAlign)) { | |
| case XFA_AttributeEnum::Center: | |
| pLayoutChild->m_sPos.x = | |
| (fContentWidthLimit - pLayoutChild->m_sSize.width) / 2; | |
| break; | |
| case XFA_AttributeEnum::Right: | |
| pLayoutChild->m_sPos.x = | |
| fContentWidthLimit - pLayoutChild->m_sSize.width; | |
| break; | |
| case XFA_AttributeEnum::Left: | |
| default: | |
| pLayoutChild->m_sPos.x = 0; | |
| break; | |
| } | |
| } | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = | |
| pLayoutChild->m_sPos.x + pLayoutChild->m_sSize.width; | |
| if (fContentWidthLimit < FLT_MAX && | |
| fContentWidthLimit > fChildSuppliedWidth) { | |
| fChildSuppliedWidth = fContentWidthLimit; | |
| } | |
| fContentCalculatedWidth = | |
| std::max(fContentCalculatedWidth, fChildSuppliedWidth); | |
| } | |
| fCurrentRowY += pLayoutChild->m_sSize.height; | |
| } | |
| if (bContainerHeightAutoSize) | |
| fContentCalculatedHeight = std::max(fContentCalculatedHeight, fCurrentRowY); | |
| containerSize = CalculateContainerComponentSizeFromContentSize( | |
| m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth, | |
| bContainerHeightAutoSize, fContentCalculatedHeight, containerSize); | |
| SetCurrentComponentSize(containerSize); | |
| } | |
| bool CXFA_ItemLayoutProcessor::IsAddNewRowForTrailer( | |
| CXFA_ContentLayoutItem* pTrailerItem) { | |
| if (!pTrailerItem) | |
| return false; | |
| float fWidth = pTrailerItem->m_sSize.width; | |
| XFA_AttributeEnum eLayout = | |
| m_pFormNode->JSNode()->GetEnum(XFA_Attribute::Layout); | |
| return eLayout == XFA_AttributeEnum::Tb || m_fWidthLimite <= fWidth; | |
| } | |
| float CXFA_ItemLayoutProcessor::InsertKeepLayoutItems() { | |
| if (m_arrayKeepItems.empty()) | |
| return 0; | |
| if (!m_pLayoutItem) { | |
| m_pLayoutItem = CreateContentLayoutItem(m_pFormNode); | |
| m_pLayoutItem->m_sSize.clear(); | |
| } | |
| float fTotalHeight = 0; | |
| for (auto iter = m_arrayKeepItems.rbegin(); iter != m_arrayKeepItems.rend(); | |
| iter++) { | |
| AddLeaderAfterSplit(this, *iter); | |
| fTotalHeight += (*iter)->m_sSize.height; | |
| } | |
| m_arrayKeepItems.clear(); | |
| return fTotalHeight; | |
| } | |
| bool CXFA_ItemLayoutProcessor::ProcessKeepForSplit( | |
| CXFA_ItemLayoutProcessor* pParentProcessor, | |
| CXFA_ItemLayoutProcessor* pChildProcessor, | |
| XFA_ItemLayoutProcessorResult eRetValue, | |
| std::vector<CXFA_ContentLayoutItem*>* rgCurLineLayoutItem, | |
| float* fContentCurRowAvailWidth, | |
| float* fContentCurRowHeight, | |
| float* fContentCurRowY, | |
| bool* bAddedItemInRow, | |
| bool* bForceEndPage, | |
| XFA_ItemLayoutProcessorResult* result) { | |
| if (!pParentProcessor || !pChildProcessor) | |
| return false; | |
| if (pParentProcessor->m_pCurChildNode->GetIntact() == | |
| XFA_AttributeEnum::None && | |
| pChildProcessor->m_bHasAvailHeight) | |
| return false; | |
| if (!ExistContainerKeep(pParentProcessor->m_pCurChildNode, true)) | |
| return false; | |
| CFX_SizeF childSize = pChildProcessor->GetCurrentComponentSize(); | |
| std::vector<CXFA_ContentLayoutItem*> keepLayoutItems; | |
| if (pParentProcessor->JudgePutNextPage(pParentProcessor->m_pLayoutItem, | |
| childSize.height, &keepLayoutItems)) { | |
| m_arrayKeepItems.clear(); | |
| for (auto* item : keepLayoutItems) { | |
| pParentProcessor->m_pLayoutItem->RemoveChild(item); | |
| *fContentCurRowY -= item->m_sSize.height; | |
| m_arrayKeepItems.push_back(item); | |
| } | |
| *bAddedItemInRow = true; | |
| *bForceEndPage = true; | |
| *result = XFA_ItemLayoutProcessorResult::PageFullBreak; | |
| return true; | |
| } | |
| rgCurLineLayoutItem->push_back(pChildProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height); | |
| *result = eRetValue; | |
| return true; | |
| } | |
| bool CXFA_ItemLayoutProcessor::JudgePutNextPage( | |
| CXFA_ContentLayoutItem* pParentLayoutItem, | |
| float fChildHeight, | |
| std::vector<CXFA_ContentLayoutItem*>* pKeepItems) { | |
| if (!pParentLayoutItem) | |
| return false; | |
| float fItemsHeight = 0; | |
| for (CXFA_ContentLayoutItem* pChildLayoutItem = | |
| (CXFA_ContentLayoutItem*)pParentLayoutItem->m_pFirstChild; | |
| pChildLayoutItem; | |
| pChildLayoutItem = | |
| (CXFA_ContentLayoutItem*)pChildLayoutItem->m_pNextSibling) { | |
| if (ExistContainerKeep(pChildLayoutItem->m_pFormNode, false)) { | |
| pKeepItems->push_back(pChildLayoutItem); | |
| fItemsHeight += pChildLayoutItem->m_sSize.height; | |
| } else { | |
| pKeepItems->clear(); | |
| fItemsHeight = 0; | |
| } | |
| } | |
| fItemsHeight += fChildHeight; | |
| return m_pPageMgr->GetNextAvailContentHeight(fItemsHeight); | |
| } | |
| void CXFA_ItemLayoutProcessor::ProcessUnUseBinds(CXFA_Node* pFormNode) { | |
| if (!pFormNode) | |
| return; | |
| CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator( | |
| pFormNode); | |
| for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode; | |
| pNode = sIterator.MoveToNext()) { | |
| if (pNode->IsContainerNode()) { | |
| CXFA_Node* pBindNode = pNode->GetBindData(); | |
| if (pBindNode) { | |
| pBindNode->RemoveBindItem(pNode); | |
| pNode->JSNode()->SetBindingNode(nullptr); | |
| } | |
| } | |
| pNode->SetFlag(XFA_NodeFlag_UnusedNode, true); | |
| } | |
| } | |
| void CXFA_ItemLayoutProcessor::ProcessUnUseOverFlow( | |
| CXFA_Node* pLeaderNode, | |
| CXFA_Node* pTrailerNode, | |
| CXFA_ContentLayoutItem* pTrailerItem, | |
| CXFA_Node* pFormNode) { | |
| ProcessUnUseBinds(pLeaderNode); | |
| ProcessUnUseBinds(pTrailerNode); | |
| if (!pFormNode) | |
| return; | |
| if (pFormNode->GetElementType() == XFA_Element::Overflow || | |
| pFormNode->GetElementType() == XFA_Element::Break) { | |
| pFormNode = pFormNode->GetNodeItem(XFA_NODEITEM_Parent); | |
| } | |
| if (pLeaderNode && pFormNode) | |
| pFormNode->RemoveChild(pLeaderNode, true); | |
| if (pTrailerNode && pFormNode) | |
| pFormNode->RemoveChild(pTrailerNode, true); | |
| if (pTrailerItem) | |
| XFA_ReleaseLayoutItem(pTrailerItem); | |
| } | |
| XFA_ItemLayoutProcessorResult CXFA_ItemLayoutProcessor::DoLayoutFlowedContainer( | |
| bool bUseBreakControl, | |
| XFA_AttributeEnum eFlowStrategy, | |
| float fHeightLimit, | |
| float fRealHeight, | |
| CXFA_LayoutContext* pContext, | |
| bool bRootForceTb) { | |
| m_bHasAvailHeight = true; | |
| bool bBreakDone = false; | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| bool bForceEndPage = false; | |
| bool bIsManualBreak = false; | |
| if (m_pCurChildPreprocessor) { | |
| m_pCurChildPreprocessor->m_ePreProcessRs = | |
| XFA_ItemLayoutProcessorResult::Done; | |
| } | |
| CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| m_pFormNode, &bContainerWidthAutoSize, &bContainerHeightAutoSize); | |
| if (pContext && pContext->m_bCurColumnWidthAvaiable) { | |
| bContainerWidthAutoSize = false; | |
| containerSize.width = pContext->m_fCurColumnWidth; | |
| } | |
| if (!bContainerHeightAutoSize) | |
| containerSize.height -= m_fUsedSize; | |
| if (!bContainerHeightAutoSize) { | |
| CXFA_Node* pParentNode = m_pFormNode->GetNodeItem(XFA_NODEITEM_Parent); | |
| bool bFocrTb = false; | |
| if (pParentNode && | |
| GetLayout(pParentNode, &bFocrTb) == XFA_AttributeEnum::Row) { | |
| CXFA_Node* pChildContainer = m_pFormNode->GetNodeItem( | |
| XFA_NODEITEM_FirstChild, XFA_ObjectType::ContainerNode); | |
| if (pChildContainer && | |
| pChildContainer->GetNodeItem(XFA_NODEITEM_NextSibling, | |
| XFA_ObjectType::ContainerNode)) { | |
| containerSize.height = 0; | |
| bContainerHeightAutoSize = true; | |
| } | |
| } | |
| } | |
| CXFA_Node* pMarginNode = | |
| m_pFormNode->GetFirstChildByClass(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fTopInset = 0; | |
| float fRightInset = 0; | |
| float fBottomInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::LeftInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fTopInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::TopInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::RightInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| fBottomInset = pMarginNode->JSNode() | |
| ->GetMeasure(XFA_Attribute::BottomInset) | |
| .ToUnit(XFA_Unit::Pt); | |
| } | |
| float fContentWidthLimit = | |
| bContainerWidthAutoSize ? FLT_MAX | |
| : containerSize.width - fLeftInset - fRightInset; | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| float fAvailHeight = fHeightLimit - fTopInset - fBottomInset; | |
| if (fAvailHeight < 0) | |
| m_bHasAvailHeight = false; | |
| fRealHeight = fRealHeight - fTopInset - fBottomInset; | |
| float fContentCurRowY = 0; | |
| CXFA_ContentLayoutItem* pLayoutChild = nullptr; | |
| if (m_pLayoutItem) { | |
| if (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Done && | |
| eFlowStrategy != XFA_AttributeEnum::Tb) { | |
| pLayoutChild = (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild; | |
| for (CXFA_ContentLayoutItem* pLayoutNext = pLayoutChild; pLayoutNext; | |
| pLayoutNext = (CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling) { | |
| if (pLayoutNext->m_sPos.y != pLayoutChild->m_sPos.y) | |
| pLayoutChild = pLayoutNext; | |
| } | |
| } | |
| for (CXFA_ContentLayoutItem* pLayoutTempChild = | |
| (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild; | |
| pLayoutTempChild != pLayoutChild; | |
| pLayoutTempChild = | |
| (CXFA_ContentLayoutItem*)pLayoutTempChild->m_pNextSibling) { | |
| if (!XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutTempChild->m_pFormNode)) | |
| continue; | |
| fContentCalculatedWidth = std::max( | |
| fContentCalculatedWidth, | |
| pLayoutTempChild->m_sPos.x + pLayoutTempChild->m_sSize.width); | |
| fContentCalculatedHeight = std::max( | |
| fContentCalculatedHeight, | |
| pLayoutTempChild->m_sPos.y + pLayoutTempChild->m_sSize.height); | |
| } | |
| if (pLayoutChild) | |
| fContentCurRowY = pLayoutChild->m_sPos.y; | |
| else | |
| fContentCurRowY = fContentCalculatedHeight; | |
| } | |
| fContentCurRowY += InsertKeepLayoutItems(); | |
| if (m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::None) { | |
| GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, | |
| true); | |
| } | |
| fContentCurRowY += InsertPendingItems(this, m_pFormNode); | |
| if (m_pCurChildPreprocessor && | |
| m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::Container) { | |
| if (ExistContainerKeep(m_pCurChildPreprocessor->GetFormNode(), false)) { | |
| m_pKeepHeadNode = m_pCurChildNode; | |
| m_bIsProcessKeep = true; | |
| m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Keep; | |
| } | |
| } | |
| while (m_nCurChildNodeStage != XFA_ItemLayoutProcessorStages::Done) { | |
| float fContentCurRowHeight = 0; | |
| float fContentCurRowAvailWidth = fContentWidthLimit; | |
| m_fWidthLimite = fContentCurRowAvailWidth; | |
| std::vector<CXFA_ContentLayoutItem*> rgCurLineLayoutItems[3]; | |
| uint8_t uCurHAlignState = | |
| (eFlowStrategy != XFA_AttributeEnum::Rl_tb ? 0 : 2); | |
| if (pLayoutChild) { | |
| for (CXFA_ContentLayoutItem* pLayoutNext = pLayoutChild; pLayoutNext; | |
| pLayoutNext = (CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling) { | |
| if (!pLayoutNext->m_pNextSibling && m_pCurChildPreprocessor && | |
| m_pCurChildPreprocessor->m_pFormNode == pLayoutNext->m_pFormNode) { | |
| pLayoutNext->m_pNext = m_pCurChildPreprocessor->m_pLayoutItem; | |
| m_pCurChildPreprocessor->m_pLayoutItem = pLayoutNext; | |
| break; | |
| } | |
| uint8_t uHAlign = HAlignEnumToInt( | |
| pLayoutNext->m_pFormNode->JSNode()->GetEnum(XFA_Attribute::HAlign)); | |
| rgCurLineLayoutItems[uHAlign].push_back(pLayoutNext); | |
| if (eFlowStrategy == XFA_AttributeEnum::Lr_tb) { | |
| if (uHAlign > uCurHAlignState) | |
| uCurHAlignState = uHAlign; | |
| } else if (uHAlign < uCurHAlignState) { | |
| uCurHAlignState = uHAlign; | |
| } | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace(pLayoutNext->m_pFormNode)) { | |
| if (pLayoutNext->m_sSize.height > fContentCurRowHeight) | |
| fContentCurRowHeight = pLayoutNext->m_sSize.height; | |
| fContentCurRowAvailWidth -= pLayoutNext->m_sSize.width; | |
| } | |
| } | |
| if ((CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild == | |
| pLayoutChild) { | |
| m_pLayoutItem->m_pFirstChild = nullptr; | |
| } else { | |
| CXFA_ContentLayoutItem* pLayoutNext = | |
| (CXFA_ContentLayoutItem*)m_pLayoutItem->m_pFirstChild; | |
| for (; pLayoutNext; | |
| pLayoutNext = | |
| (CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling) { | |
| if ((CXFA_ContentLayoutItem*)pLayoutNext->m_pNextSibling == | |
| pLayoutChild) { | |
| pLayoutNext->m_pNextSibling = nullptr; | |
| break; | |
| } | |
| } | |
| } | |
| CXFA_ContentLayoutItem* pLayoutNextTemp = | |
| (CXFA_ContentLayoutItem*)pLayoutChild; | |
| while (pLayoutNextTemp) { | |
| pLayoutNextTemp->m_pParent = nullptr; | |
| CXFA_ContentLayoutItem* pSaveLayoutNext = | |
| (CXFA_ContentLayoutItem*)pLayoutNextTemp->m_pNextSibling; | |
| pLayoutNextTemp->m_pNextSibling = nullptr; | |
| pLayoutNextTemp = pSaveLayoutNext; | |
| } | |
| pLayoutChild = nullptr; | |
| } | |
| while (m_pCurChildNode) { | |
| std::unique_ptr<CXFA_ItemLayoutProcessor> pProcessor; | |
| bool bAddedItemInRow = false; | |
| fContentCurRowY += InsertPendingItems(this, m_pFormNode); | |
| switch (m_nCurChildNodeStage) { | |
| case XFA_ItemLayoutProcessorStages::Keep: | |
| case XFA_ItemLayoutProcessorStages::None: | |
| break; | |
| case XFA_ItemLayoutProcessorStages::BreakBefore: { | |
| for (auto* item : m_arrayKeepItems) { | |
| m_pLayoutItem->RemoveChild(item); | |
| fContentCalculatedHeight -= item->m_sSize.height; | |
| } | |
| CXFA_Node* pLeaderNode = nullptr; | |
| CXFA_Node* pTrailerNode = nullptr; | |
| bool bCreatePage = false; | |
| if (!bUseBreakControl || !m_pPageMgr || | |
| !m_pPageMgr->ProcessBreakBeforeOrAfter(m_pCurChildNode, true, | |
| pLeaderNode, pTrailerNode, | |
| bCreatePage) || | |
| m_pFormNode->GetElementType() == XFA_Element::Form || | |
| !bCreatePage) { | |
| break; | |
| } | |
| if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) | |
| AddPendingNode(this, pLeaderNode, true); | |
| if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) { | |
| if (m_pFormNode->GetNodeItem(XFA_NODEITEM_Parent) | |
| ->GetElementType() == XFA_Element::Form && | |
| !m_pLayoutItem) { | |
| AddPendingNode(this, pTrailerNode, true); | |
| } else { | |
| auto pTempProcessor = | |
| pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pTrailerNode, | |
| nullptr); | |
| InsertFlowedItem( | |
| this, pTempProcessor.get(), bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, containerSize.height, eFlowStrategy, | |
| &uCurHAlignState, rgCurLineLayoutItems, false, FLT_MAX, | |
| FLT_MAX, fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, pContext, false); | |
| } | |
| } | |
| GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, | |
| m_pFormNode, true); | |
| bForceEndPage = true; | |
| bIsManualBreak = true; | |
| goto SuspendAndCreateNewRow; | |
| } | |
| case XFA_ItemLayoutProcessorStages::BreakAfter: { | |
| CXFA_Node* pLeaderNode = nullptr; | |
| CXFA_Node* pTrailerNode = nullptr; | |
| bool bCreatePage = false; | |
| if (!bUseBreakControl || !m_pPageMgr || | |
| !m_pPageMgr->ProcessBreakBeforeOrAfter(m_pCurChildNode, false, | |
| pLeaderNode, pTrailerNode, | |
| bCreatePage) || | |
| m_pFormNode->GetElementType() == XFA_Element::Form) { | |
| break; | |
| } | |
| if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) { | |
| auto pTempProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| pTrailerNode, nullptr); | |
| InsertFlowedItem( | |
| this, pTempProcessor.get(), bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, containerSize.height, eFlowStrategy, | |
| &uCurHAlignState, rgCurLineLayoutItems, false, FLT_MAX, FLT_MAX, | |
| fContentWidthLimit, &fContentCurRowY, &fContentCurRowAvailWidth, | |
| &fContentCurRowHeight, &bAddedItemInRow, &bForceEndPage, | |
| pContext, false); | |
| } | |
| if (!bCreatePage) { | |
| if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) { | |
| CalculateRowChildPosition( | |
| rgCurLineLayoutItems, eFlowStrategy, bContainerHeightAutoSize, | |
| bContainerWidthAutoSize, &fContentCalculatedWidth, | |
| &fContentCalculatedHeight, &fContentCurRowY, | |
| fContentCurRowHeight, fContentWidthLimit, false); | |
| rgCurLineLayoutItems->clear(); | |
| auto pTempProcessor = | |
| pdfium::MakeUnique<CXFA_ItemLayoutProcessor>(pLeaderNode, | |
| nullptr); | |
| InsertFlowedItem( | |
| this, pTempProcessor.get(), bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, containerSize.height, eFlowStrategy, | |
| &uCurHAlignState, rgCurLineLayoutItems, false, FLT_MAX, | |
| FLT_MAX, fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, pContext, false); | |
| } | |
| } else { | |
| if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) | |
| AddPendingNode(this, pLeaderNode, true); | |
| } | |
| GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, | |
| m_pFormNode, true); | |
| if (bCreatePage) { | |
| bForceEndPage = true; | |
| bIsManualBreak = true; | |
| if (m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::Done) | |
| bBreakDone = true; | |
| } | |
| goto SuspendAndCreateNewRow; | |
| } | |
| case XFA_ItemLayoutProcessorStages::BookendLeader: { | |
| CXFA_Node* pLeaderNode = nullptr; | |
| if (m_pCurChildPreprocessor) { | |
| pProcessor.reset(m_pCurChildPreprocessor); | |
| m_pCurChildPreprocessor = nullptr; | |
| } else if (m_pPageMgr && | |
| m_pPageMgr->ProcessBookendLeaderOrTrailer( | |
| m_pCurChildNode, true, pLeaderNode)) { | |
| pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| pLeaderNode, m_pPageMgr); | |
| } | |
| if (pProcessor) { | |
| if (InsertFlowedItem( | |
| this, pProcessor.get(), bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, containerSize.height, | |
| eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, | |
| bUseBreakControl, fAvailHeight, fRealHeight, | |
| fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, pContext, | |
| false) != XFA_ItemLayoutProcessorResult::Done) { | |
| goto SuspendAndCreateNewRow; | |
| } else { | |
| pProcessor.reset(); | |
| } | |
| } | |
| break; | |
| } | |
| case XFA_ItemLayoutProcessorStages::BookendTrailer: { | |
| CXFA_Node* pTrailerNode = nullptr; | |
| if (m_pCurChildPreprocessor) { | |
| pProcessor.reset(m_pCurChildPreprocessor); | |
| m_pCurChildPreprocessor = nullptr; | |
| } else if (m_pPageMgr && | |
| m_pPageMgr->ProcessBookendLeaderOrTrailer( | |
| m_pCurChildNode, false, pTrailerNode)) { | |
| pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| pTrailerNode, m_pPageMgr); | |
| } | |
| if (pProcessor) { | |
| if (InsertFlowedItem( | |
| this, pProcessor.get(), bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, containerSize.height, | |
| eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, | |
| bUseBreakControl, fAvailHeight, fRealHeight, | |
| fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, pContext, | |
| false) != XFA_ItemLayoutProcessorResult::Done) { | |
| goto SuspendAndCreateNewRow; | |
| } else { | |
| pProcessor.reset(); | |
| } | |
| } | |
| break; | |
| } | |
| case XFA_ItemLayoutProcessorStages::Container: { | |
| ASSERT(m_pCurChildNode->IsContainerNode()); | |
| if (m_pCurChildNode->GetElementType() == XFA_Element::Variables) | |
| break; | |
| if (fContentCurRowY >= fHeightLimit + XFA_LAYOUT_FLOAT_PERCISION && | |
| XFA_ItemLayoutProcessor_IsTakingSpace(m_pCurChildNode)) { | |
| bForceEndPage = true; | |
| goto SuspendAndCreateNewRow; | |
| } | |
| if (!m_pCurChildNode->IsContainerNode()) | |
| break; | |
| bool bNewRow = false; | |
| if (m_pCurChildPreprocessor) { | |
| pProcessor.reset(m_pCurChildPreprocessor); | |
| m_pCurChildPreprocessor = nullptr; | |
| bNewRow = true; | |
| } else { | |
| pProcessor = pdfium::MakeUnique<CXFA_ItemLayoutProcessor>( | |
| m_pCurChildNode, m_pPageMgr); | |
| } | |
| InsertPendingItems(pProcessor.get(), m_pCurChildNode); | |
| XFA_ItemLayoutProcessorResult rs = InsertFlowedItem( | |
| this, pProcessor.get(), bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, containerSize.height, eFlowStrategy, | |
| &uCurHAlignState, rgCurLineLayoutItems, bUseBreakControl, | |
| fAvailHeight, fRealHeight, fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, pContext, bNewRow); | |
| switch (rs) { | |
| case XFA_ItemLayoutProcessorResult::ManualBreak: | |
| bIsManualBreak = true; | |
| case XFA_ItemLayoutProcessorResult::PageFullBreak: | |
| bForceEndPage = true; | |
| case XFA_ItemLayoutProcessorResult::RowFullBreak: | |
| goto SuspendAndCreateNewRow; | |
| case XFA_ItemLayoutProcessorResult::Done: | |
| default: | |
| fContentCurRowY += | |
| InsertPendingItems(pProcessor.get(), m_pCurChildNode); | |
| pProcessor.reset(); | |
| } | |
| break; | |
| } | |
| case XFA_ItemLayoutProcessorStages::Done: | |
| break; | |
| default: | |
| break; | |
| } | |
| GotoNextContainerNode(m_pCurChildNode, m_nCurChildNodeStage, m_pFormNode, | |
| true); | |
| if (bAddedItemInRow && eFlowStrategy == XFA_AttributeEnum::Tb) | |
| break; | |
| continue; | |
| SuspendAndCreateNewRow: | |
| if (pProcessor) | |
| m_pCurChildPreprocessor = pProcessor.release(); | |
| break; | |
| } | |
| CalculateRowChildPosition( | |
| rgCurLineLayoutItems, eFlowStrategy, bContainerHeightAutoSize, | |
| bContainerWidthAutoSize, &fContentCalculatedWidth, | |
| &fContentCalculatedHeight, &fContentCurRowY, fContentCurRowHeight, | |
| fContentWidthLimit, bRootForceTb); | |
| m_fWidthLimite = fContentCurRowAvailWidth; | |
| if (bForceEndPage) | |
| break; | |
| } | |
| bool bRetValue = | |
| m_nCurChildNodeStage == XFA_ItemLayoutProcessorStages::Done && | |
| m_PendingNodes.empty(); | |
| if (bBreakDone) | |
| bRetValue = false; | |
| containerSize = CalculateContainerComponentSizeFromContentSize( | |
| m_pFormNode, bContainerWidthAutoSize, fContentCalculatedWidth, | |
| bContainerHeightAutoSize, fContentCalculatedHeight, containerSize); | |
| if (containerSize.height >= XFA_LAYOUT_FLOAT_PERCISION || m_pLayoutItem || | |
| bRetValue) { | |
| if (!m_pLayoutItem) | |
| m_pLayoutItem = CreateContentLayoutItem(m_pFormNode); | |
| containerSize.height = std::max(containerSize.height, 0.f); | |
| SetCurrentComponentSize(containerSize); | |
| if (bForceEndPage) | |
| m_fUsedSize = 0; | |
| else | |
| m_fUsedSize += m_pLayoutItem->m_sSize.height; | |
| } | |
| return bRetValue | |
| ? XFA_ItemLayoutProcessorResult::Done | |
| : (bIsManualBreak ? XFA_ItemLayoutProcessorResult::ManualBreak | |
| : XFA_ItemLayoutProcessorResult::PageFullBreak); | |
| } | |
| bool CXFA_ItemLayoutProcessor::CalculateRowChildPosition( | |
| std::vector<CXFA_ContentLayoutItem*> (&rgCurLineLayoutItems)[3], | |
| XFA_AttributeEnum eFlowStrategy, | |
| bool bContainerHeightAutoSize, | |
| bool bContainerWidthAutoSize, | |
| float* fContentCalculatedWidth, | |
| float* fContentCalculatedHeight, | |
| float* fContentCurRowY, | |
| float fContentCurRowHeight, | |
| float fContentWidthLimit, | |
| bool bRootForceTb) { | |
| int32_t nGroupLengths[3] = {0, 0, 0}; | |
| float fGroupWidths[3] = {0, 0, 0}; | |
| int32_t nTotalLength = 0; | |
| for (int32_t i = 0; i < 3; i++) { | |
| nGroupLengths[i] = pdfium::CollectionSize<int32_t>(rgCurLineLayoutItems[i]); | |
| for (int32_t c = nGroupLengths[i], j = 0; j < c; j++) { | |
| nTotalLength++; | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[i][j]->m_pFormNode)) { | |
| fGroupWidths[i] += rgCurLineLayoutItems[i][j]->m_sSize.width; | |
| } | |
| } | |
| } | |
| if (!nTotalLength) { | |
| if (bContainerHeightAutoSize) { | |
| *fContentCalculatedHeight = | |
| std::min(*fContentCalculatedHeight, *fContentCurRowY); | |
| } | |
| return false; | |
| } | |
| if (!m_pLayoutItem) | |
| m_pLayoutItem = CreateContentLayoutItem(m_pFormNode); | |
| if (eFlowStrategy != XFA_AttributeEnum::Rl_tb) { | |
| float fCurPos; | |
| fCurPos = 0; | |
| for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) { | |
| if (bRootForceTb) { | |
| rgCurLineLayoutItems[0][j]->m_sPos = CalculatePositionedContainerPos( | |
| rgCurLineLayoutItems[0][j]->m_pFormNode, | |
| rgCurLineLayoutItems[0][j]->m_sSize); | |
| } else { | |
| rgCurLineLayoutItems[0][j]->m_sPos = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[0][j]->m_pFormNode)) { | |
| fCurPos += rgCurLineLayoutItems[0][j]->m_sSize.width; | |
| } | |
| } | |
| m_pLayoutItem->AddChild(rgCurLineLayoutItems[0][j]); | |
| m_fLastRowWidth = fCurPos; | |
| } | |
| fCurPos = (fContentWidthLimit + fGroupWidths[0] - fGroupWidths[1] - | |
| fGroupWidths[2]) / | |
| 2; | |
| for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) { | |
| if (bRootForceTb) { | |
| rgCurLineLayoutItems[1][j]->m_sPos = CalculatePositionedContainerPos( | |
| rgCurLineLayoutItems[1][j]->m_pFormNode, | |
| rgCurLineLayoutItems[1][j]->m_sSize); | |
| } else { | |
| rgCurLineLayoutItems[1][j]->m_sPos = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[1][j]->m_pFormNode)) { | |
| fCurPos += rgCurLineLayoutItems[1][j]->m_sSize.width; | |
| } | |
| } | |
| m_pLayoutItem->AddChild(rgCurLineLayoutItems[1][j]); | |
| m_fLastRowWidth = fCurPos; | |
| } | |
| fCurPos = fContentWidthLimit - fGroupWidths[2]; | |
| for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) { | |
| if (bRootForceTb) { | |
| rgCurLineLayoutItems[2][j]->m_sPos = CalculatePositionedContainerPos( | |
| rgCurLineLayoutItems[2][j]->m_pFormNode, | |
| rgCurLineLayoutItems[2][j]->m_sSize); | |
| } else { | |
| rgCurLineLayoutItems[2][j]->m_sPos = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[2][j]->m_pFormNode)) { | |
| fCurPos += rgCurLineLayoutItems[2][j]->m_sSize.width; | |
| } | |
| } | |
| m_pLayoutItem->AddChild(rgCurLineLayoutItems[2][j]); | |
| m_fLastRowWidth = fCurPos; | |
| } | |
| } else { | |
| float fCurPos; | |
| fCurPos = fGroupWidths[0]; | |
| for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) { | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[0][j]->m_pFormNode)) { | |
| fCurPos -= rgCurLineLayoutItems[0][j]->m_sSize.width; | |
| } | |
| rgCurLineLayoutItems[0][j]->m_sPos = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| m_pLayoutItem->AddChild(rgCurLineLayoutItems[0][j]); | |
| m_fLastRowWidth = fCurPos; | |
| } | |
| fCurPos = (fContentWidthLimit + fGroupWidths[0] + fGroupWidths[1] - | |
| fGroupWidths[2]) / | |
| 2; | |
| for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) { | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[1][j]->m_pFormNode)) { | |
| fCurPos -= rgCurLineLayoutItems[1][j]->m_sSize.width; | |
| } | |
| rgCurLineLayoutItems[1][j]->m_sPos = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| m_pLayoutItem->AddChild(rgCurLineLayoutItems[1][j]); | |
| m_fLastRowWidth = fCurPos; | |
| } | |
| fCurPos = fContentWidthLimit; | |
| for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) { | |
| if (XFA_ItemLayoutProcessor_IsTakingSpace( | |
| rgCurLineLayoutItems[2][j]->m_pFormNode)) { | |
| fCurPos -= rgCurLineLayoutItems[2][j]->m_sSize.width; | |
| } | |
| rgCurLineLayoutItems[2][j]->m_sPos = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| m_pLayoutItem->AddChild(rgCurLineLayoutItems[2][j]); | |
| m_fLastRowWidth = fCurPos; | |
| } | |
| } | |
| m_fLastRowY = *fContentCurRowY; | |
| *fContentCurRowY += fContentCurRowHeight; | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = fGroupWidths[0]; | |
| if (fContentWidthLimit < FLT_MAX && | |
| fContentWidthLimit > fChildSuppliedWidth) { | |
| fChildSuppliedWidth = fContentWidthLimit; | |
| } | |
| *fContentCalculatedWidth = | |
| std::max(*fContentCalculatedWidth, fChildSuppliedWidth); | |
| } | |
| if (bContainerHeightAutoSize) { | |
| *fContentCalculatedHeight = | |
| std::max(*fContentCalculatedHeight, *fContentCurRowY); | |
| } | |
| return true; | |
| } | |
| CXFA_Node* CXFA_ItemLayoutProcessor::GetSubformSetParent( | |
| CXFA_Node* pSubformSet) { | |
| if (pSubformSet && pSubformSet->GetElementType() == XFA_Element::SubformSet) { | |
| CXFA_Node* pParent = pSubformSet->GetNodeItem(XFA_NODEITEM_Parent); | |
| while (pParent) { | |
| if (pParent->GetElementType() != XFA_Element::SubformSet) | |
| return pParent; | |
| pParent = pParent->GetNodeItem(XFA_NODEITEM_Parent); | |
| } | |
| } | |
| return pSubformSet; | |
| } | |
| void CXFA_ItemLayoutProcessor::DoLayoutField() { | |
| if (m_pLayoutItem) | |
| return; | |
| ASSERT(m_pCurChildNode == XFA_LAYOUT_INVALIDNODE); | |
| m_pLayoutItem = CreateContentLayoutItem(m_pFormNode); | |
| if (!m_pLayoutItem) | |
| return; | |
| CXFA_Document* pDocument = m_pFormNode->GetDocument(); | |
| CXFA_FFNotify* pNotify = pDocument->GetNotify(); | |
| CFX_SizeF size(-1, -1); | |
| pNotify->StartFieldDrawLayout(m_pFormNode, size.width, size.height); | |
| int32_t nRotate = | |
| XFA_MapRotation(m_pFormNode->JSNode()->GetInteger(XFA_Attribute::Rotate)); | |
| if (nRotate == 90 || nRotate == 270) | |
| std::swap(size.width, size.height); | |
| SetCurrentComponentSize(size); | |
| } | |
| XFA_ItemLayoutProcessorResult CXFA_ItemLayoutProcessor::DoLayout( | |
| bool bUseBreakControl, | |
| float fHeightLimit, | |
| float fRealHeight, | |
| CXFA_LayoutContext* pContext) { | |
| switch (m_pFormNode->GetElementType()) { | |
| case XFA_Element::Subform: | |
| case XFA_Element::Area: | |
| case XFA_Element::ExclGroup: | |
| case XFA_Element::SubformSet: { | |
| bool bRootForceTb = false; | |
| CXFA_Node* pLayoutNode = GetSubformSetParent(m_pFormNode); | |
| XFA_AttributeEnum eLayoutStrategy = GetLayout(pLayoutNode, &bRootForceTb); | |
| switch (eLayoutStrategy) { | |
| case XFA_AttributeEnum::Tb: | |
| case XFA_AttributeEnum::Lr_tb: | |
| case XFA_AttributeEnum::Rl_tb: | |
| return DoLayoutFlowedContainer(bUseBreakControl, eLayoutStrategy, | |
| fHeightLimit, fRealHeight, pContext, | |
| bRootForceTb); | |
| case XFA_AttributeEnum::Position: | |
| case XFA_AttributeEnum::Row: | |
| case XFA_AttributeEnum::Rl_row: | |
| default: | |
| DoLayoutPositionedContainer(pContext); | |
| m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Done; | |
| return XFA_ItemLayoutProcessorResult::Done; | |
| case XFA_AttributeEnum::Table: | |
| DoLayoutTableContainer(pLayoutNode); | |
| m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Done; | |
| return XFA_ItemLayoutProcessorResult::Done; | |
| } | |
| } | |
| case XFA_Element::Draw: | |
| case XFA_Element::Field: | |
| DoLayoutField(); | |
| m_nCurChildNodeStage = XFA_ItemLayoutProcessorStages::Done; | |
| return XFA_ItemLayoutProcessorResult::Done; | |
| case XFA_Element::ContentArea: | |
| return XFA_ItemLayoutProcessorResult::Done; | |
| default: | |
| return XFA_ItemLayoutProcessorResult::Done; | |
| } | |
| } | |
| CFX_SizeF CXFA_ItemLayoutProcessor::GetCurrentComponentSize() { | |
| return CFX_SizeF(m_pLayoutItem->m_sSize.width, m_pLayoutItem->m_sSize.height); | |
| } | |
| void CXFA_ItemLayoutProcessor::SetCurrentComponentPos(const CFX_PointF& pos) { | |
| m_pLayoutItem->m_sPos = pos; | |
| } | |
| void CXFA_ItemLayoutProcessor::SetCurrentComponentSize(const CFX_SizeF& size) { | |
| m_pLayoutItem->m_sSize = size; | |
| } | |
| bool CXFA_ItemLayoutProcessor::JudgeLeaderOrTrailerForOccur( | |
| CXFA_Node* pFormNode) { | |
| if (!pFormNode) | |
| return false; | |
| CXFA_Node* pTemplate = pFormNode->GetTemplateNode(); | |
| if (!pTemplate) | |
| pTemplate = pFormNode; | |
| int32_t iMax = | |
| CXFA_OccurData(pTemplate->GetFirstChildByClass(XFA_Element::Occur)) | |
| .GetMax(); | |
| if (iMax < 0) | |
| return true; | |
| int32_t iCount = m_PendingNodesCount[pTemplate]; | |
| if (iCount >= iMax) | |
| return false; | |
| m_PendingNodesCount[pTemplate] = iCount + 1; | |
| return true; | |
| } |