| // Copyright 2014 The PDFium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "xfa/fxfa/layout/cxfa_contentlayoutprocessor.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <utility> | |
| #include <vector> | |
| #include "core/fxcrt/check.h" | |
| #include "core/fxcrt/containers/adapters.h" | |
| #include "core/fxcrt/notreached.h" | |
| #include "core/fxcrt/stl_util.h" | |
| #include "fxjs/gc/container_trace.h" | |
| #include "fxjs/xfa/cjx_object.h" | |
| #include "xfa/fxfa/cxfa_ffdoc.h" | |
| #include "xfa/fxfa/cxfa_ffnotify.h" | |
| #include "xfa/fxfa/cxfa_ffwidget.h" | |
| #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h" | |
| #include "xfa/fxfa/layout/cxfa_layoutprocessor.h" | |
| #include "xfa/fxfa/layout/cxfa_viewlayoutitem.h" | |
| #include "xfa/fxfa/layout/cxfa_viewlayoutprocessor.h" | |
| #include "xfa/fxfa/parser/cxfa_document.h" | |
| #include "xfa/fxfa/parser/cxfa_keep.h" | |
| #include "xfa/fxfa/parser/cxfa_localemgr.h" | |
| #include "xfa/fxfa/parser/cxfa_margin.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_occur.h" | |
| #include "xfa/fxfa/parser/cxfa_para.h" | |
| #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h" | |
| #include "xfa/fxfa/parser/xfa_utils.h" | |
| namespace { | |
| using NextPosRow = std::array<uint8_t, 9>; | |
| constexpr std::array<const NextPosRow, 4> kNextPosTable = {{ | |
| {{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}}, | |
| }}; | |
| std::vector<WideString> SeparateStringOnSpace( | |
| pdfium::span<const wchar_t> spStr) { | |
| std::vector<WideString> ret; | |
| if (spStr.empty()) { | |
| return ret; | |
| } | |
| size_t nPos = 0; | |
| size_t nToken = 0; | |
| while (nPos < spStr.size()) { | |
| if (spStr[nPos] == L' ') { | |
| ret.emplace_back(WideStringView(spStr.subspan(nToken, nPos - nToken))); | |
| nToken = nPos + 1; | |
| } | |
| nPos++; | |
| } | |
| ret.emplace_back(WideStringView(spStr.subspan(nToken, nPos - nToken))); | |
| return ret; | |
| } | |
| void UpdateWidgetSize(CXFA_ContentLayoutItem* pLayoutItem, | |
| float* pWidth, | |
| float* pHeight) { | |
| CXFA_Node* pNode = pLayoutItem->GetFormNode(); | |
| switch (pNode->GetElementType()) { | |
| case XFA_Element::Subform: | |
| case XFA_Element::Area: | |
| case XFA_Element::ExclGroup: | |
| case XFA_Element::SubformSet: { | |
| if (*pWidth < -kXFALayoutPrecision) { | |
| *pWidth = pLayoutItem->s_size_.width; | |
| } | |
| if (*pHeight < -kXFALayoutPrecision) { | |
| *pHeight = pLayoutItem->s_size_.height; | |
| } | |
| break; | |
| } | |
| case XFA_Element::Draw: | |
| case XFA_Element::Field: { | |
| pNode->GetDocument()->GetNotify()->StartFieldDrawLayout(pNode, pWidth, | |
| pHeight); | |
| 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) { | |
| std::optional<CXFA_Measurement> wValue = | |
| pFormNode->JSObject()->TryMeasure(XFA_Attribute::W, false); | |
| if (wValue.has_value() && wValue->GetValue() > kXFALayoutPrecision) { | |
| containerSize.width = wValue->ToUnit(XFA_Unit::Pt); | |
| *bContainerWidthAutoSize = false; | |
| } | |
| std::optional<CXFA_Measurement> hValue = | |
| pFormNode->JSObject()->TryMeasure(XFA_Attribute::H, false); | |
| if (hValue.has_value() && hValue->GetValue() > kXFALayoutPrecision) { | |
| containerSize.height = hValue->ToUnit(XFA_Unit::Pt); | |
| *bContainerHeightAutoSize = false; | |
| } | |
| } | |
| if (*bContainerWidthAutoSize && eType == XFA_Element::Subform) { | |
| std::optional<CXFA_Measurement> maxW = | |
| pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxW, false); | |
| if (maxW.has_value() && maxW->GetValue() > kXFALayoutPrecision) { | |
| containerSize.width = maxW->ToUnit(XFA_Unit::Pt); | |
| *bContainerWidthAutoSize = false; | |
| } | |
| std::optional<CXFA_Measurement> maxH = | |
| pFormNode->JSObject()->TryMeasure(XFA_Attribute::MaxH, false); | |
| if (maxH.has_value() && maxH->GetValue() > kXFALayoutPrecision) { | |
| 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_Margin* pMarginNode = | |
| pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin); | |
| if (bContainerWidthAutoSize) { | |
| componentSize.width = fContentCalculatedWidth; | |
| if (pMarginNode) { | |
| std::optional<CXFA_Measurement> leftInset = | |
| pMarginNode->JSObject()->TryMeasure(XFA_Attribute::LeftInset, false); | |
| if (leftInset.has_value()) { | |
| componentSize.width += leftInset->ToUnit(XFA_Unit::Pt); | |
| } | |
| std::optional<CXFA_Measurement> rightInset = | |
| pMarginNode->JSObject()->TryMeasure(XFA_Attribute::RightInset, false); | |
| if (rightInset.has_value()) { | |
| componentSize.width += rightInset->ToUnit(XFA_Unit::Pt); | |
| } | |
| } | |
| } | |
| if (bContainerHeightAutoSize) { | |
| componentSize.height = fContentCalculatedHeight; | |
| if (pMarginNode) { | |
| std::optional<CXFA_Measurement> topInset = | |
| pMarginNode->JSObject()->TryMeasure(XFA_Attribute::TopInset, false); | |
| if (topInset.has_value()) { | |
| componentSize.height += topInset->ToUnit(XFA_Unit::Pt); | |
| } | |
| std::optional<CXFA_Measurement> bottomInset = | |
| pMarginNode->JSObject()->TryMeasure(XFA_Attribute::BottomInset, | |
| false); | |
| if (bottomInset.has_value()) { | |
| componentSize.height += bottomInset->ToUnit(XFA_Unit::Pt); | |
| } | |
| } | |
| } | |
| return componentSize; | |
| } | |
| CFX_FloatRect GetMarginInset(const CXFA_Margin* pMargin) { | |
| CFX_FloatRect inset; | |
| if (!pMargin) { | |
| return inset; | |
| } | |
| inset.left = pMargin->JSObject()->GetMeasureInUnit(XFA_Attribute::LeftInset, | |
| XFA_Unit::Pt); | |
| inset.top = pMargin->JSObject()->GetMeasureInUnit(XFA_Attribute::TopInset, | |
| XFA_Unit::Pt); | |
| inset.right = pMargin->JSObject()->GetMeasureInUnit(XFA_Attribute::RightInset, | |
| XFA_Unit::Pt); | |
| inset.bottom = pMargin->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::BottomInset, XFA_Unit::Pt); | |
| return inset; | |
| } | |
| void RelocateTableRowCells(CXFA_ContentLayoutItem* pLayoutRow, | |
| const std::vector<float>& rgSpecifiedColumnWidths, | |
| XFA_AttributeValue eLayout) { | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| const CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| pLayoutRow->GetFormNode(), &bContainerWidthAutoSize, | |
| &bContainerHeightAutoSize); | |
| CXFA_Margin* pMargin = | |
| pLayoutRow->GetFormNode()->GetFirstChildByClass<CXFA_Margin>( | |
| XFA_Element::Margin); | |
| const CFX_FloatRect inset = GetMarginInset(pMargin); | |
| const float fContentWidthLimit = | |
| bContainerWidthAutoSize ? FLT_MAX | |
| : containerSize.width - inset.left - inset.right; | |
| const float fContentCurrentHeight = | |
| pLayoutRow->s_size_.height - inset.top - inset.bottom; | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| float fCurrentColX = 0; | |
| size_t nCurrentColIdx = 0; | |
| bool bMetWholeRowCell = false; | |
| for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem(); | |
| if (!pLayoutChild) { | |
| continue; | |
| } | |
| const int32_t nOriginalColSpan = | |
| pLayoutChild->GetFormNode()->JSObject()->GetInteger( | |
| XFA_Attribute::ColSpan); | |
| size_t nColSpan; | |
| if (nOriginalColSpan > 0) { | |
| nColSpan = static_cast<size_t>(nOriginalColSpan); | |
| } else if (nOriginalColSpan == -1) { | |
| nColSpan = rgSpecifiedColumnWidths.size(); | |
| } else { | |
| continue; | |
| } | |
| CHECK(nCurrentColIdx <= rgSpecifiedColumnWidths.size()); | |
| const size_t remaining = rgSpecifiedColumnWidths.size() - nCurrentColIdx; | |
| nColSpan = std::min(nColSpan, remaining); | |
| float fColSpanWidth = 0; | |
| for (size_t i = 0; i < nColSpan; i++) { | |
| fColSpanWidth += rgSpecifiedColumnWidths[nCurrentColIdx + i]; | |
| } | |
| if (nOriginalColSpan == -1 || | |
| nColSpan != static_cast<size_t>(nOriginalColSpan)) { | |
| fColSpanWidth = bMetWholeRowCell ? 0 | |
| : std::max(fColSpanWidth, | |
| pLayoutChild->s_size_.height); | |
| } | |
| if (nOriginalColSpan == -1) { | |
| bMetWholeRowCell = true; | |
| } | |
| pLayoutChild->s_pos_ = CFX_PointF(fCurrentColX, 0); | |
| pLayoutChild->s_size_.width = fColSpanWidth; | |
| if (!pLayoutChild->GetFormNode()->PresenceRequiresSpace()) { | |
| continue; | |
| } | |
| fCurrentColX += fColSpanWidth; | |
| nCurrentColIdx += nColSpan; | |
| float fNewHeight = bContainerHeightAutoSize ? -1 : fContentCurrentHeight; | |
| UpdateWidgetSize(pLayoutChild, &fColSpanWidth, &fNewHeight); | |
| pLayoutChild->s_size_.height = fNewHeight; | |
| if (bContainerHeightAutoSize) { | |
| fContentCalculatedHeight = | |
| std::max(fContentCalculatedHeight, pLayoutChild->s_size_.height); | |
| } | |
| } | |
| if (bContainerHeightAutoSize) { | |
| for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem(); | |
| if (!pLayoutChild) { | |
| continue; | |
| } | |
| UpdateWidgetSize(pLayoutChild, &pLayoutChild->s_size_.width, | |
| &fContentCalculatedHeight); | |
| float fOldChildHeight = pLayoutChild->s_size_.height; | |
| pLayoutChild->s_size_.height = fContentCalculatedHeight; | |
| CXFA_Para* pParaNode = | |
| pLayoutChild->GetFormNode()->GetFirstChildByClass<CXFA_Para>( | |
| XFA_Element::Para); | |
| if (!pParaNode || !pLayoutChild->GetFirstChild()) { | |
| continue; | |
| } | |
| float fOffHeight = fContentCalculatedHeight - fOldChildHeight; | |
| XFA_AttributeValue eVType = | |
| pParaNode->JSObject()->GetEnum(XFA_Attribute::VAlign); | |
| switch (eVType) { | |
| case XFA_AttributeValue::Middle: | |
| fOffHeight = fOffHeight / 2; | |
| break; | |
| case XFA_AttributeValue::Bottom: | |
| break; | |
| case XFA_AttributeValue::Top: | |
| default: | |
| fOffHeight = 0; | |
| break; | |
| } | |
| if (fOffHeight <= 0) { | |
| continue; | |
| } | |
| for (CXFA_LayoutItem* pInnerIter = pLayoutChild->GetFirstChild(); | |
| pInnerIter; pInnerIter = pInnerIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pInnerChild = pInnerIter->AsContentLayoutItem(); | |
| if (pInnerChild) { | |
| pInnerChild->s_pos_.y += fOffHeight; | |
| } | |
| } | |
| } | |
| } | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = fCurrentColX; | |
| if (fContentWidthLimit < FLT_MAX && | |
| fContentWidthLimit > fChildSuppliedWidth) { | |
| fChildSuppliedWidth = fContentWidthLimit; | |
| } | |
| fContentCalculatedWidth = | |
| std::max(fContentCalculatedWidth, fChildSuppliedWidth); | |
| } else { | |
| fContentCalculatedWidth = containerSize.width - inset.left - inset.right; | |
| } | |
| if (pLayoutRow->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout) == | |
| XFA_AttributeValue::Rl_row) { | |
| for (CXFA_LayoutItem* pIter = pLayoutRow->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem(); | |
| if (!pLayoutChild) { | |
| continue; | |
| } | |
| pLayoutChild->s_pos_.x = fContentCalculatedWidth - | |
| pLayoutChild->s_pos_.x - | |
| pLayoutChild->s_size_.width; | |
| } | |
| } | |
| pLayoutRow->s_size_ = CalculateContainerComponentSizeFromContentSize( | |
| pLayoutRow->GetFormNode(), bContainerWidthAutoSize, | |
| fContentCalculatedWidth, bContainerHeightAutoSize, | |
| fContentCalculatedHeight, containerSize); | |
| } | |
| XFA_AttributeValue GetLayout(CXFA_Node* pFormNode, bool* bRootForceTb) { | |
| *bRootForceTb = false; | |
| std::optional<XFA_AttributeValue> layoutMode = | |
| pFormNode->JSObject()->TryEnum(XFA_Attribute::Layout, false); | |
| if (layoutMode.has_value()) { | |
| return layoutMode.value(); | |
| } | |
| CXFA_Node* pParentNode = pFormNode->GetParent(); | |
| if (pParentNode && pParentNode->GetElementType() == XFA_Element::Form) { | |
| *bRootForceTb = true; | |
| return XFA_AttributeValue::Tb; | |
| } | |
| return XFA_AttributeValue::Position; | |
| } | |
| bool ExistContainerKeep(CXFA_Node* pCurNode, bool bPreFind) { | |
| if (!pCurNode || !pCurNode->PresenceRequiresSpace()) { | |
| return false; | |
| } | |
| CXFA_Node* pPreContainer = bPreFind ? pCurNode->GetPrevContainerSibling() | |
| : pCurNode->GetNextContainerSibling(); | |
| if (!pPreContainer) { | |
| return false; | |
| } | |
| CXFA_Keep* pKeep = | |
| pCurNode->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep); | |
| if (pKeep) { | |
| XFA_Attribute eKeepType = XFA_Attribute::Previous; | |
| if (!bPreFind) { | |
| eKeepType = XFA_Attribute::Next; | |
| } | |
| std::optional<XFA_AttributeValue> previous = | |
| pKeep->JSObject()->TryEnum(eKeepType, false); | |
| if (previous == XFA_AttributeValue::ContentArea || | |
| previous == XFA_AttributeValue::PageArea) { | |
| return true; | |
| } | |
| } | |
| pKeep = pPreContainer->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep); | |
| if (!pKeep) { | |
| return false; | |
| } | |
| XFA_Attribute eKeepType = XFA_Attribute::Next; | |
| if (!bPreFind) { | |
| eKeepType = XFA_Attribute::Previous; | |
| } | |
| std::optional<XFA_AttributeValue> next = | |
| pKeep->JSObject()->TryEnum(eKeepType, false); | |
| if (next == XFA_AttributeValue::ContentArea || | |
| next == XFA_AttributeValue::PageArea) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> FindBreakBeforeNode( | |
| CXFA_Node* pContainerNode, | |
| CXFA_Node** pCurActionNode) { | |
| for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode; | |
| pBreakNode = pBreakNode->GetNextSibling()) { | |
| switch (pBreakNode->GetElementType()) { | |
| case XFA_Element::BreakBefore: | |
| *pCurActionNode = pBreakNode; | |
| return CXFA_ContentLayoutProcessor::Stage::kBreakBefore; | |
| case XFA_Element::Break: | |
| if (pBreakNode->JSObject()->GetEnum(XFA_Attribute::Before) == | |
| XFA_AttributeValue::Auto) { | |
| break; | |
| } | |
| *pCurActionNode = pBreakNode; | |
| return CXFA_ContentLayoutProcessor::Stage::kBreakBefore; | |
| default: | |
| break; | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> FindBreakAfterNode( | |
| CXFA_Node* pContainerNode, | |
| CXFA_Node** pCurActionNode) { | |
| for (CXFA_Node* pBreakNode = pContainerNode; pBreakNode; | |
| pBreakNode = pBreakNode->GetNextSibling()) { | |
| switch (pBreakNode->GetElementType()) { | |
| case XFA_Element::BreakAfter: | |
| *pCurActionNode = pBreakNode; | |
| return CXFA_ContentLayoutProcessor::Stage::kBreakAfter; | |
| case XFA_Element::Break: | |
| if (pBreakNode->JSObject()->GetEnum(XFA_Attribute::After) == | |
| XFA_AttributeValue::Auto) { | |
| break; | |
| } | |
| *pCurActionNode = pBreakNode; | |
| return CXFA_ContentLayoutProcessor::Stage::kBreakAfter; | |
| default: | |
| break; | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| void DeleteLayoutGeneratedNode(CXFA_Node* pGenerateNode) { | |
| CXFA_FFNotify* pNotify = pGenerateNode->GetDocument()->GetNotify(); | |
| auto* pDocLayout = | |
| CXFA_LayoutProcessor::FromDocument(pGenerateNode->GetDocument()); | |
| CXFA_NodeIterator sIterator(pGenerateNode); | |
| for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; | |
| pNode = sIterator.MoveToNext()) { | |
| CXFA_ContentLayoutItem* pCurLayoutItem = | |
| ToContentLayoutItem(pNode->JSObject()->GetLayoutItem()); | |
| while (pCurLayoutItem) { | |
| CXFA_ContentLayoutItem* pNextLayoutItem = pCurLayoutItem->GetNext(); | |
| pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem); | |
| pCurLayoutItem = pNextLayoutItem; | |
| } | |
| } | |
| pGenerateNode->GetParent()->RemoveChildAndNotify(pGenerateNode, true); | |
| } | |
| uint8_t HAlignEnumToInt(XFA_AttributeValue eHAlign) { | |
| switch (eHAlign) { | |
| case XFA_AttributeValue::Center: | |
| return 1; | |
| case XFA_AttributeValue::Right: | |
| return 2; | |
| case XFA_AttributeValue::Left: | |
| default: | |
| return 0; | |
| } | |
| } | |
| bool FindLayoutItemSplitPos(CXFA_ContentLayoutItem* pLayoutItem, | |
| float fCurVerticalOffset, | |
| float* fProposedSplitPos, | |
| bool* bAppChange, | |
| bool bCalculateMargin) { | |
| CXFA_Node* pFormNode = pLayoutItem->GetFormNode(); | |
| if (*fProposedSplitPos <= fCurVerticalOffset + kXFALayoutPrecision || | |
| *fProposedSplitPos > fCurVerticalOffset + pLayoutItem->s_size_.height - | |
| kXFALayoutPrecision) { | |
| return false; | |
| } | |
| switch (pFormNode->GetIntact()) { | |
| case XFA_AttributeValue::None: { | |
| bool bAnyChanged = false; | |
| CXFA_Document* document = pFormNode->GetDocument(); | |
| CXFA_FFNotify* pNotify = document->GetNotify(); | |
| float fCurTopMargin = 0; | |
| float fCurBottomMargin = 0; | |
| CXFA_Margin* pMarginNode = | |
| pFormNode->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin); | |
| if (pMarginNode && bCalculateMargin) { | |
| fCurTopMargin = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::TopInset, XFA_Unit::Pt); | |
| fCurBottomMargin = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::BottomInset, XFA_Unit::Pt); | |
| } | |
| bool bChanged = true; | |
| while (bChanged) { | |
| bChanged = false; | |
| { | |
| std::optional<float> fRelSplitPos = pFormNode->FindSplitPos( | |
| pNotify->GetFFDoc()->GetDocView(), pLayoutItem->GetIndex(), | |
| *fProposedSplitPos - fCurVerticalOffset); | |
| if (fRelSplitPos.has_value()) { | |
| bAnyChanged = true; | |
| bChanged = true; | |
| *fProposedSplitPos = fCurVerticalOffset + fRelSplitPos.value(); | |
| *bAppChange = true; | |
| if (*fProposedSplitPos <= | |
| fCurVerticalOffset + kXFALayoutPrecision) { | |
| return true; | |
| } | |
| } | |
| } | |
| float fRelSplitPos = *fProposedSplitPos - fCurBottomMargin; | |
| for (CXFA_LayoutItem* pIter = pLayoutItem->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pChildItem = pIter->AsContentLayoutItem(); | |
| if (!pChildItem) { | |
| continue; | |
| } | |
| float fChildOffset = | |
| fCurVerticalOffset + fCurTopMargin + pChildItem->s_pos_.y; | |
| bool bChange = false; | |
| if (FindLayoutItemSplitPos(pChildItem, fChildOffset, &fRelSplitPos, | |
| &bChange, bCalculateMargin)) { | |
| if (fRelSplitPos - fChildOffset < kXFALayoutPrecision && bChange) { | |
| *fProposedSplitPos = fRelSplitPos - fCurTopMargin; | |
| } else { | |
| *fProposedSplitPos = fRelSplitPos + fCurBottomMargin; | |
| } | |
| bAnyChanged = true; | |
| bChanged = true; | |
| if (*fProposedSplitPos <= | |
| fCurVerticalOffset + kXFALayoutPrecision) { | |
| return true; | |
| } | |
| if (bAnyChanged) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return bAnyChanged; | |
| } | |
| case XFA_AttributeValue::ContentArea: | |
| case XFA_AttributeValue::PageArea: { | |
| *fProposedSplitPos = fCurVerticalOffset; | |
| return true; | |
| } | |
| default: | |
| return false; | |
| } | |
| } | |
| CFX_PointF CalculatePositionedContainerPos(CXFA_Node* pNode, | |
| const CFX_SizeF& size) { | |
| XFA_AttributeValue eAnchorType = | |
| pNode->JSObject()->GetEnum(XFA_Attribute::AnchorType); | |
| int32_t nAnchorType = 0; | |
| switch (eAnchorType) { | |
| case XFA_AttributeValue::TopLeft: | |
| nAnchorType = 0; | |
| break; | |
| case XFA_AttributeValue::TopCenter: | |
| nAnchorType = 1; | |
| break; | |
| case XFA_AttributeValue::TopRight: | |
| nAnchorType = 2; | |
| break; | |
| case XFA_AttributeValue::MiddleLeft: | |
| nAnchorType = 3; | |
| break; | |
| case XFA_AttributeValue::MiddleCenter: | |
| nAnchorType = 4; | |
| break; | |
| case XFA_AttributeValue::MiddleRight: | |
| nAnchorType = 5; | |
| break; | |
| case XFA_AttributeValue::BottomLeft: | |
| nAnchorType = 6; | |
| break; | |
| case XFA_AttributeValue::BottomCenter: | |
| nAnchorType = 7; | |
| break; | |
| case XFA_AttributeValue::BottomRight: | |
| nAnchorType = 8; | |
| break; | |
| default: | |
| break; | |
| } | |
| CFX_PointF pos( | |
| pNode->JSObject()->GetMeasureInUnit(XFA_Attribute::X, XFA_Unit::Pt), | |
| pNode->JSObject()->GetMeasureInUnit(XFA_Attribute::Y, XFA_Unit::Pt)); | |
| int32_t nRotate = | |
| XFA_MapRotation(pNode->JSObject()->GetInteger(XFA_Attribute::Rotate)) / | |
| 90; | |
| int32_t nAbsoluteAnchorType = kNextPosTable[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_ContentLayoutProcessor::CXFA_ContentLayoutProcessor( | |
| cppgc::Heap* pHeap, | |
| CXFA_Node* pNode, | |
| CXFA_ViewLayoutProcessor* pViewLayoutProcessor) | |
| : heap_(pHeap), | |
| form_node_(pNode), | |
| view_layout_processor_(pViewLayoutProcessor) { | |
| DCHECK(GetFormNode()); | |
| DCHECK(GetFormNode()->IsContainerNode() || | |
| GetFormNode()->GetElementType() == XFA_Element::Form); | |
| old_layout_item_ = | |
| ToContentLayoutItem(GetFormNode()->JSObject()->GetLayoutItem()); | |
| } | |
| CXFA_ContentLayoutProcessor::~CXFA_ContentLayoutProcessor() = default; | |
| void CXFA_ContentLayoutProcessor::Trace(cppgc::Visitor* visitor) const { | |
| visitor->Trace(form_node_); | |
| visitor->Trace(cur_child_node_); | |
| visitor->Trace(keep_head_node_); | |
| visitor->Trace(keep_tail_node_); | |
| visitor->Trace(layout_item_); | |
| visitor->Trace(old_layout_item_); | |
| visitor->Trace(view_layout_processor_); | |
| visitor->Trace(cur_child_preprocessor_); | |
| ContainerTrace(visitor, array_keep_items_); | |
| ContainerTrace(visitor, pending_nodes_); | |
| ContainerTrace(visitor, pending_nodes_count_); | |
| } | |
| CXFA_ContentLayoutItem* CXFA_ContentLayoutProcessor::CreateContentLayoutItem( | |
| CXFA_Node* pFormNode) { | |
| if (!pFormNode) { | |
| return nullptr; | |
| } | |
| if (old_layout_item_) { | |
| CXFA_ContentLayoutItem* pLayoutItem = old_layout_item_; | |
| old_layout_item_ = old_layout_item_->GetNext(); | |
| return pLayoutItem; | |
| } | |
| CXFA_FFNotify* pNotify = pFormNode->GetDocument()->GetNotify(); | |
| auto* pNewLayoutItem = cppgc::MakeGarbageCollected<CXFA_ContentLayoutItem>( | |
| GetHeap()->GetAllocationHandle(), pFormNode, | |
| pNotify->OnCreateContentLayoutItem(pFormNode)); | |
| CXFA_ContentLayoutItem* pPrevLayoutItem = | |
| ToContentLayoutItem(pFormNode->JSObject()->GetLayoutItem()); | |
| if (pPrevLayoutItem) { | |
| pPrevLayoutItem->GetLast()->InsertAfter(pNewLayoutItem); | |
| } else { | |
| pFormNode->JSObject()->SetLayoutItem(pNewLayoutItem); | |
| } | |
| return pNewLayoutItem; | |
| } | |
| float CXFA_ContentLayoutProcessor::FindSplitPos(float fProposedSplitPos) { | |
| DCHECK(layout_item_); | |
| auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true); | |
| XFA_AttributeValue eLayout = value.value_or(XFA_AttributeValue::Position); | |
| bool bCalculateMargin = eLayout != XFA_AttributeValue::Position; | |
| while (fProposedSplitPos > kXFALayoutPrecision) { | |
| bool bAppChange = false; | |
| if (!FindLayoutItemSplitPos(layout_item_.Get(), 0, &fProposedSplitPos, | |
| &bAppChange, bCalculateMargin)) { | |
| break; | |
| } | |
| } | |
| return fProposedSplitPos; | |
| } | |
| void CXFA_ContentLayoutProcessor::SplitLayoutItem( | |
| CXFA_ContentLayoutItem* pLayoutItem, | |
| CXFA_ContentLayoutItem* pSecondParent, | |
| float fSplitPos) { | |
| float fCurTopMargin = 0; | |
| float fCurBottomMargin = 0; | |
| auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true); | |
| XFA_AttributeValue eLayout = value.value_or(XFA_AttributeValue::Position); | |
| bool bCalculateMargin = true; | |
| if (eLayout == XFA_AttributeValue::Position) { | |
| bCalculateMargin = false; | |
| } | |
| CXFA_Margin* pMarginNode = | |
| pLayoutItem->GetFormNode()->GetFirstChildByClass<CXFA_Margin>( | |
| XFA_Element::Margin); | |
| if (pMarginNode && bCalculateMargin) { | |
| fCurTopMargin = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::TopInset, XFA_Unit::Pt); | |
| fCurBottomMargin = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::BottomInset, XFA_Unit::Pt); | |
| } | |
| CXFA_ContentLayoutItem* pSecondLayoutItem = nullptr; | |
| if (cur_child_preprocessor_ && | |
| cur_child_preprocessor_->GetFormNode() == pLayoutItem->GetFormNode()) { | |
| pSecondLayoutItem = cur_child_preprocessor_->CreateContentLayoutItem( | |
| pLayoutItem->GetFormNode()); | |
| } else { | |
| pSecondLayoutItem = CreateContentLayoutItem(pLayoutItem->GetFormNode()); | |
| } | |
| pSecondLayoutItem->s_pos_.x = pLayoutItem->s_pos_.x; | |
| pSecondLayoutItem->s_size_.width = pLayoutItem->s_size_.width; | |
| pSecondLayoutItem->s_pos_.y = 0; | |
| pSecondLayoutItem->s_size_.height = pLayoutItem->s_size_.height - fSplitPos; | |
| pLayoutItem->s_size_.height -= pSecondLayoutItem->s_size_.height; | |
| if (pLayoutItem->GetFirstChild()) { | |
| pSecondLayoutItem->s_size_.height += fCurTopMargin; | |
| } | |
| bool bOrphanedItem = false; | |
| if (pSecondParent) { | |
| pSecondParent->AppendLastChild(pSecondLayoutItem); | |
| if (fCurTopMargin > 0 && pLayoutItem->GetFirstChild()) { | |
| pSecondParent->s_size_.height += fCurTopMargin; | |
| for (CXFA_LayoutItem* pParentIter = pSecondParent->GetParent(); | |
| pParentIter; pParentIter = pParentIter->GetParent()) { | |
| CXFA_ContentLayoutItem* pContentItem = | |
| pParentIter->AsContentLayoutItem(); | |
| if (!pContentItem) { | |
| continue; | |
| } | |
| pContentItem->s_size_.height += fCurTopMargin; | |
| } | |
| } | |
| } else if (pLayoutItem->GetParent()) { | |
| pLayoutItem->GetParent()->InsertAfter(pSecondLayoutItem, pLayoutItem); | |
| } else { | |
| // Parentless |pLayoutitem| would like to have |pSecondLayoutItem| as a | |
| // sibling, but that would violate the tree invariant. Instead, keep | |
| // it an orphan and add it as a child of |pLayoutItem| after performing | |
| // the split. | |
| bOrphanedItem = true; | |
| } | |
| std::vector<CXFA_ContentLayoutItem*> children; | |
| while (auto* pFirst = ToContentLayoutItem(pLayoutItem->GetFirstChild())) { | |
| children.push_back(pFirst); | |
| pLayoutItem->RemoveChild(children.back()); | |
| } | |
| float lHeightForKeep = 0; | |
| float fAddMarginHeight = 0; | |
| std::vector<CXFA_ContentLayoutItem*> keepLayoutItems; | |
| for (CXFA_ContentLayoutItem* pChildItem : children) { | |
| if (fSplitPos <= fCurTopMargin + pChildItem->s_pos_.y + fCurBottomMargin + | |
| kXFALayoutPrecision) { | |
| if (!ExistContainerKeep(pChildItem->GetFormNode(), true)) { | |
| pChildItem->s_pos_.y -= fSplitPos - fCurBottomMargin; | |
| pChildItem->s_pos_.y += lHeightForKeep; | |
| pChildItem->s_pos_.y += fAddMarginHeight; | |
| pSecondLayoutItem->AppendLastChild(pChildItem); | |
| continue; | |
| } | |
| if (lHeightForKeep < kXFALayoutPrecision) { | |
| for (CXFA_ContentLayoutItem* pPreItem : keepLayoutItems) { | |
| pLayoutItem->RemoveChild(pPreItem); | |
| pPreItem->s_pos_.y -= fSplitPos; | |
| if (pPreItem->s_pos_.y < 0) { | |
| pPreItem->s_pos_.y = 0; | |
| } | |
| if (pPreItem->s_pos_.y + pPreItem->s_size_.height > lHeightForKeep) { | |
| pPreItem->s_pos_.y = lHeightForKeep; | |
| lHeightForKeep += pPreItem->s_size_.height; | |
| pSecondLayoutItem->s_size_.height += pPreItem->s_size_.height; | |
| if (pSecondParent) { | |
| pSecondParent->s_size_.height += pPreItem->s_size_.height; | |
| } | |
| } | |
| pSecondLayoutItem->AppendLastChild(pPreItem); | |
| } | |
| } | |
| pChildItem->s_pos_.y -= fSplitPos; | |
| pChildItem->s_pos_.y += lHeightForKeep; | |
| pChildItem->s_pos_.y += fAddMarginHeight; | |
| pSecondLayoutItem->AppendLastChild(pChildItem); | |
| continue; | |
| } | |
| if (fSplitPos + kXFALayoutPrecision >= fCurTopMargin + fCurBottomMargin + | |
| pChildItem->s_pos_.y + | |
| pChildItem->s_size_.height) { | |
| pLayoutItem->AppendLastChild(pChildItem); | |
| if (ExistContainerKeep(pChildItem->GetFormNode(), false)) { | |
| keepLayoutItems.push_back(pChildItem); | |
| } else { | |
| keepLayoutItems.clear(); | |
| } | |
| continue; | |
| } | |
| float fOldHeight = pSecondLayoutItem->s_size_.height; | |
| SplitLayoutItem( | |
| pChildItem, pSecondLayoutItem, | |
| fSplitPos - fCurTopMargin - fCurBottomMargin - pChildItem->s_pos_.y); | |
| fAddMarginHeight = pSecondLayoutItem->s_size_.height - fOldHeight; | |
| pLayoutItem->AppendLastChild(pChildItem); | |
| } | |
| if (bOrphanedItem) { | |
| pLayoutItem->AppendLastChild(pSecondLayoutItem); | |
| } | |
| } | |
| void CXFA_ContentLayoutProcessor::SplitLayoutItem(float fSplitPos) { | |
| DCHECK(layout_item_); | |
| SplitLayoutItem(layout_item_.Get(), nullptr, fSplitPos); | |
| } | |
| CXFA_ContentLayoutItem* CXFA_ContentLayoutProcessor::ExtractLayoutItem() { | |
| CXFA_ContentLayoutItem* pLayoutItem = layout_item_; | |
| if (pLayoutItem) { | |
| layout_item_ = ToContentLayoutItem(pLayoutItem->GetNextSibling()); | |
| pLayoutItem->RemoveSelfIfParented(); | |
| } | |
| if (cur_child_node_stage_ != Stage::kDone || !old_layout_item_) { | |
| return pLayoutItem; | |
| } | |
| CXFA_FFNotify* pNotify = | |
| old_layout_item_->GetFormNode()->GetDocument()->GetNotify(); | |
| auto* pDocLayout = CXFA_LayoutProcessor::FromDocument( | |
| old_layout_item_->GetFormNode()->GetDocument()); | |
| while (old_layout_item_) { | |
| CXFA_ContentLayoutItem* pToDeleteItem = old_layout_item_; | |
| old_layout_item_ = pToDeleteItem->GetNext(); | |
| if (pToDeleteItem == pLayoutItem) { | |
| break; | |
| } | |
| pNotify->OnLayoutItemRemoving(pDocLayout, pToDeleteItem); | |
| pToDeleteItem->RemoveSelfIfParented(); | |
| } | |
| return pLayoutItem; | |
| } | |
| void CXFA_ContentLayoutProcessor::GotoNextContainerNodeSimple() { | |
| std::tie(cur_child_node_stage_, cur_child_node_) = GotoNextContainerNode( | |
| cur_child_node_stage_, GetFormNode(), cur_child_node_); | |
| } | |
| std::pair<CXFA_ContentLayoutProcessor::Stage, CXFA_Node*> | |
| CXFA_ContentLayoutProcessor::GotoNextContainerNode(Stage nCurStage, | |
| CXFA_Node* pParentContainer, | |
| CXFA_Node* pCurActionNode) { | |
| CXFA_Node* pChildContainer = nullptr; | |
| switch (nCurStage) { | |
| case Stage::kBreakBefore: | |
| case Stage::kBreakAfter: { | |
| pChildContainer = pCurActionNode->GetParent(); | |
| break; | |
| } | |
| case Stage::kKeep: | |
| case Stage::kContainer: | |
| pChildContainer = pCurActionNode; | |
| break; | |
| default: | |
| pChildContainer = nullptr; | |
| break; | |
| } | |
| std::optional<Stage> ret; | |
| switch (nCurStage) { | |
| case Stage::kKeep: | |
| ret = HandleKeep(pChildContainer->GetFirstChild(), &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| break; | |
| case Stage::kNone: | |
| pCurActionNode = nullptr; | |
| [[fallthrough]]; | |
| case Stage::kBookendLeader: | |
| ret = HandleBookendLeader(pParentContainer, &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| pCurActionNode = nullptr; | |
| [[fallthrough]]; | |
| case Stage::kBreakBefore: | |
| ret = HandleBreakBefore(pChildContainer, &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| break; | |
| case Stage::kContainer: | |
| pCurActionNode = nullptr; | |
| [[fallthrough]]; | |
| case Stage::kBreakAfter: | |
| ret = HandleBreakAfter(pChildContainer, &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| break; | |
| case Stage::kBookendTrailer: | |
| ret = HandleBookendTrailer(pParentContainer, &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| [[fallthrough]]; | |
| default: | |
| return {Stage::kDone, nullptr}; | |
| } | |
| ret = HandleCheckNextChildContainer(pParentContainer, pChildContainer, | |
| &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| pCurActionNode = nullptr; | |
| ret = HandleBookendTrailer(pParentContainer, &pCurActionNode); | |
| if (ret.has_value()) { | |
| return {ret.value(), pCurActionNode}; | |
| } | |
| return {Stage::kDone, nullptr}; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::ProcessKeepNodesForCheckNext( | |
| CXFA_Node** pCurActionNode, | |
| CXFA_Node** pNextContainer, | |
| bool* pLastKeepNode) { | |
| const bool bCanSplit = | |
| (*pNextContainer)->GetIntact() == XFA_AttributeValue::None; | |
| const bool bNextKeep = ExistContainerKeep(*pNextContainer, false); | |
| if (bNextKeep && !bCanSplit) { | |
| if (!is_process_keep_ && !keep_break_finish_) { | |
| keep_head_node_ = *pNextContainer; | |
| is_process_keep_ = true; | |
| } | |
| return std::nullopt; | |
| } | |
| if (!is_process_keep_ || !keep_head_node_) { | |
| if (keep_break_finish_) { | |
| *pLastKeepNode = true; | |
| } | |
| keep_break_finish_ = false; | |
| return std::nullopt; | |
| } | |
| keep_tail_node_ = *pNextContainer; | |
| if (keep_break_finish_) { | |
| *pNextContainer = keep_head_node_; | |
| ProcessKeepNodesEnd(); | |
| return std::nullopt; | |
| } | |
| std::optional<Stage> ret = | |
| FindBreakBeforeNode((*pNextContainer)->GetFirstChild(), pCurActionNode); | |
| if (!ret.has_value()) { | |
| *pNextContainer = keep_head_node_; | |
| ProcessKeepNodesEnd(); | |
| return std::nullopt; | |
| } | |
| return ret; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::ProcessKeepNodesForBreakBefore( | |
| CXFA_Node** pCurActionNode, | |
| CXFA_Node* pContainerNode) { | |
| if (keep_tail_node_ == pContainerNode) { | |
| *pCurActionNode = keep_head_node_; | |
| ProcessKeepNodesEnd(); | |
| return Stage::kContainer; | |
| } | |
| CXFA_Node* pBreakAfterNode = pContainerNode->GetFirstChild(); | |
| return FindBreakAfterNode(pBreakAfterNode, pCurActionNode); | |
| } | |
| void CXFA_ContentLayoutProcessor::DoLayoutPageArea( | |
| CXFA_ViewLayoutItem* pPageAreaLayoutItem) { | |
| CXFA_Node* pFormNode = pPageAreaLayoutItem->GetFormNode(); | |
| CXFA_Node* pCurChildNode = nullptr; | |
| CXFA_LayoutItem* pBeforeItem = nullptr; | |
| Stage nCurChildNodeStage = Stage::kNone; | |
| while (true) { | |
| std::tie(nCurChildNodeStage, pCurChildNode) = | |
| GotoNextContainerNode(nCurChildNodeStage, pFormNode, pCurChildNode); | |
| if (!pCurChildNode) { | |
| break; | |
| } | |
| if (nCurChildNodeStage != Stage::kContainer || | |
| pCurChildNode->GetElementType() == XFA_Element::Variables) { | |
| continue; | |
| } | |
| auto* pProcessor = cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pCurChildNode, nullptr); | |
| pProcessor->DoLayout(false, FLT_MAX, FLT_MAX); | |
| if (!pProcessor->HasLayoutItem()) { | |
| continue; | |
| } | |
| pProcessor->SetCurrentComponentPos(CalculatePositionedContainerPos( | |
| pCurChildNode, pProcessor->GetCurrentComponentSize())); | |
| CXFA_LayoutItem* pProcessItem = pProcessor->ExtractLayoutItem(); | |
| if (!pBeforeItem) { | |
| pPageAreaLayoutItem->AppendFirstChild(pProcessItem); | |
| } else { | |
| pPageAreaLayoutItem->InsertAfter(pProcessItem, pBeforeItem); | |
| } | |
| pBeforeItem = pProcessItem; | |
| } | |
| pBeforeItem = nullptr; | |
| CXFA_LayoutItem* pLayoutItem = pPageAreaLayoutItem->GetFirstChild(); | |
| while (pLayoutItem) { | |
| if (!pLayoutItem->IsContentLayoutItem() || | |
| pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::Draw) { | |
| pLayoutItem = pLayoutItem->GetNextSibling(); | |
| continue; | |
| } | |
| if (pLayoutItem->GetFormNode()->GetElementType() != XFA_Element::Draw) { | |
| continue; | |
| } | |
| CXFA_LayoutItem* pNextLayoutItem = pLayoutItem->GetNextSibling(); | |
| pPageAreaLayoutItem->RemoveChild(pLayoutItem); | |
| if (!pBeforeItem) { | |
| pPageAreaLayoutItem->AppendFirstChild(pLayoutItem); | |
| } else { | |
| pPageAreaLayoutItem->InsertAfter(pLayoutItem, pBeforeItem); | |
| } | |
| pBeforeItem = pLayoutItem; | |
| pLayoutItem = pNextLayoutItem; | |
| } | |
| } | |
| void CXFA_ContentLayoutProcessor::DoLayoutPositionedContainer( | |
| Context* context) { | |
| if (layout_item_) { | |
| return; | |
| } | |
| layout_item_ = CreateContentLayoutItem(GetFormNode()); | |
| auto value = GetFormNode()->JSObject()->TryEnum(XFA_Attribute::Layout, true); | |
| bool bIgnoreXY = value.value_or(XFA_AttributeValue::Position) != | |
| XFA_AttributeValue::Position; | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| GetFormNode(), &bContainerWidthAutoSize, &bContainerHeightAutoSize); | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| float fHiddenContentCalculatedWidth = 0; | |
| float fHiddenContentCalculatedHeight = 0; | |
| if (!cur_child_node_) { | |
| GotoNextContainerNodeSimple(); | |
| } | |
| int32_t iColIndex = 0; | |
| for (; cur_child_node_; GotoNextContainerNodeSimple()) { | |
| if (cur_child_node_stage_ != Stage::kContainer) { | |
| continue; | |
| } | |
| if (cur_child_node_->GetElementType() == XFA_Element::Variables) { | |
| continue; | |
| } | |
| auto* pProcessor = cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), cur_child_node_, | |
| view_layout_processor_); | |
| if (context && context->prg_specified_column_widths_) { | |
| int32_t iColSpan = | |
| cur_child_node_->JSObject()->GetInteger(XFA_Attribute::ColSpan); | |
| if (iColSpan <= fxcrt::CollectionSize<int32_t>( | |
| *context->prg_specified_column_widths_) - | |
| iColIndex) { | |
| context->cur_column_width_ = 0.0f; | |
| if (iColSpan == -1) { | |
| iColSpan = fxcrt::CollectionSize<int32_t>( | |
| *context->prg_specified_column_widths_); | |
| } | |
| for (int32_t i = 0; iColIndex + i < iColSpan; ++i) { | |
| context->cur_column_width_.value() += | |
| (*context->prg_specified_column_widths_)[iColIndex + i]; | |
| } | |
| if (context->cur_column_width_.value() == 0) { | |
| context->cur_column_width_.reset(); | |
| } | |
| iColIndex += iColSpan >= 0 ? iColSpan : 0; | |
| } | |
| } | |
| pProcessor->DoLayoutInternal(false, FLT_MAX, FLT_MAX, context); | |
| if (!pProcessor->HasLayoutItem()) { | |
| continue; | |
| } | |
| CFX_SizeF size = pProcessor->GetCurrentComponentSize(); | |
| bool bChangeParentSize = false; | |
| if (cur_child_node_->PresenceRequiresSpace()) { | |
| bChangeParentSize = true; | |
| } | |
| CFX_PointF absolutePos; | |
| if (!bIgnoreXY) { | |
| absolutePos = CalculatePositionedContainerPos(cur_child_node_, size); | |
| } | |
| pProcessor->SetCurrentComponentPos(absolutePos); | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = absolutePos.x + size.width; | |
| if (bChangeParentSize) { | |
| fContentCalculatedWidth = | |
| std::max(fContentCalculatedWidth, fChildSuppliedWidth); | |
| } else { | |
| if (fHiddenContentCalculatedWidth < fChildSuppliedWidth && | |
| cur_child_node_->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 && | |
| cur_child_node_->GetElementType() != XFA_Element::Subform) { | |
| fHiddenContentCalculatedHeight = fChildSuppliedHeight; | |
| } | |
| } | |
| } | |
| layout_item_->AppendLastChild(pProcessor->ExtractLayoutItem()); | |
| } | |
| XFA_VERSION eVersion = GetFormNode()->GetDocument()->GetCurVersionMode(); | |
| if (fContentCalculatedWidth == 0 && eVersion < XFA_VERSION_207) { | |
| fContentCalculatedWidth = fHiddenContentCalculatedWidth; | |
| } | |
| if (fContentCalculatedHeight == 0 && eVersion < XFA_VERSION_207) { | |
| fContentCalculatedHeight = fHiddenContentCalculatedHeight; | |
| } | |
| containerSize = CalculateContainerComponentSizeFromContentSize( | |
| GetFormNode(), bContainerWidthAutoSize, fContentCalculatedWidth, | |
| bContainerHeightAutoSize, fContentCalculatedHeight, containerSize); | |
| SetCurrentComponentSize(containerSize); | |
| } | |
| void CXFA_ContentLayoutProcessor::DoLayoutTableContainer( | |
| CXFA_Node* pLayoutNode) { | |
| if (layout_item_) { | |
| return; | |
| } | |
| if (!pLayoutNode) { | |
| pLayoutNode = GetFormNode(); | |
| } | |
| DCHECK(!cur_child_node_); | |
| layout_item_ = CreateContentLayoutItem(GetFormNode()); | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| CFX_SizeF containerSize = CalculateContainerSpecifiedSize( | |
| GetFormNode(), &bContainerWidthAutoSize, &bContainerHeightAutoSize); | |
| float fContentCalculatedWidth = 0; | |
| float fContentCalculatedHeight = 0; | |
| CXFA_Margin* pMarginNode = | |
| GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fRightInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::LeftInset, XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::RightInset, XFA_Unit::Pt); | |
| } | |
| float fContentWidthLimit = | |
| bContainerWidthAutoSize ? FLT_MAX | |
| : containerSize.width - fLeftInset - fRightInset; | |
| WideString wsColumnWidths = | |
| pLayoutNode->JSObject()->GetCData(XFA_Attribute::ColumnWidths); | |
| if (!wsColumnWidths.IsEmpty()) { | |
| for (auto& width : SeparateStringOnSpace(wsColumnWidths.span())) { | |
| width.TrimFront(L' '); | |
| if (width.IsEmpty()) { | |
| continue; | |
| } | |
| rg_specified_column_widths_.push_back( | |
| CXFA_Measurement(width.AsStringView()).ToUnit(XFA_Unit::Pt)); | |
| } | |
| } | |
| int32_t iSpecifiedColumnCount = | |
| fxcrt::CollectionSize<int32_t>(rg_specified_column_widths_); | |
| Context layoutContext; | |
| layoutContext.prg_specified_column_widths_ = &rg_specified_column_widths_; | |
| Context* pLayoutContext = | |
| iSpecifiedColumnCount > 0 ? &layoutContext : nullptr; | |
| if (!cur_child_node_) { | |
| GotoNextContainerNodeSimple(); | |
| } | |
| for (; cur_child_node_; GotoNextContainerNodeSimple()) { | |
| layoutContext.cur_column_width_.reset(); | |
| if (cur_child_node_stage_ != Stage::kContainer) { | |
| continue; | |
| } | |
| auto* pProcessor = cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), cur_child_node_, | |
| view_layout_processor_); | |
| pProcessor->DoLayoutInternal(false, FLT_MAX, FLT_MAX, pLayoutContext); | |
| if (!pProcessor->HasLayoutItem()) { | |
| continue; | |
| } | |
| layout_item_->AppendLastChild(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 (CXFA_LayoutItem* pIter = layout_item_->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem(); | |
| if (!pLayoutChild) { | |
| continue; | |
| } | |
| if (pLayoutChild->GetFormNode()->GetElementType() != | |
| XFA_Element::Subform) { | |
| continue; | |
| } | |
| if (!pLayoutChild->GetFormNode()->PresenceRequiresSpace()) { | |
| continue; | |
| } | |
| XFA_AttributeValue eLayout = | |
| pLayoutChild->GetFormNode()->JSObject()->GetEnum( | |
| XFA_Attribute::Layout); | |
| if (eLayout != XFA_AttributeValue::Row && | |
| eLayout != XFA_AttributeValue::Rl_row) { | |
| continue; | |
| } | |
| CXFA_ContentLayoutItem* pRowLayoutCell = | |
| ToContentLayoutItem(pLayoutChild->GetFirstChild()); | |
| if (pRowLayoutCell) { | |
| rgRowItems.push_back(pRowLayoutCell); | |
| int32_t iColSpan = | |
| pRowLayoutCell->GetFormNode()->JSObject()->GetInteger( | |
| XFA_Attribute::ColSpan); | |
| rgRowItemsSpan.push_back(iColSpan); | |
| rgRowItemsWidth.push_back(pRowLayoutCell->s_size_.width); | |
| } | |
| } | |
| iRowCount = fxcrt::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 || | |
| !rgRowItems[i]->GetFormNode()->PresenceRequiresSpace())) { | |
| CXFA_ContentLayoutItem* pNewCell = | |
| ToContentLayoutItem(rgRowItems[i]->GetNextSibling()); | |
| if (rgRowItemsSpan[i] < 0 && | |
| rgRowItems[i]->GetFormNode()->PresenceRequiresSpace()) { | |
| pNewCell = nullptr; | |
| } | |
| rgRowItems[i] = pNewCell; | |
| rgRowItemsSpan[i] = | |
| pNewCell ? pNewCell->GetFormNode()->JSObject()->GetInteger( | |
| XFA_Attribute::ColSpan) | |
| : 0; | |
| rgRowItemsWidth[i] = pNewCell ? pNewCell->s_size_.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 - | |
| fxcrt::CollectionSize<int32_t>(rg_specified_column_widths_); | |
| for (int32_t j = 0; j < c; j++) { | |
| rg_specified_column_widths_.push_back(0); | |
| } | |
| } | |
| if (rg_specified_column_widths_[iColCount] < kXFALayoutPrecision) { | |
| bAutoCol = true; | |
| } | |
| if (bAutoCol && | |
| rg_specified_column_widths_[iColCount] < rgRowItemsWidth[i]) { | |
| rg_specified_column_widths_[iColCount] = rgRowItemsWidth[i]; | |
| } | |
| } | |
| if (!bMoreColumns) { | |
| continue; | |
| } | |
| float fFinalColumnWidth = 0.0f; | |
| if (fxcrt::IndexInBounds(rg_specified_column_widths_, iColCount)) { | |
| fFinalColumnWidth = rg_specified_column_widths_[iColCount]; | |
| } | |
| for (int32_t i = 0; i < iRowCount; ++i) { | |
| if (!rgRowItems[i]) { | |
| continue; | |
| } | |
| --rgRowItemsSpan[i]; | |
| rgRowItemsWidth[i] -= fFinalColumnWidth; | |
| } | |
| ++iColCount; | |
| } | |
| } | |
| float fCurrentRowY = 0; | |
| for (CXFA_LayoutItem* pIter = layout_item_->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayoutChild = pIter->AsContentLayoutItem(); | |
| if (!pLayoutChild || | |
| !pLayoutChild->GetFormNode()->PresenceRequiresSpace()) { | |
| continue; | |
| } | |
| if (pLayoutChild->GetFormNode()->GetElementType() == XFA_Element::Subform) { | |
| XFA_AttributeValue eSubformLayout = | |
| pLayoutChild->GetFormNode()->JSObject()->GetEnum( | |
| XFA_Attribute::Layout); | |
| if (eSubformLayout == XFA_AttributeValue::Row || | |
| eSubformLayout == XFA_AttributeValue::Rl_row) { | |
| RelocateTableRowCells(pLayoutChild, rg_specified_column_widths_, | |
| eSubformLayout); | |
| } | |
| } | |
| pLayoutChild->s_pos_.y = fCurrentRowY; | |
| if (bContainerWidthAutoSize) { | |
| pLayoutChild->s_pos_.x = 0; | |
| } else { | |
| switch (pLayoutChild->GetFormNode()->JSObject()->GetEnum( | |
| XFA_Attribute::HAlign)) { | |
| case XFA_AttributeValue::Center: | |
| pLayoutChild->s_pos_.x = | |
| (fContentWidthLimit - pLayoutChild->s_size_.width) / 2; | |
| break; | |
| case XFA_AttributeValue::Right: | |
| pLayoutChild->s_pos_.x = | |
| fContentWidthLimit - pLayoutChild->s_size_.width; | |
| break; | |
| case XFA_AttributeValue::Left: | |
| default: | |
| pLayoutChild->s_pos_.x = 0; | |
| break; | |
| } | |
| } | |
| if (bContainerWidthAutoSize) { | |
| float fChildSuppliedWidth = | |
| pLayoutChild->s_pos_.x + pLayoutChild->s_size_.width; | |
| if (fContentWidthLimit < FLT_MAX && | |
| fContentWidthLimit > fChildSuppliedWidth) { | |
| fChildSuppliedWidth = fContentWidthLimit; | |
| } | |
| fContentCalculatedWidth = | |
| std::max(fContentCalculatedWidth, fChildSuppliedWidth); | |
| } | |
| fCurrentRowY += pLayoutChild->s_size_.height; | |
| } | |
| if (bContainerHeightAutoSize) { | |
| fContentCalculatedHeight = std::max(fContentCalculatedHeight, fCurrentRowY); | |
| } | |
| containerSize = CalculateContainerComponentSizeFromContentSize( | |
| GetFormNode(), bContainerWidthAutoSize, fContentCalculatedWidth, | |
| bContainerHeightAutoSize, fContentCalculatedHeight, containerSize); | |
| SetCurrentComponentSize(containerSize); | |
| } | |
| bool CXFA_ContentLayoutProcessor::IsAddNewRowForTrailer( | |
| CXFA_ContentLayoutItem* pTrailerItem) { | |
| if (!pTrailerItem) { | |
| return false; | |
| } | |
| float fWidth = pTrailerItem->s_size_.width; | |
| XFA_AttributeValue eLayout = | |
| GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout); | |
| return eLayout == XFA_AttributeValue::Tb || width_limit_ <= fWidth; | |
| } | |
| float CXFA_ContentLayoutProcessor::InsertKeepLayoutItems() { | |
| if (array_keep_items_.empty()) { | |
| return 0; | |
| } | |
| if (!layout_item_) { | |
| layout_item_ = CreateContentLayoutItem(GetFormNode()); | |
| layout_item_->s_size_.clear(); | |
| } | |
| float fTotalHeight = 0; | |
| for (const auto& item : pdfium::Reversed(array_keep_items_)) { | |
| AddLeaderAfterSplit(item); | |
| fTotalHeight += item->s_size_.height; | |
| } | |
| array_keep_items_.clear(); | |
| return fTotalHeight; | |
| } | |
| bool CXFA_ContentLayoutProcessor::ProcessKeepForSplit( | |
| CXFA_ContentLayoutProcessor* pChildProcessor, | |
| Result eRetValue, | |
| ContentLayoutItemVector& rgCurLineLayoutItem, | |
| float* fContentCurRowAvailWidth, | |
| float* fContentCurRowHeight, | |
| float* fContentCurRowY, | |
| bool* bAddedItemInRow, | |
| bool* bForceEndPage, | |
| Result* result) { | |
| if (!pChildProcessor) { | |
| return false; | |
| } | |
| if (cur_child_node_->GetIntact() == XFA_AttributeValue::None && | |
| pChildProcessor->has_avail_height_) { | |
| return false; | |
| } | |
| if (!ExistContainerKeep(cur_child_node_, true)) { | |
| return false; | |
| } | |
| CFX_SizeF childSize = pChildProcessor->GetCurrentComponentSize(); | |
| std::vector<CXFA_ContentLayoutItem*> keepLayoutItems; | |
| if (JudgePutNextPage(layout_item_.Get(), childSize.height, | |
| &keepLayoutItems)) { | |
| array_keep_items_.clear(); | |
| for (CXFA_ContentLayoutItem* item : keepLayoutItems) { | |
| layout_item_->RemoveChild(item); | |
| *fContentCurRowY -= item->s_size_.height; | |
| array_keep_items_.push_back(item); | |
| } | |
| *bAddedItemInRow = true; | |
| *bForceEndPage = true; | |
| *result = Result::kPageFullBreak; | |
| return true; | |
| } | |
| rgCurLineLayoutItem.push_back(pChildProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height); | |
| *result = eRetValue; | |
| return true; | |
| } | |
| bool CXFA_ContentLayoutProcessor::JudgePutNextPage( | |
| CXFA_ContentLayoutItem* pParentLayoutItem, | |
| float fChildHeight, | |
| std::vector<CXFA_ContentLayoutItem*>* pKeepItems) { | |
| if (!pParentLayoutItem) { | |
| return false; | |
| } | |
| float fItemsHeight = 0; | |
| for (CXFA_LayoutItem* pIter = pParentLayoutItem->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pChildLayoutItem = pIter->AsContentLayoutItem(); | |
| if (!pChildLayoutItem) { | |
| continue; | |
| } | |
| if (ExistContainerKeep(pChildLayoutItem->GetFormNode(), false)) { | |
| pKeepItems->push_back(pChildLayoutItem); | |
| fItemsHeight += pChildLayoutItem->s_size_.height; | |
| } else { | |
| pKeepItems->clear(); | |
| fItemsHeight = 0; | |
| } | |
| } | |
| fItemsHeight += fChildHeight; | |
| return view_layout_processor_->GetNextAvailContentHeight(fItemsHeight); | |
| } | |
| void CXFA_ContentLayoutProcessor::ProcessUnUseBinds(CXFA_Node* pFormNode) { | |
| if (!pFormNode) { | |
| return; | |
| } | |
| CXFA_NodeIterator 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->SetBindingNode(nullptr); | |
| } | |
| } | |
| pNode->SetFlag(XFA_NodeFlag::kUnusedNode); | |
| } | |
| } | |
| void CXFA_ContentLayoutProcessor::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->GetParent(); | |
| } | |
| if (pLeaderNode && pFormNode) { | |
| pFormNode->RemoveChildAndNotify(pLeaderNode, true); | |
| } | |
| if (pTrailerNode && pFormNode) { | |
| pFormNode->RemoveChildAndNotify(pTrailerNode, true); | |
| } | |
| if (pTrailerItem) { | |
| XFA_ReleaseLayoutItem(pTrailerItem); | |
| } | |
| } | |
| CXFA_ContentLayoutProcessor::Result | |
| CXFA_ContentLayoutProcessor::DoLayoutFlowedContainer( | |
| bool bUseBreakControl, | |
| XFA_AttributeValue eFlowStrategy, | |
| float fHeightLimit, | |
| float fRealHeight, | |
| Context* context, | |
| bool bRootForceTb) { | |
| has_avail_height_ = true; | |
| if (cur_child_preprocessor_) { | |
| cur_child_preprocessor_->pre_process_rs_ = Result::kDone; | |
| } | |
| bool bContainerWidthAutoSize = true; | |
| bool bContainerHeightAutoSize = true; | |
| CFX_SizeF container_size = CalculateContainerSpecifiedSize( | |
| GetFormNode(), &bContainerWidthAutoSize, &bContainerHeightAutoSize); | |
| AdjustContainerSpecifiedSize(context, &container_size, | |
| &bContainerWidthAutoSize, | |
| &bContainerHeightAutoSize); | |
| CXFA_Margin* pMargin = | |
| GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin); | |
| CFX_FloatRect inset = GetMarginInset(pMargin); | |
| float fContentWidthLimit = | |
| bContainerWidthAutoSize ? FLT_MAX | |
| : container_size.width - inset.left - inset.right; | |
| float fAvailHeight = fHeightLimit - inset.top - inset.bottom; | |
| if (fAvailHeight < 0) { | |
| has_avail_height_ = false; | |
| } | |
| fRealHeight = fRealHeight - inset.top - inset.bottom; | |
| CFX_SizeF calculated_size; | |
| float fContentCurRowY = 0; | |
| CXFA_ContentLayoutItem* pLastChild = nullptr; | |
| if (layout_item_) { | |
| pLastChild = FindLastContentLayoutItem(eFlowStrategy); | |
| calculated_size = CalculateLayoutItemSize(pLastChild); | |
| fContentCurRowY = | |
| pLastChild ? pLastChild->s_pos_.y : calculated_size.height; | |
| } | |
| fContentCurRowY += InsertKeepLayoutItems(); | |
| if (cur_child_node_stage_ == Stage::kNone) { | |
| GotoNextContainerNodeSimple(); | |
| } | |
| fContentCurRowY += InsertPendingItems(GetFormNode()); | |
| if (cur_child_preprocessor_ && cur_child_node_stage_ == Stage::kContainer) { | |
| if (ExistContainerKeep(cur_child_preprocessor_->GetFormNode(), false)) { | |
| keep_head_node_ = cur_child_node_; | |
| is_process_keep_ = true; | |
| cur_child_node_stage_ = Stage::kKeep; | |
| } | |
| } | |
| bool bForceEndPage = false; | |
| bool bBreakDone = false; | |
| bool bIsManualBreak = false; | |
| while (cur_child_node_stage_ != Stage::kDone) { | |
| float fContentCurRowHeight = 0; | |
| float fContentCurRowAvailWidth = fContentWidthLimit; | |
| width_limit_ = fContentCurRowAvailWidth; | |
| std::array<ContentLayoutItemVector, 3> rgCurLineLayoutItems; | |
| uint8_t uCurHAlignState = | |
| (eFlowStrategy != XFA_AttributeValue::Rl_tb ? 0 : 2); | |
| if (pLastChild) { | |
| for (CXFA_LayoutItem* pNext = pLastChild; pNext; | |
| pNext = pNext->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayoutNext = pNext->AsContentLayoutItem(); | |
| if (!pLayoutNext) { | |
| continue; | |
| } | |
| if (!pLayoutNext->GetNextSibling() && cur_child_preprocessor_ && | |
| cur_child_preprocessor_->GetFormNode() == | |
| pLayoutNext->GetFormNode()) { | |
| if (cur_child_preprocessor_->layout_item_ && | |
| cur_child_preprocessor_->layout_item_ != pLayoutNext) { | |
| pLayoutNext->InsertAfter( | |
| cur_child_preprocessor_->layout_item_.Get()); | |
| } | |
| cur_child_preprocessor_->layout_item_ = pLayoutNext; | |
| break; | |
| } | |
| uint8_t uHAlign = | |
| HAlignEnumToInt(pLayoutNext->GetFormNode()->JSObject()->GetEnum( | |
| XFA_Attribute::HAlign)); | |
| rgCurLineLayoutItems[uHAlign].emplace_back(pLayoutNext); | |
| if (eFlowStrategy == XFA_AttributeValue::Lr_tb) { | |
| if (uHAlign > uCurHAlignState) { | |
| uCurHAlignState = uHAlign; | |
| } | |
| } else if (uHAlign < uCurHAlignState) { | |
| uCurHAlignState = uHAlign; | |
| } | |
| if (pLayoutNext->GetFormNode()->PresenceRequiresSpace()) { | |
| if (pLayoutNext->s_size_.height > fContentCurRowHeight) { | |
| fContentCurRowHeight = pLayoutNext->s_size_.height; | |
| } | |
| fContentCurRowAvailWidth -= pLayoutNext->s_size_.width; | |
| } | |
| } | |
| CXFA_ContentLayoutItem* pLayoutNextTemp = pLastChild; | |
| while (pLayoutNextTemp) { | |
| CXFA_ContentLayoutItem* pSaveLayoutNext = | |
| ToContentLayoutItem(pLayoutNextTemp->GetNextSibling()); | |
| pLayoutNextTemp->RemoveSelfIfParented(); | |
| pLayoutNextTemp = pSaveLayoutNext; | |
| } | |
| pLastChild = nullptr; | |
| } | |
| while (cur_child_node_) { | |
| CXFA_ContentLayoutProcessor* pProcessor = nullptr; | |
| bool bAddedItemInRow = false; | |
| fContentCurRowY += InsertPendingItems(GetFormNode()); | |
| switch (cur_child_node_stage_) { | |
| case Stage::kKeep: | |
| case Stage::kNone: | |
| break; | |
| case Stage::kBreakBefore: { | |
| if (layout_item_) { | |
| for (auto& item : array_keep_items_) { | |
| layout_item_->RemoveChild(item); | |
| calculated_size.height -= item->s_size_.height; | |
| } | |
| } | |
| if (!bUseBreakControl || !view_layout_processor_) { | |
| break; | |
| } | |
| std::optional<CXFA_ViewLayoutProcessor::BreakData> break_data = | |
| view_layout_processor_->ProcessBreakBefore(cur_child_node_); | |
| if (!break_data.has_value() || !break_data.value().bCreatePage || | |
| GetFormNode()->GetElementType() == XFA_Element::Form) { | |
| break; | |
| } | |
| CXFA_Node* pLeaderNode = break_data.value().pLeader; | |
| CXFA_Node* pTrailerNode = break_data.value().pTrailer; | |
| if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) { | |
| AddPendingNode(pLeaderNode, true); | |
| } | |
| if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) { | |
| if (GetFormNode()->GetParent()->GetElementType() == | |
| XFA_Element::Form && | |
| !layout_item_) { | |
| AddPendingNode(pTrailerNode, true); | |
| } else { | |
| auto* pTempProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pTrailerNode, | |
| nullptr); | |
| InsertFlowedItem( | |
| pTempProcessor, bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, container_size.height, | |
| eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, false, | |
| FLT_MAX, FLT_MAX, fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, context, false); | |
| } | |
| } | |
| GotoNextContainerNodeSimple(); | |
| bForceEndPage = true; | |
| bIsManualBreak = true; | |
| goto SuspendAndCreateNewRow; | |
| } | |
| case Stage::kBreakAfter: { | |
| if (!bUseBreakControl || !view_layout_processor_) { | |
| break; | |
| } | |
| std::optional<CXFA_ViewLayoutProcessor::BreakData> break_data = | |
| view_layout_processor_->ProcessBreakAfter(cur_child_node_); | |
| if (!break_data.has_value() || | |
| GetFormNode()->GetElementType() == XFA_Element::Form) { | |
| break; | |
| } | |
| CXFA_Node* pLeaderNode = break_data.value().pLeader; | |
| CXFA_Node* pTrailerNode = break_data.value().pTrailer; | |
| bool bCreatePage = break_data.value().bCreatePage; | |
| if (JudgeLeaderOrTrailerForOccur(pTrailerNode)) { | |
| auto* pTempProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pTrailerNode, | |
| nullptr); | |
| InsertFlowedItem(pTempProcessor, bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, container_size.height, | |
| eFlowStrategy, &uCurHAlignState, | |
| rgCurLineLayoutItems, false, FLT_MAX, FLT_MAX, | |
| fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, context, false); | |
| } | |
| if (!bCreatePage) { | |
| if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) { | |
| CalculateRowChildPosition( | |
| rgCurLineLayoutItems, eFlowStrategy, bContainerHeightAutoSize, | |
| bContainerWidthAutoSize, &calculated_size.width, | |
| &calculated_size.height, &fContentCurRowY, | |
| fContentCurRowHeight, fContentWidthLimit, false); | |
| rgCurLineLayoutItems.front().clear(); | |
| auto* pTempProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pLeaderNode, | |
| nullptr); | |
| InsertFlowedItem( | |
| pTempProcessor, bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, container_size.height, | |
| eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, false, | |
| FLT_MAX, FLT_MAX, fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, context, false); | |
| } | |
| } else { | |
| if (JudgeLeaderOrTrailerForOccur(pLeaderNode)) { | |
| AddPendingNode(pLeaderNode, true); | |
| } | |
| } | |
| GotoNextContainerNodeSimple(); | |
| if (bCreatePage) { | |
| bForceEndPage = true; | |
| bIsManualBreak = true; | |
| if (cur_child_node_stage_ == Stage::kDone) { | |
| bBreakDone = true; | |
| } | |
| } | |
| goto SuspendAndCreateNewRow; | |
| } | |
| case Stage::kBookendLeader: { | |
| if (cur_child_preprocessor_) { | |
| pProcessor = cur_child_preprocessor_.Get(); | |
| cur_child_preprocessor_ = nullptr; | |
| } else if (view_layout_processor_) { | |
| CXFA_Node* pLeaderNode = | |
| view_layout_processor_->ProcessBookendLeader(cur_child_node_); | |
| if (pLeaderNode) { | |
| pProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pLeaderNode, | |
| view_layout_processor_); | |
| } | |
| } | |
| if (pProcessor) { | |
| if (InsertFlowedItem( | |
| pProcessor, bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, container_size.height, | |
| eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, | |
| bUseBreakControl, fAvailHeight, fRealHeight, | |
| fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, context, | |
| false) != Result::kDone) { | |
| goto SuspendAndCreateNewRow; | |
| } | |
| pProcessor = nullptr; | |
| } | |
| break; | |
| } | |
| case Stage::kBookendTrailer: { | |
| if (cur_child_preprocessor_) { | |
| pProcessor = cur_child_preprocessor_; | |
| cur_child_preprocessor_.Clear(); | |
| } else if (view_layout_processor_) { | |
| CXFA_Node* pTrailerNode = | |
| view_layout_processor_->ProcessBookendTrailer(cur_child_node_); | |
| if (pTrailerNode) { | |
| pProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pTrailerNode, | |
| view_layout_processor_); | |
| } | |
| } | |
| if (pProcessor) { | |
| if (InsertFlowedItem( | |
| pProcessor, bContainerWidthAutoSize, | |
| bContainerHeightAutoSize, container_size.height, | |
| eFlowStrategy, &uCurHAlignState, rgCurLineLayoutItems, | |
| bUseBreakControl, fAvailHeight, fRealHeight, | |
| fContentWidthLimit, &fContentCurRowY, | |
| &fContentCurRowAvailWidth, &fContentCurRowHeight, | |
| &bAddedItemInRow, &bForceEndPage, context, | |
| false) != Result::kDone) { | |
| goto SuspendAndCreateNewRow; | |
| } | |
| pProcessor = nullptr; | |
| } | |
| break; | |
| } | |
| case Stage::kContainer: { | |
| DCHECK(cur_child_node_->IsContainerNode()); | |
| if (cur_child_node_->GetElementType() == XFA_Element::Variables) { | |
| break; | |
| } | |
| if (fContentCurRowY >= fHeightLimit + kXFALayoutPrecision && | |
| cur_child_node_->PresenceRequiresSpace()) { | |
| bForceEndPage = true; | |
| goto SuspendAndCreateNewRow; | |
| } | |
| if (!cur_child_node_->IsContainerNode()) { | |
| break; | |
| } | |
| bool bNewRow = false; | |
| if (cur_child_preprocessor_) { | |
| pProcessor = cur_child_preprocessor_; | |
| cur_child_preprocessor_.Clear(); | |
| bNewRow = true; | |
| } else { | |
| pProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), | |
| cur_child_node_, view_layout_processor_); | |
| } | |
| pProcessor->InsertPendingItems(cur_child_node_); | |
| Result rs = InsertFlowedItem( | |
| pProcessor, bContainerWidthAutoSize, bContainerHeightAutoSize, | |
| container_size.height, eFlowStrategy, &uCurHAlignState, | |
| rgCurLineLayoutItems, bUseBreakControl, fAvailHeight, fRealHeight, | |
| fContentWidthLimit, &fContentCurRowY, &fContentCurRowAvailWidth, | |
| &fContentCurRowHeight, &bAddedItemInRow, &bForceEndPage, context, | |
| bNewRow); | |
| switch (rs) { | |
| case Result::kManualBreak: | |
| bIsManualBreak = true; | |
| [[fallthrough]]; | |
| case Result::kPageFullBreak: | |
| bForceEndPage = true; | |
| [[fallthrough]]; | |
| case Result::kRowFullBreak: | |
| goto SuspendAndCreateNewRow; | |
| case Result::kDone: | |
| fContentCurRowY += | |
| pProcessor->InsertPendingItems(cur_child_node_); | |
| pProcessor = nullptr; | |
| break; | |
| } | |
| break; | |
| } | |
| case Stage::kDone: | |
| break; | |
| } | |
| GotoNextContainerNodeSimple(); | |
| if (bAddedItemInRow && eFlowStrategy == XFA_AttributeValue::Tb) { | |
| break; | |
| } | |
| continue; | |
| SuspendAndCreateNewRow: | |
| if (pProcessor) { | |
| cur_child_preprocessor_ = pProcessor; | |
| pProcessor = nullptr; | |
| } | |
| break; | |
| } | |
| CalculateRowChildPosition(rgCurLineLayoutItems, eFlowStrategy, | |
| bContainerHeightAutoSize, bContainerWidthAutoSize, | |
| &calculated_size.width, &calculated_size.height, | |
| &fContentCurRowY, fContentCurRowHeight, | |
| fContentWidthLimit, bRootForceTb); | |
| width_limit_ = fContentCurRowAvailWidth; | |
| if (bForceEndPage) { | |
| break; | |
| } | |
| } | |
| bool bRetValue = | |
| cur_child_node_stage_ == Stage::kDone && pending_nodes_.empty(); | |
| if (bBreakDone) { | |
| bRetValue = false; | |
| } | |
| container_size = CalculateContainerComponentSizeFromContentSize( | |
| GetFormNode(), bContainerWidthAutoSize, calculated_size.width, | |
| bContainerHeightAutoSize, calculated_size.height, container_size); | |
| if (container_size.height >= kXFALayoutPrecision || layout_item_ || | |
| bRetValue) { | |
| if (!layout_item_) { | |
| layout_item_ = CreateContentLayoutItem(GetFormNode()); | |
| } | |
| container_size.height = std::max(container_size.height, 0.f); | |
| SetCurrentComponentSize(container_size); | |
| if (bForceEndPage) { | |
| used_size_ = 0; | |
| } else { | |
| used_size_ += layout_item_->s_size_.height; | |
| } | |
| } | |
| if (bRetValue) { | |
| return Result::kDone; | |
| } | |
| return bIsManualBreak ? Result::kManualBreak : Result::kPageFullBreak; | |
| } | |
| bool CXFA_ContentLayoutProcessor::CalculateRowChildPosition( | |
| std::array<ContentLayoutItemVector, 3>& rgCurLineLayoutItems, | |
| XFA_AttributeValue eFlowStrategy, | |
| bool bContainerHeightAutoSize, | |
| bool bContainerWidthAutoSize, | |
| float* fContentCalculatedWidth, | |
| float* fContentCalculatedHeight, | |
| float* fContentCurRowY, | |
| float fContentCurRowHeight, | |
| float fContentWidthLimit, | |
| bool bRootForceTb) { | |
| std::array<int32_t, 3> nGroupLengths = {}; | |
| std::array<float, 3> fGroupWidths = {}; | |
| int32_t nTotalLength = 0; | |
| for (int32_t i = 0; i < 3; i++) { | |
| nGroupLengths[i] = fxcrt::CollectionSize<int32_t>(rgCurLineLayoutItems[i]); | |
| for (int32_t c = nGroupLengths[i], j = 0; j < c; j++) { | |
| nTotalLength++; | |
| if (rgCurLineLayoutItems[i][j]->GetFormNode()->PresenceRequiresSpace()) { | |
| fGroupWidths[i] += rgCurLineLayoutItems[i][j]->s_size_.width; | |
| } | |
| } | |
| } | |
| if (!nTotalLength) { | |
| if (bContainerHeightAutoSize) { | |
| *fContentCalculatedHeight = | |
| std::min(*fContentCalculatedHeight, *fContentCurRowY); | |
| } | |
| return false; | |
| } | |
| if (!layout_item_) { | |
| layout_item_ = CreateContentLayoutItem(GetFormNode()); | |
| } | |
| if (eFlowStrategy != XFA_AttributeValue::Rl_tb) { | |
| float fCurPos; | |
| fCurPos = 0; | |
| for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) { | |
| if (bRootForceTb) { | |
| rgCurLineLayoutItems[0][j]->s_pos_ = CalculatePositionedContainerPos( | |
| rgCurLineLayoutItems[0][j]->GetFormNode(), | |
| rgCurLineLayoutItems[0][j]->s_size_); | |
| } else { | |
| rgCurLineLayoutItems[0][j]->s_pos_ = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| if (rgCurLineLayoutItems[0][j] | |
| ->GetFormNode() | |
| ->PresenceRequiresSpace()) { | |
| fCurPos += rgCurLineLayoutItems[0][j]->s_size_.width; | |
| } | |
| } | |
| layout_item_->AppendLastChild(rgCurLineLayoutItems[0][j]); | |
| last_row_width_ = 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]->s_pos_ = CalculatePositionedContainerPos( | |
| rgCurLineLayoutItems[1][j]->GetFormNode(), | |
| rgCurLineLayoutItems[1][j]->s_size_); | |
| } else { | |
| rgCurLineLayoutItems[1][j]->s_pos_ = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| if (rgCurLineLayoutItems[1][j] | |
| ->GetFormNode() | |
| ->PresenceRequiresSpace()) { | |
| fCurPos += rgCurLineLayoutItems[1][j]->s_size_.width; | |
| } | |
| } | |
| layout_item_->AppendLastChild(rgCurLineLayoutItems[1][j]); | |
| last_row_width_ = fCurPos; | |
| } | |
| fCurPos = fContentWidthLimit - fGroupWidths[2]; | |
| for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) { | |
| if (bRootForceTb) { | |
| rgCurLineLayoutItems[2][j]->s_pos_ = CalculatePositionedContainerPos( | |
| rgCurLineLayoutItems[2][j]->GetFormNode(), | |
| rgCurLineLayoutItems[2][j]->s_size_); | |
| } else { | |
| rgCurLineLayoutItems[2][j]->s_pos_ = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| if (rgCurLineLayoutItems[2][j] | |
| ->GetFormNode() | |
| ->PresenceRequiresSpace()) { | |
| fCurPos += rgCurLineLayoutItems[2][j]->s_size_.width; | |
| } | |
| } | |
| layout_item_->AppendLastChild(rgCurLineLayoutItems[2][j]); | |
| last_row_width_ = fCurPos; | |
| } | |
| } else { | |
| float fCurPos; | |
| fCurPos = fGroupWidths[0]; | |
| for (int32_t c = nGroupLengths[0], j = 0; j < c; j++) { | |
| if (rgCurLineLayoutItems[0][j]->GetFormNode()->PresenceRequiresSpace()) { | |
| fCurPos -= rgCurLineLayoutItems[0][j]->s_size_.width; | |
| } | |
| rgCurLineLayoutItems[0][j]->s_pos_ = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| layout_item_->AppendLastChild(rgCurLineLayoutItems[0][j]); | |
| last_row_width_ = fCurPos; | |
| } | |
| fCurPos = (fContentWidthLimit + fGroupWidths[0] + fGroupWidths[1] - | |
| fGroupWidths[2]) / | |
| 2; | |
| for (int32_t c = nGroupLengths[1], j = 0; j < c; j++) { | |
| if (rgCurLineLayoutItems[1][j]->GetFormNode()->PresenceRequiresSpace()) { | |
| fCurPos -= rgCurLineLayoutItems[1][j]->s_size_.width; | |
| } | |
| rgCurLineLayoutItems[1][j]->s_pos_ = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| layout_item_->AppendLastChild(rgCurLineLayoutItems[1][j]); | |
| last_row_width_ = fCurPos; | |
| } | |
| fCurPos = fContentWidthLimit; | |
| for (int32_t c = nGroupLengths[2], j = 0; j < c; j++) { | |
| if (rgCurLineLayoutItems[2][j]->GetFormNode()->PresenceRequiresSpace()) { | |
| fCurPos -= rgCurLineLayoutItems[2][j]->s_size_.width; | |
| } | |
| rgCurLineLayoutItems[2][j]->s_pos_ = | |
| CFX_PointF(fCurPos, *fContentCurRowY); | |
| layout_item_->AppendLastChild(rgCurLineLayoutItems[2][j]); | |
| last_row_width_ = fCurPos; | |
| } | |
| } | |
| last_row_y_ = *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_ContentLayoutProcessor::GetSubformSetParent( | |
| CXFA_Node* pSubformSet) { | |
| if (pSubformSet && pSubformSet->GetElementType() == XFA_Element::SubformSet) { | |
| CXFA_Node* pParent = pSubformSet->GetParent(); | |
| while (pParent) { | |
| if (pParent->GetElementType() != XFA_Element::SubformSet) { | |
| return pParent; | |
| } | |
| pParent = pParent->GetParent(); | |
| } | |
| } | |
| return pSubformSet; | |
| } | |
| void CXFA_ContentLayoutProcessor::DoLayoutField() { | |
| if (layout_item_) { | |
| return; | |
| } | |
| DCHECK(!cur_child_node_); | |
| layout_item_ = CreateContentLayoutItem(GetFormNode()); | |
| if (!layout_item_) { | |
| return; | |
| } | |
| CXFA_Document* document = GetFormNode()->GetDocument(); | |
| CXFA_FFNotify* pNotify = document->GetNotify(); | |
| CFX_SizeF size(-1, -1); | |
| pNotify->StartFieldDrawLayout(GetFormNode(), &size.width, &size.height); | |
| int32_t nRotate = XFA_MapRotation( | |
| GetFormNode()->JSObject()->GetInteger(XFA_Attribute::Rotate)); | |
| if (nRotate == 90 || nRotate == 270) { | |
| std::swap(size.width, size.height); | |
| } | |
| SetCurrentComponentSize(size); | |
| } | |
| CXFA_ContentLayoutProcessor::Result CXFA_ContentLayoutProcessor::DoLayout( | |
| bool bUseBreakControl, | |
| float fHeightLimit, | |
| float fRealHeight) { | |
| return DoLayoutInternal(bUseBreakControl, fHeightLimit, fRealHeight, nullptr); | |
| } | |
| CXFA_ContentLayoutProcessor::Result | |
| CXFA_ContentLayoutProcessor::DoLayoutInternal(bool bUseBreakControl, | |
| float fHeightLimit, | |
| float fRealHeight, | |
| Context* context) { | |
| switch (GetFormNode()->GetElementType()) { | |
| case XFA_Element::Subform: | |
| case XFA_Element::Area: | |
| case XFA_Element::ExclGroup: | |
| case XFA_Element::SubformSet: { | |
| bool bRootForceTb = false; | |
| CXFA_Node* pLayoutNode = GetSubformSetParent(GetFormNode()); | |
| XFA_AttributeValue eLayoutStrategy = | |
| GetLayout(pLayoutNode, &bRootForceTb); | |
| switch (eLayoutStrategy) { | |
| case XFA_AttributeValue::Tb: | |
| case XFA_AttributeValue::Lr_tb: | |
| case XFA_AttributeValue::Rl_tb: | |
| return DoLayoutFlowedContainer(bUseBreakControl, eLayoutStrategy, | |
| fHeightLimit, fRealHeight, context, | |
| bRootForceTb); | |
| case XFA_AttributeValue::Position: | |
| case XFA_AttributeValue::Row: | |
| case XFA_AttributeValue::Rl_row: | |
| default: | |
| DoLayoutPositionedContainer(context); | |
| cur_child_node_stage_ = Stage::kDone; | |
| return Result::kDone; | |
| case XFA_AttributeValue::Table: | |
| DoLayoutTableContainer(pLayoutNode); | |
| cur_child_node_stage_ = Stage::kDone; | |
| return Result::kDone; | |
| } | |
| } | |
| case XFA_Element::Draw: | |
| case XFA_Element::Field: | |
| DoLayoutField(); | |
| cur_child_node_stage_ = Stage::kDone; | |
| return Result::kDone; | |
| case XFA_Element::ContentArea: | |
| default: | |
| return Result::kDone; | |
| } | |
| } | |
| CFX_SizeF CXFA_ContentLayoutProcessor::GetCurrentComponentSize() { | |
| return CFX_SizeF(layout_item_->s_size_.width, layout_item_->s_size_.height); | |
| } | |
| void CXFA_ContentLayoutProcessor::SetCurrentComponentPos( | |
| const CFX_PointF& pos) { | |
| layout_item_->s_pos_ = pos; | |
| } | |
| void CXFA_ContentLayoutProcessor::SetCurrentComponentSize( | |
| const CFX_SizeF& size) { | |
| layout_item_->s_size_ = size; | |
| } | |
| bool CXFA_ContentLayoutProcessor::JudgeLeaderOrTrailerForOccur( | |
| CXFA_Node* pFormNode) { | |
| if (!pFormNode) { | |
| return false; | |
| } | |
| CXFA_Node* pTemplate = pFormNode->GetTemplateNodeIfExists(); | |
| if (!pTemplate) { | |
| pTemplate = pFormNode; | |
| } | |
| auto* pOccur = | |
| pTemplate->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur); | |
| if (!pOccur) { | |
| return false; | |
| } | |
| int32_t iMax = pOccur->GetMax(); | |
| if (iMax < 0) { | |
| return true; | |
| } | |
| int32_t iCount = pending_nodes_count_[pTemplate]; | |
| if (iCount >= iMax) { | |
| return false; | |
| } | |
| pending_nodes_count_[pTemplate] = iCount + 1; | |
| return true; | |
| } | |
| void CXFA_ContentLayoutProcessor::UpdatePendingItemLayout( | |
| CXFA_ContentLayoutItem* pLayoutItem) { | |
| XFA_AttributeValue eLayout = | |
| pLayoutItem->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout); | |
| switch (eLayout) { | |
| case XFA_AttributeValue::Row: | |
| case XFA_AttributeValue::Rl_row: | |
| RelocateTableRowCells(pLayoutItem, rg_specified_column_widths_, eLayout); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| void CXFA_ContentLayoutProcessor::AddTrailerBeforeSplit( | |
| float fSplitPos, | |
| CXFA_ContentLayoutItem* pTrailerLayoutItem, | |
| bool bUseInherited) { | |
| if (!pTrailerLayoutItem) { | |
| return; | |
| } | |
| float fHeight = pTrailerLayoutItem->s_size_.height; | |
| if (bUseInherited) { | |
| float fNewSplitPos = 0; | |
| if (fSplitPos - fHeight > kXFALayoutPrecision) { | |
| fNewSplitPos = FindSplitPos(fSplitPos - fHeight); | |
| } | |
| if (fNewSplitPos > kXFALayoutPrecision) { | |
| SplitLayoutItem(fNewSplitPos); | |
| } | |
| return; | |
| } | |
| UpdatePendingItemLayout(pTrailerLayoutItem); | |
| CXFA_Margin* pMargin = | |
| GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin); | |
| CFX_FloatRect inset = GetMarginInset(pMargin); | |
| if (!IsAddNewRowForTrailer(pTrailerLayoutItem)) { | |
| pTrailerLayoutItem->s_pos_.y = last_row_y_; | |
| pTrailerLayoutItem->s_pos_.x = last_row_width_; | |
| layout_item_->s_size_.width += pTrailerLayoutItem->s_size_.width; | |
| layout_item_->AppendLastChild(pTrailerLayoutItem); | |
| return; | |
| } | |
| float fNewSplitPos = 0; | |
| if (fSplitPos - fHeight > kXFALayoutPrecision) { | |
| fNewSplitPos = FindSplitPos(fSplitPos - fHeight); | |
| } | |
| if (fNewSplitPos > kXFALayoutPrecision) { | |
| SplitLayoutItem(fNewSplitPos); | |
| pTrailerLayoutItem->s_pos_.y = fNewSplitPos - inset.top - inset.bottom; | |
| } else { | |
| pTrailerLayoutItem->s_pos_.y = fSplitPos - inset.top - inset.bottom; | |
| } | |
| switch (pTrailerLayoutItem->GetFormNode()->JSObject()->GetEnum( | |
| XFA_Attribute::HAlign)) { | |
| case XFA_AttributeValue::Right: | |
| pTrailerLayoutItem->s_pos_.x = layout_item_->s_size_.width - inset.right - | |
| pTrailerLayoutItem->s_size_.width; | |
| break; | |
| case XFA_AttributeValue::Center: | |
| pTrailerLayoutItem->s_pos_.x = | |
| (layout_item_->s_size_.width - inset.left - inset.right - | |
| pTrailerLayoutItem->s_size_.width) / | |
| 2; | |
| break; | |
| case XFA_AttributeValue::Left: | |
| default: | |
| pTrailerLayoutItem->s_pos_.x = inset.left; | |
| break; | |
| } | |
| layout_item_->s_size_.height += fHeight; | |
| layout_item_->AppendLastChild(pTrailerLayoutItem); | |
| } | |
| void CXFA_ContentLayoutProcessor::AddLeaderAfterSplit( | |
| CXFA_ContentLayoutItem* pLeaderLayoutItem) { | |
| UpdatePendingItemLayout(pLeaderLayoutItem); | |
| CXFA_Margin* pMarginNode = | |
| GetFormNode()->GetFirstChildByClass<CXFA_Margin>(XFA_Element::Margin); | |
| float fLeftInset = 0; | |
| float fRightInset = 0; | |
| if (pMarginNode) { | |
| fLeftInset = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::LeftInset, XFA_Unit::Pt); | |
| fRightInset = pMarginNode->JSObject()->GetMeasureInUnit( | |
| XFA_Attribute::RightInset, XFA_Unit::Pt); | |
| } | |
| float fHeight = pLeaderLayoutItem->s_size_.height; | |
| for (CXFA_LayoutItem* pIter = layout_item_->GetFirstChild(); pIter; | |
| pIter = pIter->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pContentItem = pIter->AsContentLayoutItem(); | |
| if (!pContentItem) { | |
| continue; | |
| } | |
| pContentItem->s_pos_.y += fHeight; | |
| } | |
| pLeaderLayoutItem->s_pos_.y = 0; | |
| switch (pLeaderLayoutItem->GetFormNode()->JSObject()->GetEnum( | |
| XFA_Attribute::HAlign)) { | |
| case XFA_AttributeValue::Right: | |
| pLeaderLayoutItem->s_pos_.x = layout_item_->s_size_.width - fRightInset - | |
| pLeaderLayoutItem->s_size_.width; | |
| break; | |
| case XFA_AttributeValue::Center: | |
| pLeaderLayoutItem->s_pos_.x = | |
| (layout_item_->s_size_.width - fLeftInset - fRightInset - | |
| pLeaderLayoutItem->s_size_.width) / | |
| 2; | |
| break; | |
| case XFA_AttributeValue::Left: | |
| default: | |
| pLeaderLayoutItem->s_pos_.x = fLeftInset; | |
| break; | |
| } | |
| layout_item_->s_size_.height += fHeight; | |
| layout_item_->AppendLastChild(pLeaderLayoutItem); | |
| } | |
| void CXFA_ContentLayoutProcessor::AddPendingNode(CXFA_Node* pPendingNode, | |
| bool bBreakPending) { | |
| pending_nodes_.push_back(pPendingNode); | |
| break_pending_ = bBreakPending; | |
| } | |
| float CXFA_ContentLayoutProcessor::InsertPendingItems( | |
| CXFA_Node* pCurChildNode) { | |
| float fTotalHeight = 0; | |
| if (pending_nodes_.empty()) { | |
| return fTotalHeight; | |
| } | |
| if (!layout_item_) { | |
| layout_item_ = CreateContentLayoutItem(pCurChildNode); | |
| layout_item_->s_size_.clear(); | |
| } | |
| while (!pending_nodes_.empty()) { | |
| auto* pPendingProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), pending_nodes_.front(), | |
| nullptr); | |
| pending_nodes_.pop_front(); | |
| pPendingProcessor->DoLayout(false, FLT_MAX, FLT_MAX); | |
| CXFA_ContentLayoutItem* pPendingLayoutItem = nullptr; | |
| if (pPendingProcessor->HasLayoutItem()) { | |
| pPendingLayoutItem = pPendingProcessor->ExtractLayoutItem(); | |
| } | |
| if (pPendingLayoutItem) { | |
| AddLeaderAfterSplit(pPendingLayoutItem); | |
| if (break_pending_) { | |
| fTotalHeight += pPendingLayoutItem->s_size_.height; | |
| } | |
| } | |
| } | |
| return fTotalHeight; | |
| } | |
| CXFA_ContentLayoutProcessor::Result | |
| CXFA_ContentLayoutProcessor::InsertFlowedItem( | |
| CXFA_ContentLayoutProcessor* pProcessor, | |
| bool bContainerWidthAutoSize, | |
| bool bContainerHeightAutoSize, | |
| float fContainerHeight, | |
| XFA_AttributeValue eFlowStrategy, | |
| uint8_t* uCurHAlignState, | |
| std::array<ContentLayoutItemVector, 3>& rgCurLineLayoutItems, | |
| bool bUseBreakControl, | |
| float fAvailHeight, | |
| float fRealHeight, | |
| float fContentWidthLimit, | |
| float* fContentCurRowY, | |
| float* fContentCurRowAvailWidth, | |
| float* fContentCurRowHeight, | |
| bool* bAddedItemInRow, | |
| bool* bForceEndPage, | |
| Context* pLayoutContext, | |
| bool bNewRow) { | |
| bool bTakeSpace = pProcessor->GetFormNode()->PresenceRequiresSpace(); | |
| uint8_t uHAlign = HAlignEnumToInt( | |
| cur_child_node_->JSObject()->GetEnum(XFA_Attribute::HAlign)); | |
| if (bContainerWidthAutoSize) { | |
| uHAlign = 0; | |
| } | |
| if ((eFlowStrategy != XFA_AttributeValue::Rl_tb && | |
| uHAlign < *uCurHAlignState) || | |
| (eFlowStrategy == XFA_AttributeValue::Rl_tb && | |
| uHAlign > *uCurHAlignState)) { | |
| return Result::kRowFullBreak; | |
| } | |
| *uCurHAlignState = uHAlign; | |
| bool bIsOwnSplit = | |
| pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None; | |
| bool bUseRealHeight = bTakeSpace && bContainerHeightAutoSize && bIsOwnSplit && | |
| pProcessor->GetFormNode()->GetParent()->GetIntact() == | |
| XFA_AttributeValue::None; | |
| bool bIsTransHeight = bTakeSpace; | |
| if (bIsTransHeight && !bIsOwnSplit) { | |
| bool bRootForceTb = false; | |
| XFA_AttributeValue eLayoutStrategy = | |
| GetLayout(pProcessor->GetFormNode(), &bRootForceTb); | |
| if (eLayoutStrategy == XFA_AttributeValue::Lr_tb || | |
| eLayoutStrategy == XFA_AttributeValue::Rl_tb) { | |
| bIsTransHeight = false; | |
| } | |
| } | |
| bool bUseInherited = false; | |
| Context layoutContext; | |
| if (view_layout_processor_) { | |
| CXFA_Node* pOverflowNode = | |
| view_layout_processor_->QueryOverflow(GetFormNode()); | |
| if (pOverflowNode) { | |
| layoutContext.overflow_node_ = pOverflowNode; | |
| layoutContext.overflow_processor_ = this; | |
| pLayoutContext = &layoutContext; | |
| } | |
| } | |
| Result eRetValue = Result::kDone; | |
| if (!bNewRow || pProcessor->pre_process_rs_ == Result::kDone) { | |
| eRetValue = pProcessor->DoLayoutInternal( | |
| bTakeSpace && bUseBreakControl, | |
| bUseRealHeight ? fRealHeight - *fContentCurRowY : FLT_MAX, | |
| bIsTransHeight ? fRealHeight - *fContentCurRowY : FLT_MAX, | |
| pLayoutContext); | |
| pProcessor->pre_process_rs_ = eRetValue; | |
| } else { | |
| eRetValue = pProcessor->pre_process_rs_; | |
| pProcessor->pre_process_rs_ = Result::kDone; | |
| } | |
| if (!pProcessor->HasLayoutItem()) { | |
| return eRetValue; | |
| } | |
| CFX_SizeF childSize = pProcessor->GetCurrentComponentSize(); | |
| if (bUseRealHeight && fRealHeight < kXFALayoutPrecision) { | |
| fRealHeight = FLT_MAX; | |
| fAvailHeight = FLT_MAX; | |
| } | |
| if (bTakeSpace && | |
| (childSize.width > *fContentCurRowAvailWidth + kXFALayoutPrecision) && | |
| (fContentWidthLimit - *fContentCurRowAvailWidth > kXFALayoutPrecision)) { | |
| return Result::kRowFullBreak; | |
| } | |
| CXFA_Node* pOverflowLeaderNode = nullptr; | |
| CXFA_Node* pOverflowTrailerNode = nullptr; | |
| CXFA_Node* pFormNode = nullptr; | |
| CXFA_ContentLayoutItem* pTrailerLayoutItem = nullptr; | |
| bool bIsAddTrailerHeight = false; | |
| if (view_layout_processor_ && | |
| pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None) { | |
| pFormNode = | |
| view_layout_processor_->QueryOverflow(pProcessor->GetFormNode()); | |
| if (!pFormNode && pLayoutContext && pLayoutContext->overflow_processor_) { | |
| pFormNode = pLayoutContext->overflow_node_; | |
| bUseInherited = true; | |
| } | |
| std::optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data = | |
| view_layout_processor_->ProcessOverflow(pFormNode, false); | |
| if (overflow_data.has_value()) { | |
| pOverflowLeaderNode = overflow_data.value().pLeader; | |
| pOverflowTrailerNode = overflow_data.value().pTrailer; | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowTrailerNode)) { | |
| if (pOverflowTrailerNode) { | |
| auto* pOverflowLeaderProcessor = | |
| cppgc::MakeGarbageCollected<CXFA_ContentLayoutProcessor>( | |
| GetHeap()->GetAllocationHandle(), GetHeap(), | |
| pOverflowTrailerNode, nullptr); | |
| pOverflowLeaderProcessor->DoLayout(false, FLT_MAX, FLT_MAX); | |
| pTrailerLayoutItem = | |
| pOverflowLeaderProcessor->HasLayoutItem() | |
| ? pOverflowLeaderProcessor->ExtractLayoutItem() | |
| : nullptr; | |
| } | |
| bIsAddTrailerHeight = | |
| bUseInherited | |
| ? IsAddNewRowForTrailer(pTrailerLayoutItem) | |
| : pProcessor->IsAddNewRowForTrailer(pTrailerLayoutItem); | |
| if (bIsAddTrailerHeight) { | |
| childSize.height += pTrailerLayoutItem->s_size_.height; | |
| bIsAddTrailerHeight = true; | |
| } | |
| } | |
| } | |
| } | |
| if (!bTakeSpace || | |
| *fContentCurRowY + childSize.height <= | |
| fAvailHeight + kXFALayoutPrecision || | |
| (!bContainerHeightAutoSize && | |
| used_size_ + fAvailHeight + kXFALayoutPrecision >= fContainerHeight)) { | |
| if (!bTakeSpace || eRetValue == Result::kDone) { | |
| if (pProcessor->use_inherited_) { | |
| if (pTrailerLayoutItem) { | |
| pProcessor->AddTrailerBeforeSplit(childSize.height, | |
| pTrailerLayoutItem, false); | |
| } | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode)) { | |
| pProcessor->AddPendingNode(pOverflowLeaderNode, false); | |
| } | |
| pProcessor->use_inherited_ = false; | |
| } else { | |
| if (bIsAddTrailerHeight) { | |
| childSize.height -= pTrailerLayoutItem->s_size_.height; | |
| } | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| } | |
| CXFA_ContentLayoutItem* pChildLayoutItem = | |
| pProcessor->ExtractLayoutItem(); | |
| if (ExistContainerKeep(pProcessor->GetFormNode(), false) && | |
| pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None) { | |
| if (pChildLayoutItem) { | |
| array_keep_items_.push_back(pChildLayoutItem); | |
| } | |
| } else { | |
| array_keep_items_.clear(); | |
| } | |
| rgCurLineLayoutItems[uHAlign].push_back(pChildLayoutItem); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = | |
| std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| return Result::kDone; | |
| } | |
| if (eRetValue == Result::kPageFullBreak) { | |
| if (pProcessor->use_inherited_) { | |
| if (pTrailerLayoutItem) { | |
| pProcessor->AddTrailerBeforeSplit(childSize.height, | |
| pTrailerLayoutItem, false); | |
| } | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode)) { | |
| pProcessor->AddPendingNode(pOverflowLeaderNode, false); | |
| } | |
| pProcessor->use_inherited_ = false; | |
| } else { | |
| if (bIsAddTrailerHeight) { | |
| childSize.height -= pTrailerLayoutItem->s_size_.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; | |
| } | |
| Result eResult; | |
| if (ProcessKeepForSplit(pProcessor, eRetValue, rgCurLineLayoutItems[uHAlign], | |
| fContentCurRowAvailWidth, fContentCurRowHeight, | |
| fContentCurRowY, bAddedItemInRow, bForceEndPage, | |
| &eResult)) { | |
| return eResult; | |
| } | |
| *bForceEndPage = true; | |
| float fSplitPos = pProcessor->FindSplitPos(fAvailHeight - *fContentCurRowY); | |
| if (fSplitPos > kXFALayoutPrecision) { | |
| XFA_AttributeValue eLayout = | |
| pProcessor->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout); | |
| if (eLayout == XFA_AttributeValue::Tb && eRetValue == Result::kDone) { | |
| 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 Result::kPageFullBreak; | |
| } | |
| if (view_layout_processor_ && !pProcessor->use_inherited_ && | |
| eRetValue != Result::kPageFullBreak) { | |
| view_layout_processor_->ProcessOverflow(pFormNode, true); | |
| } | |
| if (pTrailerLayoutItem && bIsAddTrailerHeight) { | |
| pProcessor->AddTrailerBeforeSplit(fSplitPos, pTrailerLayoutItem, | |
| bUseInherited); | |
| } else { | |
| pProcessor->SplitLayoutItem(fSplitPos); | |
| } | |
| if (bUseInherited) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, pTrailerLayoutItem, | |
| pFormNode); | |
| use_inherited_ = true; | |
| } else { | |
| CXFA_LayoutItem* firstChild = pProcessor->layout_item_->GetFirstChild(); | |
| if (firstChild && !firstChild->GetNextSibling() && | |
| firstChild->GetFormNode()->IsLayoutGeneratedNode()) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| } else if (pProcessor->JudgeLeaderOrTrailerForOccur( | |
| pOverflowLeaderNode)) { | |
| pProcessor->AddPendingNode(pOverflowLeaderNode, false); | |
| } | |
| } | |
| if (pProcessor->layout_item_->GetNextSibling()) { | |
| childSize = pProcessor->GetCurrentComponentSize(); | |
| rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = | |
| std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| } | |
| return Result::kPageFullBreak; | |
| } | |
| if (*fContentCurRowY <= kXFALayoutPrecision) { | |
| childSize = pProcessor->GetCurrentComponentSize(); | |
| if (pProcessor->view_layout_processor_->GetNextAvailContentHeight( | |
| childSize.height)) { | |
| if (view_layout_processor_) { | |
| if (!pFormNode && pLayoutContext) { | |
| pFormNode = pLayoutContext->overflow_processor_->GetFormNode(); | |
| } | |
| view_layout_processor_->ProcessOverflow(pFormNode, true); | |
| } | |
| if (bUseInherited) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, | |
| pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| use_inherited_ = true; | |
| } | |
| return Result::kPageFullBreak; | |
| } | |
| rgCurLineLayoutItems[uHAlign].push_back(pProcessor->ExtractLayoutItem()); | |
| *bAddedItemInRow = true; | |
| if (bTakeSpace) { | |
| *fContentCurRowAvailWidth -= childSize.width; | |
| *fContentCurRowHeight = std::max(*fContentCurRowHeight, childSize.height); | |
| } | |
| if (eRetValue == Result::kDone) { | |
| *bForceEndPage = false; | |
| } | |
| return eRetValue; | |
| } | |
| XFA_AttributeValue eLayout = | |
| pProcessor->GetFormNode()->JSObject()->GetEnum(XFA_Attribute::Layout); | |
| if (pProcessor->GetFormNode()->GetIntact() == XFA_AttributeValue::None && | |
| eLayout == XFA_AttributeValue::Tb) { | |
| if (view_layout_processor_) { | |
| std::optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data = | |
| view_layout_processor_->ProcessOverflow(pFormNode, true); | |
| if (overflow_data.has_value()) { | |
| pOverflowLeaderNode = overflow_data.value().pLeader; | |
| pOverflowTrailerNode = overflow_data.value().pTrailer; | |
| } | |
| } | |
| if (pTrailerLayoutItem) { | |
| pProcessor->AddTrailerBeforeSplit(fSplitPos, pTrailerLayoutItem, false); | |
| } | |
| if (pProcessor->JudgeLeaderOrTrailerForOccur(pOverflowLeaderNode)) { | |
| pProcessor->AddPendingNode(pOverflowLeaderNode, false); | |
| } | |
| return Result::kPageFullBreak; | |
| } | |
| if (eRetValue != Result::kDone) { | |
| return Result::kPageFullBreak; | |
| } | |
| if (!pFormNode && pLayoutContext) { | |
| pFormNode = pLayoutContext->overflow_processor_->GetFormNode(); | |
| } | |
| if (view_layout_processor_) { | |
| std::optional<CXFA_ViewLayoutProcessor::OverflowData> overflow_data = | |
| view_layout_processor_->ProcessOverflow(pFormNode, true); | |
| if (overflow_data.has_value()) { | |
| pOverflowLeaderNode = overflow_data.value().pLeader; | |
| pOverflowTrailerNode = overflow_data.value().pTrailer; | |
| } | |
| } | |
| if (bUseInherited) { | |
| pProcessor->ProcessUnUseOverFlow(pOverflowLeaderNode, pOverflowTrailerNode, | |
| pTrailerLayoutItem, pFormNode); | |
| use_inherited_ = true; | |
| } | |
| return Result::kPageFullBreak; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::HandleKeep(CXFA_Node* pBreakAfterNode, | |
| CXFA_Node** pCurActionNode) { | |
| if (keep_break_finish_) { | |
| return std::nullopt; | |
| } | |
| return FindBreakAfterNode(pBreakAfterNode, pCurActionNode); | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::HandleBookendLeader(CXFA_Node* pParentContainer, | |
| CXFA_Node** pCurActionNode) { | |
| for (CXFA_Node* pBookendNode = *pCurActionNode | |
| ? (*pCurActionNode)->GetNextSibling() | |
| : pParentContainer->GetFirstChild(); | |
| pBookendNode; pBookendNode = pBookendNode->GetNextSibling()) { | |
| switch (pBookendNode->GetElementType()) { | |
| case XFA_Element::Bookend: | |
| case XFA_Element::Break: | |
| *pCurActionNode = pBookendNode; | |
| return Stage::kBookendLeader; | |
| default: | |
| break; | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::HandleBreakBefore(CXFA_Node* pChildContainer, | |
| CXFA_Node** pCurActionNode) { | |
| if (!*pCurActionNode) { | |
| return std::nullopt; | |
| } | |
| CXFA_Node* pBreakBeforeNode = (*pCurActionNode)->GetNextSibling(); | |
| if (!keep_break_finish_) { | |
| std::optional<Stage> ret = | |
| FindBreakBeforeNode(pBreakBeforeNode, pCurActionNode); | |
| if (ret.has_value()) { | |
| return ret.value(); | |
| } | |
| } | |
| if (is_process_keep_) { | |
| return ProcessKeepNodesForBreakBefore(pCurActionNode, pChildContainer); | |
| } | |
| *pCurActionNode = pChildContainer; | |
| return Stage::kContainer; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::HandleBreakAfter(CXFA_Node* pChildContainer, | |
| CXFA_Node** pCurActionNode) { | |
| if (*pCurActionNode) { | |
| CXFA_Node* pBreakAfterNode = (*pCurActionNode)->GetNextSibling(); | |
| return FindBreakAfterNode(pBreakAfterNode, pCurActionNode); | |
| } | |
| CXFA_Node* pBreakAfterNode = pChildContainer->GetFirstChild(); | |
| return HandleKeep(pBreakAfterNode, pCurActionNode); | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::HandleCheckNextChildContainer( | |
| CXFA_Node* pParentContainer, | |
| CXFA_Node* pChildContainer, | |
| CXFA_Node** pCurActionNode) { | |
| CXFA_Node* pNextChildContainer = | |
| pChildContainer ? pChildContainer->GetNextContainerSibling() | |
| : pParentContainer->GetFirstContainerChild(); | |
| while (pNextChildContainer && pNextChildContainer->IsLayoutGeneratedNode()) { | |
| CXFA_Node* pSaveNode = pNextChildContainer; | |
| pNextChildContainer = pNextChildContainer->GetNextContainerSibling(); | |
| if (pSaveNode->IsUnusedNode()) { | |
| DeleteLayoutGeneratedNode(pSaveNode); | |
| } | |
| } | |
| if (!pNextChildContainer) { | |
| return std::nullopt; | |
| } | |
| bool bLastKeep = false; | |
| std::optional<Stage> ret = ProcessKeepNodesForCheckNext( | |
| pCurActionNode, &pNextChildContainer, &bLastKeep); | |
| if (ret.has_value()) { | |
| return ret.value(); | |
| } | |
| if (!keep_break_finish_ && !bLastKeep) { | |
| ret = FindBreakBeforeNode(pNextChildContainer->GetFirstChild(), | |
| pCurActionNode); | |
| if (ret.has_value()) { | |
| return ret.value(); | |
| } | |
| } | |
| *pCurActionNode = pNextChildContainer; | |
| return is_process_keep_ ? Stage::kKeep : Stage::kContainer; | |
| } | |
| std::optional<CXFA_ContentLayoutProcessor::Stage> | |
| CXFA_ContentLayoutProcessor::HandleBookendTrailer(CXFA_Node* pParentContainer, | |
| CXFA_Node** pCurActionNode) { | |
| for (CXFA_Node* pBookendNode = *pCurActionNode | |
| ? (*pCurActionNode)->GetNextSibling() | |
| : pParentContainer->GetFirstChild(); | |
| pBookendNode; pBookendNode = pBookendNode->GetNextSibling()) { | |
| switch (pBookendNode->GetElementType()) { | |
| case XFA_Element::Bookend: | |
| case XFA_Element::Break: | |
| *pCurActionNode = pBookendNode; | |
| return Stage::kBookendTrailer; | |
| default: | |
| break; | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| void CXFA_ContentLayoutProcessor::ProcessKeepNodesEnd() { | |
| keep_break_finish_ = true; | |
| keep_head_node_ = nullptr; | |
| keep_tail_node_ = nullptr; | |
| is_process_keep_ = false; | |
| } | |
| void CXFA_ContentLayoutProcessor::AdjustContainerSpecifiedSize( | |
| Context* context, | |
| CFX_SizeF* pSize, | |
| bool* pContainerWidthAutoSize, | |
| bool* pContainerHeightAutoSize) { | |
| if (context && context->cur_column_width_.has_value()) { | |
| pSize->width = context->cur_column_width_.value(); | |
| *pContainerWidthAutoSize = false; | |
| } | |
| if (*pContainerHeightAutoSize) { | |
| return; | |
| } | |
| pSize->height -= used_size_; | |
| CXFA_Node* pParentNode = GetFormNode()->GetParent(); | |
| bool bFocrTb = false; | |
| if (!pParentNode || | |
| GetLayout(pParentNode, &bFocrTb) != XFA_AttributeValue::Row) { | |
| return; | |
| } | |
| CXFA_Node* pChildContainer = GetFormNode()->GetFirstContainerChild(); | |
| if (!pChildContainer || !pChildContainer->GetNextContainerSibling()) { | |
| return; | |
| } | |
| pSize->height = 0; | |
| *pContainerHeightAutoSize = true; | |
| } | |
| CXFA_ContentLayoutItem* CXFA_ContentLayoutProcessor::FindLastContentLayoutItem( | |
| XFA_AttributeValue eFlowStrategy) { | |
| if (cur_child_node_stage_ == Stage::kDone || | |
| eFlowStrategy == XFA_AttributeValue::Tb) { | |
| return nullptr; | |
| } | |
| CXFA_ContentLayoutItem* pLastChild = | |
| ToContentLayoutItem(layout_item_->GetFirstChild()); | |
| for (CXFA_LayoutItem* pNext = pLastChild; pNext; | |
| pNext = pNext->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pContentNext = pNext->AsContentLayoutItem(); | |
| if (pContentNext && pContentNext->s_pos_.y != pLastChild->s_pos_.y) { | |
| pLastChild = pContentNext; | |
| } | |
| } | |
| return pLastChild; | |
| } | |
| CFX_SizeF CXFA_ContentLayoutProcessor::CalculateLayoutItemSize( | |
| const CXFA_ContentLayoutItem* pLastChild) { | |
| CFX_SizeF size; | |
| for (CXFA_LayoutItem* pChild = layout_item_->GetFirstChild(); | |
| pChild != pLastChild; pChild = pChild->GetNextSibling()) { | |
| CXFA_ContentLayoutItem* pLayout = pChild->AsContentLayoutItem(); | |
| if (!pLayout || !pLayout->GetFormNode()->PresenceRequiresSpace()) { | |
| continue; | |
| } | |
| float fWidth = pLayout->s_pos_.x + pLayout->s_size_.width; | |
| float fHeight = pLayout->s_pos_.y + pLayout->s_size_.height; | |
| size.width = std::max(size.width, fWidth); | |
| size.height = std::max(size.height, fHeight); | |
| } | |
| return size; | |
| } | |
| CXFA_ContentLayoutProcessor::Context::Context() = default; | |
| CXFA_ContentLayoutProcessor::Context::~Context() = default; |