Skip to content

Commit 25659fb

Browse files
committed
Implement 2D transform CSS properties in renderers
DEVSIX-1310
1 parent cedd237 commit 25659fb

File tree

7 files changed

+73
-11
lines changed

7 files changed

+73
-11
lines changed

layout/src/main/java/com/itextpdf/layout/property/Property.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ private Property() {
175175
public static final int TEXT_RENDERING_MODE = 71;
176176
public static final int TEXT_RISE = 72;
177177
public static final int TOP = 73;
178+
public static final int TRANSFORM = 101;
178179
public static final int UNDERLINE = 74;
179180
public static final int VERTICAL_ALIGNMENT = 75;
180181
/**

layout/src/main/java/com/itextpdf/layout/renderer/AbstractRenderer.java

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -788,11 +788,10 @@ protected boolean clipBackgroundArea(DrawContext drawContext, Rectangle outerBor
788788
public void drawChildren(DrawContext drawContext) {
789789
List<IRenderer> waitingRenderers = new ArrayList<>();
790790
for (IRenderer child : childRenderers) {
791-
if (FloatingHelper.isRendererFloating(child)) {
791+
if (FloatingHelper.isRendererFloating(child) || child.getProperty(Property.TRANSFORM) != null) {
792792
RootRenderer rootRenderer = getRootRenderer();
793-
if (rootRenderer != null) {
793+
if (rootRenderer != null && !rootRenderer.waitingDrawingElements.contains(child)) {
794794
rootRenderer.waitingDrawingElements.add(child);
795-
child.setProperty(Property.FLOAT, null);
796795
} else {
797796
waitingRenderers.add(child);
798797
}
@@ -1523,6 +1522,22 @@ protected Rectangle calculateAbsolutePdfBBox() {
15231522
}
15241523
}
15251524

1525+
float[] transform = renderer.<float[]>getProperty(Property.TRANSFORM);
1526+
if (transform != null) {
1527+
if (renderer instanceof BlockRenderer) {
1528+
BlockRenderer blockRenderer = (BlockRenderer) renderer;
1529+
AffineTransform rotationTransform = blockRenderer.createTransformationInsideOccupiedArea();
1530+
transformPoints(contentBoxPoints, rotationTransform);
1531+
} else if (renderer instanceof ImageRenderer) {
1532+
ImageRenderer imageRenderer = (ImageRenderer) renderer;
1533+
AffineTransform rotationTransform = imageRenderer.createTransformationInsideOccupiedArea();
1534+
transformPoints(contentBoxPoints, rotationTransform);
1535+
} else if (renderer instanceof TableRenderer) {
1536+
TableRenderer tableRenderer = (TableRenderer) renderer;
1537+
AffineTransform rotationTransform = tableRenderer.createTransformationInsideOccupiedArea();
1538+
transformPoints(contentBoxPoints, rotationTransform);
1539+
}
1540+
}
15261541
renderer = (AbstractRenderer) renderer.parent;
15271542
}
15281543

@@ -1801,4 +1816,37 @@ private static float calculatePaddingBorderHeight(AbstractRenderer renderer) {
18011816
return dummy.getHeight();
18021817
}
18031818

1819+
/**
1820+
* This method creates {@link AffineTransform} instance that could be used
1821+
* to transform content inside the occupied area,
1822+
* considering the centre of the occupiedArea as the origin of a coordinate system for transformation.
1823+
*
1824+
* @return {@link AffineTransform} that transforms the content and places it inside occupied area.
1825+
*/
1826+
protected AffineTransform createTransformationInsideOccupiedArea() {
1827+
Rectangle backgroundArea = applyMargins(occupiedArea.clone().getBBox(), false);
1828+
float x = backgroundArea.getX();
1829+
float y = backgroundArea.getY();
1830+
float height = backgroundArea.getHeight();
1831+
float width = backgroundArea.getWidth();
1832+
1833+
AffineTransform transform = AffineTransform.getTranslateInstance(-1 * (x + width / 2), -1 * (y + height / 2));
1834+
transform.preConcatenate((new AffineTransform((float[]) this.getProperty(Property.TRANSFORM))));
1835+
transform.preConcatenate(AffineTransform.getTranslateInstance(x + width / 2, y + height / 2));
1836+
1837+
return transform;
1838+
}
1839+
1840+
protected void beginTranformationIfApplied(PdfCanvas canvas) {
1841+
if (this.getProperty(Property.TRANSFORM) != null) {
1842+
AffineTransform transform = createTransformationInsideOccupiedArea();
1843+
canvas.saveState().concatMatrix(transform);
1844+
}
1845+
}
1846+
1847+
protected void endTranformationIfApplied(PdfCanvas canvas) {
1848+
if (this.getProperty(Property.TRANSFORM) != null) {
1849+
canvas.restoreState();
1850+
}
1851+
}
18041852
}

layout/src/main/java/com/itextpdf/layout/renderer/BlockRenderer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
432432
if (rotation != null) {
433433
applyRotationLayout(layoutContext.getArea().getBBox().clone());
434434
if (isNotFittingLayoutArea(layoutContext.getArea())) {
435-
if(isNotFittingWidth(layoutContext.getArea()) && !isNotFittingHeight(layoutContext.getArea())) {
435+
if (isNotFittingWidth(layoutContext.getArea()) && !isNotFittingHeight(layoutContext.getArea())) {
436436
LoggerFactory.getLogger(getClass()).warn(MessageFormatUtil.format(LogMessageConstant.ELEMENT_DOES_NOT_FIT_AREA, "It fits by height so it will be forced placed"));
437437
} else if (!Boolean.TRUE.equals(getPropertyAsBoolean(Property.FORCED_PLACEMENT))) {
438438
return new MinMaxWidthLayoutResult(LayoutResult.NOTHING, null, null, this, this);
@@ -524,6 +524,7 @@ public void draw(DrawContext drawContext) {
524524
}
525525
}
526526

527+
beginTranformationIfApplied(drawContext.getCanvas());
527528
applyDestinationsAndAnnotation(drawContext);
528529

529530
boolean isRelativePosition = isRelativePosition();
@@ -575,6 +576,7 @@ public void draw(DrawContext drawContext) {
575576
}
576577

577578
flushed = true;
579+
endTranformationIfApplied(drawContext.getCanvas());
578580
}
579581

580582
@Override
@@ -791,7 +793,7 @@ float getOverflowPartHeight(OverflowPropertyValue overflowY, Rectangle parentBox
791793
}
792794

793795
protected float applyBordersPaddingsMargins(Rectangle parentBBox, Border[] borders, float[] paddings) {
794-
float parentWidth = parentBBox.getWidth();
796+
float parentWidth = parentBBox.getWidth();
795797

796798
applyMargins(parentBBox, false);
797799
applyBorderBox(parentBBox, borders, false);

layout/src/main/java/com/itextpdf/layout/renderer/CanvasRenderer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public CanvasRenderer(Canvas canvas) {
6969
* Creates a CanvasRenderer from its corresponding layout object.
7070
* Defines whether the content should be flushed immediately after addition {@link #addChild(IRenderer)} or not
7171
*
72-
* @param canvas the {@link com.itextpdf.layout.Canvas} which this object should manage
72+
* @param canvas the {@link com.itextpdf.layout.Canvas} which this object should manage
7373
* @param immediateFlush the value which stands for immediate flushing
7474
*/
7575
public CanvasRenderer(Canvas canvas, boolean immediateFlush) {
@@ -92,9 +92,8 @@ public void addChild(IRenderer renderer) {
9292
*/
9393
@Override
9494
protected void flushSingleRenderer(IRenderer resultRenderer) {
95-
if (FloatingHelper.isRendererFloating(resultRenderer)) {
95+
if (!waitingDrawingElements.contains(resultRenderer) && (FloatingHelper.isRendererFloating(resultRenderer) || resultRenderer.getProperty(Property.TRANSFORM) != null)) {
9696
waitingDrawingElements.add(resultRenderer);
97-
resultRenderer.setProperty(Property.FLOAT, null);
9897
return;
9998
}
10099

@@ -129,6 +128,7 @@ protected LayoutArea updateCurrentArea(LayoutResult overflowResult) {
129128

130129
/**
131130
* For {@link CanvasRenderer}, this has a meaning of the renderer that will be used for relayout.
131+
*
132132
* @return relayout renderer.
133133
*/
134134
@Override

layout/src/main/java/com/itextpdf/layout/renderer/DocumentRenderer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public LayoutArea getOccupiedArea() {
7979

8080
/**
8181
* For {@link DocumentRenderer}, this has a meaning of the renderer that will be used for relayout.
82+
*
8283
* @return relayout renderer.
8384
*/
8485
@Override
@@ -109,9 +110,8 @@ protected LayoutArea updateCurrentArea(LayoutResult overflowResult) {
109110
}
110111

111112
protected void flushSingleRenderer(IRenderer resultRenderer) {
112-
if (FloatingHelper.isRendererFloating(resultRenderer)) {
113+
if (!waitingDrawingElements.contains(resultRenderer) && (FloatingHelper.isRendererFloating(resultRenderer) || resultRenderer.getProperty(Property.TRANSFORM) != null)) {
113114
waitingDrawingElements.add(resultRenderer);
114-
resultRenderer.setProperty(Property.FLOAT, null);
115115
return;
116116
}
117117

layout/src/main/java/com/itextpdf/layout/renderer/ImageRenderer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ This file is part of the iText (R) project.
7373
import org.slf4j.LoggerFactory;
7474

7575
import com.itextpdf.io.util.MessageFormatUtil;
76+
7677
import java.util.List;
7778

7879
public class ImageRenderer extends AbstractRenderer implements ILeafElementRenderer {
@@ -322,6 +323,9 @@ public void draw(DrawContext drawContext) {
322323
drawContext.getCanvas().saveState();
323324
applyConcatMatrix(drawContext, angle);
324325
}
326+
327+
beginTranformationIfApplied(drawContext.getCanvas());
328+
325329
super.draw(drawContext);
326330
if (angle != null) {
327331
drawContext.getCanvas().restoreState();
@@ -340,6 +344,9 @@ public void draw(DrawContext drawContext) {
340344
PdfXObject xObject = ((Image) (getModelElement())).getXObject();
341345
beginElementOpacityApplying(drawContext);
342346
canvas.addXObject(xObject, matrix[0], matrix[1], matrix[2], matrix[3], (float) fixedXPosition + deltaX, (float) fixedYPosition);
347+
348+
endTranformationIfApplied(drawContext.getCanvas());
349+
343350
endElementOpacityApplying(drawContext);
344351
if (Boolean.TRUE.equals(getPropertyAsBoolean(Property.FLUSH_ON_DRAW))) {
345352
xObject.flush();

layout/src/main/java/com/itextpdf/layout/renderer/TableRenderer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ public LayoutResult layout(LayoutContext layoutContext) {
702702
CellRenderer overflowCell = (CellRenderer) rows.get(i)[col].getModelElement().getRenderer().setParent(this);
703703
rows.get(i)[col].isLastRendererForModelElement = false;
704704
overflowRows.setCell(i - row, col, null);
705-
overflowRows.setCell(targetOverflowRowIndex[col] - row, col, overflowCell);
705+
overflowRows.setCell(targetOverflowRowIndex[col] - row, col, overflowCell);
706706
CellRenderer originalCell = rows.get(i)[col];
707707
rows.get(i)[col] = null;
708708
rows.get(targetOverflowRowIndex[col])[col] = originalCell;
@@ -941,7 +941,9 @@ public void draw(DrawContext drawContext) {
941941
applyGeneratedAccessibleAttributes(tagPointer, layoutAttributes);
942942
}
943943

944+
beginTranformationIfApplied(drawContext.getCanvas());
944945
super.draw(drawContext);
946+
endTranformationIfApplied(drawContext.getCanvas());
945947

946948
tagPointer.moveToParent();
947949

@@ -950,7 +952,9 @@ public void draw(DrawContext drawContext) {
950952
tagPointer.removeElementConnectionToTag(accessibleElement);
951953
}
952954
} else {
955+
beginTranformationIfApplied(drawContext.getCanvas());
953956
super.draw(drawContext);
957+
endTranformationIfApplied(drawContext.getCanvas());
954958
}
955959
}
956960

0 commit comments

Comments
 (0)