Java 更改页面时出现PdfBox问题

Java 更改页面时出现PdfBox问题,java,pdf,pdf-generation,rendering,pdfbox,Java,Pdf,Pdf Generation,Rendering,Pdfbox,我不太喜欢问这样的问题,但我已经花了整整三天的时间试图解决我代码中的这个错误 我知道这是一个逻辑问题,我知道如何在头脑中解决它,但当涉及到将我的想法转化为代码时,我就是无法让它按我想要的方式工作 我正在做一个合同背书(合同的修改),它比较了两个表中的数据,如果其中任何一个表发生了变化,那么它只绘制了这些信息 有时,信息会发生变化,例如条件变长或变短,这就是问题所在 我做了一个算法,当它完成绘制信息时,得到最小Y坐标,但当我更改页面时,Y坐标必须重置为700f,然后从那里重新开始绘制 我使用的代码

我不太喜欢问这样的问题,但我已经花了整整三天的时间试图解决我代码中的这个错误

我知道这是一个逻辑问题,我知道如何在头脑中解决它,但当涉及到将我的想法转化为代码时,我就是无法让它按我想要的方式工作

我正在做一个合同背书(合同的修改),它比较了两个表中的数据,如果其中任何一个表发生了变化,那么它只绘制了这些信息

有时,信息会发生变化,例如条件变长或变短,这就是问题所在

我做了一个算法,当它完成绘制信息时,得到最小Y坐标,但当我更改页面时,Y坐标必须重置为
700f
,然后从那里重新开始绘制

我使用的代码最初是由另一个程序员编写的,他已经不在这里了,我不知道如何处理PDF

但我认为这些方法可以帮助你帮助我

我如何在PDF上比较和绘制信息的示例:

sOffA
sOffE
是来自名为
SubscriptionOffer
的类的对象,
a
代表
Agreement
,而
E
代表
背书
。背书是MySQL上的表
协议
的副本,但信息已修改。也许这是无关的信息,也许不是

if (!nullOrEmpty(sOffA.getGeneralConditions()) &&
    !nullOrEmpty(sOffE.getGeneralConditions())) {
    if (!getStringValue(sOffA.getGeneralConditions()).
    equals(getStringValue(sOffE.getGeneralConditions()))) {
    minYs[0] = pdf.rText(LEFT_MARGIN, y, 10, 
                 constants.generalConditions(),
                 getStringValue(sOffA.
                        getGeneralConditions()));

    minYs[1] = pdf.rText(HALF_PAGE, y, 10, 
                 constants.generalConditions(),
                 getStringValue(sOffE.
                        getGeneralConditions()));

    y = checkY(pdf, minYs);
    }
} else if (nullOrEmpty(sOffA.getGeneralConditions()) &&
       !nullOrEmpty(sOffE.getGeneralConditions())) {
    minYs[0] = pdf.rText(LEFT_MARGIN, y, 10, "", "");

    minYs[1] = pdf.rText(HALF_PAGE, y, 10, 
             constants.generalConditions(),
             getStringValue(sOffE.
                    getGeneralConditions()));

    y = checkY(pdf, minYs);
} else if (!nullOrEmpty(sOffA.getGeneralConditions()) &&
       nullOrEmpty(sOffE.getGeneralConditions())) {
    minYs[0] = pdf.rText(LEFT_MARGIN, y, 10, 
             constants.generalConditions(),
             getStringValue(sOffA.
                    getGeneralConditions()));

    minYs[1] = pdf.rText(HALF_PAGE, y, 10, "", "");

    y = checkY(pdf, minYs);
}
我使用的阵列如下所示:

private float[] minYs = new float[] {700, 700, 700, 700, 700, 700, 700,
                                     700, 700};
这是一种检查Y的方法,它检查数组中哪个Y(如上所示)是最低的,然后将所有元素的整个数组重新启动回700f。然后检查最低Y坐标下的空间是否足以绘制下一个项目

private float checkY(PdfRenderingEndorsement pdf, float... ys) throws Exception {
    float y2 = getMinY(pdf, ys);
    for (int i = 0; i < ys.length; i++) {
        ys[i] = 700;
    }
    y2 = pdf.checkContentStream(y2, 5, 10);
    return y2;      
}
获取对象的字符串值并检查它们是否为null或空的方法:

private boolean nullOrEmpty(String s) {
    return s == null || s.isEmpty();
}

private String getStringValue(Object o) {
    if (o == null) {
        return "";
    }
    return getStringValue(o, null);
}

private String getStringValue(Object o, Class< ? extends Unit> clazz) {
    if (o instanceof Boolean) {
        Boolean bd = (Boolean) o;
        if (bd) {
        return constants.getString("dbeditorYes");
        } else {
        return constants.getString("dbeditorNo");
        }
    } else if (o instanceof Date) {
        Date date = (Date) o;
        DateFormat df = new SimpleDateFormat(DateConstants.DATE_FORMAT);
        return df.format(date);
    } else if (o instanceof Enum) {
        Enum en = (Enum) o;
        return constants.enumMap().get(en.toString());
    } else if (o instanceof Integer) {
        Integer integer = (Integer) o;
        if (clazz == null) {
        return String.valueOf(integer);
        } else {
        Unit entry = unitSvc.get(clazz, integer);
        String[] params = entry.toString().split("\\|");
        String label = params.length == 1 ? params[0] : params[1];
        return label;
        }
    } else if (o instanceof BigDecimal) {
        BigDecimal bd = (BigDecimal) o;
        DecimalFormat df = new DecimalFormat("#,##0");
        df = new DecimalFormat("#,##0.00");
        return df.format(bd);
    } else if (o instanceof Float) {
        Float bd = (Float) o;
        DecimalFormat df = new DecimalFormat("#,##0");
        df = new DecimalFormat("#,##0.00");
        return df.format(bd);
    } else if (o instanceof String) {
        String td = (String) o;
        return td;
    }
    return "";
}
这是每个方法的原始(
)输出的一部分

如果您看不到PDF,请告诉我,但这里还有一些截图:

这是一个在
getMinY
方法上使用非注释代码的输出

这一修改的结果如下:

正如您可以看到的,第一个输出“跳转”到下一页,因为我们假设“Texto de Poliza”右侧的文本以Y coord 680、670或类似的形式结束,但在左侧,我绘制了一个空字段
(“”)
,以90或100结尾,接近这些数字

然后比较100<670?是的,然后我取50到100,它比我的
下页边距
(60)低,所以它关闭了实际页面(现在是文本结束的地方,但认为它在前面的页面上),并创建了一个新页面

差不多一年前我问过一个类似的问题,这些类几乎都是这些文件的副本,但我的逻辑上的这个错误只出现在这些文件中,因为数据处理方式有点不同

在那一堆文字之后,我希望有人能读到它,并帮我一把,也许我遗漏了一个重要的部分,但找不到它

提前谢谢

编辑

在将@mkl的答案添加到我的方法中之后,我发现当双方的信息都没有改变时,就会产生一个缺口,看起来也不太好

这就是我将数据发送到
PdfRenderingEndorsementAlternative
的方式:

for (String[] data: sOppData) {
        //float y = renderer.getPreviousBandBase;
        for (int i = 0; i < data.length - 2; i += 3) {
            if (!nullOrEmpty(data[i + 1]) &&
                !nullOrEmpty(data[i + 2])) {
                if (!data[i + 1].equals(data[i + 2])) {
                renderer.
                    render(new BandColumn(leftHalfPageField,
                              data[i], data[i + 1]),
                       new BandColumn(rightHalfPageField,
                              data[i], data[i + 2])
                       );
                }
            } else if (nullOrEmpty(data[i + 1]) &&
                   !nullOrEmpty(data[i + 2])) {
                renderer.
                render(new BandColumn(leftHalfPageField, 
                              "", ""),
                       new BandColumn(rightHalfPageField,
                              data[i], data[i + 2])
                       );
            } else if (!nullOrEmpty(data[i + 1]) &&
                   nullOrEmpty(data[i + 2])) {
                renderer.
                render(new BandColumn(leftHalfPageField,
                              data[i], data[i + 1]),
                       new BandColumn(rightHalfPageField,
                              "", "")
                       );
            }
        }
        //float y2 = renderer.getPreviousBandBase();
        /*if (y2 < y) 
            renderer.gap(20);
        */
        renderer.gap(20); 
  }

正如已经在评论中暗示的(实际上已经在您的评论中),我认为渲染类的整个体系结构需要大修。基于您的
PdfRenderingEndorsement
我创建了以下类
PdfRenderingEndorsementAlternative
,它代表了该渲染的另一种方法:

public class PdfRenderingEndorsementAlternative implements AutoCloseable
{
    //
    // misc constants
    //
    static final int FIELD_WIDTH = 70;
    static final int HALF_WIDTH = 325;
    static final int TEXT_WIDTH = 410;

    static final float BOTTOM_MARGIN = 70;
    static final int LEFT_MARGIN = 50;

    //
    // rendering
    //
    public void gap(int size)
    {
        previousBandBase-=size;
    }

    public void render(BandColumn... columns) throws IOException
    {
        if (content == null)
            newPage();

        final List<Chunk> chunks = new ArrayList<Chunk>();
        for (BandColumn column : columns)
        {
            chunks.addAll(column.toChunks());
        }

        float offset = 0;
        while (!chunks.isEmpty())
        {
            float lowestAddedY = previousBandBase;
            float highestBaseBeforeNonAdded = Float.NEGATIVE_INFINITY;
            List<Chunk> added = new ArrayList<Chunk>();
            for (Chunk chunk: chunks)
            {
                float y = previousBandBase + chunk.y + offset; 
                if (y >= BOTTOM_MARGIN)
                {
                    content.beginText();
                    content.setFont(chunk.font, 9);
                    content.moveTextPositionByAmount(chunk.x, y);
                    content.drawString(chunk.text);
                    content.endText();
                    // draw
                    if (y < lowestAddedY)
                        lowestAddedY = y;
                    added.add(chunk);
                }
                else
                {
                    float baseBefore = chunk.y + chunk.space;
                    if (baseBefore > highestBaseBeforeNonAdded)
                        highestBaseBeforeNonAdded = baseBefore;
                }
            }
            chunks.removeAll(added);
            if (!chunks.isEmpty())
            {
                newPage();
                offset = -highestBaseBeforeNonAdded;
            }
            else
            {
                previousBandBase = lowestAddedY;
            }
        }
    }

    static public class BandColumn
    {
        public enum Layout
        {
            headerText(150, TEXT_WIDTH, 0, 10),
            leftHalfPageField(LEFT_MARGIN, FIELD_WIDTH, HALF_WIDTH - 2 * FIELD_WIDTH - 10, 10),
            rightHalfPageField(HALF_WIDTH, FIELD_WIDTH, HALF_WIDTH - 2 * FIELD_WIDTH - 10, 10);

            Layout(float x, int fieldWidth, int valueWidth, int space)
            {
                this.x = x;
                this.fieldWidth = fieldWidth;
                this.valueWidth = valueWidth;
                this.space = space;
            }

            final float x;
            final int fieldWidth, valueWidth, space;
        }

        public BandColumn(Layout layout, String labelField, String value)
        {
            this(layout.x, layout.space, labelField, value, layout.fieldWidth, layout.valueWidth);
        }

        public BandColumn(float x, int space, String labelField, String value, int fieldWidth, int valueWidth)
        {
            this.x = x;
            this.space = space;
            this.labelField = labelField;
            this.value = value;
            this.fieldWidth = fieldWidth;
            this.valueWidth = valueWidth;
        }

        List<Chunk> toChunks() throws IOException
        {
            final List<Chunk> result = new ArrayList<Chunk>();
            result.addAll(toChunks(0, fieldWidth, PDType1Font.TIMES_BOLD, labelField));
            result.addAll(toChunks(10 + fieldWidth, valueWidth, PDType1Font.TIMES_ROMAN, value));
            return result;
        }

        List<Chunk> toChunks(int offset, int width, PDFont font, String text) throws IOException
        {
            if (text == null || text.length() == 0)
                return Collections.emptyList();

            final List<Chunk> result = new ArrayList<Chunk>();
            float y = -space;
            List<String> rows = getRows(text, width, font);
            for (String row: rows)
            {
                result.add(new Chunk(x+offset, y, space, font, row));
                y-= space;
            }
            return result;
        }

        final float x;
        final int space, fieldWidth, valueWidth;
        final String labelField, value;
    }

    //
    // constructor
    //
    public PdfRenderingEndorsementAlternative(PDDocument doc, InputStream logo, 
            String[] header) throws IOException
    {
        this.doc = doc;
        this.header = header;
        logoImg = new PDJpeg(doc, logo);
    }

    //
    // AutoCloseable implementation
    //
    @Override
    public void close() throws IOException
    {
        if (content != null)
        {
            content.close();
            content = null;
        }
    }

    //
    // helper methods
    //
    void newPage() throws IOException
    {
        close();

        PDPage page = new PDPage(PDPage.PAGE_SIZE_LETTER);
        doc.addPage(page);
        content = new PDPageContentStream(doc, page);
        content.drawImage(logoImg, 50, 720);
        content.setLineWidth(.5f);
        content.setNonStrokingColor(Color.GRAY);
        content.drawLine(50, 710, 562, 710);

        previousBandBase = 770;
        render(new BandColumn(BandColumn.Layout.headerText, "ENDOSO", null));
        for (String head: header)
            render(new BandColumn(BandColumn.Layout.headerText, head, null));

        content.setNonStrokingColor(Color.BLACK);
        previousBandBase = 680;
    }

    // original method
    static List<String> getRows(String text, int width, PDFont font) throws IOException
    {
        float textWidth = font.getStringWidth(text) / 1000f * 9f;
        ArrayList<String> result = new ArrayList<String>();// Lists.newArrayList();
        if (textWidth < width)
        {
            result.add(text);
            return result;
        }

        float spaceWidth = font.getStringWidth(" ") / 1000f * 9f;
        String[] paragraphs = text.split("\n|\r\n|\r");
        for (String paragraph : paragraphs)
        {
            float pWidth = font.getStringWidth(paragraph) / 1000f * 9f;
            if (pWidth < width)
            {
                result.add(paragraph);
                continue;
            }

            float widthCount = 0f;
            String[] words = paragraph.trim().split(" ");
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < words.length; j++)
            {
                if (words[j].trim().length() == 0)
                {
                    continue;
                }

                float wWidth = font.getStringWidth(words[j]) / 1000f * 9f;
                float totalWidth = widthCount + wWidth + spaceWidth;
                if (totalWidth < width + spaceWidth)
                {
                    sb.append(words[j]);
                    sb.append(" ");
                    widthCount = totalWidth;
                }
                else
                {
                    result.add(sb.toString().trim());
                    sb = new StringBuilder();
                    sb.append(words[j]);
                    sb.append(" ");
                    widthCount = totalWidth - widthCount;
                }
            }
            result.add(sb.toString().trim());
        }
        return result;
    }

    //
    // helper classes
    //
    static class Chunk
    {
        Chunk(float x, float y, int space, PDFont font, String text)
        {
            this.x = x;
            this.y = y;
            this.space = space;
            this.font = font;
            this.text = text;
        }

        final float x, y;
        final int space;
        final PDFont font;
        final String text;
    }

    //
    // members
    //
    private final PDDocument doc;
    private final PDJpeg logoImg;
    private final String[] header;

    private PDPageContentStream content = null;
    private float previousBandBase = 0;
}
()

如您所见,调用者不必再关心y位置,一切都是在renderer类中完成的。结果是:

我使用您的第一个示例PDF中的数据作为输入,结果如下:


正如您所见,没有页面跳转,也没有重叠文本。

正如我已经对您的前一个问题进行了评论,我怀疑从rtext内部检查页面切换是否是一个好主意,它应该由某个方法控制,并注意为所有列中的当前条目添加的内容。@mkl您是否有一个示例,或者该方法的另一个问题的链接?或者我能分析的类似的东西?对不起,我迟了回答。谢谢现在我将阅读一些文档并尝试一些方法。我对pdf编程没有那么丰富的经验,所以我犯了很多错误(我不知道如何用pdf做一个简单的“Hello World”,我的意思是不是从0开始)。我已经开始阅读如何用PDFBox和Maven从0开始pdf,我仍然停留在Maven配置上,但一旦我有了一个MCVE,我就会发布它每次在for循环中,不管是否渲染了条目。仅在呈现条目后添加间隙。我以前怎么看不到?我在想一个
else{continue}
用于
if(!data[I+1].equals(data[I+2])
我将在周一测试它……我正在测试解决方案,我花了一段时间将它调整到项目中,你介意我在4小时内开始一个悬赏(在添加悬赏之前错过时间)吗在周末接受你的答案,这样你就能得到赏金了?顺便说一句,非常感谢你,看到这种方法,我想我是在尝试重新发明轮子(而且是以错误的方式)。无论如何,我将继续尝试配置Maven,以从0创建项目,并学习如何在将来为PDF进行MCVE。您是否介意…-我不介意,但请不要觉得有义务这么做。我希望此代码示例可以帮助您。当然,它对我有帮助。我没有义务,但非常感谢。非常感谢:)我有疑问,你会在哪里检查两边是否都有变化?也就是说,即使信息没有变化,也会产生20的差距。我想用现在的Y和结束的Y,但我想我会回到我的方法中,这会带来
for (String[] data: sOppData) {
        //float y = renderer.getPreviousBandBase;
        for (int i = 0; i < data.length - 2; i += 3) {
            if (!nullOrEmpty(data[i + 1]) &&
                !nullOrEmpty(data[i + 2])) {
                if (!data[i + 1].equals(data[i + 2])) {
                renderer.
                    render(new BandColumn(leftHalfPageField,
                              data[i], data[i + 1]),
                       new BandColumn(rightHalfPageField,
                              data[i], data[i + 2])
                       );
                }
            } else if (nullOrEmpty(data[i + 1]) &&
                   !nullOrEmpty(data[i + 2])) {
                renderer.
                render(new BandColumn(leftHalfPageField, 
                              "", ""),
                       new BandColumn(rightHalfPageField,
                              data[i], data[i + 2])
                       );
            } else if (!nullOrEmpty(data[i + 1]) &&
                   nullOrEmpty(data[i + 2])) {
                renderer.
                render(new BandColumn(leftHalfPageField,
                              data[i], data[i + 1]),
                       new BandColumn(rightHalfPageField,
                              "", "")
                       );
            }
        }
        //float y2 = renderer.getPreviousBandBase();
        /*if (y2 < y) 
            renderer.gap(20);
        */
        renderer.gap(20); 
  }
private ArrayList<String []> renderSubscriptionOpportunity(
               SubscriptionOpp sOppA, SubscriptionOpp sOppE, 
               ArrayList<Location> lcA, ArrayList<Location> lcE) 
    throws Exception {
    ArrayList <String[]> sOppData = new ArrayList<String[]>();

    sOppData.add(new String[] {constants.currencyId(),
                   getStringValue(sOppA.getCurrencyId(), 
                          Currency.class),
                   getStringValue(sOppE.getCurrencyId(),
                          Currency.class)});

    sOppData.add(new String[] {constants.contractName(),
                   getStringValue(sOppA.getContractName()),
                   getStringValue(sOppE.getContractName())});

    sOppData.add(new String[] {constants.mainActivityId(),
                   getStringValue(sOppA.getMainActivityId(),
                          MainActivity.class),
                   getStringValue(sOppE.getMainActivityId(),
                          MainActivity.class)});

    //here add location table

    if (lcA.size() > 1 && lcE.size() > 1) {
        int lastIdA = 0;
        int lastIdE = 0;
        int size = lcA.size() >= lcE.size() ? lcA.size() : lcE.size();

        LOGGER.trace("size: " + size + " lcA.size(): " + lcA.size() +
             " lcE.size(): " + lcE.size());
        for (int pos = 1; pos < lcA.size(); pos++) {
        StringBuilder aSb = new StringBuilder();
        StringBuilder eSb = new StringBuilder();
        String valueA = "";
        String valueE = "";
        if (pos < lcA.size()) {
            Country countryA = unitSvc.get(Country.class, 
                           lcA.get(pos).getCountryId());
            LOGGER.trace("Entro1");
            if (countryA.getId() != lastIdA) {
            aSb.append(countryA.getName());
            lastIdA = countryA.getId();
            } else {
            aSb.append("");
            }
        } else {
            aSb.append("");
        }       
        if (pos < lcE.size()) {
            Country countryE = unitSvc.get(Country.class, 
                           lcE.get(pos).getCountryId());
            LOGGER.trace("Entro2");
            if (countryE.getId() != lastIdE) {
            eSb.append(countryE.getName());
            lastIdE = countryE.getId();
            } else {
            eSb.append("");
            }
        } else {
            eSb.append("");
        }
        valueA = aSb.toString();
        valueE = eSb.toString();
        sOppData.add(new String[] {pos == 1 ? constants.countryId() : 
                       "", valueA, valueE});
        }
    }
    return sOppData;
}
public class PdfRenderingEndorsementAlternative implements AutoCloseable
{
    //
    // misc constants
    //
    static final int FIELD_WIDTH = 70;
    static final int HALF_WIDTH = 325;
    static final int TEXT_WIDTH = 410;

    static final float BOTTOM_MARGIN = 70;
    static final int LEFT_MARGIN = 50;

    //
    // rendering
    //
    public void gap(int size)
    {
        previousBandBase-=size;
    }

    public void render(BandColumn... columns) throws IOException
    {
        if (content == null)
            newPage();

        final List<Chunk> chunks = new ArrayList<Chunk>();
        for (BandColumn column : columns)
        {
            chunks.addAll(column.toChunks());
        }

        float offset = 0;
        while (!chunks.isEmpty())
        {
            float lowestAddedY = previousBandBase;
            float highestBaseBeforeNonAdded = Float.NEGATIVE_INFINITY;
            List<Chunk> added = new ArrayList<Chunk>();
            for (Chunk chunk: chunks)
            {
                float y = previousBandBase + chunk.y + offset; 
                if (y >= BOTTOM_MARGIN)
                {
                    content.beginText();
                    content.setFont(chunk.font, 9);
                    content.moveTextPositionByAmount(chunk.x, y);
                    content.drawString(chunk.text);
                    content.endText();
                    // draw
                    if (y < lowestAddedY)
                        lowestAddedY = y;
                    added.add(chunk);
                }
                else
                {
                    float baseBefore = chunk.y + chunk.space;
                    if (baseBefore > highestBaseBeforeNonAdded)
                        highestBaseBeforeNonAdded = baseBefore;
                }
            }
            chunks.removeAll(added);
            if (!chunks.isEmpty())
            {
                newPage();
                offset = -highestBaseBeforeNonAdded;
            }
            else
            {
                previousBandBase = lowestAddedY;
            }
        }
    }

    static public class BandColumn
    {
        public enum Layout
        {
            headerText(150, TEXT_WIDTH, 0, 10),
            leftHalfPageField(LEFT_MARGIN, FIELD_WIDTH, HALF_WIDTH - 2 * FIELD_WIDTH - 10, 10),
            rightHalfPageField(HALF_WIDTH, FIELD_WIDTH, HALF_WIDTH - 2 * FIELD_WIDTH - 10, 10);

            Layout(float x, int fieldWidth, int valueWidth, int space)
            {
                this.x = x;
                this.fieldWidth = fieldWidth;
                this.valueWidth = valueWidth;
                this.space = space;
            }

            final float x;
            final int fieldWidth, valueWidth, space;
        }

        public BandColumn(Layout layout, String labelField, String value)
        {
            this(layout.x, layout.space, labelField, value, layout.fieldWidth, layout.valueWidth);
        }

        public BandColumn(float x, int space, String labelField, String value, int fieldWidth, int valueWidth)
        {
            this.x = x;
            this.space = space;
            this.labelField = labelField;
            this.value = value;
            this.fieldWidth = fieldWidth;
            this.valueWidth = valueWidth;
        }

        List<Chunk> toChunks() throws IOException
        {
            final List<Chunk> result = new ArrayList<Chunk>();
            result.addAll(toChunks(0, fieldWidth, PDType1Font.TIMES_BOLD, labelField));
            result.addAll(toChunks(10 + fieldWidth, valueWidth, PDType1Font.TIMES_ROMAN, value));
            return result;
        }

        List<Chunk> toChunks(int offset, int width, PDFont font, String text) throws IOException
        {
            if (text == null || text.length() == 0)
                return Collections.emptyList();

            final List<Chunk> result = new ArrayList<Chunk>();
            float y = -space;
            List<String> rows = getRows(text, width, font);
            for (String row: rows)
            {
                result.add(new Chunk(x+offset, y, space, font, row));
                y-= space;
            }
            return result;
        }

        final float x;
        final int space, fieldWidth, valueWidth;
        final String labelField, value;
    }

    //
    // constructor
    //
    public PdfRenderingEndorsementAlternative(PDDocument doc, InputStream logo, 
            String[] header) throws IOException
    {
        this.doc = doc;
        this.header = header;
        logoImg = new PDJpeg(doc, logo);
    }

    //
    // AutoCloseable implementation
    //
    @Override
    public void close() throws IOException
    {
        if (content != null)
        {
            content.close();
            content = null;
        }
    }

    //
    // helper methods
    //
    void newPage() throws IOException
    {
        close();

        PDPage page = new PDPage(PDPage.PAGE_SIZE_LETTER);
        doc.addPage(page);
        content = new PDPageContentStream(doc, page);
        content.drawImage(logoImg, 50, 720);
        content.setLineWidth(.5f);
        content.setNonStrokingColor(Color.GRAY);
        content.drawLine(50, 710, 562, 710);

        previousBandBase = 770;
        render(new BandColumn(BandColumn.Layout.headerText, "ENDOSO", null));
        for (String head: header)
            render(new BandColumn(BandColumn.Layout.headerText, head, null));

        content.setNonStrokingColor(Color.BLACK);
        previousBandBase = 680;
    }

    // original method
    static List<String> getRows(String text, int width, PDFont font) throws IOException
    {
        float textWidth = font.getStringWidth(text) / 1000f * 9f;
        ArrayList<String> result = new ArrayList<String>();// Lists.newArrayList();
        if (textWidth < width)
        {
            result.add(text);
            return result;
        }

        float spaceWidth = font.getStringWidth(" ") / 1000f * 9f;
        String[] paragraphs = text.split("\n|\r\n|\r");
        for (String paragraph : paragraphs)
        {
            float pWidth = font.getStringWidth(paragraph) / 1000f * 9f;
            if (pWidth < width)
            {
                result.add(paragraph);
                continue;
            }

            float widthCount = 0f;
            String[] words = paragraph.trim().split(" ");
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < words.length; j++)
            {
                if (words[j].trim().length() == 0)
                {
                    continue;
                }

                float wWidth = font.getStringWidth(words[j]) / 1000f * 9f;
                float totalWidth = widthCount + wWidth + spaceWidth;
                if (totalWidth < width + spaceWidth)
                {
                    sb.append(words[j]);
                    sb.append(" ");
                    widthCount = totalWidth;
                }
                else
                {
                    result.add(sb.toString().trim());
                    sb = new StringBuilder();
                    sb.append(words[j]);
                    sb.append(" ");
                    widthCount = totalWidth - widthCount;
                }
            }
            result.add(sb.toString().trim());
        }
        return result;
    }

    //
    // helper classes
    //
    static class Chunk
    {
        Chunk(float x, float y, int space, PDFont font, String text)
        {
            this.x = x;
            this.y = y;
            this.space = space;
            this.font = font;
            this.text = text;
        }

        final float x, y;
        final int space;
        final PDFont font;
        final String text;
    }

    //
    // members
    //
    private final PDDocument doc;
    private final PDJpeg logoImg;
    private final String[] header;

    private PDPageContentStream content = null;
    private float previousBandBase = 0;
}
PDDocument document = new PDDocument();
PdfRenderingEndorsementAlternative renderer = new PdfRenderingEndorsementAlternative(document, logoStream, header);

renderer.render(
        new BandColumn(leftHalfPageField, "Nombre del contrato/asegurado:", "Prueba Jesus Fac No Prop"),
        new BandColumn(rightHalfPageField, "Nombre del contrato/asegurado:", "Prueba Jesus Fac No Prop con Endoso")
        );

renderer.gap(20);

renderer.render(
        new BandColumn(leftHalfPageField, "País:", "México"),
        new BandColumn(rightHalfPageField, "País:", "México")
        );

renderer.close();
document.save(new File(RESULT_FOLDER, "Endorsement.pdf"));