I am generating pdf files by XML data.
I calculate the height of a paragraph element as :
float paraWidth = 0.0f;
for (Object o : el.getChunks()) {
paraWidth += ((Chunk) o).getWidthPoint();
}
float paraHeight = paraWidth/PageSize.A4.getWidth();
But this method does not works correctly.
Can you give me an idea?
Your question is strange. According to the header of your question, you want to know the height of a string, but your code shows that you are asking for the width of a String.
Please take a look at the FoobarFilmFestival example.
If bf is a BaseFont instance, then you can use:
float ascent = bf.getAscentPoint("Some String", 12);
float descent = bf.getDescentPoint("Some String", 12);
This will return the height above the baseline and the height below the baseline, when we use a font size of 12. As you probably know, the font size is an indication of the average height. It's not the actual height. It's just a number we work with.
The total height will be:
float height = ascent - descent;
Or maybe you want to know the number of lines taken by a Paragraph and multiply that with the leading. In that case, there are different possibilities. As it's not clear from your question what you want (height of chunks, width of chunks, vertical position of the baseline,...), you won't get any better answers than the ones that are already given. Please rephrase your question if the height of the glyphs in a Chunk wasn't what you expected.
Firstly why you iterating over Chunk collection casted to Object ? If all elements of this collection are Chunk, use this:
for (Chunk c : el.getChunks()) {
paraWidth += c.getWidthPoint();
}
What do you mean saying method does not works correctly ?
Related
Im working with PDFClown to analyze and work with PDFDocuments. My aim is to highlight all numbers within a table. For all numbers which belong together (For example: All numbers in one column of a table) I will create one TextMarkup with a List of Quads. First of all it looks like everythink work well: All highlights on the left belong to one TextMarkup and all Highlights on the right belong to another TextMarkup.
But when analyzing the size of the TextMarkup the size is bigger than it looks at the picture. So when drawing for example a rectangle arround the left TextMarkup box the rectangle intersects the other column despite no highlight of the left TextMarkup intersects the other column. Is there a way to optimize the Box of the TextMarkup? I think there is a bulbous ending of the box so that the box is intersecting the other TextMarkup
This is the code which creates the TextMarkup:
List<Quad> highlightQuads = new ArrayList<Quad>();
for (TextMarkup textMarkup : textMarkupsForOneAnnotation) {
Rectangle2D textBox = textMarkup.getBox();
Rectangle2D.Double rectangle = new Rectangle2D.Double(textBox.getX(), textBox.getY(), textBox.getWidth(), textBox.getHeight());
highlightQuads.add(Quad.get(rectangle));
}
if (highlightQuads.size() > 0) {
TextMarkup _textMarkup = new TextMarkup(pagesOfNewFile.get(lastFoundNewFilePage).getPage(), highlightQuads,"", MarkupTypeEnum.Highlight);
_textMarkup.setColor(DeviceRGBColor.get(Color.GREEN));
_textMarkup.setVisible(true);
allTextMarkUps.add(_textMarkup);
}
Here is an example file Example
Thank You !!
Your code is not really self contained (I cannot run it as it in particular misses the input data), so I could only do a bit of PDF Clown code analysis. That code analysis, though, did indeed turn up a PDF Clown implementation detail that would explain your observation.
How does PDF Clown calculate the dimensions of the markup annotation?
The markup annotation rectangle must be big enough to include all quads plus start and end decorations (rounded left and right caps on markup rectangle).
PDF Clown calculates this rectangle as follows in TextMarkup:
public void setMarkupBoxes(
List<Quad> value
)
{
PdfArray quadPointsObject = new PdfArray();
double pageHeight = getPage().getBox().getHeight();
Rectangle2D box = null;
for(Quad markupBox : value)
{
/*
NOTE: Despite the spec prescription, Point 3 and Point 4 MUST be inverted.
*/
Point2D[] markupBoxPoints = markupBox.getPoints();
quadPointsObject.add(PdfReal.get(markupBoxPoints[0].getX())); // x1.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[0].getY())); // y1.
quadPointsObject.add(PdfReal.get(markupBoxPoints[1].getX())); // x2.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[1].getY())); // y2.
quadPointsObject.add(PdfReal.get(markupBoxPoints[3].getX())); // x4.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[3].getY())); // y4.
quadPointsObject.add(PdfReal.get(markupBoxPoints[2].getX())); // x3.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[2].getY())); // y3.
if(box == null)
{box = markupBox.getBounds2D();}
else
{box.add(markupBox.getBounds2D());}
}
getBaseDataObject().put(PdfName.QuadPoints, quadPointsObject);
/*
NOTE: Box width is expanded to make room for end decorations (e.g. rounded highlight caps).
*/
double markupBoxMargin = getMarkupBoxMargin(box.getHeight());
box.setRect(box.getX() - markupBoxMargin, box.getY(), box.getWidth() + markupBoxMargin * 2, box.getHeight());
setBox(box);
refreshAppearance();
}
private static double getMarkupBoxMargin(
double boxHeight
)
{return boxHeight * .25;}
So it takes the bounding box of all the quads and adds left and right margins each as wide as a quarter of the height of this whole bounding box.
What is the result in your case?
While this added margin width is sensible if there is only a single quad, in case of your markup annotation which includes many quads on top of one another, this results in a giant, unnecessary margin.
How to improve the code?
As the added caps depend on the individual caps and not their combined bounding box, one can improve the code by using the maximum height of the individual quads instead of the height of the bounding box of all quads, e.g. like this:
Rectangle2D box = null;
double maxQuadHeight = 0;
for(Quad markupBox : value)
{
double quadHeight = markupBox.getBounds2D().getHeight();
if (quadHeight > maxQuadHeight)
maxQuadHeight = quadHeight;
...
}
...
double markupBoxMargin = getMarkupBoxMargin(maxQuadHeight);
box.setRect(box.getX() - markupBoxMargin, box.getY(), box.getWidth() + markupBoxMargin * 2, box.getHeight());
setBox(box);
If you don't want to patch PDF Clown for this, you can also execute this code (with minor adaptations) after constructing the TextMarkup _textMarkup to correct the precalculated annotation rectangle.
Is this fixing a PDF Clown error?
It is not an error as there is no need for the text markup annotation rectangle to be minimal; PDF Clown could also always use the whole crop box for each such annotation.
I would assume, though, that the author of the code wanted to calculate a somewhat minimal rectangle but only optimized for single line and so in a way did not live up to his own expectations...
Are there other problems in this code?
Yes. The text a markup annotation marks needs not be horizontal, it may be there at an angle, it could even be vertical. In such a case some margin would also be needed at the top and the bottom of the annotation rectangle, not (only) at the left and the right.
I have a function which I've been using for a good while in java which calculates cell widths for a pdftable from a given array of string values.
It works very well, and I recently wrote a version of the function in c# and it doesn't give the expected result (i.e text is wrapped to multiple lines) - both java and c# code shown below any help much appreciated
This is the Java version;
float[] CalculateCellWidths(String[] CellHeaders, Font CellFont)
{
float[] CellWidths = new float[CellHeaders.length];
for (int i = 0; i < CellHeaders.length; i++)
{
CellWidths[i] = CellFont.getCalculatedBaseFont(true).getWidthPoint(CellHeaders[i], CellFont.getCalculatedSize());
}
return CellWidths;
}
This is the C# version;
float[] CalculateCellWidths(String[] CellHeaders, iTextSharp.text.Font CellFont)
{
float[] CellWidths = new float[CellHeaders.Length];
for (int i = 0; i < CellHeaders.Length; i++)
{
CellWidths[i] = CellFont.GetCalculatedBaseFont(true).GetWidthPoint(CellHeaders[i], CellFont.CalculatedSize);
}
return CellWidths;
}
I see from your comment that the problem has been solved, but that you don't know why, so here's a small explanation about LockWidth.
There are two ways to define the width of a table. Either the table takes a percentage of the available width. This is a relative width that depends on page size and page margins. Historically, this percentage is 80% and the table is centered. You can change this width percentage with the setWidthPercentage() method. If you use a method to set the widths of the invidual columns, those widths will be treated as relative widths, for instance { 10, 10, 20 } means that you have a table with 3 columns where the first two columns take a quarter of the available width and the third column takes half.
You can also set the absolute width of a table. You can set the total width with the setTotalWidth() method. You can also define the absolute widths of the columns. However, these absolute widths are ignored in favor of the default width percentage as long as you don't "lock" the width. This is what happens if you use setLockedWidth(true) (Java) or table.LockedWidth = true (C#).
Based on your comment, I think the problem was caused by locking the width of the columns so that absolute values were used instead of relative values.
I want to label my hashmarks in my grid for my graph, however when I use even font size 1 it is way to big! Is there a way to make a font size smaller than 1? Am I missing something with how I'm coding it?
Here's the code which generates the grid and attempts to put a label on the hash.
for (double k = myStart1; k <= myEnd1; k = k + (myEnd1 - myStart1) / 8) {
g2.setColor(Color.BLACK);
g2.draw(new Line2D.Double(k, (max - min) / 60, k, -(max - min) / 60));
String labelx=String.valueOf(k);
Float xCo=Float.parseFloat(Double.toString(k));
g2.setFont(new Font("SansSerif",Font.PLAIN,1));
g2.drawString(labelx, xCo, 0);
}
Here's a screenshot of the graph produced by x^2.
As I'm sure you've already noted, the Font constructor takes an int for the size parameter- effectively rendering impossible the construction of a font (using this method, at least) which has a size between 0 and 1.
I did, however, find the deriveFont method of the Font class particularly interesting:
public Font deriveFont(float size)
Creates a new Font object by replicating the current Font object and applying a new size to it.
Parameters:
size - the size for the new Font.
The deriveFont method, which claims to construct a new Font with the given size, takes a float as the parameter- therefore, it might be possible to do something like this:
Font theFont = new Font("SansSerif",Font.PLAIN,1);
theFont = theFont.deriveFont(0.5);
g2.setFont(theFont);
Resulting in a font with a size of 0.5.
Now, I haven't tested this myself- setting up a Graphics program takes time, so you're in a much better position to try it out than me. But just throwing it out there as a possibility.
I've large string, i want to split it. i got screen width and height using below code,
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenHeight = metrics.heightPixels;
screenWidth = metrics.widthPixels;
I want to know how many character to display on screen.
how to calculate ? and split the string.?
On a Swing / AWT Java platform, you could use a FontMetrics object to measure the width of the particular characters you are trying to display.
References:
How to calculate the font's width?
But it would probably be simpler to use something that can take care of the character rendering and wrapping for you.
On the Android platform, the Paint class has a number of methods that will help you do this kind of thing.
i think if you set width property to wrapcontent than string automatically split and display on next line.
use this property
android:layout_width="wrap_content"
android:layout_height="wrap_content"
use the substring method
String substring(int startIndex, int endIndex)
split the strings per line and store them in an array of Strings
String originalString=" ....some long text " ;
String stringsByLine[]= new String(screenHeight);
int i,j=0;
for ( i = 0; j<originalString.length;i++,j++){
stringsByLine[i]=originalString.substring(j,j+screenWidth);
j+=screenWidth;
}
havent tried it myself, but this logic should work. :-)
What is the recommended way of printing a text document as a pdf using absolute positioning ?
I am having a table that I have to print. I am also having the data type lengths and starting positions of the columns.
Since the existing table was a character based, there was no problem in its positioning. But even after using a monotype font (Courier, 10) I am not able to properly position the data and last column(s) of each row erroneously skip to the next line.
In order to present my data as close as the character one, I divided the page into different columns(based on its page size) and then add the contents at the desired place. I am adding chunks of data into the paragraph.
paragraph.add(new Chunk(new VerticalPositionMark(), columnNo*ptUnit, false));
I have tried to tweak the page size, font size and margin lengths, but the data is not properly displayed. Have you encountered any such problems ? please do share your thoughts.
Have you tried ColumnText
When i want to write a paragraph and I do know the amount of lines...I do a cycle incrementing (even it says incrementing and is minus is because the pdf is from "south" to "north" (0 - height) the y in a proportion of the fontsize, something like this
//_valueArray is my string[]
//fontSize is the value of the Size of the font...
//1.5 it's just a magic number :) that give me the space line that i need
//cbLocal is the PdfContentByte of the pdf
for (i = 0; i < _valueArray.Length; i++)
{
var p = new Phrase(_valueArray[i], font);
ColumnText.ShowTextAligned(cbLocal, align, p, x, y, 0);
if (i + 1 != _valueArray.Length)
{
y = y - (fontSize*1.5f);
}
}