【问题标题】:Want to create a pdf with a form having rectangles as header and paragraph in footer and the sections in the body想要创建一个 pdf,其表单具有矩形作为页眉和页脚中的段落以及正文中的部分
【发布时间】:2020-09-13 02:13:51
【问题描述】:

我写到现在的代码如下:

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 = 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】:

    我不熟悉使用 PdfWriter,但我曾经通过使用 CSS 设置 Html 样式并生成附加代码在 android 上制作一些漂亮的 pdf。
    将文档打印为 PDF 文件是较新 Android 版本中的原生功能,因此如果这是一个可能的解决方案,那将非常容易。我想还有一些库可以通过转换 html 页面来创建漂亮的 pdf。如果您对这种解决方案感兴趣,我可以查找我的一些旧资源:)


    我通过以下提交在 github 上推送了一个完整的工作示例:https://github.com/Hatzen/BusinessPdfs/tree/a508909044131f2c94ece6c00d8fd0a785546ae7

    用于创建 pdf 的应用程序的 kotlin 部分:

        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>
    

    结果:

    【讨论】:

    • 如果您能提供我的资源,那就太好了,因为我自己无法找到解决方案。
    • 我尝试运行您提到的 github 存储库。但它每次都崩溃并且无法正常工作。
    • @shruity 该应用程序绝对可以在我的 Android 8.0 设备上运行。对您的错误的描述也会很有用
    • 我刚刚再次检查了代码。现在它工作正常。但问题是答案在问题之下。我希望它从我附加的图像中的同一行开始。
    • @shruity 我刚刚添加了一个版本,答案和问题从同一行开始。目前最新的提交是github.com/Hatzen/BusinessPdfs/tree/…
    猜你喜欢
    • 1970-01-01
    • 2012-02-08
    • 2021-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多