更改页面时 PdfBox 问题

2023-12-08

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

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

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

有时,随着条件的变化,信息的长度会增加或减少,这就是问题所在。

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

我使用的代码首先是由另一位程序员编写的,他已经不在这里了,我不知道如何做MCVE与 PDF 的。

但我认为以下方法可以帮助您帮助我。

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

sOffA and sOffE是来自名为的类的对象SubscriptionOffer and A代表Agreement while E for Endorsement。背书是表格的副本Agreement在 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};

这是方法checkY,它检查数组中哪个 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;      
}

我猜这个方法是我的逻辑问题所在,正如你所看到的,我尝试了不同的方法,原始算法只是for-each然后我尝试了不同的事情但没有成功。

private float getMinY(PdfRenderingEndorsement pdf, float... ys) {
    float result = 700f;
    float lowest1 = 0, lowest2 = 0;
    for (int i = 0; i < ys.length; i++) {
        for (int j = 0; j < ys.length; j++) {
        if (ys[j] > ys[i]) {
            float aux = ys[i];
            ys[i] = ys[j];
            ys[j] = aux;
            //lowest1 = ys[i];
            //lowest2 = ys[j];
        }
        }
    }
    if (ys.length > 1) {
        lowest1 = ys[0];
        lowest2 = ys[1];
    }
    LOGGER.trace("lowest1: " + lowest1);
    LOGGER.trace("lowest2: " + lowest2);
    LOGGER.trace("newPage " + pdf.getNewPage());
    /*if(pdf.getNewPage()) {
        return lowest1 > lowest2 ? lowest2 : lowest1;
      }
    */
    /*for (float y : ys) {
        if (y < result) {
        result = y;
        }
        }*/
    return lowest1 > lowest2 && pdf.getNewPage() ? lowest1 : lowest2;
    //return lowest1;
}

我获取对象的 String 值并检查它们是否为 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 "";
}

以上所有方法都对应PdfEndorsement class.

这是PdfRenderingEndorsementclass 是实际绘制数据的类(这是完整的类,因为使用了所有方法):

public class PdfRenderingEndorsement {

    private static final Logger LOGGER = Logger.
    getLogger(PdfRenderingEndorsement.class);

    private static final float BOTTOM_MARGIN = 60;

    private static final int DESC_WIDTH = 269; //For description fields

    private static final int FIELD_WIDTH = 70;

    private static final int FIELD_WIDTH2 = 60;

    private static final int FIELD_WIDTH3 = 60;

    private static final int FIELD1 = 112;

    private static final int VALUE1 = 112;

    private static final int FIELD2 = 80;

    private static final int VALUE2 = 80;

    private static final int VALUE_WIDTH = 80;

    private static final int VALUE_WIDTH2 = 60;

    private static final int VALUE_WIDTH3 = 80;

    private static final int HALF_WIDTH = 325;

    private static final int TEXT_WIDTH = 410; //For text fields

    private final RwaConstants constants = ConstantsGetter.getInstance();

    private final PDDocument doc;

    private final String logoPath;

    private final String[] header;

    private int count = 0;

    private boolean newPage;

    private PDPageContentStream content;

    /**
     * Empty constructor. Used only to initialize the rendering class and call
     * it's methods.
     */
    public PdfRenderingEndorsement(PDDocument doc, String logoPath, 
                   String[] header) {
    this.doc = doc;
    this.logoPath = logoPath;
    this.header = header;
    }

    public float checkContentStream2(float y, int lines, int space) 
    throws Exception {
    float newY = checkYCoord2(y, lines, space);
    if (newY == 700) {
        if (content != null) {
        content.close();
        }

        File file = new File(logoPath);
        PDJpeg logoImg = new PDJpeg(doc, new FileInputStream(file));
        PDPage page = new PDPage(PDPage.PAGE_SIZE_LETTER);
        doc.addPage(page);
        content = new PDPageContentStream(doc, page);
        content.drawImage(logoImg, 50, 720);
        rHeader();
    }
    return newY;
    }

    private float checkYCoord2(float y, int lines, int space) {
    float newY = y;
    for (int i = 0; i < lines; i++) {
        if ((newY - space) <= BOTTOM_MARGIN) {
        newY = 700f;
        return newY;
        } else {
        newY = newY - space;
        }
    }
    return y;
    }

    public boolean getNewPage() {
    return newPage;
    }

    public float checkContentStream(float y) throws Exception {
    float newY = checkYCoord(y, 1, 10);
    if (newY == 700) {
        if (content != null) {
        content.close();
        }
        File file = new File(logoPath);
        PDJpeg logoImg = new PDJpeg(doc, new FileInputStream(file));
        PDPage page = new PDPage(PDPage.PAGE_SIZE_LETTER);
        doc.addPage(page);
        content = new PDPageContentStream(doc, page);
        content.drawImage(logoImg, 50, 720);
        rHeader();
    }
    return newY;
    }

    public float checkYCoord(float y, int lines, int space) {
    float newY = y;
    for (int i = 0; i < lines; i++) {
        if ((newY - space) <= BOTTOM_MARGIN) {
        newY = 700f;
        return newY;
        } else {
        newY = newY - space;
        }
    }
    return y;
    }

    public float checkContentStream(float y, int lines, int space) 
    throws Exception {
    float newY = checkYCoord(y, lines, space);
    if (newY == 700) {
        if (content != null) {
        content.close();
        }
        File file = new File(logoPath);
        PDJpeg logoImg = new PDJpeg(doc, new FileInputStream(file));
        PDPage page = new PDPage(PDPage.PAGE_SIZE_LETTER);
        doc.addPage(page);
        content = new PDPageContentStream(doc, page);
        content.drawImage(logoImg, 50, 720);
        rHeader();
    }
    return newY;
    }

    public void closeContentStream() throws Exception {
    if (content != null) {
        content.close();
    }
    }

    /**
     * Renders the header for slip documents.
     */
    public void rHeader() throws Exception {
    float y = 760f;
    content.setLineWidth(.5f);
    content.setFont(PDType1Font.TIMES_ROMAN, 9);
    content.setNonStrokingColor(Color.GRAY);
    content.drawLine(50, 710, 562, 710);

    y = rText(150, y + 19, 10, constants.endorsement(), null,
          TEXT_WIDTH, 0);
    y = rText(150, y + 9, 10, header[0], null, TEXT_WIDTH, 0);
    y = rText(150, y + 9, 10, header[1], null, TEXT_WIDTH, 0);
    y = rText(150, y + 9, 10, header[2], null, TEXT_WIDTH, 0);
    y = rText(150, y + 9, 10, header[3], null, TEXT_WIDTH, 0);
    content.setNonStrokingColor(Color.BLACK);
    content.setFont(PDType1Font.TIMES_ROMAN, 9);
    }

    public float rText(float x, float y, int space, String labelField,
               String value) 
    throws Exception {
    return rText(x, y, space, labelField, value, FIELD_WIDTH, 
             HALF_WIDTH - 2 * FIELD_WIDTH - 10);    
    }

    public float rTextLR(float x, float y, int space, String labelField,
               String value) 
    throws Exception {
    return rText(x, y, space, labelField, value, 0, 
             HALF_WIDTH - 2 * FIELD_WIDTH - 10);    
    }

    public float rText(float x, float y, int space, String labelField,
               String value, int fieldWidth) 
    throws Exception {
    if (fieldWidth == 0) {
        return rText(x, y, space, labelField, value, FIELD_WIDTH2, 
             VALUE_WIDTH2);
    } else if (fieldWidth == 1) {
        return rText(x, y, space, labelField, value, FIELD_WIDTH3,
             VALUE_WIDTH3);
    } else if (fieldWidth == 2) {
        return rText(x, y, space, labelField, value, TEXT_WIDTH,
             TEXT_WIDTH);
    }
    return y;
    }


    public float getFieldSize(int fs) {
    switch(fs) {
    case 1:
        return (FIELD_WIDTH + VALUE_WIDTH);
    case 2:
        return (FIELD_WIDTH + DESC_WIDTH);
    case 3:
        return (FIELD_WIDTH + TEXT_WIDTH);
    case 4:
        return (HALF_WIDTH - FIELD_WIDTH);
    case 5:
        return (FIELD_WIDTH + TEXT_WIDTH);
    case 6:
        return (FIELD_WIDTH + TEXT_WIDTH);
    case 7:
        return (FIELD1 + 19) / 2;
    case 8:
        return (FIELD2 + 19) / 2;
    default:
        return 0;
    }
    }

    public void paintLinesH(float y) throws Exception {
    content.drawLine(49, y - 6, 327, y - 6);
    content.drawLine(335, y - 6, 563, y - 6);
    }

    public void paintLinesV(float x, float yMax, float yMin)
    throws Exception {
    content.drawLine(x - 1, yMax - 6, x - 1, yMin - 6);
    }

    public float rText(float x, float y, int space, String labelField,
               String value, int fieldWidth, int valueWidth) 
    throws Exception {
    PDFont font = PDType1Font.TIMES_BOLD;
    content.setFont(font, 9);
    float y1 = 0f;
    float y2 = 0f;
    if (value == null) {
        return rText(labelField, fieldWidth, x, y - 19, space, font, false);
    } else {
        if (labelField == null) {
        font = PDType1Font.TIMES_ROMAN;
        content.setFont(font, 9);
        return rText(value, valueWidth, x, y - 19, space, font, true);
        } else {
        y1 = rText(labelField, fieldWidth, x, y - 30, space, font, 
               false);
        font = PDType1Font.TIMES_ROMAN;
        content.setFont(font, 9);
        float y3 = y;
        y2 = rText(value, valueWidth, x + fieldWidth + 10, y - 30,
               space, font, true);
        if (y3 < y2) {
            return y2;
        } else {
            if (y1 >= y2) {
            return y2;
            } else {
            return y1;
            }
        }
        }
    }
    }

    private ArrayList<String> getRows(String text, int width, PDFont font)
    throws Exception {
    float textWidth = font.getStringWidth(text) / 1000f * 9f;
    ArrayList<String> result = 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;
    }

    private float rText(String text, int width, float x, float y, int space,
            PDFont font, boolean isValue) throws Exception {
    float newY = y;
    int rowHeight = 0;
    newPage = false;
    ArrayList<String> rowList = getRows(text, width, font);
    if (isValue) {
        for (String row : rowList) {
        if (rowHeight >= 10) {
            newY = checkContentStream(newY - 10);
            newY = newY == 700 ? 680 : newY;
            if (newY <= 700 && !newPage) {
            newPage = true;
            }
            rowHeight = newY == 680 ? 0 : rowHeight;
        }
        content.beginText();
        content.moveTextPositionByAmount(x, newY);
        content.drawString(row);
        content.endText();
        rowHeight = rowHeight + 10;
        }
    } else {
        for (String row : rowList) {
        content.beginText();
        content.moveTextPositionByAmount(x, newY - rowHeight);
        content.drawString(row);
        content.endText();
        rowHeight = rowHeight + 10;
        }
        newY -= (rowHeight - 10);
    }
    return newY;
    }
}

这是一个PDF 示例的输出与原始(for-each) 方法。

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

enter image description here

这是一个PDF 示例带有非注释代码的输出getMinY method.

修改后的输出如下:

enter image description here

正如您所看到的,第一个输出“跳”到下一页,因为假设“Texto de Poliza”右侧的文本以 Y 坐标 680、670 或类似的值结束,但在左侧我画了一个空字段("")最终结果是 90 或 100 接近这些数字。

然后比较 100 BOTTOM_MARGIN(即 60),因此它关闭实际页面(现在是文本结束的位置,但认为它位于之前的页面上)并创建一个新页面。

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

在那面文字墙之后,我希望有人能读到它并真正帮助我,也许我错过了一个重要的部分,但无法找到它。

提前致谢。

EDIT

在将 @mkl 的答案添加到我的方法中后,我发现当双方都没有更改信息时,它会产生间隙并且看起来不太好。

enter image description here

这就是我发送数据的方式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); 
  }

上述评论的验证是否正确,或者我会回到过去产生错误的方法吗?我应该添加getPreviousBandBase()方法上Pdf渲染认可替代方案或者以另一种方式思考直接进行render() method?

这就是我获取当前双方具有相同信息的数据的方式。

一个例子是:

sOppA.getContractName() and sOppE.getContractName()都是"Hello World"由于两者相等,因此会留下间隙,如屏幕截图所示。

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;
}

正如评论中已经暗示的那样(实际上已经在对您的评论中前一个问题)我认为渲染类的整个架构需要彻底修改。根据您的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;
}

(PdfRenderingEndorsementAlternative.java)

此类基于带的概念,即内容的水平条纹,其中包含任意数量的包含字段名称和/或字段值的列。

它可以这样使用:

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"));

(RenderEndorsement.java)

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

the result

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

result for sample data

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

更改页面时 PdfBox 问题 的相关文章

随机推荐

  • Hmisc Latex功能需要去掉第一行

    我在 rmarkdown 文件中使用 Hmisc 当我创建一个表时 这就是我所做的 output pdf document r Arrests Stats results asis message FALSE warning FALSE e
  • Spring OAuth2 禁用 TokenEndpoint 的 HTTP 基本身份验证

    我从 Spring OAuth2 开始 到目前为止一切顺利 我已经通过配置保护了我的应用程序 但我有一个问题 我的客户端不支持 HTTP 基本授权 有没有办法禁用 oauth token 端点的 HTTP 基本身份验证 我想在 JSON 正
  • 修剪字符串开头和结尾的空格

    我正在尝试找到一种方法来修剪标题字符串开头和结尾的空格 我正在使用这个 但它似乎不起作用 title title replace s s g 有任何想法吗 注 截至 2015 年 所有主流浏览器 包括IE gt 9 都支持String pr
  • 使用 twinx 时定义宽高比

    如何设置使用的绘图的纵横比twinx 下面我举三个例子 without twinx突出显示我如何设置宽高比 仅与twinx说明如何重置先前定义的宽高比 我尝试定义两者的长宽比y使用时的轴twinx 这不起作用 我正在使用 matplotli
  • 在显示日期时,如何停止在 GridView 中显示时间?

    我正在开发一个 Web 应用程序 ASP NET 2 0 和 C 其中 我有一个从 Oracle 数据库获取数据的 gridview 我需要显示的一些数据是日期 但是当我的 gridview 中的日期显示如下时 2009 04 02 00
  • 传单圆半径根据 y/lng 坐标而变化

    我使用地图框 传单来显示人体图片而不是常规地图 我正在使用传单绘制 我需要能够创建一个圆并在保持其半径的同时移动它 然而 当我将其移向地图 屏幕底部时 大小呈指数级增加 我希望它保持相同的大小 我认为这与投影或 CRS 有关 但我不确定如何
  • jenkins 无法安装插件 - docker image

    以下是 jenkins 镜像 2 190 2 版本 中用于安装插件的相关片段 FROM jenkins jenkins 2 190 2 Add jenkins plugin COPY plugins txt usr share jenkin
  • 与 HashMap 一起使用[重复]

    这个问题在这里已经有答案了 我有一个 java 类 它将 servlet 属性设置为 HashMap 对象 request setAttribute types da getSecurityTypes where request是一个 Ht
  • 如何使用 JUnit 测试 ConfigurationProperties?

    这是我第一次使用外部化配置和 yaml 我创建了一个 yaml 其中使用类名作为 KEY 字段名称作为 VALUE YAML project test service computator exclude field from beeing
  • boost::named_mutex:最后一个进程关闭时安全清理

    我有一个资源 需要保护一个进程内以及多个进程之间的访问 我通过创建一个命名互斥体来管理这个问题boost interprocess named recursive mutex 而且效果很好 include
  • 尝试将 PDT 中的日期时间解析为 ZonedDateTime 表示形式

    我应该如何解析 PDT 时区中的日期时间值 06 24 2017 07 00 AM PDT 我想保留时区 以便我可以根据网站访问者的偏好来表示其他时区的时间 我尝试使用ZonedDateTime但我收到解析错误 java time Zone
  • 415 在 ajax 调用 Spring mvc 中发送 json 对象时不支持的媒体类型

    我正在从 ajax 调用将 json 对象发送到 spring mvc 控制器 这是我发送到控制器的 json 对象 id 7 priority 8 startTime 2015 09 23 01 01 00 0 我的ajax调用 ajax
  • 如何改进 for 循环而不导致运行时超时,

    我目前正在开发一个项目 该项目使用大约 500 个循环交叉验证 2 张工作表 ROSTER First Name Last Name DoB Judith Barragan 4 10 1959 Kelly Benitez 9 14 1993
  • MacOS excel 中的这段代码相当于什么?

    有人可以建议使用等效的代码吗Mac 版 Excel这会产生与下面在 Windows 中相同的结果吗 Path CreateObject WScript Shell SpecialFolders Desktop ActiveWorkbook
  • 如何使用 OnItemClickListener 根据单击的项目启动新意图?

    我希望能够使用 Intent 类启动新活动 我知道如何使用以下代码行启动活动 Intent myIntent new Intent v getContext bylocationactivity class startActivityFor
  • 不能使用可变变量作为安全函数的参数

    下面的脚本无法编译 它抛出错误Cannot use a mutable variable as an argument of the security function我不明白为什么 我在安全函数中使用的参数不是可变变量 当我注释掉该行时h
  • Chrome 应用程序中的无限文件存储

    我想将无限数量的文件保存到用户的硬盘驱动器 而不需要用户单击对话框 我见过的关于使用无限存储的唯一文档在这里 https developers google com chrome whitepapers storage 它表示它仅适用于 C
  • 左连接和内连接的性能差异

    左连接和内连接在性能方面有什么区别吗 我使用 SQL Server 2012 至少有一种情况LEFT OUTER JOIN是一个更好的选择 INNER JOIN 我谈到使用获得相同的结果OUTER代替INNER 示例 我正在使用Advent
  • .vimrc 文件中的 是什么?

    I see
  • 更改页面时 PdfBox 问题

    我不太喜欢问这类问题 但是 我已经花了整整 3 天时间试图解决我的代码中的这个错误 我知道这是一个逻辑问题 我知道如何在脑海中解决它 但是当涉及到将我的想法转化为代码时 我就是无法让它按照我想要的方式工作 我正在处理一份合同背书 合同的修改