itext7 Java Create PdfExplicitDestination for titles in existing pdf - java

I am using PdfExplicitDestination as a page number, for titles by reading the existing pdf content from the page,
but I need to point the focus on specific text content while click on the bookmark.
for (int page = 1; page <= pdf.getNumberOfPages(); page++) {
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
String currentText = PdfTextExtractor.getTextFromPage(pdf.getPage(page), strategy);
if (currentText.contains("title")) {
k.addDestination(PdfExplicitDestination.createXYZ(pdf.getPage(page), pdf.getPage(page).getPageSize().getLeft(), pdf.getPage(page).getPageSize().getTop(), 0));
//System.out.println(currentText);
}
}
I need to find the position of the title in the pdf page to set "float top" value.
PdfExplicitDestination.createXYZ(pageNum, left, top, zoom)
Can any one please help to get it from the existing content in the pdf.

This task can be approached in a number of ways. One of the way is to go over page content in "stripes" (rectangles with small height), and only consider content from such a small rectangle at a time. If you find a text piece in such rectangle then you know that somewhere between upper and lower bound of Y position given by the rectangle coordinates lies the desired text content. You can e.g. create the destination to point to the topmost coordinate in that case - it might be a bit above the desired text but the difference will be small depending on the rectangle height you select.
The following code snipped contains example implementation of the presented idea. There are two parameters - windowHeight which must be tall enough to fit a piece of content you are looking for, but the smaller this variable is the better accuracy you get in the result. Parameter step defines how many such rectangles of height windowHeight we will try on each page. The smaller the parameter is the better accuracy you get, but bigger parameter values optimize performance. Up to a specific use case to tweak those trade-offs.
final float windowHeight = 30;
final float step = 10;
for (int page = 1; page <= pdf.getNumberOfPages(); page++) {
Rectangle pageSize = pdf.getPage(page).getPageSize();
for (float upperPoint = pageSize.getHeight(); upperPoint > 0; upperPoint -= step) {
IEventFilter filter = new TextRegionEventFilter(new Rectangle(0, upperPoint - windowHeight, pageSize.getWidth(), windowHeight));
LocationTextExtractionStrategy strategy = new LocationTextExtractionStrategy();
FilteredTextEventListener listener = new FilteredTextEventListener(strategy, filter);
new PdfCanvasProcessor(listener).processPageContent(pdf.getPage(page));
if (strategy.getResultantText().contains("title")) {
float top = upperPoint; // This is the topmost point of the rectangle
break; // Break here not to capture same text twice
}
}
}

Related

Itext PDF Shrink issue with hyperlink bounding box is not getting Shrinked

I am working with PDF shrinking and then watermarking it and for the same I am using itextpdf-5.5.1.jar. Here is the code which I use to shrink PDF. In code xPercentage and xPercentage value is 0.9f. When I shrink PDF having content table , content on the page is shrinking properly. When I go to table of content the bounding box of hyperlink is getting misplaced. I noticed that size of bounding box is same for Original and shrink output document. How do I shrink bounding box of hyperlink with respect to content?
public void shrinkPDF(String strFilePath , String strFileName) throws Exception{
PdfReader reader = new PdfReader(strFilePath+"//"+strFileName);
PdfStamper stamper = new PdfStamper(reader, new
FileOutputStream(strFilePath+"//Shrink_"+strFileName));
int n = reader.getNumberOfPages();
Map mpPDFLayer = stamper.getPdfLayers();
for (int p = 1; p <= n; p++) {
float offsetX = (reader.getPageSize(p).getWidth() * (1 - xPercentage)) / 2;
float offsetY = (reader.getPageSize(p).getHeight() * (1 - yPercentage)) / 2;
stamper.getUnderContent(p).setLiteral(
String.format("\nq %s 0 0 %s %s %s cm\nq\n",
xPercentage, yPercentage, offsetX, offsetY));
stamper.getOverContent(p).setLiteral("\nQ\nQ\n");
}
stamper.close();
reader.close();
}
Your code shrinks only the content but it does not accordingly move and shrink annotations. So what you have to do additionally is to iterate over the annotations of each page and shrink them.
This in particular means that you have to shrink and move the Rect annotation rectangle. Depending on the nature of the respective annotation, though, there also are other coordinate values in them, e.g. the QuadPoints in case of a link or the L endpoint coordinates of a line.
BTW, your content shrinking code makes assumptions on the origin of the user space coordinate system; it appears to assume that the origin is in the lower left of the crop box and that the crop box and the media box coincide.

how to change exist text position in pdf by itext

sorry,you may not understand my problems,because i am not good at english.
i want to add some labels at the top and bottom of pdf,but the label position can set minus.if i set a minus,i should make heigh larger to set label.
i got help from How to resize existing pdf page size
to change my pdf pagesize.then i encountered another problem,when i set lly as a
minus, my text was truncated,then i want to add bottom length to top, but i do not know how to move the text up to make the text in center.
key codes
float newTop = rectangle.getTop();
if (printSet.getHeaderMargins() < 0) {
newTop += height2Offset(PrintSet.defaultMargins - printSet.getHeaderMargins());
headMargins = height2Offset(PrintSet.defaultMargins);
}
if (printSet.getFooterMargins() < 0) {
newTop += height2Offset(PrintSet.defaultMargins - printSet.getFooterMargins());
footMargins = height2Offset(PrintSet.defaultMargins);
}
float[] newBoxValues = new float[] {
rectangle.getLeft(),
rectangle.getBottom(),
rectangle.getRight(),
newTop
};
PdfArray newBox = new PdfArray(newBoxValues);
PdfDictionary pageDict = reader.getPageN(page + 1);
pageDict.put(PdfName.CROPBOX, newBox);
pageDict.put(PdfName.MEDIABOX, newBox);
Currently, you are defining a new page size like this:
float[] newBoxValues = new float[] {
rectangle.getLeft(),
rectangle.getBottom(),
rectangle.getRight(),
newTop
};
This creates a bigger rectangle, but that rectangle only expands the page towards the top.
I think you should create the new rectangle like this:
float[] newBoxValues = new float[] {
rectangle.getLeft(),
rectangle.getBottom() - extramarginBottom,
rectangle.getRight(),
rectangle.getTop() + extramarginTop
};
I can't help you define the value of extramarginBottom and extramarginTop because I'm not sure what your height2Offset() method is supposed to do, nor what PrintSet.defaultMargins, printSet.getHeaderMargins(), and printSet.getFooterMargins() are about.
Basically, extramarginTop is the height to be added at the top, whereas extramarginBottom is the height to be added at the bottom:

JavaFX print tableview on multiple pages

So, my problem is that I need to print the content of my tableview, but I have so many items in it, that it only prints the first 23 of them.
I found a few solutions here already, unfortunately they didn't help much.
This is my print method:
#FXML
private void printIt() {
Printer printer = Printer.getDefaultPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.DEFAULT);
double scaleX = pageLayout.getPrintableWidth() / logBookTable.getBoundsInParent().getWidth();
double scaleY = pageLayout.getPrintableHeight() / logBookTable.getBoundsInParent().getHeight();
logBookTable.getTransforms().add(new Scale(scaleX, scaleY));
PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
boolean successPrintDialog = job.showPrintDialog(dialogStage);
if(successPrintDialog){
boolean success = job.printPage(pageLayout,logBookTable);
if (success) {
job.endJob();
}
}
}
}
I looked through a lot of posts before I was able to come up with an answer. The key is to extend the height of the tableview to show without the need to have a scroll on the right of the screen. (There are ways of doing this without skewing your programs layout. I did not address that part in this questions. There are very good answer on how to accomplish this task.) Once you have extend the height of the tableview to the show all of it's rows without the need for a scroll bar. Then just print one page at a time starting from height of zero going to a negative height of one page.(In this case: close to 11 inches down. Borders play a role in this.) The next page should pick up where the last page ended and print down to almost 11 more inches. (from about -11 inches to about -22 inches.) This pattern continues until the whole height of the tableview has been traversed.
Printer printer = Printer.getDefaultPrinter(); //get the default printer
javafx.print.PageLayout pageLayout = printer.createPageLayout(Paper.NA_LETTER, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT); //create a pagelayout. I used Paper.NA_LETTER for a standard 8.5 x 11 in page.
PrinterJob job = PrinterJob.createPrinterJob();//create a printer job
if(job.showPrintDialog(taMain.getScene().getWindow()))// this is very useful it allows you to save the file as a pdf instead using all of your printer's paper. A dialog box pops up, allowing you to change the "name" option from your default printer to Adobe pdf.
{
double pagePrintableWidth = pageLayout.getPrintableWidth(); //this should be 8.5 inches for this page layout.
double pagePrintableHeight = pageLayout.getPrintableHeight();// this should be 11 inches for this page layout.
tblvMain.prefHeightProperty().bind(Bindings.size(tblvMain.getItems()).multiply(35));// If your cells' rows are variable size you add the .multiply and play with the input value until your output is close to what you want. If your cells' rows are the same height, I think you can use .multiply(1). This changes the height of your tableView to show all rows in the table.
tblvMain.minHeightProperty().bind(tblvMain.prefHeightProperty());//You can probably play with this to see if it's really needed. Comment it out to find out.
tblvMain.maxHeightProperty().bind(tblvMain.prefHeightProperty());//You can probably play with this to see if it' really needed. Comment it out to find out.
double scaleX = pagePrintableWidth / tblvMain.getBoundsInParent().getWidth();//scaling down so that the printing width fits within the paper's width bound.
double scaleY = scaleX; //scaling the height using the same scale as the width. This allows the writing and the images to maintain their scale, or not look skewed.
double localScale = scaleX; //not really needed since everything is scaled down at the same ratio. scaleX is used thoughout the program to scale the print out.
double numberOfPages = Math.ceil((tblvMain.getPrefHeight() * localScale) / pagePrintableHeight);//used to figure out the number of pages that will be printed.
//System.out.println("pref Height: " + tblvMain.getPrefHeight());
//System.out.println("number of pages: " + numberOfPages);
tblvMain.getTransforms().add(new Scale(scaleX, (scaleY)));//scales the printing. Allowing the width to say within the papers width, and scales the height to do away with skewed letters and images.
tblvMain.getTransforms().add(new Translate(0, 0));// starts the first print at the top left corner of the image that needs to be printed
//Since the height of what needs to be printed is longer than the paper's heights we use gridTransfrom to only select the part to be printed for a given page.
Translate gridTransform = new Translate();
tblvMain.getTransforms().add(gridTransform);
//now we loop though the image that needs to be printed and we only print a subimage of the full image.
//for example: In the first loop we only pint the printable image from the top down to the height of a standard piece of paper. Then we print starting from were the last printed page ended down to the height of the next page. This happens until all of the pages are printed.
// first page prints from 0 height to -11 inches height, Second page prints from -11 inches height to -22 inches height, etc.
for(int i = 0; i < numberOfPages; i++)
{
gridTransform.setY(-i * (pagePrintableHeight / localScale));
job.printPage(pageLayout, tblvMain);
}
job.endJob();//finally end the printing job.
Found a solution myself
1) create for loop
2)create new table and insert items, which are not being printed
3) print
Printer printer = Printer.getDefaultPrinter();
Printer printer = Printer.getDefaultPrinter();PrinterJob printerJob = PrinterJob.createPrinterJob();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.HARDWARE_MINIMUM);
printerJob.getJobSettings().setPageLayout(pageLayout);
Stage stage = (Stage) anchorPane.getScene().getWindow();
boolean openPrintDialog = printerJob.showPrintDialog(stage);
if(openPrintDialog){
tableView.setScaleX(0.8);
tableView.setScaleY(0.8);
tableView.setTranslateX(-70);
tableView.setTranslateY(-50);
ObservableList<List<SimpleStringProperty>> allPrintItems = tableView.getItems();
ObservableList <List<SimpleStringProperty>> pageList = FXCollections.observableArrayList();
boolean printing = false;
for(int i=0; i<allPrintItems.size(); i++) {
List<SimpleStringProperty> oneRow = allPrintItems.get(i);
pageList.add(oneRow);
if(i!=0 && (i%24==0 || i == (allPrintItems.size()-1))){
tableView.setItems(pageList);
printing = printerJob.printPage(tableView);
pageList.clear();
}
}
tableView.setItems(allPrintItems);
if(printing)printerJob.endJob();
tableView.setScaleX(1.0);
tableView.setScaleY(1.0);
tableView.setTranslateX(0);
tableView.setTranslateY(0);

iText - crop out a part of pdf file

I have a small problem and I'm trying for some time to find out a solution.
Long story short I have to remove the top part of each page from a pdf with itext. I managed to do this with CROPBOX, but the problem is that this will make the pages smaller by removing the top part.
Can someone help me to implement this so the page size remains the same. My idea would be to override the top page with a white rectangle, but after many tries I didn't manage to do this.
This is the current code I'm using to crop the page.
PdfRectangle rect = new PdfRectangle(55, 0, 1000, 1000);
PdfDictionary pageDict;
for (int curentPage = 2; curentPage <= pdfReader.getNumberOfPages(); curentPage++) {
pageDict = pdfReader.getPageN(curentPage);
pageDict.put(PdfName.CROPBOX, rect);
}
In your code sample, you are cropping the pages. This reduces the visible size of the page.
Based on your description, you don't want cropping. Instead you want clipping.
I've written an example that clips the content of all pages of a PDF by introducing a margin of 200 user units (that's quite a margin). The example is called ClipPdf and you can see a clipped page here: hero_clipped.pdf (the iText superhero has lost arms, feet and part of his head in the clipping process.)
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
int n = reader.getNumberOfPages();
PdfDictionary page;
PdfArray media;
for (int p = 1; p <= n; p++) {
page = reader.getPageN(p);
media = page.getAsArray(PdfName.CROPBOX);
if (media == null) {
media = page.getAsArray(PdfName.MEDIABOX);
}
float llx = media.getAsNumber(0).floatValue() + 200;
float lly = media.getAsNumber(1).floatValue() + 200;
float w = media.getAsNumber(2).floatValue() - media.getAsNumber(0).floatValue() - 400;
float h = media.getAsNumber(3).floatValue() - media.getAsNumber(1).floatValue() - 400;
String command = String.format(
"\nq %.2f %.2f %.2f %.2f re W n\nq\n",
llx, lly, w, h);
stamper.getUnderContent(p).setLiteral(command);
stamper.getOverContent(p).setLiteral("\nQ\nQ\n");
}
stamper.close();
reader.close();
}
Obviously, you need to study this code before using it. Once you understand this code, you'll know that this code will only work for pages that aren't rotated. If you understand the code well, you should have no problem adapting the example for rotated pages.
Update
The re operator constructs a rectangle. It takes four parameters (the values preceding the operator) that define a rectangle: the x coordinate of the lower-left corner, the y coordinate of the lower-left corner, the width and the height.
The W operator sets the clipping path. We have just drawn a rectangle; this rectangle will be used to clip the content that follows.
The n operator starts a new path. It discards the paths we've constructed so far. In this case, it prevents that the rectangle we have drawn (and that we use as clipping path) is actually drawn.
The q and Q operators save and restore the graphics state stack, but that's rather obvious.
All of this is explained in ISO-32000-1 (available online if you Google well) and in the book The ABC of PDF.

Java: Getting a font with a specific height in pixels

It’s easy to determine the rendered height of a font using FontMetrics, but what about the other way around? How can I obtain a font that will fit into a specific height in pixels?
"Give me Verdana in a size that is 30 pixels high from ascender to descender."
How do I ask Java for this?
I know this is a very old question, but someone might still find it:
The font height in Java (and many other places) is given in "typographic points", which are defined as roughly 1/72nd of an inch.
To calculate the points needed for a certain pixel height, you should be able to use the following:
double fontSize= 72.0 * pixelSize / Toolkit.getDefaultToolkit().getScreenResolution();
I haven't tested this extensively yet, but it seems to work for the monitors that I've used. I'll report back if I ever find a case where it doesn't work.
For the standard system fonts I've used this with, this sets the height of a capital letter (i.e. the ascent) to the provided pixel size. If you need to set the ascent+descent to the pixel size, you can correct the value using the FontMetrics:
FontMetrics m= g.getFontMetrics(font); // g is your current Graphics object
double totalSize= fontSize * (m.getAscent() + m.getDescent()) / m.getAscent();
Of course, the actual pixel-height of some specific letters will depend on the letter and the font used, so if you want to make sure that your "H" is some exact number of pixels tall, you might still want to use the trial-and-error methods mentioned in the other answers. Just keep in mind that if you use these methods to get the size for each specific text you want to display (as #Bob suggested), you might end up with a random font-size-mess on your screen where a text like "ace" will have much bigger letters than "Tag". To avoid this, I would pick one specific letter or letter sequence ("T" or "Tg" or something) and fix that one to your pixel height once and then use the font size you get from that everywhere.
I don't think there's a "direct" way to find a font by height; only an indirect way... by looping through the sizes, and testing the height of each is <= required height.
If you're doing this once, just loop through them... if you've doing it "on the fly" then do a binary search, it'll be quicker.
I'm not aware of a way to get a font by its actual height in pixels. It depends on the context it's used in so there's probably no shorter way than to sample for the best match. It should be pretty quick to look for sizes up or down from the designed height. Here's an example method that does that:
public Font getFont(String name, int style, int height) {
int size = height;
Boolean up = null;
while (true) {
Font font = new Font(name, style, size);
int testHeight = getFontMetrics(font).getHeight();
if (testHeight < height && up != Boolean.FALSE) {
size++;
up = Boolean.TRUE;
} else if (testHeight > height && up != Boolean.TRUE) {
size--;
up = Boolean.FALSE;
} else {
return font;
}
}
}
WhiteFang34's code is useful in combination with the following method that returns the actual height of a specific string. It might be a bit slow for real-time rendering, especially for large fonts/strings and I'm sure it can be further optimised, but for now it meets my own needs and is fast enough to run in a back-end process.
/*
* getFontRenderedHeight
* *************************************************************************
* Summary: Font metrics do not give an accurate measurement of the rendered
* font height for certain strings because the space between the ascender
* limit and baseline is not always fully used and descenders may not be
* present. for example the strings '0' 'a' 'f' and 'j' are all different
* heights from top to bottom but the metrics returned are always the same.
* If you want to place text that exactly fills a specific height, you need
* to work out what the exact height is for the specific string. This method
* achieves that by rendering the text and then scanning the top and bottom
* rows until the real height of the string is found.
*/
/**
* Calculate the actual height of rendered text for a specific string more
* accurately than metrics when ascenders and descenders may not be present
* <p>
* Note: this method is probably not very efficient for repeated measurement
* of large strings and large font sizes but it works quite effectively for
* short strings. Consider measuring a subset of your string value. Also
* beware of measuring symbols such as '-' and '.' the results may be
* unexpected!
*
* #param string
* The text to measure. You might be able to speed this process
* up by only measuring a single character or subset of your
* string i.e if you know your string ONLY contains numbers and
* all the numbers in the font are the same height, just pass in
* a single digit rather than the whole numeric string.
* #param font
* The font being used. Obviously the size of the font affects
* the result
* #param targetGraphicsContext
* The graphics context the text will actually be rendered in.
* This is passed in so the rendering options for anti-aliasing
* can be matched.
* #return Integer - the exact actual height of the text.
* #author Robert Heritage [mrheritage#gmail.com]
*/
public Integer getFontRenderedHeight(String string, Font font, Graphics2D targetGraphicsContext) {
BufferedImage image;
Graphics2D g;
Color textColour = Color.white;
// In the first instance; use a temporary BufferedImage object to render
// the text and get the font metrics.
image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
g = image.createGraphics();
FontMetrics metrics = g.getFontMetrics(font);
Rectangle2D rect = metrics.getStringBounds(string, g);
// now set up the buffered Image with a canvas size slightly larger than
// the font metrics - this guarantees that there is at least one row of
// black pixels at the top and the bottom
image = new BufferedImage((int) rect.getWidth() + 1, (int) metrics.getHeight() + 2, BufferedImage.TYPE_INT_RGB);
g = image.createGraphics();
// take the rendering hints from the target graphics context to ensure
// the results are accurate.
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, targetGraphicsContext.getRenderingHint(RenderingHints.KEY_ANTIALIASING));
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, targetGraphicsContext.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));
g.setColor(textColour);
g.setFont(font);
g.drawString(string, 0, image.getHeight());
// scan the bottom row - descenders will be cropped initially, so the
// text will need to be moved up (down in the co-ordinates system) to
// fit it in the canvas if it contains any. This may need to be done a
// few times until there is a row of black pixels at the bottom.
boolean foundBottom, foundTop = false;
int offset = 0;
do {
g.setColor(Color.BLACK);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.setColor(textColour);
g.drawString(string, 0, image.getHeight() - offset);
foundBottom = true;
for (int x = 0; x < image.getWidth(); x++) {
if (image.getRGB(x, image.getHeight() - 1) != Color.BLACK.getRGB()) {
foundBottom = false;
}
}
offset++;
} while (!foundBottom);
System.out.println(image.getHeight());
// Scan the top of the image downwards one line at a time until it
// contains a non-black pixel. This loop uses the break statement to
// stop the while loop as soon as a non-black pixel is found, this
// avoids the need to scan the rest of the line
int y = 0;
do {
for (int x = 0; x < image.getWidth(); x++) {
if (image.getRGB(x, y) != Color.BLACK.getRGB()) {
foundTop = true;
break;
}
}
y++;
} while (!foundTop);
return image.getHeight() - y;
}

Categories

Resources