I have created BaseTable according to example from https://github.com/dhorions/boxable/wiki
float margin = 50;
float yStartNewPage = myPage.getMediaBox().getHeight() - (2 * margin);
float tableWidth = myPage.getMediaBox().getWidth() - (2 * margin);
boolean drawContent = true;
float yStart = yStartNewPage;
float bottomMargin = 70;
float yPosition = 550;
BaseTable table = new BaseTable(yPosition, yStartNewPage, bottomMargin, tableWidth, margin, mainDocument, myPage, true, drawContent);
Row<PDPage> headerRow = table.createRow(15f);
Cell<PDPage> cell = headerRow.createCell(100, "Header");
table.addHeaderRow(headerRow);
Row<PDPage> row = table.createRow(12);
cell = row.createCell(30, "Data 1");
cell = row.createCell(70, "Some value");
table.draw();
contentStream.close();
mainDocument.addPage(myPage);
mainDocument.save("testfile.pdf");
mainDocument.close();
Table looks fine
but when I want change cell height like this
cell.setHeight(5f);
Content is not drawn in cell
I was trying with changing row height, font size change, but it didn't help.
Do You know how to fix it?
After some debugging I've noticed that the cell height can be changed like this:
cell.setHeight(cell.getTextHeight() + 0.5f);
It's important to pick cell.getTextHeight() and then add Your value, if You only put some number like 12f it won't work
Related
I want to put an image set in the center of a cell of excel, I use XSSFClientAnchor to anchor the picture position, but still not working like in picture 1.
How to set the image in the center of a cell, like in picture 2.
InputStream iStream = new FileInputStream(iList.get(q.getProductID()));
byte[] bytes = IOUtils.toByteArray(iStream);
int pictureIdx = wb.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
XSSFDrawing patriarch = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = new XSSFClientAnchor();
anchor.setCol1(1);
anchor.setRow1(row);
anchor.setCol2(2);
anchor.setRow2(row + 1);
Picture pic = patriarch.createPicture(anchor, pictureIdx);
pic.resize();
Images are not cell contents but hover over the sheet in a separate layer called drawing. They are anchored to the cells. A ClientAnchor provides following settings:
col1 = column index where left edge of picture is anchored on. So left edge of picture is anchored on left column edge of col1.
dx1 = difference in x direction. So left edge of picture is anchored on left column edge of col1 + dx1.
row1 = row index where top edge of picture is anchored on. So top edge of picture is anchored on top row edge of row1.
dy1 = difference in y direction. So top edge of picture is anchored on top row edge of row1 + dy1.
col2 = column index where right edge of picture is anchored on. So right edge of picture is anchored on left column edge of col2.
dx2 = difference in x direction. So right edge of picture is anchored on left column edge of col1 + dx2.
row2 = row index where bottom edge of picture is anchored on. So bottom edge of picture is anchored on top row edge of row2.
dy2 = difference in y direction. So bottom edge of picture is anchored on top row edge of row2 + dy2.
Thus, given a full two-cell-anchor, this determines the position of picture well as it's size.
If size of picture shall be it's native size, then only one-cell-anchor is needed. There col1+dx1 and row1+dy1 determines the position of top left edge of picture. The size is given by the native size of the picture.
If only col1 and row1 is set without dx1 and dy1, then top left edge of picture always is anchored to left edge of col1 and top edge of row1. So if centering over a cell is needed, then dx1 and dy1 needs to be calculated. To calculate dx1 and dy1 one needs to know the width and height of the picture as well as the width and height of the cell. Sounds simple but there are multiple different measurement units used for width and height of the cell and there are big differences between binary BIFF (*.xls) file system and Office Open XML (*.xlsx) file system.
The following code provides putPictureCentered method which puts a picture in sheet's drawing anchored to a cell given by colIdx and rowIdx. If possible, it calculates dx1 and dy1 so that the picture is anchored centered over the cell. It uses pixels as the common measurement unit. It considers differences between binary BIFF (*.xls) file system and Office Open XML (*.xlsx) file system. So it works for Sheet, may it be XSSFSheet or HSSFSheet.
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class CenterImageOverCell {
static void putPictureCentered(Sheet sheet, String picturePath, int pictureType, int colIdx, int rowIdx) throws Exception {
Workbook wb = sheet.getWorkbook();
//load the picture
InputStream inputStream = new FileInputStream(picturePath);
byte[] bytes = IOUtils.toByteArray(inputStream);
int pictureIdx = wb.addPicture(bytes, pictureType);
inputStream.close();
//create an anchor with upper left cell colIdx/rowIdx, only one cell anchor since bottom right depends on resizing
CreationHelper helper = wb.getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(colIdx);
anchor.setRow1(rowIdx);
//create a picture anchored to colIdx and rowIdx
Drawing drawing = sheet.createDrawingPatriarch();
Picture pict = drawing.createPicture(anchor, pictureIdx);
//get the picture width in px
int pictWidthPx = pict.getImageDimension().width;
//get the picture height in px
int pictHeightPx = pict.getImageDimension().height;
//get column width of column in px
float columnWidthPx = sheet.getColumnWidthInPixels(colIdx);
//get the height of row in px
Row row = sheet.getRow(rowIdx);
float rowHeightPt = row.getHeightInPoints();
float rowHeightPx = rowHeightPt * Units.PIXEL_DPI / Units.POINT_DPI;
//is horizontal centering possible?
if (pictWidthPx <= columnWidthPx) {
//calculate the horizontal center position
int horCenterPosPx = Math.round(columnWidthPx/2f - pictWidthPx/2f);
//set the horizontal center position as Dx1 of anchor
if (wb instanceof XSSFWorkbook) {
anchor.setDx1(horCenterPosPx * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
} else if (wb instanceof HSSFWorkbook) {
//see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
int DEFAULT_COL_WIDTH = 10 * 256;
anchor.setDx1(Math.round(horCenterPosPx * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75f * DEFAULT_COL_WIDTH / columnWidthPx));
}
} else {
System.out.println("Picture is too width. Horizontal centering is not possible.");
//TODO: Log instead of System.out.println
}
//is vertical centering possible?
if (pictHeightPx <= rowHeightPx) {
//calculate the vertical center position
int vertCenterPosPx = Math.round(rowHeightPx/2f - pictHeightPx/2f);
//set the vertical center position as Dy1 of anchor
if (wb instanceof XSSFWorkbook) {
anchor.setDy1(Math.round(vertCenterPosPx * Units.EMU_PER_PIXEL)); //in unit EMU for XSSF
} else if (wb instanceof HSSFWorkbook) {
//see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
float DEFAULT_ROW_HEIGHT = 12.75f;
anchor.setDy1(Math.round(vertCenterPosPx * Units.PIXEL_DPI / Units.POINT_DPI * 14.75f * DEFAULT_ROW_HEIGHT / rowHeightPx));
}
} else {
System.out.println("Picture is too height. Vertical centering is not possible.");
//TODO: Log instead of System.out.println
}
//resize the picture to it's native size
pict.resize();
}
public static void main(String[] args) throws Exception {
//Workbook wb = new HSSFWorkbook(); String resultName = "CenterImageTest.xls";
Workbook wb = new XSSFWorkbook(); String resultName = "CenterImageTest.xlsx";
Sheet sheet = wb.createSheet("Sheet1");
int colIdx = 1;
int colWidth = 20; //in default character widths
int rowIdx = 1;
float rowHeight = 100; //in points
//========================prepare sheet
//create cell
Row row = sheet.createRow(rowIdx);
Cell cell = row.createCell(colIdx);
//set column width of colIdx in default character widths
sheet.setColumnWidth(colIdx, colWidth * 256);
//set row height of rowIdx in points
row.setHeightInPoints(rowHeight);
//========================end prepare sheet
//put image centered
String picturePath = "./pict100x100.png"; // small image
//String picturePath = "./pict100x200.png"; // image too height
//String picturePath = "./pict200x100.png"; // image too width
//String picturePath = "./pict200x200.png"; // image too big
putPictureCentered(sheet, picturePath, Workbook.PICTURE_TYPE_PNG, colIdx, rowIdx);
FileOutputStream fileOut = new FileOutputStream("./" + resultName);
wb.write(fileOut);
fileOut.close();
wb.close();
}
}
I'm trying to retrieve the x, y coordinates from a Paragraph created in iText. I followed the approved answer in How to get vertical cursor position when writing document in iText 7? but I'm not getting the expected result.
PdfDocument pdfDoc = new PdfDocument(new PdfWriter("output/ITextSandbox/Coordinates.pdf"));
pdfDoc.setDefaultPageSize(PageSize.LETTER); // 8.5 x 11
Document document = new Document(pdfDoc);
PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER, PdfEncodings.UTF8);
document.setFont(font);
document.setFontSize(10);
Paragraph paragraph = null;
// Print 5 lines to ensure the y coord is sufficiently moved away from the top of the page.
for (int i = 1; i <= 5; i++)
{
paragraph = new Paragraph();
paragraph.add(new Text("Line " + i));
document.add(paragraph);
}
// Print a new paragraph from which to obtain the x, y coordinates.
paragraph = new Paragraph();
paragraph.add(new Text("Line 6"));
document.add(paragraph);
// Follow the steps from the approved answer in
// https://stackoverflow.com/questions/51953723/how-to-get-vertical-cursor-position-when-writing-document-in-itext-7
IRenderer renderer = paragraph.createRendererSubTree().setParent(document.getRenderer());
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult = renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
float y = layoutResult.getOccupiedArea().getBBox().getY();
float x = layoutResult.getOccupiedArea().getBBox().getX();
System.out.println("x = " + x + ", y = " + y); // y should be approximately 630, not 710.
With standard margins and 10 pt font, the coordinates for the 6th line should approximately be x = 0, y = 630. Instead, I get y = 710.
The code sample in the question simulates rendering on a certain layout area. That approach works to determine the coordinates on the page if the exact same elements are simulated on the exact same layout area.
In the question, only the 6th paragraph is simulated, so the larger y coordinate (higher on the page) is expected. To get the correct y coordinate, the 5 preceding paragraphs would also have to be simulated.
Moreover, the layout area is not the same as page. This code does not take into account the page margins correctly:
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult =
renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
With the default page margins of 36, the correct layout area would be:
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult =
renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(36, 36, width, height))));
A much easier way to get the current coordinates after rendering some elements is:
/*...*/
paragraph = new Paragraph();
paragraph.add(new Text("Line 6"));
document.add(paragraph);
Rectangle remaining = document.getRenderer().getCurrentArea().getBBox();
float y = remaining.getTop();
System.out.println("y = " + y);
Result: y = 631.6011
To illustrate the remaining layout area, let's draw it on the page:
PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(1));
canvas.setStrokeColor(ColorConstants.RED).rectangle(remaining).stroke();
With some different page margins:
document.setMargins(5, 25, 15, 5);
I want to add a text to the PDF using PDFBox API and rotate it by 45 Degree and place it at the center of the page, The text is dynamic and should be placed in the center always, I got everything else to work except centering piece, I'll appreciate any help.
I have this code:
Point2D.Float pageCenter = getCenter(page);
float stringWidth = getStringWidth(watermarkText, font, fontSize);
float textX = pageCenter.x - stringWidth / 2F + center.x;
System.out.println(textX);
float textY = pageCenter.y + center.y;
//System.out.println("Inside cross"+textX+", "+textY);
fontSize = 110.0f;
cs.transform(Matrix.getRotateInstance(Math.toRadians(45), textX, textY));
cs.moveTo(0, 0);
cs.lineTo(125, 0);
r0.setNonStrokingAlphaConstant(0.20f);
This is the result i want:
Output PDF
What I do is to first rotate based on the calculated angle. In this "rotated world" I do a horizontal offset so that the text is in the middle, and also move the text vertically a bit lower, so that it is in the "vertical" middle of an imagined diagonal line (horizontal in the "rotated world").
try (PDDocument doc = new PDDocument())
{
PDPage page = new PDPage();
doc.addPage(page);
PDFont font = PDType1Font.HELVETICA_BOLD;
try (PDPageContentStream cs =
new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true))
// use this long constructor when working on existing PDFs
{
float fontHeight = 110;
String text = "Watermark";
float width = page.getMediaBox().getWidth();
float height = page.getMediaBox().getHeight();
int rotation = page.getRotation();
switch (rotation)
{
case 90:
width = page.getMediaBox().getHeight();
height = page.getMediaBox().getWidth();
cs.transform(Matrix.getRotateInstance(Math.toRadians(90), height, 0));
break;
case 180:
cs.transform(Matrix.getRotateInstance(Math.toRadians(180), width, height));
break;
case 270:
width = page.getMediaBox().getHeight();
height = page.getMediaBox().getWidth();
cs.transform(Matrix.getRotateInstance(Math.toRadians(270), 0, width));
break;
default:
break;
}
float stringWidth = font.getStringWidth(text) / 1000 * fontHeight;
float diagonalLength = (float) Math.sqrt(width * width + height * height);
float angle = (float) Math.atan2(height, width);
float x = (diagonalLength - stringWidth) / 2; // "horizontal" position in rotated world
float y = -fontHeight / 4; // 4 is a trial-and-error thing, this lowers the text a bit
cs.transform(Matrix.getRotateInstance(angle, 0, 0));
cs.setFont(font, fontHeight);
//cs.setRenderingMode(RenderingMode.STROKE); // for "hollow" effect
PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
gs.setNonStrokingAlphaConstant(0.2f);
gs.setStrokingAlphaConstant(0.2f);
gs.setBlendMode(BlendMode.MULTIPLY);
cs.setGraphicsStateParameters(gs);
// some API weirdness here. When int, range is 0..255.
// when float, this would be 0..1f
cs.setNonStrokingColor(255, 0, 0);
cs.setStrokingColor(255, 0, 0);
cs.beginText();
cs.newLineAtOffset(x, y);
cs.showText(text);
cs.endText();
}
doc.save("watermarked.pdf");
}
Note that I've set both stroking and non stroking (= fill). This is useful for people who want to try the (disabled) "hollow" appearance, that one uses stroking only. The default mode is fill, i.e. non-stroking.
I am using com.lowagie.text in my pdf. While generating it, I need to highlight a few text.This is the code i have.I need the code for Highlighting text in a chunk.
PdfContentByte cb = writer.getDirectContent();
Chunk chunck = new Chunk(arraySpec[k],font);
Phrase phrase = new Phrase(8.0f+ 1);
phrase.add(chunck);
ColumnText columnText = new ColumnText(cb);
columnText.addText(phrase);
//verify the bounding box size
if (attributeBoundingBoxTextWidth == 0){
attributeBoundingBoxTextWidth = (int) boardWidth;
}
float llx = xpos;
float lly = 0;
float urx = llx + width;
float ury = refYPos - sizeOftext;
refYPos = ury;
float leading = 8.0f+ 1;
columnText.setSimpleColumn(llx, lly, urx, ury, leading, Element.ALIGN_LEFT);
columnText.go();
I got a solution for the above post.
columnText.getCanvas().setColorFill(Color.decode("#FFA500"));
columnText.getCanvas().rectangle(llx, ury-(leading) ,columnText.getWidth(phrase) , sizeOftext);
columnText.getCanvas().fill();
columnText.go();
I need to add shape (specifically circle) to xls-document using POI. So, I wrote following code:
private void drawLabel(HSSFSheet sheet, HSSFCell cell) {
final short columnIndex = (short) cell.getColumnIndex();
final int rowIndex = cell.getRowIndex();
HSSFClientAnchor anchor = new HSSFClientAnchor(60, 60, 200, 200, columnIndex, rowIndex, columnIndex, rowIndex);
anchor.setAnchorType(ClientAnchor.MOVE_DONT_RESIZE);
HSSFSimpleShape shape = sheet.getDrawingPatriarch().createSimpleShape(anchor);
shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
...
}
It works. But if I try to change column width programmatically (e.g. sheet.setColumnWidth(0, 5 * 256); (suppose that shape has been added to cell with column index == 0)), circle distorts and becomes like an oval (although anchor's type is set to MOVE_DONT_RESIZE).
What is wrong in my approach and is there a way to draw a shape which would not resize along with corresponding cell?
I have not find proper solution of this problem. But the one way to solve problem is to scale shape (specifically its anchor) according to its actual size:
private void drawLabel(HSSFSheet sheet, HSSFCell cell) {
final int defaultColumnWidth = 2048; // column width before resizing
final int defaultRowHeight = 255; // row height before resizing
final double xDistortionFactor = 1.0 * defaultColumnWidth / sheet.getColumnWidth(cell.getColumnIndex());
final double yDistortionFactor = 1.0 * defaultRowHeight / cell.getRow().getHeight();
final int dx1 = (int) (60 * xDistortionFactor);
final int dy1 = (int) (60 * yDistortionFactor);
final int dx2 = (int) (200 * xDistortionFactor);
final int dy2 = (int) (200 * yDistortionFactor);
final short columnIndex = (short) cell.getColumnIndex();
final int rowIndex = cell.getRowIndex();
final HSSFClientAnchor anchor = new HSSFClientAnchor(dx1, dy1, dx2, dy2, columnIndex, rowIndex, columnIndex, rowIndex);
anchor.setAnchorType(ClientAnchor.MOVE_DONT_RESIZE);
HSSFSimpleShape shape = sheet.getDrawingPatriarch().createSimpleShape(anchor);
shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
...
}
This solution is not graceful, but it just works.