Is the PdfWriter.getverticalposition() stopped in

2019-08-27 03:15发布

问题:

I am trying to add a IBlockElement at the end of the last page in the Itext 7 version my approach is

  1. After writing all the elements to the pdf document get the vertical position from the writer with writer.getVerticalPosition()
  2. Calculate the available space on the current page by using the bottomMargin as a reference.
  3. If space is less than the space required then add another blank page
  4. Create and insert a container of fixed height with text vertical alignment to bottom Add IBlockElement content to the container

However when I am using it

var PdfWriter= new PdfWriter(memoryStream, writerProperties)
PdfWriter.getverticalposition()

I am getting error:

PdfWriter writer does not contain a definition of getverticalposition(). No method getverticalposition() accept first argument of type PdfWriter are you missing assembly reference?

Do I have to change the assembly reference or something?

EDIT DATE: 10-Nov-2018

private class BottomBlockElement : Div
    {
        public BottomBlockElement(IBlockElement wrapping)
        {
            base.SetKeepTogether(true);
            base.Add(wrapping);
            //add(wrapping);
            //setKeepTogether(true);

        }

        override protected IRenderer MakeNewRenderer()
        {
            return new BottomBlockRenderer(this);
        }
    }

    private class BottomBlockRenderer : DivRenderer
    {
        public BottomBlockRenderer(BottomBlockElement modelElement) : base(modelElement)
        {
        }

        override public LayoutResult Layout(LayoutContext layoutContext)
        {
            LayoutResult result = base.Layout(layoutContext);
            if (result.GetStatus() == LayoutResult.FULL)
            {
                float leftoverHeight = result.GetOccupiedArea().GetBBox().GetBottom() - layoutContext.GetArea().GetBBox().GetBottom();
                Move(0, -leftoverHeight);
                return new LayoutResult(result.GetStatus(), layoutContext.GetArea(), result.GetSplitRenderer(), result.GetOverflowRenderer());
            }
            else
            {
                return result;
            }
        }

        public override IRenderer GetNextRenderer()
        {
            return new BottomBlockRenderer((BottomBlockElement)modelElement);
        }

    }

But still the text is overlapping

回答1:

iText7 still allows you to get analogue of current vertical position, but as @mkl noted in his comment, there are are lot of nuances with this concept because iText7 support much more complex layout strategies.

In general you should try to think out of the box sometimes when migrating from iText5 to iText7 - there are a ton of things you can do much easier with iText7, including your use case of adding content to the bottom of last page.

You can use iText7 analogue of CSS absolute positioning to add content to the bottom. To do that, you have to specify position property as well as bottom offset. There is still no fancy public API to do that because the functionality is still under constant improvement, but you can do that with setting necessary properties manually. All you need to do is add these lines:

glueToBottom.setProperty(Property.POSITION, LayoutPosition.ABSOLUTE);
glueToBottom.setProperty(Property.BOTTOM, 0);

To demonstrate the result, let's add some content first and then the block element to the end of the last page.

Document document = new Document(pdfDocument);

for (int i = 0; i < 40; i++) {
    document.add(new Paragraph("Hello " + i));
}

IBlockElement glueToBottom = new Paragraph("Hi, I am the bottom content")
        .setFontSize(25)
        .setWidth(UnitValue.createPercentValue(100))
        .setBackgroundColor(ColorConstants.RED)
        .setTextAlignment(TextAlignment.CENTER);
glueToBottom.setProperty(Property.POSITION, LayoutPosition.ABSOLUTE);
glueToBottom.setProperty(Property.BOTTOM, 0);

document.add(glueToBottom);

document.close();

You will get something like this at page 2 (last page), which matches your description exactly:

UPD: The algorithm in question was updated and now contains logic of inserting a new page for the content at the bottom of the page if the content at the bottom would overlap with existing content had the page not been inserted. To achieve same behavior you would need a slight modification of the code above: instead of setting positioning properties via setProperty let's implement our own element and corresponding renderer and wrap your block element into this implementation. We will add our element now as follows:

document.add(new BottomBlockElement(glueToBottom));

The implementations are straightforward - just move the element to the bottom between laying it out and drawing. This is a bit verbose in code but still quite clear:

private static class BottomBlockElement extends Div {
    public BottomBlockElement(IBlockElement wrapping) {
        super();
        add(wrapping);
        setKeepTogether(true);
    }

    @Override
    protected IRenderer makeNewRenderer() {
        return new BottomBlockRenderer(this);
    }
}

private static class BottomBlockRenderer extends DivRenderer {
    public BottomBlockRenderer(BottomBlockElement modelElement) {
        super(modelElement);
    }

    @Override
    public LayoutResult layout(LayoutContext layoutContext) {
        LayoutResult result = super.layout(layoutContext);
        if (result.getStatus() == LayoutResult.FULL) {
            float leftoverHeight = result.getOccupiedArea().getBBox().getBottom() - layoutContext.getArea().getBBox().getBottom();
            move(0, -leftoverHeight);
            return new LayoutResult(result.getStatus(), layoutContext.getArea(), result.getSplitRenderer(), result.getOverflowRenderer());
        } else {
            return result;
        }
    }

    @Override
    public IRenderer getNextRenderer() {
        return new BottomBlockRenderer((BottomBlockElement) modelElement);
    }
}

Now our main part looks like this:

Document document = new Document(pdfDocument);

for (int i = 0; i < 58; i++) {
    document.add(new Paragraph("Hello " + i));
}

IBlockElement glueToBottom = new Paragraph("Hi, I am the bottom content")
        .setFontSize(25)
        .setWidth(UnitValue.createPercentValue(100))
        .setBorder(new SolidBorder(ColorConstants.RED, 1))
        .setTextAlignment(TextAlignment.CENTER);

document.add(new BottomBlockElement(glueToBottom));

document.close();

And the result is the last page containing only the bottom block if the previous one does not have enough space:



标签: c# itext itext7