Context
I've been working on dynamic PDF generation for a while now, and I've come across my final requirement. Alas, it's a nightmare. I'm using the iText library, version 2.1.7.
I'm basically trying to get a formatting like the top row (which I just mocked up in paint). The other rows are how it looks at the moment, but I'm having some real trouble with getting them to line up properly!
Code
The code being used to generate each color coded block is here:
String currentPrice = p.getPrice();
String timeStr = p.getTime();
Chunk price = new Chunk(currentPrice);
Chunk time = (Chunk) generatePdfElement("Timestamp", timeStr);
if (priceDbl > lastPrice) {
// set color to blue.
price.setBackground(WebColors.getRGBColor("#7777FF"));
time.setBackground(WebColors.getRGBColor("#7777FF"));
} else if (priceDbl < lastPrice) {
// set to red.
price.setBackground(WebColors.getRGBColor("#FF0000"));
time.setBackground(WebColors.getRGBColor("#FF0000"));
}
Paragraph pricePara = new Paragraph();
pricePara.add(price);
pricePara.add(generateBreakLine());
pricePara.add(time);
pricePara.add(generateBreakLine());
// Add the new price data to the list of all the prices for this cell.
allPrices.add(pricePara);
allPrices is then added to a paragraph and put into the cell:
Paragraph pricesCellValue = new Paragraph();
pricesCellValue.addAll(allPrices);
PdfPCell pricesCell = new PdfPCell(pricesCellValue);
pricesCell.setBackgroundColor(WebColors.getRGBColor(getRowStr()));
selectionsTable.addCell(pricesCell);
// Add each cell to the table to create the row.
The approaches I've tried
I tried the obvious, which was removing the last breakline from each Chunk. This didn't work, and it just looked exactly the same, although each Chunk was closer together.
I also tried changing from Paragraph to Phrase, which means the code looked like this:
Phrase pricePara = new Phrase();
pricePara.add(price);
pricePara.add(generateBreakLine());
pricePara.add(time);
//pricePara.add(generateBreakLine());
// Add the new price data to the list of all the prices for this cell.
allPrices.add(pricePara);
And this was the result:
So now I'm fresh out of ideas! Does anyone else have any suggestions, or some experience with iText in this area?
Edit
Just for clarity, generateBreakLine() generates a new empty Paragraph object.
I used a nested PdfPTable in the last cell, to format the positioning of each Phrase. Works like a dream!
Related
I asked a question related to the issue I was having before, but a new one arose. Originally, I wanted the capability of highlighting a row in my table with a single click, and editing a cell with a double click. greg-449 gave me the solution to the problem (getting rid of the FocusCellManager and simply using EditingSupport along with the other classes I had, and setting the editorActivationStrategy to have the MOUSE_DOUBLE_CLICK_SELECTION bit. However, now when I type text into a cell, the text is not saved into my model. On the other hand, when I use the FocusCellManager, the text is indeed saved.
Now, I have to use CellEditors, as there is already a very long list of CellEditors set up for the specific columns. I am not sure if it is possible to use EditingSupport along with CellEditors, or if it necessary at all. I am attaching some snippets.
Relevant Section of DatumTableViewer.java:
FocusCellOwnerDrawHighlighter drawHighlighter = new FocusCellOwnerDrawHighlighter(this); //removed this to fix the highlighting issue
final TableViewerFocusCellManager mgr = new TableViewerFocusCellManager(this, drawHighlighter); //removed this to fix the highlighting issue.
final ColumnViewerEditorActivationStrategy editorActivationSupport = getEditorActivationStrategy();
int tableKeyboardTraversalFeature = getKeyboardTraversalFeature();
TableViewerEditor.create(this, mgr, editorActivationSupport, tableKeyboardActivationFeature);
DatumColumnEmnum[] tableColumnsAsArray = getTableColumnsAsArray();
createTable(this.getTable(), tableColumnsAsArray);
setColumnProperties(properties.getPresentedColumnNames); //properties is passed into constructor
setCellEditors(tableColumnsAsArray); //I think this doesn't work properly without FocusCellManager
DatumCellModifier modifier = new DatumCellModifier(this, properties, myExpressionDataProvider); // myExpressionDataProvider is passed into constructor)
setCellModifier(modifier);
setContentProvider(contentProvider);
setInput(_myDatumList); // myDatumList set before this snippet
}
setCellEditors:
private void setCellEditors(DatumColumnEnum[] columns)
{
cellEditor[] editors = new CellEditor[columns.length];
for(int i = 0; i < columns.length; i++){
DatumColumnEnum columnID = columns[i];
switch(columnID) // cases have been trimmed to shorten this snippet
{
case DATUM_ID_COLUMN:
case DATUM_NUMBER_COLUMN:
case DATUM_NAME_COLUMN:
editors[i] = new TextCellEditor(this.getTable());
((Text) editors[i].getControl()).setTextLimit(60);
break;
}
}
this.setCellEditors(editors);
}
I suspect these snippets contain the problem, but if not, then please let me know and I'll add the other functions as well.
How can i change the selected word font color?
I want to create a function for this, but my level is so basic with this api...
This is my pseudo cod:
public String setTextColor(String word){
String coloredWord = null;
/**
* setColor method here
*/
return coloredWord;
}
So, this function have to return the colored word.
Please help, if you can :)
You need to set the Run Property. This works similarly to Rich Text Strings in the Excel API, but is a bit different.
// p is your paragraph object
XWPFRun r = p.createRun();
r.setColor("ff0000");
r.setText("This text is red");
r = p.createRun();
r.setText(" but this text is black");
All text in a given run has the same formatting properties. Any time those properties need to change, you need to add a new run to the paragraph.
After I solved my problem on docx4j previously, I'm be able to use it now.
I just try to run the sample code from this link
http://www.smartjava.org/content/create-complex-word-docx-documents-programatically-docx4j
with some modification.
let's say I have two documents.
One is main template that have about 2-3 pages.
Second one have only 1 paragraph of text with various of style (Bold, Italic, Underline, Font Size, etc).
I want to replace a parameter in my template with a paragraph in the second document.
The result is it can replace my parameter with a paragraph but there is a problem with style. What I can observe with many experiment is:
Indent still there
New Line still there
Underline move along too
Font Color/ Font Size is working
Bold/Italic not come along
Font Family not come along
Here is my code
private static void replaceParagraph2(String placeholder, WordprocessingMLPackage template, ContentAccessor addTo) throws Exception {
//get the paragraph
WordprocessingMLPackage paragraph_template = getTemplate("./resources/input/paragraph.docx");
List<Object> paragraphs_LineList = getAllElementFromObject(paragraph_template.getMainDocumentPart(), P.class);
// get the template
List<Object> template_lineList = getAllElementFromObject(template.getMainDocumentPart(), P.class);
int position = 0;
P toReplace = null;
//find placeholder position
for (Object p : template_lineList) {
List<Object> texts = getAllElementFromObject(p, Text.class);
for (Object t : texts) {
Text content = (Text) t;
if (content.getValue().equals(placeholder)) {
toReplace = (P) p;
position = template_lineList.indexOf(toReplace);
break;
}
}
}
//add paragraph into template
for (int i = 0; i < paragraphs_LineList.size(); i++) {
P para = (P) XmlUtils.deepCopy(paragraphs_LineList.get(i));
addTo.getContent().add(position + 1 + i, para);
}
// remove the placeholder on the template
((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);
}
Do I missing something?
PS. I debug to check the object of template. It seems that bold value in the P object is config to null. (It's booleanTrueifNull type I think.)
The formatting comes from direct formatting (in rPr and pPr elements in the paragraph), and also from the styles part. If no style is specified, the default styles will be used.
You'll need to look at the XML in your paragraph, and in the styles part.
Microsoft Word (2010 at least) has a useful "Reveal Formatting" sidebar which can help you to understand where the formatting is coming from. Click the "distinguish style source" at the bottom.
There is code in docx4j (used by its PDF output) to determine the effective formatting. I guess you could use that to specifically apply the effective formatting from your source to each run in your target.
I'm getting unexpected results when I try to keep rows in an iText table together. Below is some standalone code that creates a PDF with the results I'm seeing.
FileOutputStream out = new FileOutputStream(new File("table.pdf"));
Document document = new Document(new Rectangle(612, 242));
PdfWriter.getInstance(document, out);
document.open();
PdfPTable table = new PdfPTable(1);
table.setWidthPercentage(100);
for (int i = 0; i < 10; i++) {
PdfPCell cell;
if (i == 9) {
cell = new PdfPCell(new Phrase("Two\nLines"));
}
else {
cell = new PdfPCell(new Phrase(Integer.toString(i)));
}
table.addCell(cell);
}
table.keepRowsTogether(new int[] { 8, 9 });
document.add(table);
document.close();
The example creates a table that is two pixels too small to fit on the first page, forcing the last row onto the next page. I would expect, however, since I added the array using keepRowsTogether, to see the first eight rows to stay on one page and the last two to stay together on the next page but that isn't the case as shown by the example images below. Instead the seventh row (counting from zero) is also carried over to the next page.
According to the API documentation found here, keepRowsTogether "Defines which rows should not allow a page break (if possible)." That indicates to me that row seven, which isn't included in the array, should allow a page break.
Does anyone have any ideas how to keep the seventh row from getting carried over to the next page when it definitely fits on the first?
Solution: After talking to Bruno, I realized I misunderstood how keepRowsTogether works. All I need to do for the example above is change table.keepRowsTogether(new int[] { 8, 9 }); to table.keepRowsTogether(9).
I think there's a misunderstanding about what keepRowsTogether() means. If you use:
table.keepRowsTogether(new int[] { 8, 9 });
You indicate that the table won't split at row 8 or 9. This is consistent with what I see.
Maybe you want something like this: split_at_row.pdf
This PDF is created using SplitRowAtSpecificRow. Instead of keepRowsTogether(), it uses:
table.setBreakPoints(8);
This means that iText will give preference to splitting the table after row 8 (start counting at 0). You can introduce more than one breakpoint. For instance:
table.setBreakPoints(4, 8, 12);
Is there a way to create a table of contents using Java PDFBox library?
The table of contents should be clickable (jump to the right page)
Thanks.
There's no simple method for doing this, but here's an approach. I haven't figured out how to attach links directly to text, so my approach means you have to draw the annotations as rectangles and the text separately. It's a bit rough around the edges, but it works.
// there are other types of destinations, choose what is appropriate
PDPageXYZDestination dest = new PDPageXYZDestination();
// the indexing is odd here. if you are doing this on the first page of the pdf
// that page is -1, the next is 0, the next is 1 and so on. odd.
dest.setPageNumber(3);
dest.setLeft(0);
dest.setTop(0); // link to top of page, this is the XYZ part
PDActionGoTo action = new PDActionGoTo();
action.setDestination(dest);
PDAnnotationLink link = new PDAnnotationLink();
link.setAction(action);
link.setDestination(dest);
PDRectangle rect = new PDRectangle();
// just making these x,y coords up for sample
rect.setLowerLeftX(72);
rect.setLowerLeftY(600);
rect.setUpperRightX(144);
rect.setUpperRightY(620);
PDPage page = // however you are getting your table of contents page, eg new PDPage() or doc.getDocumentCatalog().getAllPages().get(0)
page.getAnnotations().add(link);
PDPageContentStream stream = new PDPageContentStream(doc, page, true, true);
stream.beginText();
stream.setTextTranslation(85, 600); // made these up, have to test to see if padding is correct
stream.drawString("Page 1");
stream.endText();
stream.close();
Phew! That's a lotta code. That should get you on your way. You can make the rectangle the same color as your document background if you want it to look like they are just clicking a link, but that requires more experimentation.