All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.layout.renderer.CellRenderer Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    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 .
 */
package com.itextpdf.layout.renderer;

import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.geom.AffineTransform;
import com.itextpdf.kernel.geom.Matrix;
import com.itextpdf.kernel.geom.NoninvertibleTransformException;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.IPropertyContainer;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.exceptions.LayoutExceptionMessageConstant;
import com.itextpdf.layout.properties.BorderCollapsePropertyValue;
import com.itextpdf.layout.properties.Property;
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.layout.layout.LayoutContext;

/**
 * Represents a renderer for the {@link Cell} layout element.
 */
public class CellRenderer extends BlockRenderer {
    /**
     * Creates a CellRenderer from its corresponding layout object.
     *
     * @param modelElement the {@link com.itextpdf.layout.element.Cell} which this object should manage
     */
    public CellRenderer(Cell modelElement) {
        super(modelElement);
        assert modelElement != null;
        setProperty(Property.ROWSPAN, modelElement.getRowspan());
        setProperty(Property.COLSPAN, modelElement.getColspan());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IPropertyContainer getModelElement() {
        return super.getModelElement();
    }

    @Override
    protected Float retrieveWidth(float parentBoxWidth) {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected AbstractRenderer createSplitRenderer(int layoutResult) {
        CellRenderer splitRenderer = (CellRenderer) getNextRenderer();
        splitRenderer.parent = parent;
        splitRenderer.modelElement = modelElement;
        splitRenderer.occupiedArea = occupiedArea;
        splitRenderer.isLastRendererForModelElement = false;
        splitRenderer.addAllProperties(getOwnProperties());
        return splitRenderer;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected AbstractRenderer createOverflowRenderer(int layoutResult) {
        CellRenderer overflowRenderer = (CellRenderer) getNextRenderer();
        overflowRenderer.parent = parent;
        overflowRenderer.modelElement = modelElement;
        overflowRenderer.addAllProperties(getOwnProperties());
        return overflowRenderer;
    }

    @Override
    public void drawBackground(DrawContext drawContext) {
        PdfCanvas canvas = drawContext.getCanvas();
        Matrix ctm = canvas.getGraphicsState().getCtm();

        // Avoid rotation
        Float angle = this.getPropertyAsFloat(Property.ROTATION_ANGLE);
        boolean avoidRotation = null != angle && hasProperty(Property.BACKGROUND);
        boolean restoreRotation = hasOwnProperty(Property.ROTATION_ANGLE);
        if (avoidRotation) {
            AffineTransform transform = new AffineTransform(ctm.get(0), ctm.get(1), ctm.get(3), ctm.get(4), ctm.get(6), ctm.get(7));
            try {
                transform = transform.createInverse();
            } catch (NoninvertibleTransformException e) {
                throw new PdfException(LayoutExceptionMessageConstant.NONINVERTIBLE_MATRIX_CANNOT_BE_PROCESSED, e);
            }
            transform.concatenate(new AffineTransform());
            canvas.concatMatrix(transform);
            setProperty(Property.ROTATION_ANGLE, null);
        }

        super.drawBackground(drawContext);

        // restore concat matrix and rotation angle
        if (avoidRotation) {
            if (restoreRotation) {
                setProperty(Property.ROTATION_ANGLE, angle);
            } else {
                deleteOwnProperty(Property.ROTATION_ANGLE);
            }
            canvas.concatMatrix(new AffineTransform(ctm.get(0), ctm.get(1), ctm.get(3), ctm.get(4), ctm.get(6), ctm.get(7)));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void drawBorder(DrawContext drawContext) {
        if (BorderCollapsePropertyValue.SEPARATE.equals(parent.getProperty(Property.BORDER_COLLAPSE))) {
            super.drawBorder(drawContext);
        } else {
            // Do nothing here. Border drawing for cells is done on TableRenderer.
        }
    }

    @Override
    protected Rectangle applyBorderBox(Rectangle rect, Border[] borders, boolean reverse) {
        if (BorderCollapsePropertyValue.SEPARATE.equals(parent.getProperty(Property.BORDER_COLLAPSE))) {
            super.applyBorderBox(rect, borders, reverse);
        } else {
            // Do nothing here. Borders are processed on TableRenderer level.
        }

        return rect;
    }

    @Override
    protected Rectangle applyMargins(Rectangle rect, UnitValue[] margins, boolean reverse) {
        // If borders are separated, process border's spacing here.
        if (BorderCollapsePropertyValue.SEPARATE.equals(parent.getProperty(Property.BORDER_COLLAPSE))) {
            applySpacings(rect, reverse);
        }
        return rect;
    }

    /**
     * Applies spacings on the given rectangle.
     *
     * @param rect    a rectangle spacings will be applied on
     * @param reverse indicates whether spacings will be applied
     *                inside (in case of false) or outside (in case of true) the rectangle.
     *
     * @return a {@link Rectangle border box} of the renderer
     */
    protected Rectangle applySpacings(Rectangle rect, boolean reverse) {
        if (BorderCollapsePropertyValue.SEPARATE.equals(parent.getProperty(Property.BORDER_COLLAPSE))) {
            Float verticalBorderSpacing = this.parent.getProperty(Property.VERTICAL_BORDER_SPACING);
            Float horizontalBorderSpacing = this.parent.getProperty(Property.HORIZONTAL_BORDER_SPACING);
            float[] cellSpacings = new float[4];
            for (int i = 0; i < cellSpacings.length; i++) {
                cellSpacings[i] = 0 == i % 2
                        ? null != verticalBorderSpacing ? (float) verticalBorderSpacing : 0f
                        : null != horizontalBorderSpacing ? (float) horizontalBorderSpacing : 0f;
            }
            applySpacings(rect, cellSpacings, reverse);
        } else {
            // Do nothing here. Spacings are meaningless if borders are collapsed.
        }
        return rect;
    }

    /**
     * Applies given spacings on the given rectangle.
     *
     * @param rect    a rectangle spacings will be applied on
     * @param spacings the spacings to be applied on the given rectangle
     * @param reverse indicates whether spacings will be applied
     *                inside (in case of false) or outside (in case of true) the rectangle.
     *
     * @return a {@link Rectangle border box} of the renderer
     */
    protected Rectangle applySpacings(Rectangle rect, float[] spacings, boolean reverse) {
        if (BorderCollapsePropertyValue.SEPARATE.equals(parent.getProperty(Property.BORDER_COLLAPSE))) {
            rect.applyMargins(spacings[0] / 2, spacings[1] / 2, spacings[2] / 2, spacings[3] / 2, reverse);
        } else {
            // Do nothing here. Spacings are meaningless if borders are collapsed.
        }
        return rect;
    }

    /**
     * Gets a new instance of this class to be used as a next renderer, after this renderer is used, if
     * {@link #layout(LayoutContext)} is called more than once.
     *
     * 

* If a renderer overflows to the next area, iText uses this method to create a renderer * for the overflow part. So if one wants to extend {@link CellRenderer}, one should override * this method: otherwise the default method will be used and thus the default rather than the custom * renderer will be created. * @return new renderer instance */ @Override public IRenderer getNextRenderer() { logWarningIfGetNextRendererNotOverridden(CellRenderer.class, this.getClass()); return new CellRenderer((Cell) getModelElement()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy