问题
The code I have written till now is below:
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();
}
}
}
Then for the paragraph I have added data as below: 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();
I am not able to get it how to add the sections as shown in image. Also I want the data to be adjusted in multiple pages automatically according to the text. I would be grateful if anybody could answer this. Thanks.
回答1:
I am not familiar with using PdfWriter but i used to make some pretty pdfs on android by styling Html with CSS and generating additional Code.
Printing the Document as a PDF File is a native feature in newer Android versions so if it is a possible solution it would be quite easy. I guess there are also libraries which can accomplish to create pretty pdfs by converting html pages. I could look up some old sources of mine if u are interested in this kind of solution :)
I pushed a full working example on github with the following commit: https://github.com/Hatzen/BusinessPdfs/tree/a508909044131f2c94ece6c00d8fd0a785546ae7
The kotlin part of the app to create the 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() }
}
the main html template:
<!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"> </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>
The result:
来源:https://stackoverflow.com/questions/62019377/want-to-create-a-pdf-with-a-form-having-rectangles-as-header-and-paragraph-in-fo