想创建一个以矩形为页眉,页脚为段落,正文为部分的pdf格式。

问题描述 投票:6回答:1

I want to create a pdf somewhat like this

我到现在写的代码如下。

public class HeaderFooterEvent extends PdfPageEventHelper {
    @Override
    public void onStartPage(PdfWriter writer, Document document) {
    }

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        addHeader(writer, document);
        addHeader2(writer, document);
        addFooter(writer, document);
    }

    void addHeader(PdfWriter writer, Document document){
        try {
            PdfContentByte cb = writer.getDirectContent();

            // the initial rectangle defines the max size of the content
            rect = new Rectangle(10, document.getPageSize().getBottom() + 50,
                    document.getPageSize().getWidth() - 2 * 10, document.getPageSize().getTop() - 50);

     // flip the rectangle if top and bottom were switched
            rect.normalize();

            ColumnText ct = new ColumnText(cb);
            ct.setSimpleColumn(rect);
            ct.addElement(createTable1(auditBundle, context));

     // do a simulation run
               int result = ct.go(true);

      // assume the content fits in the initial rectangle
                if (result == ColumnText.NO_MORE_TEXT) {

                // the bottom of the simulated content
                float verticalpos = ct.getYLine();

                // redefine the rectangle based on the simulation
                rect = new Rectangle(10, verticalpos, document.getPageSize().getWidth() - 2 * 10,
                        document.getPageSize().getTop() - 50);
                ct.setSimpleColumn(rect);

                // the original content was consumed in the simulation, so add it again
                ct.addElement(createTable1(auditBundle, context));

                // render again
                ct.go(false);

                // draw the rectangle
                rect.setBorder(Rectangle.BOX);
                rect.setBorderWidth(1);
                rect.setBorderColor(BaseColor.BLACK);
                cb.rectangle(rect);

            }
        } catch (DocumentException e){
            e.printStackTrace();
        }
    }

    void addHeader2(PdfWriter writer, Document document){
        try {
            float fntSize, lineSpacing;
            fntSize = 20f;
            lineSpacing = 15f;
            String title = "", recordedBy = "", entity = "", orgUnit = "", actualStartDate = "", 
actualStartTime = "";

            Paragraph paragraph = new Paragraph();
            paragraph.add(new Phrase(lineSpacing, title + " " + recordedBy + " " + entity + " " +
                    orgUnit + " " + actualStartDate + " " + actualStartTime,
                    FontFactory.getFont(FontFactory.HELVETICA, fntSize)));
            paragraph.setAlignment(Element.ALIGN_CENTER);
            paragraph.setPaddingTop(5);

            PdfContentByte cb = writer.getDirectContent();

      // the initial rectangle defines the max size of the content
            Rectangle rect2 = new Rectangle(10, document.getPageSize().getBottom() + 50,
                    document.getPageSize().getWidth() - 2 * 10, document.getPageSize().getTop() - 
 250);

// flip the rectangle if top and bottom were switched
            rect2.normalize();

            ColumnText ct = new ColumnText(cb);
            ct.setSimpleColumn(rect2);
            ct.addElement(paragraph);

     // do a simulation run

    int result = ct.go(true);

    // assume the content fits in the initial rectangle

       if (result == ColumnText.NO_MORE_TEXT) {

                // the bottom of the simulated content
                float verticalpos = ct.getYLine()+rect.getHeight();

                // redefine the rectangle based on the simulation
                rect2 = new Rectangle(10, verticalpos, document.getPageSize().getWidth() - 2 * 10,
                        document.getPageSize().getTop() - 250);
                ct.setSimpleColumn(rect2);

                // the original content was consumed in the simulation, so add it again
                ct.addElement(paragraph);

                // render again
                ct.go(false);

                // draw the rectangle
                rect2.setBorder(Rectangle.BOX);
                rect2.setBorderWidth(1);
                rect2.setBorderColor(BaseColor.BLACK);
                cb.rectangle(rect2);
            }
        } catch (DocumentException e){
            e.printStackTrace();
        }

    }

    void addFooter(PdfWriter writer, Document document){
        String text = "Report printed by %1$s at %2$s on %3$s CMO COMPLIANCE Management Software by www.cmo-compliance.com";
        Date currentTime = Calendar.getInstance().getTime();
        String currentDate = DateTimeFormatter.getUserTZDateFormat(
                DateTimeFormatter.CUSTOM_DATE_TEMPLATE_DD_MMM_YYYY
        ).format(currentTime);

        String currentTimes = DateTimeFormatter.getUserTZDateFormat(
                DateTimeFormatter.CUSTOM_24H_TIME_TEMPLATE_H_MM
        ).format(currentTime);

        PdfContentByte cb = writer.getDirectContent();

        Rectangle pageSize = document.getPageSize();
        float left = document.leftMargin();
        float right = document.rightMargin();
        float bottom = document.bottomMargin();

        Rectangle rect = new Rectangle(
                pageSize.getLeft() + left, pageSize.getBottom(),
                pageSize.getRight() - right, pageSize.getBottom() + bottom);
        cb.rectangle(rect);
        ColumnText ct = new ColumnText(writer.getDirectContent());
        ct.setSimpleColumn(rect);
        ct.addElement(new Paragraph(String.format(text, CmoApplication.getInstance().getUser().loginName, currentDate, currentTimes)));
        try {
            ct.go();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

然后,我为段落添加了如下数据。 Document document = new Document();

        // Location to save
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));

        // Open to write
        document.open();

        // Document Settings
        document.setPageSize(PageSize.A4);
        document.addCreationDate();
        document.addAuthor(CmoApplication.getInstance().getCompany().companyName);
        document.addCreator(CmoApplication.getInstance().getUser().loginName);

        HeaderFooterEvent event = new HeaderFooterEvent();
        writer.setPageEvent(event);

        Font chapterFont = FontFactory.getFont(FontFactory.HELVETICA, 16, Font.BOLDITALIC);
        Font paragraphFont = FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL);
        Chunk chunk = new Chunk("This is the title", chapterFont);
        Chapter chapter = new Chapter(new Paragraph(chunk), 1);
        chapter.setNumberDepth(0);
        chapter.add(new Paragraph("This is the paragraph", paragraphFont));
        document.add(chapter);

        document.close();

我不能够得到它如何添加的部分,如图所示。此外,我希望数据调整在多个页面自动根据文本.我将是感激的,如果任何人可以回答这个问题。谢谢。

android pdf itext
1个回答
0
投票

我不熟悉使用PdfWriter,但我曾经在android上通过CSS样式化Html和生成额外的代码来制作一些漂亮的pdf文件。将文档打印成PDF文件是新的Android版本的原生功能,所以如果它是一个可能的解决方案,这将是相当容易的。我想也有一些库可以完成通过转换html页面来创建漂亮的pdfs。如果你对这种解决方案感兴趣的话,我可以找找我的一些老资源:)


我在github上推送了一个完整的工作实例,提交的内容如下。https:/github.comHatzenBusinessPdfstreea508909044131f2c94ece6c00d8fd0a785546ae7。

kotlin部分的应用来创建pdf。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        fab.setOnClickListener { view ->
            if (isStoragePermissionGranted()) {
                createPdfFromTemplate()
            } else {
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), requestCodeStoragePermission)
            }
        }
    }

    private fun createPdfFromTemplate() {
        var htmlContent = assets.open("html/index.html").bufferedReader().use { it.readText() }

        val sections = mutableListOf("A", "B", "C", "D", "E")
        val questions = mutableListOf("1", "2", "3")
        val answers = mutableListOf("I", "II", "III", "IV")
        val replacementSection = replaceSection(sections, questions, answers)
        val replacementKeySection = "{\$SECTION}"
        htmlContent = htmlContent.replace(replacementKeySection, replacementSection)

        val target = File(Environment.getExternalStorageDirectory().toString(), "file.pdf")
        // val target = File.createTempFile("BusinessPdfPrefix-", ".pdf")
        Log.e(javaClass.simpleName, "Pdf saved as: " + target.absolutePath)

        val converter = Html2Pdf.Companion.Builder()
                .context(this)
                .html(htmlContent)
                .file(target)
                .build()

        converter.convertToPdf(object: Html2Pdf.OnCompleteConversion {
            override fun onFailed() {
                Toast.makeText(this@MainActivity, "Failed creating pdf!", Toast.LENGTH_LONG).show()
            }

            override fun onSuccess() {
                openPdf(target)
            }

        })
    }

    private fun replaceSection(sections: List<String>, questions: List<String>, answers: List<String>): String {
        val replaceableKey = "{SECTION}"
        val replaceableKeyQuestions = "{\$QUESTION}"
        var result = ""
        var isFirstSection = true
        for (section in sections) {
            val inflate = getHtmlPartFromAssets("section")
            result += inflate
            if (isFirstSection) {
                result = result.replace("{ADDITIONAL_CLASS}", "firstSection")
                isFirstSection = false
            }
            result = result.replace(replaceableKey, "Section " + section)
            result = result.replace(replaceableKeyQuestions, replaceQuestion(questions, answers))
        }
        return result
    }

    private fun replaceQuestion(questions: List<String>, answers: List<String>): String {
        val replaceableKey = "{QUESTION}"
        val replaceableKeyNumberOfQuestions = "{#ANSWER}"
        val replaceableKeyAnswers = "{\$ANSWER}"
        var result = ""
        for (question in questions) {
            val inflate = getHtmlPartFromAssets("question")
            result += inflate
            result = result.replace(replaceableKey, "Question " + question)
            result = result.replace(replaceableKeyNumberOfQuestions, answers.size.toString())
            result = result.replace(replaceableKeyAnswers, replaceAnswer(answers))
        }
        return result
    }

    private fun replaceAnswer(answers: List<String>): String {
        val replaceableKey = "{ANSWER}"
        var result = ""
        for (answer in answers) {
            val inflate = getHtmlPartFromAssets("answer")
            result += inflate
            val constant = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
            val constant2 = "<input type='checkbox'> Answer"
            result = result.replace(replaceableKey, constant2 + constant + answer)
        }
        return result
    }

    private fun getHtmlPartFromAssets(filename: String): String {
        return assets.open("html/parts/" + filename + ".html").bufferedReader().use { it.readText() }
    }

主要的html模板。

<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<style>
  * {
    font-family: "arial";
  }
  body{
    height:297mm;
    width:210mm;
  }
  li, td {
    padding: 2mm;
  }
  .header {
    width: 100%;
    border: 1mm solid black;
    min-height: 2cm;
    margin-bottom: 5mm;
  }
  .header p {
    text-align: center;
  }
  #logo {
    height: 100px;
    background-image: url(file:///android_asset/html/logo.png);
    background-size: contain;
    background-position: top right;
    background-repeat: no-repeat !important;
  }
  #content {
  }
  table {
    page-break-inside:avoid;
    width: 100%;
    border: none;
  }
  .firstSection {
    page-break-inside:auto !important;
  }
  tr {
    page-break-inside:avoid;
    page-break-after:auto;
  }
  /*https://medium.com/@Idan_Co/the-ultimate-print-html-template-with-header-footer-568f415f6d2a*/
  .footer, .footer-space {
    height: 100px;
  }
  .footer {
    border-top: 1px solid #000;
    padding: 2mm;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
  }
  @page {
    /*Define page borders here
    */
    margin: 2cm;
  }
</style>
</head>
<body>
<table>
    <tbody><tr><td>
        <div id="logo" class="header">
            <p>Text in the middle</p>
        </div>
        <div class="header">
            <p>Paragraph with from<br>details</p>
        </div>
        <div class="clear"></div>
        <div id="content">
            {$SECTION}
        </div>
    </td></tr></tbody>
    <tfoot><tr><td>
        <div class="footer-space">&nbsp;</div>
    </td></tr></tfoot>
</table>
<div class="footer">My Footer</div>
</body>
<script>
window.onload = function () {
    // Additional calculations and DOM manipulation could be done here via javascript.
    // document.body.style.backgroundColor = "red";
}
</script>
</html>

结果: enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.