How can I start a new page at paragraph level? I know I can do this at document level, but this would break my formatting.
Perhaps there is function to find the remaining lines on page depending on font and fontsize?
You can definitely do this.
But it requires some knowledge about how iText renders its content.
Internally, when a Paragraph object is rendered, it uses a ParagraphRenderer. Each ParagraphRenderer has one or more LineRenderer objects as its children. And similarly, each LineRenderer has one or more TextRenderer objects.
In order to get information about where a paragraph would be split, you can ask the Paragraph object to perform layout against a given LayoutContext object (which contains the width and height of available space, as well as some other useful information), and get the LayoutResult back.
LayoutResult will be able to tell you where the Paragraph was split.
Try the following piece of code.
w denotes the available width
h denotes the available height
Rectangle layoutRect = new Rectangle(w,h);
LayoutArea layoutArea = new LayoutArea(1,layoutRect);
LayoutContext context = new LayoutContext(layoutArea);
Text layoutText = new Text(s);
layoutText.setTextRise(0f);
layoutText.setSplitCharacters(new DefaultSplitCharacters());
layoutText.setFont(font);
layoutText.setFontSize(fontSize);
Paragraph p = new Paragraph().add(layoutText);
LayoutResult hwResult = p.createRendererSubTree().layout(context);
At that point, use an IDE to inspect the LayoutResult object. If the text was split, you should see a SplitRenderer, which can give you more information.
Related
I'm struggling with a little Java Project:
I made a Program which autofills a PDF Formular. Mostly everything works fine for me, but there is a Problem: In this PDF Formular (which is given from my company, so I have to deal with this document) is a equation Field, which is used for calculation the Costs from Number of Items and the single Price. When I insert the Price of a single Item as a String to my PDF
public void setEinzelpreis(String Einzelpreis)
{
try {
fieldList.get(30).setValue(Einzelpreis);
...
There should be the single Price on the empty field in the first row. The last Cell of the row is auto-calculated by the pdf.
When I Click in the PDF in the "empty" Field, the Value appears:
When I click to another Field, the Value disappears. This is my Problem.
I'm getting the FieldList via pdfbox and the Code for getting my fieldList of the PFD is:
try {
pdfTemplate = PDDocument.load(template);
PDDocumentCatalog docCatalog = pdfTemplate.getDocumentCatalog();
PDAcroForm acroForm = docCatalog.getAcroForm();
if (acroForm != null)
{
// Get field names
fieldList = acroForm.getFields();
}
...
So, can anybody tell what I'm doing wrong? Maybe the PDF wants a double Value for the equation and I am giving a String? But I don't know how to write a double in the FieldList. Thanks a lot for every hint!
Edit:
The PDF File which I'm using:
https://1drv.ms/b/s!Av6exjPNXlgOioouAuXL6QV4eUGkqg?e=ocfhvC
And this is the file I generated:
https://1drv.ms/b/s!Av6exjPNXlgOioovK-HuRuXW2aRy_w?e=D1ZCA8
The strange thing is: when I change the value in the document by hand, everything acts normal, even with a different Document Viewer.
First of all, the AcroForm form structure in your PDF is weird. It looks like someone used a graphical form generation tool he did not understand and clicked, dragged, dropped, copied, ... until the form in a viewer did what he wanted, not caring about it having become difficult to maintain.
In particular the Einzelpreis fields have a completely unnecessary structure of intermediate and final fields, e.g.
Thus, the field Einzelpreis in € exkl USt1 (the '€' is missing in the tree above) is not the one to fill in, it's merely an intermediary field. The actual form field to fill in is Einzelpreis in € exkl USt1.0.0.0.0.
Unfortunately you in your code simply grab the 30th field in the field list returned by PDAcroForm, and this field happens to be the intermediary field Einzelpreis in € exkl USt1; as an intermediary field it has no visible widgets of its own, so your setValue call doesn't change the visible Einzelpreis.
The JavaScript instruction calculating the Gesamtpreis uses the value from the final field, too:
AFSimple_Calculate("PRD", new Array ("Anzahl1", "Einzelpreis in € exkl USt1.0.0.0.0"));
But as the field value is inheritable and none of the .0 fields has an own value, the calculation sees the 100 once form calculation has been triggered and uses it.
Thus, you should fill the Einzelpreis in € exkl USt1.0.0.0.0 field instead. And the more secure way to retrieve it is not by index in a field list but by name:
PDField fieldByName = acroForm.getField("Einzelpreis in € exkl USt1.0.0.0.0");
(excerpt from FillInForm test testFill2020_04BeschaffungsantragEinzelpreis)
After filling that field, the "100" should be visible in your form.
The remaining problem that the Gesamtpreis value is not calculated is due to the fact already mentioned by #Tilman in a comment to the question: PDFBox doesn't use javascript. Thus, you have to calculate those values yourself and update the fields in question accordingly.
If you need to know the correct name of a form field, you can do as Tilman proposed and use the PDFBox PDFDebugger. If you hover over the field there, it will display the name in the status bar at the bottom.
By the way, the AcroForm method getFields won't return the field required here anyways. As documented in its JavaDocs, this method will return all of the documents root fields, no fields further down in the hierarchy, at least not immediately. (From the user perspective the method name getFields is a misnomer. It is accurate, though, from the PDF specification perspective as the corresponding entry in the AcroForms object has the key Fields.)
Beware, though, you probably will have to update your PDFBox version. In earlier versions PDFBox did not update appearances of fields with JavaScript actions (believing some JavaScript would fill it in anyways). I used the current 3.0.0-SNAPSHOT in which that behavior has been changed.
I am creating a Java application in which I am interacting with document file .docx. I am using Apache POI to generate it and modify into it.
The existing file is having tables like:
Tables that are created initially.
I have to add the rows in the first table so that the output is something like: The way the 2 tables should be shown
So this is like I am allowing table 1 to occupy the space till it can go in document page, it is followed by another table. This type of table is needed because after that I am removing its inside borders and it shows something like: how it should be shown with inside borders removed.
I am adding the blank rows using this piece of code..
for (int i = 0; i < itemCount; i++) {
oldRow = table.getRow(i);
newRow = table.insertNewTableRow(i + 1);
for (int j = 0; j < oldRow.getTableCells().size(); j++) {
cell = newRow.createCell();
CTTcPr ctTcPr = cell.getCTTc().addNewTcPr();
ctTcPr.addNewTcBorders().addNewTop().setVal(STBorder.NIL);
ctTcPr.addNewTcBorders().addNewBottom().setVal(STBorder.NIL);
CTTblWidth cellWidth = ctTcPr.addNewTcW();
cellWidth.setType(oldRow.getCell(j).getCTTc().getTcPr().getTcW().getType());
// sets type of width
BigInteger width = oldRow.getCell(j).getCTTc().getTcPr().getTcW().getW();
cellWidth.setW(width); // sets width
if (oldRow.getCell(j).getCTTc().getTcPr().getGridSpan() != null) {
ctTcPr.setGridSpan(oldRow.getCell(j).
getCTTc().getTcPr().getGridSpan()); // sets grid span if any
}
}
}
In few of the lines in starting, I am adding value as something like:
paragraph = row.getCell(0).getParagraphArray(0);
if (paragraph == null) {
paragraph = row.getCell(0).addParagraph();
}
run = setRunAndParagraph(paragraph);
run.setText(itemNames[i]);
All this works nicely, this code was helped by a great coder here who helped me to solve earlier issue.
Now, when I am inserting the rows, I can manually count that for A4 size page, this many rows will be there in table 1, and can iterate likewise. But, the issue is, some item names are wider than column length and as word wrap is on, row for that particular item takes height twice as normal. So, I can get number of rows but not exactly decide how many lines occupied by the items.
The rows appended are for better look and application specific so I thought of an approach that may work:
I am adding page numbers in footer, after inserting a blank row, I am checking whether total page numbers of a document changed or not, if yes that means the code stops there. And as there's 1 excess row I am removing it.
But the issue here arise is I am not able to get page number. I tried
document.getProperties().getExtendedProperties().getUnderlyingProperties().getPages();
But that always shows 1 even if total pages becomes 2. I searched for an approach stating to check form-feed but I think I am having tables here so I don't know it will be helpful approach or not. So I went for adding page number in the page as a field but that also gave 1 every time. Then I tried to do it with footer and this is how I am writing the page number.
XWPFHeaderFooterPolicy p = document.createHeaderFooterPolicy();
XWPFFooter f = p.createFooter(XWPFHeaderFooterPolicy.DEFAULT);
paragraph = f.createParagraph();
//paragraph.createRun().setText("Page: ");
paragraph.createRun();
paragraph.getCTP().addNewFldSimple().setInstr("PAGE \\* MERGEFORMAT");
Now I don't know how to get value of this field, I tried getText() on footer but it did not gave the number. I read that fields are related to paragraph so I tried to get the paragraph from the footer and tried to call getText() but the output is nothing. So can someone help me with this scenario? Right now I am getting the response like this: some part of second table going in next page.
I tried to implement solution I found, but I could not get it resolved yet. so if someone can help me in this scenario like how to fetch this page numbers from the field inserted in the document, it would be really great. I tried to provide more details so that if there's a better approach than this then also someone can suggest the same. Thank you.. :)
I add images in my cells of a table, but it doesn't fit the whole height.
The height is not fixed and can change between 2 rows.
How can my images fit the full height of my cells ?
Here is my problem :
Here is the code of creation of my table :
int nbColumns = 7 + planning.size() *4;
Table table = new Table(nbColumns);
table.setWidthPercent(100);
table.addCell(createCell(""));
table.addCell(createCell("Col1"));
table.addCell(createCell("Col2"));
DateFormat hourFormat = new SimpleDateFormat("HH", Locale.FRENCH);
for(Date hourDate : planning){
table.addCell(new Cell(1,4).setTextAlignment(TextAlignment.CENTER).add(hourFormat.format(hourDate)).setFont(regular).setFontSize(10));
}
table.addCell(createCell("Long"));
table.addCell(createCell("A"));
table.addCell(createCell("B"));
table.addCell(createCell(""));
Here is how I add my images for each cell :
String IMG = // my img path
table.addCell(createImageCell(IMG));
public static Cell createImageCell(String path) throws MalformedURLException {
Image img = new Image(ImageDataFactory.create(path));
Cell cell = new Cell().add(img.setMargins(0,0,0,0).setAutoScaleHeight(true).setAutoScale(true)).setPadding(0);
cell.setBorder(null);
return cell;
}
Posting this as an answer for the sake of visibility.
With regards to autoscaling:
setAutoScaleHeight() is currently bugged in iText7 on the develop branch (so the bug will be present in 7.0.5 and prior versions). It currently sets the AUTO_SCALE_WIDTH(probably due to a copy-paste oversight), unless the AUTO_SCALE_WIDTHproperty is already set, then it will put them both on false and set the AUTO_SCALE to true.
Fixing the typo does not result in expected behaviour, since we're now aware of the issue, a ticket for it has been added to our backlog.
Bi-directional autoscaling (via the AUTO_SCALE-property) works correctly, but will scale uniformly and thus only scale to width in these case where the cell is greater in height than width.
As for a temporary solutions or trick to bypass it, I don't have a generic one :( for now, apart from waiting for the fix.
I did a quick test with relative height declarations (which should be included in 7.0.5), but that scaled the image uniformly again. Some trial&error and Image.scaleAbsolute() can get a desired result but that's hardly automate-able. You could in theory hook into the layout-process by writing your own custom CellRenderer, extracting the height of the largest Cell in to row to use in conjuction with scaleAbsolute(), but you're kinda writing the auto-scaling logic yourself at that point.
Some remarks on the OP's posted code as well, in the interest of spreading good practice:
int nbColumns = 7 + planning.size() *4;
Table table = new Table(nbColumns);
The constructor Table(int) is deprecated (since 7.0.2) and can lead to unexpected behaviour in later versions (this was done when improving the table-layouting mechanisms). It's better to pass a UnitValue[], preferably created using UnitValue.createPercentArray(float[]) or UnitValue.createPercentArray(float[])
img.setMargins(0,0,0,0).setAutoScaleHeight(true).setAutoScale(true))
setAutoScale after setAutoScaleHeight makes the latter redundant.
Given an acrokey, is it possible to find the absolute position and dimension of that particular field (getLeft, getTop, getWidth, getHeight) ?
And is the viceversa possible - if I know the position, can I get the acrokey of the field?
First part of your question:
Suppose that you have an AcroFields instance (form), either retrieved from a PdfReader (read only) or a PdfStamper instance, then you can get the field position of the first widget that corresponds with a specific field name like this:
Rectangle rectangle = form.getFieldPositions(name).get(0).position;
Note that one field can correspond with multiple widgets. For instance, to get the second widget, you need:
Rectangle rectangle = form.getFieldPositions(name).get(1).position;
Of course: you probably also want to know the page number:
int page = form.getFieldPositions(name).get(0).page;
Second part of your question
Fields correspond with widget annotations. If you know the page number of the widget annotation, you could get the page dictionary and inspect the entries of the /Annots array. You'll have to loop over the different annotations, inspecting each annotation's /Rect entry. Once you find a match, you need to crawl the content for the field that corresponds with the annotation. That's more work than can be provided in a code sample on this site.
An application uses a jEditorPane to display html pages, which also has the ability to print said html page. We construct the MediaPrintableArea for the printerJob attributeSet like so:
float mediaWidth = mediaSize.getX(Size2DSyntax.MM);
float mediaHeight = mediaSize.getY(Size2DSyntax.MM);
float imageableX = 18;
float imageableY = 25;
float imageableWidth = (mediaWidth - (2 * imageableX));
float imageableHeight = (mediaHeight - (2 * imageableY));
MediaPrintableArea imageableArea = new MediaPrintableArea(imageableX, imageableY, imageableWidth, imageableHeight, Size2DSyntax.MM);
So we control the printable area of the page. However, when the moons align and a single line is just the right length, the end of the last character in the line is being cut off.
EX: if a line ends with the word "to", there will only be the left-most half of the 'o' visible on the printed page. I would expect if this were to run off the edge of the printable are, "to" would wrap to the next line, but its not.
Is there some other method of defining the printable area besides using the MediaPrintableArea? Is there anything that can be causing the words to not wrap or how java calculates the placement of the words?
We've also tested several other printers and printed from browsers where we can print beyond there our java print job is cutting off, so I don't think hardware problems should be considered.
You're probably rendering the JEditorPane starting from (0, 0) instead of from PageFormat.getImageableX(), PageFormat.getImageableY(). See http://java.sun.com/developer/onlineTraining/Programming/JDCBook/advprint.html for more information.