if i calculate height of each element and compare with page height then only 4 elements intervene
PdfDocument pdf = new PdfDocument(new PdfWriter(DEST));
Document document = new Document(pdf);
pdf.setDefaultPageSize(PageSize.A5);
document.setMargins(0, 25, 25, 25);
float maxHeight = document.getPdfDocument().getDefaultPageSize().getWidth() ;/* (mainPdf_model.getLeftMargin() +mainPdf_model.getRightMargin());*/
float height = 0;
String line = "Hello! Welcome to iTextPdf";
Div div = new Div();
for (int i = 0; i < 30; i++) {
Paragraph element = new Paragraph();
element.add(line + " " + i);
element.setMargin(0);
element.setPadding(0);
element.setFixedLeading(100);
div.add(element);
IRenderer rendererSubTree = element.createRendererSubTree();
LayoutResult result = rendererSubTree.setParent(document.getRenderer()).
layout(new LayoutContext(new LayoutArea(1, new Rectangle(10000, 1000))));
height+=result.getOccupiedArea().getBBox().getHeight();
if(height<maxHeight) {
System.out.println(element);
}else {
}
}
document.add(div);
document.close();
System.out.println:
com.itextpdf.layout.element.Paragraph#319b92f3
com.itextpdf.layout.element.Paragraph#fcd6521
com.itextpdf.layout.element.Paragraph#27d415d9
com.itextpdf.layout.element.Paragraph#5c18298f
In pdf i have 6 elements:
First of all, you have a bug in your code:
float maxHeight = document.getPdfDocument().getDefaultPageSize().getWidth();
should be replaced with
float maxHeight = document.getPdfDocument().getDefaultPageSize().getHeight();
When you do so, your console output will have 5 lines instead of 4 already. This is still not the 6 lines you have on your page.
The difference comes from the fact that paragraph placement logic on a document does additional calculations and shifts of the first paragraph. To make sure there is not a lot of empty space at the start of the page, the first paragraph will use roughly half of the leading instead of the full leading. If you want to get precise calculations you'd better add elements to your Div and calculate the height of a Div with several Paragraph elements inside. To make the code more optimal you can use binary search on the number of elements that would fit on a single page.
Related
In Microsoft Word, if you go to the properties of a table, and then the "Borders and Shading" section, you will see that you are able to apply borders to a table on 8 of its aspects. top, bottom, left, right, center-vertical, center-horizontal, diagonal-left, and diagonal-right
How can I turn them on selectively using POI?
In current apache poi 4.1.0 the class XWPFTable provides methods for this.
For example XWPFTable.setTopBorder:
public void setTopBorder(XWPFTable.XWPFBorderType type,
int size,
int space,
java.lang.String rgbColor)
Set Top borders for table
Parameters:
type - - XWPFTable.XWPFBorderType e.g. single, double, thick
size - - Specifies the width of the current border. The width of this border is
specified in measurements of eighths of a point, with a minimum value of two
(onefourth of a point) and a maximum value of 96 (twelve points).
Any values outside this range may be reassigned to a more appropriate value.
space - - Specifies the spacing offset that shall be used to place this border
on the table
rgbColor - - This color may either be presented as a hex value (in RRGGBB format),
or auto to allow a consumer to automatically determine the border color
as appropriate.
See XWPFTable.XWPFBorderType for possible border types.
Complete example:
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
public class CreateWordTableBorders {
public static void main(String[] args) throws Exception {
XWPFDocument document= new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The table:");
//create the table
XWPFTable table = document.createTable(3,3);
table.setWidth("100%");
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
table.getRow(row).getCell(col).setText("row " + row + ", col " + col);
}
}
//set borders
table.setTopBorder(XWPFTable.XWPFBorderType.THICK_THIN_LARGE_GAP, 32, 0, "FF0000");
table.setBottomBorder(XWPFTable.XWPFBorderType.THICK_THIN_LARGE_GAP, 32, 0, "FF0000");
table.setLeftBorder(XWPFTable.XWPFBorderType.THICK_THIN_LARGE_GAP, 32, 0, "FF0000");
table.setRightBorder(XWPFTable.XWPFBorderType.THICK_THIN_LARGE_GAP, 32, 0, "FF0000");
table.setInsideHBorder(XWPFTable.XWPFBorderType.DOT_DASH, 16, 0, "00FF00");
table.setInsideVBorder(XWPFTable.XWPFBorderType.DOTTED, 24, 0, "0000FF");
paragraph = document.createParagraph();
FileOutputStream fileOut = new FileOutputStream("create_table.docx");
document.write(fileOut);
fileOut.close();
document.close();
}
}
I need to remove property in Text (setRise) , if t.setRise(+-) gets out of fields paper.
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
Document doc = new Document(pdfDoc, PageSize.A5);
doc.setMargins(0,0,0,36);
for (int i = 0; i <50 ; i++) {
Text t = new Text("hello " + i);
if(i ==0){
t.setTextRise(7);
}
if(i==31){
t.setTextRise(-35);
}
Paragraph p = new Paragraph(t);
p.setNextRenderer(new ParagraphRen(p,doc));
p.setFixedLeading(fixedLeading);
doc.add(p);
}
doc.close();
}
class ParagraphRen extends ParagraphRenderer{
private float heightDoc;
private float marginTop;
private float marginBot;
public ParagraphRen(Paragraph modelElement, Document doc) {
super(modelElement);
this.heightDoc =doc.getPdfDocument().getDefaultPageSize().getHeight();
this.marginTop = doc.getTopMargin();
this.marginBot = doc.getBottomMargin();
}
#Override
public void drawChildren(DrawContext drawContext) {
super.drawChildren(drawContext);
Rectangle rect = this.getOccupiedAreaBBox();
List<IRenderer> childRenderers = this.getChildRenderers();
//check first line
if(rect.getTop()<=heightDoc- marginTop) {
for (IRenderer iRenderer : childRenderers) {
if (iRenderer.getModelElement().hasProperty(72)) {
Object property = iRenderer.getModelElement().getProperty(72);
float v = (Float) property + rect.getTop();
//check text more AreaPage
if(v >heightDoc){
iRenderer.getModelElement().deleteOwnProperty(72);
}
}
}
}
//check last line
if(rect.getBottom()-marginBot-rect.getHeight()*2<0){
for (IRenderer iRenderer : childRenderers) {
if (iRenderer.getModelElement().hasProperty(72)) {
Object property = iRenderer.getModelElement().getProperty(72);
//if setRise(-..) more margin bottom setRise remove
if(rect.getBottom()-marginBot-rect.getHeight()+(Float) property<0)
iRenderer.getModelElement().deleteOwnProperty(72);
}
}
}
}
}
Here i check if first lines with setRise more the paper area I remove setRise property.
And if last lines with serRise(-35) more then margin bottom I remove it.
But it doesn't work. Properties don't remove.
Your problem is as follows: drawChildren method gets called after rendering has been done. At this stage iText usually doesn't consider properties of any elements: it just places the element in its occupied area, which has been calculated before, at layout() stage.
You can overcome it with layout emulation.
Let's add all your paragraphs to a div rather than directly to the document. Then emulate adding this div to the document:
LayoutResult result = div.createRendererSubTree().setParent(doc.getRenderer()).layout(new LayoutContext(new LayoutArea(0, PageSize.A5)));
In the snippet above I've tried to layout our div on a A5-sized document.
Now you can consider the result of layout and change some elements, which will be then processed for real with Document#add. For example, to get the 30th layouted paragraph one can use:
((DivRenderer)result.getSplitRenderer()).getChildRenderers().get(30);
Some more tips:
split renderer represent the part of the content which iText can place on the area, overflow - the content which overflows.
I'm trying to use Apache POI XWPF library to produce a report in a Word docx file.
My approach is to use an existing Word Document as a Styles template. Within the template I defined a style named "SRINumberList".
So to load the template and remove everything that's not in the Header or Footer:
protected void createDocFromTemplate() {
try {
document = new XWPFDocument(this.getClass().getResourceAsStream(styleTemplate));
int pos = document.getBodyElements().size()-1;
while (pos >= 0) {
IBodyElement element = document.getBodyElements().get(pos);
if (!EnumSet.of(BodyType.HEADER, BodyType.FOOTER).contains(element.getPartType())) {
boolean success = document.removeBodyElement(pos);
logger.log(Level.INFO, "Removed body element "+pos+": "+success);
}
pos--;
}
} catch (IOException e) {
logger.log(Level.WARNING, "Not able to load style template", e);
document = new XWPFDocument();
}
}
Now within my document there are several different sections that contain a numbered lists. Each should be restart numbering from 1. This is the typical way I'm doing this:
if (itemStem.getItems().size() > 0) {
p = document.createParagraph();
p.setStyle(ParaStyle.StemAndItemTitle.styleId);
final BigInteger bulletNum = newBulletNumber();
run = p.createRun();
run.setText("Sub Items");
itemStem.getItems().stream().forEach(item -> {
XWPFParagraph p2 = document.createParagraph();
p2.setStyle(ParaStyle.NumberList.styleId);
XWPFRun run2 = p2.createRun();
run2.setText(item.getSubItemText());
});
p = document.createParagraph();
p.createRun();
}
So this correctly applies the Style that contains the number format, but there is only a single sequence (1 ... to however many list items exit in the doc). For example:
Heading 1
1. item a
2. item b
3. item c
Heading 2
4. item a
5. item d
6. item g
But what I want is:
Heading 1
1. item a
2. item b
3. item c
Heading 2
1. item a
2. item d
3. item g
So basically I'm trying to figure out how to use the style I have but restart page numbering a various spots in the document. Can someone provide a sample of how this would work?
The only way I found is to override the level in the CTNum. Another way could be to create lots of new abstract numberings/styles, but that would cost lots of style entries, when you open the document.
ArrayList<String> list = new ArrayList<String>();
list.add("SubItem 1");
list.add("SubItem 2");
list.add("SubItem 3");
XWPFNumbering numbering = document.getNumbering();
XWPFAbstractNum numAbstract = numbering.getAbstractNum(BigInteger.ONE);
for (Integer nx = 1; nx < 3; nx++) {
XWPFParagraph p = document.createParagraph();
XWPFRun run = p.createRun();
run.setText("Items " + nx.toString());
//leveloverride (start the new numbering)
BigInteger numId = numbering.addNum(numAbstract.getAbstractNum().getAbstractNumId());
XWPFNum num = numbering.getNum(numId);
CTNumLvl lvloverride = num.getCTNum().addNewLvlOverride();
lvloverride.setIlvl(BigInteger.ZERO);
CTDecimalNumber number = lvloverride.addNewStartOverride();
number.setVal(BigInteger.ONE);
for (String item : list) {
XWPFParagraph p2 = document.createParagraph();
p2.setNumID(num.getCTNum().getNumId());
CTNumPr numProp = p2.getCTP().getPPr().getNumPr();
numProp.addNewIlvl().setVal(BigInteger.ZERO);
XWPFRun run2 = p2.createRun();
run2.setText(item);
}
}
With some help from keil. I figured out the solution. I've posted a full working sample here: https://github.com/jimklo/apache-poi-sample
The trick is that you need to reference the the AbstractNum of the Numbering style defined in the document when creating a new Num that restarts the numbering.
Here are the highlights, however the key was having to determine what the AbstractNum ID is for the Style inside the document. It's seems unfortunate, that given this is just an XML doc, that there isn't some way to enumerate the existing Num's and AbstractNum's. If there is, I'd love to know the way to do that.
/**
* first discover all the numbering styles defined in the template.
* a bit brute force since I can't find a way to just enumerate all the
* abstractNum's inside the numbering.xml
*/
protected void initNumberingStyles() {
numbering = document.getNumbering();
BigInteger curIdx = BigInteger.ONE;
XWPFAbstractNum abstractNum;
while ((abstractNum = numbering.getAbstractNum(curIdx)) != null) {
if (abstractNum != null) {
CTString pStyle = abstractNum.getCTAbstractNum().getLvlArray(0).getPStyle();
if (pStyle != null) {
numberStyles.put(pStyle.getVal(), abstractNum);
}
}
curIdx = curIdx.add(BigInteger.ONE);
}
}
Now that we have a mapping from the Style to the AbstractNum, we can create a new Num that restarts via a LvlOverride and StartOverride.
/**
* This creates a new num based upon the specified numberStyle
* #param numberStyle
* #return
*/
private XWPFNum restartNumbering(String numberStyle) {
XWPFAbstractNum abstractNum = numberStyles.get(numberStyle);
BigInteger numId = numbering.addNum(abstractNum.getAbstractNum().getAbstractNumId());
XWPFNum num = numbering.getNum(numId);
CTNumLvl lvlOverride = num.getCTNum().addNewLvlOverride();
lvlOverride.setIlvl(BigInteger.ZERO);
CTDecimalNumber number = lvlOverride.addNewStartOverride();
number.setVal(BigInteger.ONE);
return num;
}
And now you can just apply that NumID to the list you're creating.
/**
* This creates a five item list with a simple heading, using the specified style..
* #param index
* #param styleName
*/
protected void createStyledNumberList(int index, String styleName) {
XWPFParagraph p = document.createParagraph();
XWPFRun run = p.createRun();
run.setText(String.format("List %d: - %s", index, styleName));
// restart numbering
XWPFNum num = restartNumbering(styleName);
for (int i=1; i<=5; i++) {
XWPFParagraph p2 = document.createParagraph();
// set the style for this paragraph
p2.setStyle(styleName);
// set numbering for paragraph
p2.setNumID(num.getCTNum().getNumId());
CTNumPr numProp = p2.getCTP().getPPr().getNumPr();
numProp.addNewIlvl().setVal(BigInteger.ZERO);
// set the text
XWPFRun run2 = p2.createRun();
run2.setText(String.format("Item #%d using '%s' style.", i, styleName));
}
// some whitespace
p = document.createParagraph();
p.createRun();
}
Again, overall I wouldn't have figured this out without the pointer that keil provided.
I have a main report which is printed horizontally. It has 5 columns.
On every column i want to put a sub report. So i created this:
And the sub-report just like this:
The problem is, when i run i get the following exception:
net.sf.jasperreports.engine.JRRuntimeException: Subreport overflowed on a band that does not support overflow.
Looks like jasper reports can't stretch the detail band vertically when there is a sub-report in it and the print order is set to horizontal.
What can I do to avoid this error and achieve what i want?
I found the solution for this problem. After a deep search i found that, sadly, there's no way to do this on Jasper Reports because, no matter what, when you have a report printed horizontally, the "Detail" band will never change it's height. So, subreports or text fields which overflows will throw an exception.
The workaround for this problem is to work without reports, with a PDF generator like iText, for example.
This is the code i did to achive what i wanted with iText if somebody needs it:
Document document = new Document();
File arquivo = new File("C:\\Users\\Mateus\\Desktop\\testezãozarãozão.pdf");
PdfWriter.getInstance(document, new FileOutputStream(arquivo));
document.open();
LinkedHashMap<Produto, LinkedHashMap<String, List<PrePedidoItem>>> produtos = createStructuredHashMap();
for (Produto produto : produtos.keySet()) {
PdfPTable table = new PdfPTable(5);
PdfPCell cellProduto = new PdfPCell();
Phrase phraseProduto = new Phrase(String.valueOf(produto));
phraseProduto.setFont(new Font(Font.FontFamily.HELVETICA, 11, Font.BOLD|Font.UNDERLINE, new BaseColor(50, 65, 200)));
cellProduto.addElement(phraseProduto);
cellProduto.setColspan(5);
cellProduto.setHorizontalAlignment(PdfPCell.ALIGN_MIDDLE);
cellProduto.setBorder(Rectangle.NO_BORDER);
cellProduto.setPaddingBottom(10);
cellProduto.setPaddingTop(20);
table.addCell(cellProduto);
LinkedHashMap<String, List<PrePedidoItem>> mapas = produtos.get(produto);
int mapasAdicionados = 0;
for (String mapa : mapas.keySet()) {
PdfPCell cellMapa = new PdfPCell();
Phrase phraseMapa = new Phrase(mapa);
phraseMapa.setFont(new Font(Font.FontFamily.HELVETICA, 9, Font.BOLD, new BaseColor(215, 100, 0)));
cellMapa.addElement(phraseMapa);
List<PrePedidoItem> itensDoMapa = mapas.get(mapa);
for (PrePedidoItem item : itensDoMapa) {
DecimalFormat df = new DecimalFormat("###,##0.00");
Phrase phraseItem = new Phrase(df.format(item.getLargura()) + " x " + df.format(item.getComprimento()));
phraseItem.setFont(new Font(Font.FontFamily.HELVETICA, 9, Font.NORMAL, BaseColor.BLACK));
cellMapa.addElement(phraseItem);
}
cellMapa.setBorder(Rectangle.NO_BORDER);
table.addCell(cellMapa);
mapasAdicionados ++;
if(mapasAdicionados == 5) {
mapasAdicionados = 0;
}
}
PdfPCell celulaPreenchimentoMapas = new PdfPCell();
celulaPreenchimentoMapas.setColspan(5 - mapasAdicionados);
celulaPreenchimentoMapas.setBorder(Rectangle.NO_BORDER);
table.addCell(celulaPreenchimentoMapas);
document.add(table);
}
document.close();
Desktop.getDesktop().open(arquivo);
I have a simple XWPFTable I made using Apache POI for docx, but if the text of the cell is short the cell/row resizes to fit around the text. How do I set it so the table/rows stay stretched from left to right of the whole page no matter the length of the text? Thanks.
Here's the table part of the code if It helps:
XWPFTable header = document.createTable(1, 3);
header.getRow(0).getCell(0).setText("LCode");
header.getRow(0).getCell(1).setText("QTY");
header.getRow(0).getCell(2).setText("Description/Justification");
XWPFTable table = document.createTable(LCodes.size(), 3);
int x = 0;
for (Map.Entry entry : new TreeMap<Integer, List<String>>(LCodes).entrySet()) {
Object LCode = entry.getKey();
table.getRow(x).getCell(0).setText("L" + LCode);
table.getRow(x).getCell(1).setText(LCodes.get(LCode).get(2));
XWPFParagraph para = table.getRow(x).getCell(2).getParagraphs().get(0);
for (int i = 0; i < 2; i++) {
if (i == 1 && LCodes.get(LCode).get(i).trim().equals("")) {
continue;
}
XWPFRun leading = para.createRun();
leading.setItalic(true);
if (i == 0) {
leading.setText("Description: ");
} else {
leading.setText("Justification: ");
}
XWPFRun trailing = para.createRun();
trailing.setText(LCodes.get(LCode).get(i));
if (i == 0 && !(LCodes.get(LCode).get(i).trim().equals(""))) {
trailing.addBreak();
}
}
x++;
}
To stretch width of xwpf table you need to perform following.
1. Create table in sample xwpf document.
2. Download the same document as zip and extract document.xml file.
3. Find
Another solution: If you dont want to go from all these and your table borders are hidden. You can add below XWPFparagraph in first column of your table and this will do the trick.
//add this para in first column to get table width, 84pts total
private XWPFParagraph getFirstColumnText(){
XWPFParagraph para=new XWPFDocument().createParagraph();
XWPFRun run=para.createRun();
run.setColor("ffffff");
run.setText("...................................................................................");
return para;
}