Is there any way to vertically-align iText (iTextSharp) text using ColumnText? - itext

Is there any way to vertically-align text using ColumnText? I am using iTextSharp
Chunk c = new Chunk(text, this.detailFont);
Phrase myText = new Phrase(c);
ct.SetSimpleColumn(myText, llx, lly, urx, ury, lineheight, Element.ALIGN_BOTTOM);
ct.Go();
My text appears at the top of the box I specify. I would like it to align with the bottom of the box.
as it is as it should be
|---------------| |---------------|
| some text | ---> | |
| | | some text |
|---------------| |---------------|

The answer of birwin is just not working. The ColumnText alignment is accept only horizontal alignments. And this is working till ColumnText text is in Text mode. When you call the ColumnText.AddElement the ColumnText switches to CompositMode and the Alignment has no effect.
If you really want to Vertical Alignment in ColumnText then you have to create a content which has aligned content and must add to ColumnText. Only the Table and Cell the correct object where vertical alignment has any effect.
So first we have to create a table, with one column and one cell. And set the size of the cell to same as column text size.
And second, this table must be added to ColumntText
Here is my code:
// create doc
var pdfDoc = new Document( PageSize.A4 );
var pdfWriter = PdfWriter.GetInstance( pdfDoc, new FileStream( Path.Combine( Path.GetTempPath(), "test.pdf" ), FileMode.Create ) );
pdfDoc.Open();
var canvas = pdfWriter.DirectContent;
canvas.SaveState();
canvas.Rectangle( 100, 100, 100, 100 );
canvas.SetRgbColorFill( 255, 128, 128 );
canvas.Fill();
canvas.RestoreState();
// create font
BaseFont bfTimes = BaseFont.CreateFont( BaseFont.TIMES_ROMAN, BaseFont.CP1252, false );
Font times = new Font( bfTimes, (float)10, Font.ITALIC, new BaseColor( (int)232434 ) );
Phrase myText = new Phrase( new Chunk( "Hello World", times ) );
ColumnText ct = new ColumnText( canvas );
/* wrong birwin code :
ct.SetSimpleColumn( myText, 100, 100, 200, 200, ct.Leading, Element.ALIGN_BOTTOM );
*/
// new working code: crreate a table
PdfPTable table = new PdfPTable( 1 );
table.SetWidths( new[] { 1 } );
table.WidthPercentage = 100;
table.AddCell( new PdfPCell( myText )
{
HorizontalAlignment = Element.ALIGN_RIGHT,
VerticalAlignment = Element.ALIGN_BOTTOM,
FixedHeight = 100
} );
ct.SetSimpleColumn( 100, 100, 200, 200 );
ct.AddElement( table );
ct.Go();
pdfDoc.Close();

The Solution:
Chunk c = new Chunk(text, this.detailFont);
Phrase myText = new Phrase(c);
ct.SetSimpleColumn(myText, llx, lly, urx, ury, lineheight, Element.ALIGN_BOTTOM);
ct.Go(true);
if (ct.LinesWritten == 1)
{
ury = ury - (this.rowheight);
}
ct.SetSimpleColumn(myText, llx, lly, urx, ury, lineheight, Element.ALIGN_BOTTOM);
ct.Go();
ct.Go(true) -- Apparently the "true" here indicates the ColumnText is created in "Simulation" mode. I was not aware of this mode before comments to my question were posted.
if (ct.LinesWritten == 1) checks the number of lines that were written. If only one line was written, I shrink the size of the upper-right-y corner of my rectangle.
ct.SetSimpleColumn(myText, llx, lly, urx, ury, lineheight, Element.ALIGN_BOTTOM); when I call this line the second time, the ury (upper-right-hand corner) location has been changed and the box is smaller.
ct.Go() - The second time I call this, I don't pass the "true" param so the second call is not simulation mode and writes the data to the document.

Related

How to add new lines on a splitted page

I'm using (in Java)
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
I'm printing a header, a logo, a picture and a table on each page. Follow is the code ( a little "pseudo" ):
Document document = new Document( PageSize.A4 );
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream( pdfFullPath ) );
writer.setPageEvent( new HeaderAndFooter() );
for ( some condition for each page ) {
document.add( theLogoInLeftTop );
document.add( new Paragraph("\n\n\n\n\n") ); <<<-- Will give room for the heder printed by the `setPageEvent`
Image thumb = Image.getInstance( theImage );
float scaler = ((document.getPageSize().getWidth() - document.leftMargin()
- document.rightMargin() ) / thumb.getWidth()) * 100;
thumb.scalePercent(scaler);
thumb.setBorder( Image.BOX );
thumb.setBorderWidth(1);
document.add( thumb );
PdfPTable table = new PdfPTable( new float[] { 1, 3 } );
table.setTotalWidth( document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin() );
table.setLockedWidth(true);
// Too much rows will cause a page break and will print
// the rows over the header.
for( some or few rows ) {
table.addCell( someLeftText );
table.addCell( someRightText );
}
document.add(table);
document.newPage();
}
document.close();
writer.close();
When I have few rows all is fine and all content is printed in one page. But when I have a lot of rows a new page is started ( not my document.newPage() ) and the rows contents are printed over my header and logo ( they don't respect the document.add( new Paragraph("\n\n\n\n\n") ); at begining of each page ).
How can I do a document.add( new Paragraph("\n\n\n\n\n") ); when a new page is started by "content overflow" so I can save the header space ?
The logo is not being printed too.
This is my header creator (Just to illustrate):
public class HeaderAndFooter extends PdfPageEventHelper {
#Override
public void onEndPage(PdfWriter writer, Document document) {
// Printing the headers using
// ColumnText.showTextAligned
}
}
Solved:
After removing all the \n's I added the new margins at the document creation.
No need of document.setMargins()
float left = 30;
float right = 30;
float top = 85;
float bottom = 20;
Document document = new Document( PageSize.A4, left, right, top, bottom );

positioning text using itextSharp in C3

I am trying to generate a pdf on button click. I am having challenge to design the page. If anybody can help on, how to position the text in particular position.
Say I want my address to in the top right corner and heading in the center.
Here is the code I am working on:
private void pdf_btn_Click(object sender, EventArgs e)
{
SaveFileDialog svg = new SaveFileDialog();
svg.ShowDialog();
using (FileStream stream = new FileStream(svg.FileName + ".pdf", FileMode.Create))
{
// Create a Document object
var document = new Document(PageSize.A4, 50, 50, 25, 25);
// Create a new PdfWrite object, writing the output to a MemoryStream
// var output = new MemoryStream();
var writer = PdfWriter.GetInstance(document, stream);
document.Open();
// First, create our fonts... (For more on working w/fonts in iTextSharp, see: http://www.mikesdotnetting.com/Article/81/iTextSharp-Working-with-Fonts
var titleFont = FontFactory.GetFont("Arial", 18, Convert.ToInt32(Font.Bold));
var AddressFont = FontFactory.GetFont("Arial", 7, Convert.ToInt32(Font.Bold));
var boldTableFont = FontFactory.GetFont("Arial", 12, Convert.ToInt32(Font.Bold));
var endingMessageFont = FontFactory.GetFont("Arial", 10, Convert.ToInt32(Font.Italic));
var bodyFont = FontFactory.GetFont("Arial", 12);
// Add the "" title
document.Add(new Paragraph("Certificate of Analysis", titleFont));
// Add Address
var text = document.Add(new Paragraph("Abc Company", AddressFont));
document.Add(new Paragraph("Tel:(xx) xxx-xxxx, (xxx) xxx-xxx", AddressFont));
document.Add(new Paragraph("Fax: (xxx) xxx-xxx, , AddressFont));
document.Close();
stream.Close();
}
}
There are different ways to achieve what you want.
Currently, you are telling iText to do the layout. If you want to center the text "Certificate of Analysis", then you could change the alignment of the Paragraph like this:
Paragraph header = new Paragraph("Certificate of Analysis", titleFont);
header.Alignment = Element.ALIGN_CENTER;
document.Add(header);
If you want the text "Abc Company" to be right-aligned, you can change he alignment like this:
Paragraph company = new Paragraph("Abc Company", AddressFont);
company.Alignment = Element.ALIGN_RIGHT;
document.Add(company);
Of course: when we add company, it will start on a new line under header. Maybe you want the text to be on the same line.
In that case, why not use a PdfPTable?
PdfPTable myTable = new PdfPTable(3);
myTable.WidthPercentage = 100;
PdfPCell mydate = new PdfPCell(new Phrase("April 22, 2015", myfont));
mydate.Border = Rectangle.NO_BORDER;
myTable.AddCell(mydate);
PdfPCell header = new PdfPCell(new Phrase("Certificate of Analysis", titleFont));
header.Border = Rectangle.NO_BORDER;
header.HorizontalAlignment = Element.ALIGN_CENTER;
myTable.AddCell(header);
PdfPCell address = new PdfPCell(new Phrase("Company Address", AddressFont));
address.Border = Rectangle.NO_BORDER;
address.HorizontalAlignment = Element.ALIGN_RIGHT;
myTable.AddCell(address);
doc.Add(myTable);
Now the available width of your page will be divided in three, the date will be aligned to the left, the header will be centered, and the address will be aligned to the right. Obviously, you can add more data to a PdfPCell and more rows to the PdfPTable. This is just a simple proof of concept.
Another option, is to add the text at an absolute position (using coordinates). This can be done by using the ColumnText object. There are plenty of examples on this in The Best iText Questions on StackOverflow

IText: How to add an inner table surrounded by text to a table

I am trying to add a table surrounded by text to an outer table in iText 5.5.4, but the inner table disappears and I can't seem to fix the problem.
Here is what I am expecting:
*********************
* Hello World *
* +++++++++++++++++ * <--
* + Goodbye World + * <-- these 3 lines never show up in the PDF
* +++++++++++++++++ * <--
* Hello World *
*********************
Here is my code example:
public class TableTest {
public static void main(String[] args) throws FileNotFoundException, DocumentException {
final Document document = new Document(PageSize.LETTER, 21, 21, 30, 35);
PdfWriter.getInstance(document, new FileOutputStream("testTable.pdf"));
document.open();
// table 2
final PdfPTable table2 = new PdfPTable(1);
table2.setSpacingBefore(0);
table2.setHorizontalAlignment(Element.ALIGN_LEFT);
table2.getDefaultCell().setBorderColor(BaseColor.RED);
table2.getDefaultCell().setBorderWidth(1);
table2.addCell("Goodbye World");
// table 1
final PdfPTable table1 = new PdfPTable(1);
table1.setSpacingBefore(0);
table1.setHorizontalAlignment(Element.ALIGN_LEFT);
table1.setWidthPercentage(100);
table1.getDefaultCell().setBorderColor(BaseColor.BLACK);
table1.getDefaultCell().setBorderWidth(1);
// contents
Phrase phrase = new Phrase();
phrase.add(new Chunk("Hello World"));
phrase.add(table2); // <--- added but doesn't show up!
phrase.add(new Chunk("Hello World"));
table1.addCell(phrase);
document.add(table1);
document.close();
}
}
This is part of a bigger report, and I am using the tables in this scenario for border and padding.
You are using text mode (to be used when you only have text) in a situation where you should use composite mode (because you are adding a table to a cell).
Please take a look at the NestedTableProblem example:
// table 2
final PdfPTable table2 = new PdfPTable(1);
table2.setHorizontalAlignment(Element.ALIGN_LEFT);
table2.getDefaultCell().setBorderColor(BaseColor.RED);
table2.getDefaultCell().setBorderWidth(1);
table2.addCell("Goodbye World");
// table 1
final PdfPTable table1 = new PdfPTable(1);
table1.setHorizontalAlignment(Element.ALIGN_LEFT);
table1.setWidthPercentage(100);
// contents
PdfPCell cell = new PdfPCell();
cell.setBorderColor(BaseColor.BLACK);
cell.setBorderWidth(1);
cell.addElement(new Chunk("Hello World"));
cell.addElement(table2);
cell.addElement(new Chunk("Hello World"));
table1.addCell(cell);
document.add(table1);
In this code snippet, the cell object is composed of different elements (that's what composite mode is about):
In your code snippet, you add several elements to a Phrase and you add this Phrase to a PdfPCell in text mode. As one element isn't ordinary text but a table, it can not be rendered.

How do I change the weight of a simulated bold font using itext

I am using the iText library to generate text. I am loading the Arial Unicode MS font which does not contain a bold style so iText is simulating the bold. This works fine, but the weight of the bold font appears too heavy compared with text generated using the Java API or even using Microsoft Word.
I tried to get the weight from the FontDescriptor, but the value returned is always 0.0
float weight = font.getBaseFont().getFontDescriptor(BaseFont.FONT_WEIGHT, fontSize);
Is there a way I can change the weight of a simulated bold font?
As an addendum to #Chris' answer: You do not need to construct those Object[]s as there is a Chunk convenience method:
BaseFont arialUnicodeMs = BaseFont.createFont("c:\\Windows\\Fonts\\ARIALUNI.TTF", BaseFont.WINANSI, BaseFont.EMBEDDED);
Font arial12 = new Font(arialUnicodeMs, 12);
Paragraph p = new Paragraph();
for (int i = 1; i < 100; i++)
{
Chunk chunk = new Chunk(String.valueOf(i) + " ", arial12);
chunk.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, i/100f, null);
p.add(chunk);
}
document.add(p);
results in
EDIT
Sorry, I just realized after posting this that you're using iText but my answer is for iTextSharp. You should, however, be able to use most of the code below. I've updated the source code link to reference the appropriate Java source.
Bold simulation (faux bold) is done by drawing the text with a stroke. When iText is asked to draw bold text with a non-bold font it defaults to applying a stroke with a width of of the font's size divided by 30. You can see this in the current source code here. The magic part is setting the chunk's text rendering mode to a stroke of your choice:
//.Net code
myChunk.Attributes[Chunk.TEXTRENDERMODE] = new Object[] { PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, MAGIC_NUMBER_HERE, null };
//Java code
myChunk.attributes.put(Chunk.TEXTRENDERMODE, new Object[]{Integer.valueOf(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE), MAGIC_NUMBER_HERE, null});
Knowing that you can just apply the same logic but using your weight preference. The sample below creates four chunks, the first normal, the second faux-bold, the third ultra-heavy faux-bold and the fourth ultra-lite faux-bold.
//.Net code below but should be fairly easy to convert to Java
//Path to our PDF
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
//Path to our font
var ff = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "ARIALUNI.TTF");
//Normal document setup, nothing special here
using (var fs = new FileStream(testFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
//Register our font
FontFactory.Register(ff, "Arial Unicode MS");
//Declare a size to use throughout the demo
var size = 20;
//Get a normal and a faux-bold version of the font
var f = FontFactory.GetFont("Arial Unicode MS", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, size, iTextSharp.text.Font.NORMAL);
var fb = FontFactory.GetFont("Arial Unicode MS", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, size, iTextSharp.text.Font.BOLD);
//Create a normal chunk
var cNormal = new Chunk("Hello ", f);
//Create a faux-bold chunk
var cFauxBold = new Chunk("Hello ", fb);
//Create an ultra heavy faux-bold
var cHeavy = new Chunk("Hello ", f);
cHeavy.Attributes = new Dictionary<string, object>();
cHeavy.Attributes[Chunk.TEXTRENDERMODE] = new Object[] { PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, size / 10f, null };
//Create a lite faux-bold
var cLite = new Chunk("Hello ", f);
cLite.Attributes = new Dictionary<string, object>();
cLite.Attributes[Chunk.TEXTRENDERMODE] = new Object[] { PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, size / 50f, null };
//Add to document
var p = new Paragraph();
p.Add(cNormal);
p.Add(cFauxBold);
p.Add(cHeavy);
p.Add(cLite);
doc.Add(p);
doc.Close();
}
}
}

iText - Strange column- / page changing behaviour with ColumnText

I am quite new to iText and trying to accomplish the following:
read a list of text files from local hd
arrange the texts of the files in a 2-column layout pdf file
add a consecutively numbered index before each text
I started with the MovieColumns1 example (http://itextpdf.com/examples/iia.php?id=64) and ended up with the following code:
final float[][] COLUMNS_COORDS = { { 36, 36, 296, 806 }, { 299, 36, 559, 806 } };
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, resultFile);
document.open();
ColumnText ct = new ColumnText(writer.getDirectContent());
ct.setSimpleColumn(COLUMNS_COORDS[0][0], COLUMNS_COORDS[0][1],
COLUMNS_COORDS[0][2], COLUMNS_COORDS[0][3]);
File textDir = new File("c:/Users/raddatz/Desktop/123/texts/");
File[] files = textDir.listFiles();
int i = 1;
int column = 0;
for (File file : files) {
String text = FileUtils.readFileToString(file, "UTF-8");
float yLine = ct.getYLine();
System.out.println("adding '" + file.getName() + "'");
PdfPCell theText = new PdfPCell(new Phrase(text, new Font(Font.HELVETICA, 10)));
theText.setBorder(Rectangle.NO_BORDER);
theText.setPaddingBottom(10);
PdfPCell runningNumber = new PdfPCell(new Phrase(new DecimalFormat("00").format(i++), new Font(
Font.HELVETICA, 14, Font.BOLDITALIC,
new Color(0.7f, 0.7f, 0.7f))));
runningNumber.setBorder(Rectangle.NO_BORDER);
runningNumber.setPaddingBottom(10);
PdfPTable table = new PdfPTable(2);
table.setWidths(new int[] { 12, 100 });
table.addCell(runningNumber);
table.addCell(theText);
ct.addElement(table);
int status = ct.go(true);
if (ColumnText.hasMoreText(status)) {
column = Math.abs(column - 1);
if (column == 0) {
document.newPage();
System.out.println("inserting new page with size :" + document.getPageSize());
}
ct.setSimpleColumn(
COLUMNS_COORDS[column][0], COLUMNS_COORDS[column][1],
COLUMNS_COORDS[column][2], COLUMNS_COORDS[column][3]);
yLine = COLUMNS_COORDS[column][3];
System.out.println("correcting yLine to: " + yLine);
} else {
ct.addElement(table);
}
ct.setYLine(yLine);
System.out.println("before adding: " + ct.getYLine());
status = ct.go(false);
System.out.println("after adding: " + ct.getYLine());
System.out.println("--------------------------------");
}
document.close();
Here you can see the result:
http://d.pr/f/NEmx
Looking at the first page of the resulting PDF I assumed everything was working out fine.
But on second page you can see the problem(s):
text #31 is not displayed completely (first line + index is cut / not in visible area)
text #46 is not displayed completely (first three lines + index is cut / not in visible area)
On page 3 everything seems to be ok again. I am really lost here.
-- UPDATE (2013-03-14) --
I have analyzed the contents of the PDF now. The problem is not that content is show in non-visible areas but that the content is not present in the pdf at all. The missing part of the content is exactly the one which would have fit in the previous column / page. So it seems like ColumnText.go(true) is manipulating the object passed by addElement() before. Can someone confirm this? If so: what can I do about it?
-- end UPDATE (2013-03-14) --
Looking forward to your reply
regards,
sven
Solved! As soon as ColumnText indicates a table will not fit the current column I reinitialize ct with a new instance of ColumnText and add the table again.
In other words:
Each instance of ColumnText is exactly dealing one column of my document.
if (ColumnText.hasMoreText(status) || mediaPoolSwitch) {
column = Math.abs(column - 1);
if (mediaPoolSwitch) {
currentMediaPool = mediapool;
column = 0;
}
if (column == 0) {
document.newPage();
writeTitle(writer.getDirectContent(), mediapool.getName());
}
ct = new ColumnText(writer.getDirectContent());
ct.addElement(table);
ct.setSimpleColumn(
COLUMNS_COORDS[column][0], COLUMNS_COORDS[column][1],
COLUMNS_COORDS[column][2], COLUMNS_COORDS[column][3]);
yLine = COLUMNS_COORDS[column][3];
LOG.debug("correcting yLine to: " + yLine);
} else {
ct.addElement(table);
}
ct.setYLine(yLine);
ct.go();