blob: 2817fa09b02bb87bfc2cacceb86eb4f81b372a37 [file] [log] [blame]
// 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* pDocument = pFormNode->GetDocument();
CXFA_FFNotify* pNotify = pDocument->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* pContext) {
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 (pContext && pContext->prg_specified_column_widths_) {
int32_t iColSpan =
cur_child_node_->JSObject()->GetInteger(XFA_Attribute::ColSpan);
if (iColSpan <= fxcrt::CollectionSize<int32_t>(
*pContext->prg_specified_column_widths_) -
iColIndex) {
pContext->cur_column_width_ = 0.0f;
if (iColSpan == -1) {
iColSpan = fxcrt::CollectionSize<int32_t>(
*pContext->prg_specified_column_widths_);
}
for (int32_t i = 0; iColIndex + i < iColSpan; ++i) {
pContext->cur_column_width_.value() +=
(*pContext->prg_specified_column_widths_)[iColIndex + i];
}
if (pContext->cur_column_width_.value() == 0) {
pContext->cur_column_width_.reset();
}
iColIndex += iColSpan >= 0 ? iColSpan : 0;
}
}
pProcessor->DoLayoutInternal(false, FLT_MAX, FLT_MAX, pContext);
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* pContext,
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(pContext, &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: {
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, pContext, 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, pContext, 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, pContext, 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, pContext,
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, pContext,
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, pContext,
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* pDocument = GetFormNode()->GetDocument();
CXFA_FFNotify* pNotify = pDocument->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* pContext) {
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, pContext,
bRootForceTb);
case XFA_AttributeValue::Position:
case XFA_AttributeValue::Row:
case XFA_AttributeValue::Rl_row:
default:
DoLayoutPositionedContainer(pContext);
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) {
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* pContext,
CFX_SizeF* pSize,
bool* pContainerWidthAutoSize,
bool* pContainerHeightAutoSize) {
if (pContext && pContext->cur_column_width_.has_value()) {
pSize->width = pContext->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;