Skip to content

Commit

Permalink
Fix border\margin\padding treating for grid and mutlicol
Browse files Browse the repository at this point in the history
DEVSIX-8418

Autoported commit.
Original commit hash: [ca150c317]
  • Loading branch information
introfog authored and iText-CI committed Jul 2, 2024
1 parent fde62db commit bdedaad
Show file tree
Hide file tree
Showing 15 changed files with 262 additions and 206 deletions.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ You should have received a copy of the GNU Affero General Public License
using iText.Test.Utils;

namespace iText.Test {
// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android)
// Android-Conversion-Skip-File (TODO DEVSIX-7377 introduce pdf\a validation on Android)
[NUnit.Framework.Category("UnitTest")]
public class VeraPdfLoggerValidationTest : ExtendedITextTest {
//\cond DO_NOT_DOCUMENT
Expand All @@ -51,7 +51,6 @@ public virtual void CheckValidatorLogsNoOutputTest() {
NUnit.Framework.Assert.IsNull(new VeraPdfValidator().Validate(DESTINATION_FOLDER + target));
}

// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android)
[NUnit.Framework.Test]
public virtual void CheckValidatorLogsWithWarningTest() {
String source = "pdfA2b_checkValidatorLogsTest_with_warnings.pdf";
Expand All @@ -64,7 +63,6 @@ public virtual void CheckValidatorLogsWithWarningTest() {
+ target));
}

// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android)
[NUnit.Framework.Test]
public virtual void CheckValidatorLogsCleanupTest() {
String fileNameWithWarnings = "pdfA2b_checkValidatorLogsTest_with_warnings.pdf";
Expand All @@ -76,23 +74,19 @@ public virtual void CheckValidatorLogsCleanupTest() {
+ "WARNING: The Top DICT does not begin with ROS operator";
NUnit.Framework.Assert.AreEqual(expectedWarningsForFileWithWarnings, new VeraPdfValidator().Validate(DESTINATION_FOLDER
+ fileNameWithWarnings));
// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android)
//We check that the logs are empty after the first check
NUnit.Framework.Assert.IsNull(new VeraPdfValidator().Validate(DESTINATION_FOLDER + fileNameWithoutWarnings
));
}

// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android)
[NUnit.Framework.Test]
public virtual void CheckValidatorLogsForFileContainingErrorsTest() {
String source = "pdfA2b_checkValidatorLogsTest_with_errors.pdf";
String target = "checkValidatorLogsForFileContainingErrorsTest.pdf";
FileUtil.Copy(SOURCE_FOLDER + source, DESTINATION_FOLDER + target);
String expectedResponseForErrors = "VeraPDF verification failed. See verification results: file:";
String result = new VeraPdfValidator().Validate(DESTINATION_FOLDER + target);
// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android));
NUnit.Framework.Assert.IsTrue(result.StartsWith(expectedResponseForErrors));
}
// Android-Conversion-Skip-Line (TODO DEVSIX-7377 introduce pdf\a validation on Android)
}
}
96 changes: 22 additions & 74 deletions itext/itext.layout/itext/layout/renderer/GridContainerRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ You should have received a copy of the GNU Affero General Public License
using System;
using System.Collections.Generic;
using iText.Kernel.Geom;
using iText.Layout.Borders;
using iText.Layout.Element;
using iText.Layout.Layout;
using iText.Layout.Properties;
Expand All @@ -36,6 +35,8 @@ public class GridContainerRenderer : BlockRenderer {

private float containerHeight = 0.0f;

private float containerWidth = 0.0f;

/// <summary>Creates a Grid renderer from its corresponding layout object.</summary>
/// <param name="modelElement">
/// the
Expand All @@ -59,13 +60,12 @@ public override LayoutResult Layout(LayoutContext layoutContext) {
//this.setProperty(Property.TREAT_AS_CONTINUOUS_CONTAINER, Boolean.TRUE);
Rectangle actualBBox = layoutContext.GetArea().GetBBox().Clone();
float? blockWidth = RetrieveWidth(actualBBox.GetWidth());
if (blockWidth != null) {
actualBBox.SetWidth((float)blockWidth);
}
ContinuousContainer.SetupContinuousContainerIfNeeded(this);
ApplyPaddings(actualBBox, false);
ApplyBorderBox(actualBBox, false);
ApplyMargins(actualBBox, false);
ApplyWidth(actualBBox, blockWidth, OverflowPropertyValue.VISIBLE);
containerWidth = actualBBox.GetWidth();
float? blockHeight = RetrieveHeight();
if (blockHeight != null && (float)blockHeight < actualBBox.GetHeight()) {
actualBBox.SetY(actualBBox.GetY() + actualBBox.GetHeight() - (float)blockHeight);
Expand All @@ -85,8 +85,8 @@ public override LayoutResult Layout(LayoutContext layoutContext) {
}
else {
this.occupiedArea = CalculateContainerOccupiedArea(layoutContext, false);
return new LayoutResult(LayoutResult.PARTIAL, this.occupiedArea, CreateSplitRenderer(layoutResult.GetSplitRenderers
()), CreateOverflowRenderer(layoutResult.GetOverflowRenderers()));
return new LayoutResult(LayoutResult.PARTIAL, this.occupiedArea, GridMulticolUtil.CreateSplitRenderer(layoutResult
.GetSplitRenderers(), this), CreateOverflowRenderer(layoutResult.GetOverflowRenderers()));
}
}
}
Expand Down Expand Up @@ -123,18 +123,6 @@ public override void AddChild(IRenderer renderer) {
return false;
}

private AbstractRenderer CreateSplitRenderer(IList<IRenderer> children) {
AbstractRenderer splitRenderer = (AbstractRenderer)GetNextRenderer();
splitRenderer.parent = parent;
splitRenderer.modelElement = modelElement;
splitRenderer.occupiedArea = occupiedArea;
splitRenderer.isLastRendererForModelElement = false;
splitRenderer.SetChildRenderers(children);
splitRenderer.AddAllProperties(GetOwnProperties());
ContinuousContainer.SetupContinuousContainerIfNeeded(splitRenderer);
return splitRenderer;
}

private AbstractRenderer CreateOverflowRenderer(IList<IRenderer> children) {
iText.Layout.Renderer.GridContainerRenderer overflowRenderer = (iText.Layout.Renderer.GridContainerRenderer
)GetNextRenderer();
Expand Down Expand Up @@ -248,64 +236,24 @@ private static LayoutContext GetCellLayoutContext(LayoutContext layoutContext, R
// Calculate grid container occupied area based on its width/height properties and cell layout areas
private LayoutArea CalculateContainerOccupiedArea(LayoutContext layoutContext, bool isFull) {
LayoutArea area = layoutContext.GetArea().Clone();
float totalHeight = UpdateOccupiedHeight(containerHeight, isFull);
if (totalHeight < area.GetBBox().GetHeight() || isFull) {
area.GetBBox().SetHeight(totalHeight);
Rectangle initialBBox = layoutContext.GetArea().GetBBox();
area.GetBBox().SetY(initialBBox.GetY() + initialBBox.GetHeight() - area.GetBBox().GetHeight());
RecalculateHeightAndWidthAfterLayout(area.GetBBox(), isFull);
}
return area;
}

// Recalculate height/width after grid sizing and re-apply height/width properties
private void RecalculateHeightAndWidthAfterLayout(Rectangle bBox, bool isFull) {
float? height = RetrieveHeight();
if (height != null) {
height = UpdateOccupiedHeight((float)height, isFull);
float heightDelta = bBox.GetHeight() - (float)height;
bBox.MoveUp(heightDelta);
bBox.SetHeight((float)height);
}
float? blockWidth = RetrieveWidth(bBox.GetWidth());
if (blockWidth != null) {
bBox.SetWidth((float)blockWidth);
}
}

private float UpdateOccupiedHeight(float initialHeight, bool isFull) {
if (isFull) {
initialHeight += SafelyRetrieveFloatProperty(Property.PADDING_BOTTOM);
initialHeight += SafelyRetrieveFloatProperty(Property.MARGIN_BOTTOM);
if (!this.HasOwnProperty(Property.BORDER) || this.GetProperty<Border>(Property.BORDER) == null) {
initialHeight += SafelyRetrieveFloatProperty(Property.BORDER_BOTTOM);
Rectangle areaBBox = area.GetBBox();
float totalContainerHeight = GridMulticolUtil.UpdateOccupiedHeight(containerHeight, isFull, isFirstLayout,
this);
if (totalContainerHeight < areaBBox.GetHeight() || isFull) {
float? height = RetrieveHeight();
if (height == null) {
areaBBox.SetHeight(totalContainerHeight);
}
else {
height = GridMulticolUtil.UpdateOccupiedHeight((float)height, isFull, isFirstLayout, this);
areaBBox.SetHeight((float)height);
}
}
initialHeight += SafelyRetrieveFloatProperty(Property.PADDING_TOP);
initialHeight += SafelyRetrieveFloatProperty(Property.MARGIN_TOP);
if (!this.HasOwnProperty(Property.BORDER) || this.GetProperty<Border>(Property.BORDER) == null) {
initialHeight += SafelyRetrieveFloatProperty(Property.BORDER_TOP);
}
// isFirstLayout is necessary to handle the case when grid container laid out on more
// than 2 pages, and on the last page layout result is full, but there is no bottom border
float TOP_AND_BOTTOM = isFull && isFirstLayout ? 2 : 1;
//If container laid out on more than 3 pages, then it is a page where there are no bottom and top borders
if (!isFull && !isFirstLayout) {
TOP_AND_BOTTOM = 0;
}
initialHeight += SafelyRetrieveFloatProperty(Property.BORDER) * TOP_AND_BOTTOM;
return initialHeight;
}

private float SafelyRetrieveFloatProperty(int property) {
Object value = this.GetProperty<Object>(property);
if (value is UnitValue) {
return ((UnitValue)value).GetValue();
}
if (value is Border) {
return ((Border)value).GetWidth();
}
return 0F;
Rectangle initialBBox = layoutContext.GetArea().GetBBox();
areaBBox.SetY(initialBBox.GetY() + initialBBox.GetHeight() - areaBBox.GetHeight());
float totalContainerWidth = GridMulticolUtil.UpdateOccupiedWidth(containerWidth, this);
areaBBox.SetWidth(totalContainerWidth);
return area;
}

// Grid layout algorithm is based on a https://drafts.csswg.org/css-grid/#layout-algorithm
Expand Down
116 changes: 116 additions & 0 deletions itext/itext.layout/itext/layout/renderer/GridMulticolUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2024 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using iText.Layout.Borders;
using iText.Layout.Properties;

namespace iText.Layout.Renderer {
//\cond DO_NOT_DOCUMENT
/// <summary>The class stores common logic for multicol and grid layout.</summary>
internal sealed class GridMulticolUtil {
private GridMulticolUtil() {
}

//\cond DO_NOT_DOCUMENT
// do nothing
/// <summary>Creates a split renderer.</summary>
/// <param name="children">children of the split renderer</param>
/// <param name="renderer">parent renderer</param>
/// <returns>
/// a new
/// <see cref="AbstractRenderer"/>
/// instance
/// </returns>
internal static AbstractRenderer CreateSplitRenderer(IList<IRenderer> children, AbstractRenderer renderer) {
AbstractRenderer splitRenderer = (AbstractRenderer)renderer.GetNextRenderer();
splitRenderer.parent = renderer.parent;
splitRenderer.modelElement = renderer.modelElement;
splitRenderer.occupiedArea = renderer.occupiedArea;
splitRenderer.isLastRendererForModelElement = false;
splitRenderer.SetChildRenderers(children);
splitRenderer.AddAllProperties(renderer.GetOwnProperties());
ContinuousContainer.SetupContinuousContainerIfNeeded(splitRenderer);
return splitRenderer;
}
//\endcond

//\cond DO_NOT_DOCUMENT
internal static float UpdateOccupiedWidth(float initialWidth, AbstractRenderer renderer) {
float result = initialWidth;
result += SafelyRetrieveFloatProperty(Property.PADDING_LEFT, renderer);
result += SafelyRetrieveFloatProperty(Property.PADDING_RIGHT, renderer);
result += SafelyRetrieveFloatProperty(Property.MARGIN_LEFT, renderer);
result += SafelyRetrieveFloatProperty(Property.MARGIN_RIGHT, renderer);
if (!renderer.HasOwnProperty(Property.BORDER) || renderer.GetProperty<Border>(Property.BORDER) == null) {
result += SafelyRetrieveFloatProperty(Property.BORDER_LEFT, renderer);
}
if (!renderer.HasOwnProperty(Property.BORDER) || renderer.GetProperty<Border>(Property.BORDER) == null) {
result += SafelyRetrieveFloatProperty(Property.BORDER_RIGHT, renderer);
}
result += SafelyRetrieveFloatProperty(Property.BORDER, renderer) * 2;
return result;
}
//\endcond

//\cond DO_NOT_DOCUMENT
internal static float UpdateOccupiedHeight(float initialHeight, bool isFull, bool isFirstLayout, AbstractRenderer
renderer) {
float result = initialHeight;
if (isFull) {
result += SafelyRetrieveFloatProperty(Property.PADDING_BOTTOM, renderer);
result += SafelyRetrieveFloatProperty(Property.MARGIN_BOTTOM, renderer);
if (!renderer.HasOwnProperty(Property.BORDER) || renderer.GetProperty<Border>(Property.BORDER) == null) {
result += SafelyRetrieveFloatProperty(Property.BORDER_BOTTOM, renderer);
}
}
result += SafelyRetrieveFloatProperty(Property.PADDING_TOP, renderer);
result += SafelyRetrieveFloatProperty(Property.MARGIN_TOP, renderer);
if (!renderer.HasOwnProperty(Property.BORDER) || renderer.GetProperty<Border>(Property.BORDER) == null) {
result += SafelyRetrieveFloatProperty(Property.BORDER_TOP, renderer);
}
// isFirstLayout is necessary to handle the case when multicol container layouted in more
// than 2 pages, and on the last page layout result is full, but there is no bottom border
float TOP_AND_BOTTOM = isFull && isFirstLayout ? 2 : 1;
// Multicol container layouted in more than 3 pages, and there is a page where there are no bottom and top borders
if (!isFull && !isFirstLayout) {
TOP_AND_BOTTOM = 0;
}
result += SafelyRetrieveFloatProperty(Property.BORDER, renderer) * TOP_AND_BOTTOM;
return result;
}
//\endcond

private static float SafelyRetrieveFloatProperty(int property, AbstractRenderer renderer) {
Object value = renderer.GetProperty<Object>(property);
if (value is UnitValue) {
return ((UnitValue)value).GetValue();
}
if (value is Border) {
return ((Border)value).GetWidth();
}
return 0F;
}
}
//\endcond
}
Loading

0 comments on commit bdedaad

Please sign in to comment.