I'm having trouble to set underline and overline by using PdfContentByte in iText. I want to set underline to all field in sectionArea == 1 || section Area == 3 as mentioned in getFontForFormat. So far i can only do bold style and i need it to be underlined and overlined too.
Here is the code:
public void doOutputField(Field field) {
String fieldAsString = field.toString();
BaseFont baseFont = getFontForFormat(field);
float fontSize = 11;
Point bottomLeft = bottomLeftOfField(field, 11, baseFont);
int align;
align = PdfContentByte.ALIGN_LEFT;
//PdfContentByte content
content.beginText();
content.setFontAndSize(baseFont, fontSize);
content.setColorFill(Color.BLACK);
double lineHeight = field.getOutputHeight();
content.showTextAligned(align, fieldAsString, (float) bottomLeft.x,
(float) bottomLeft.y, 0f);
bottomLeft.y -= lineHeight;
content.endText();
}
public BaseFont getFontForFormat(Field field) {
try {
if (field.getSection().getArea().getArea() == 1
|| field.getSection().getArea().getArea() == 3) {
BaseFont bf = BaseFont.createFont(BaseFont.TIMES_BOLD,
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
return bf;
} else {
BaseFont bf = BaseFont.createFont("Times-Roman",
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
return bf;
}
} catch (Exception e) {
}
return null;
}
Thanks in advance
Edit (Solved by Bruno Lowagie):
This problem can be solved by utilizing ColumnText.
if (field.getSection().getArea().getArea() == 1
|| field.getSection().getArea().getArea() == 3) {
Chunk chunk = new Chunk(fieldAsString);
chunk.setUnderline(+1f, -2f);
if (field.getSection().getArea().getArea() == 3) {
chunk.setUnderline(+1f, (float) field.getBoundHeight());
}
Font font = new Font();
font.setFamily("Times Roman");
font.setStyle(Font.BOLD);
font.setSize((float) 11);
chunk.setFont(font);
Paragraph p = new Paragraph();
p.add(chunk);
ColumnText ct = new ColumnText(content);
ct.setSimpleColumn(p, (float)bottomLeft.x, (float)bottomLeft.y,
(float)field.getBoundWidth() + (float)bottomLeft.x,
(float)field.getBoundHeight() + (float)bottomLeft.y,
(float)lineHeight, align);
try {
ct.go();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Thanks
You're making it yourself difficult by using PdfContentByte.showTextAligned(). Is there any reason why you don't want to use ColumnText?
With PdfContentByte, you have to handle the text state —beginText() and endText()—, the font —setFontAndSize()—, and you can only add String values. If you want to add lines (e.g. to underline), you need moveTo(), lineTo(), stroke() operations. These operators need coordinates, so you'll need to measure the size of the line using the BaseFont in combination with the String and the font size. There's some math involved.
If you use ColumnText, you have the option of adding one line at a time using ColumnText.showTextAligned(). Or you can define a column using setSimpleColumn() and let iText take care of distributing the text over different lines. In both cases, you don't have to worry about handling the text state, nor about the font and size. ColumnText accepts Phrase objects, and these objects consists of Chunk objects for which you can define underline and overline values. In this case, iText does all the math for you.
Related
I'm trying to generate a PDF report consisting of sentences in multiple languages. For that I'm using Google NOTO fonts, but google CJK fonts don't support some of the Latin special characters. For that reason, my PDFBox is failing to generate a report or sometimes shows weird characters.
Does anyone have any appropriate solution? I tried multiple things, but was unable to find a single TTF file that can support all Unicode. I also tried falling back to different font files, but that will be too much work.
Languages I support: Japanese, German, Spanish, Portuguese, English.
Note: I don't want to use arialuni.ttf file due to licensing issues.
Can anyone suggest anything?
Here is the code that will be in release 2.0.14 in the examples subproject:
/**
* Output a text without knowing which font is the right one. One use case is a worldwide
* address list. Only LTR languages are supported, RTL (e.g. Hebrew, Arabic) are not
* supported so they would appear in the wrong direction.
* Complex scripts (Thai, Arabic, some Indian languages) are also not supported, any output
* will look weird. There is an (unfinished) effort here:
* https://issues.apache.org/jira/browse/PDFBOX-4189
*
* #author Tilman Hausherr
*/
public class EmbeddedMultipleFonts
{
public static void main(String[] args) throws IOException
{
try (PDDocument document = new PDDocument())
{
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDFont font1 = PDType1Font.HELVETICA; // always have a simple font as first one
TrueTypeCollection ttc2 = new TrueTypeCollection(new File("c:/windows/fonts/batang.ttc"));
PDType0Font font2 = PDType0Font.load(document, ttc2.getFontByName("Batang"), true); // Korean
TrueTypeCollection ttc3 = new TrueTypeCollection(new File("c:/windows/fonts/mingliu.ttc"));
PDType0Font font3 = PDType0Font.load(document, ttc3.getFontByName("MingLiU"), true); // Chinese
PDType0Font font4 = PDType0Font.load(document, new File("c:/windows/fonts/mangal.ttf")); // Indian
PDType0Font font5 = PDType0Font.load(document, new File("c:/windows/fonts/ArialUni.ttf")); // Fallback
try (PDPageContentStream cs = new PDPageContentStream(document, page))
{
cs.beginText();
List<PDFont> fonts = new ArrayList<>();
fonts.add(font1);
fonts.add(font2);
fonts.add(font3);
fonts.add(font4);
fonts.add(font5);
cs.newLineAtOffset(20, 700);
showTextMultiple(cs, "abc 한국 中国 भारत 日本 abc", fonts, 20);
cs.endText();
}
document.save("example.pdf");
}
}
static void showTextMultiple(PDPageContentStream cs, String text, List<PDFont> fonts, float size)
throws IOException
{
try
{
// first try all at once
fonts.get(0).encode(text);
cs.setFont(fonts.get(0), size);
cs.showText(text);
return;
}
catch (IllegalArgumentException ex)
{
// do nothing
}
// now try separately
int i = 0;
while (i < text.length())
{
boolean found = false;
for (PDFont font : fonts)
{
try
{
String s = text.substring(i, i + 1);
font.encode(s);
// it works! Try more with this font
int j = i + 1;
for (; j < text.length(); ++j)
{
String s2 = text.substring(j, j + 1);
if (isWinAnsiEncoding(s2.codePointAt(0)) && font != fonts.get(0))
{
// Without this segment, the example would have a flaw:
// This code tries to keep the current font, so
// the second "abc" would appear in a different font
// than the first one, which would be weird.
// This segment assumes that the first font has WinAnsiEncoding.
// (all static PDType1Font Times / Helvetica / Courier fonts)
break;
}
try
{
font.encode(s2);
}
catch (IllegalArgumentException ex)
{
// it's over
break;
}
}
s = text.substring(i, j);
cs.setFont(font, size);
cs.showText(s);
i = j;
found = true;
break;
}
catch (IllegalArgumentException ex)
{
// didn't work, will try next font
}
}
if (!found)
{
throw new IllegalArgumentException("Could not show '" + text.substring(i, i + 1) +
"' with the fonts provided");
}
}
}
static boolean isWinAnsiEncoding(int unicode)
{
String name = GlyphList.getAdobeGlyphList().codePointToName(unicode);
if (".notdef".equals(name))
{
return false;
}
return WinAnsiEncoding.INSTANCE.contains(name);
}
}
Alternatives to arialuni can be found here:
https://en.wikipedia.org/wiki/Open-source_Unicode_typefaces
If I use font size appearance.setLayer2FontSize(6.0f); it sets font size for both Name and Description.
PdfReader reader = null;
PdfSigner signer = null;
try {
reader = new PdfReader(inStream);
signer = new PdfSigner(reader, pdfos, false);
} catch (IOException e) {
LOGGER.error("Error while loading PDF");
throw new DigitalSignException("Error while loading PDF", e);
}
int noOfPages = signer.getDocument().getNumberOfPages();
PdfSignatureAppearance appearance = signer.getSignatureAppearance().setReason(reason).setLocation(loc)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(250, 100, 200, 80);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
appearance.setLayer2FontSize(6.0f);
appearance.setPageRect(rect).setPageNumber(noOfPages);
signer.setFieldName("sign");
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, bouncyCastleProvider.getName());
IExternalDigest digest = new BouncyCastleDigest();
try {
signer.signDetached(digest, pks, chain, null, null, null, 0, subfilter);
} catch (IOException | GeneralSecurityException e) {
LOGGER.error("Error while adding digital signature to PDF");
throw new DigitalSignException("Error while adding digital signature to PDF", e);
}
Is there a way to set different font sizes for Name and description
(Name should be little bigger than description)
The whole Layer2Text is a single String, whether you set it or iText builds it, and it is typeset as a single paragraph using a single font and font size. Thus, NO, you cannot ask iText to draw your Layer2Text or its default text using multiple styles for different parts of it.
What you can do, though, is to retrieve the PdfFormXObject Layer2 before iText has created its appearance on it, and you can draw anything in any style on it.
So, instead of
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
appearance.setLayer2FontSize(6.0f);
appearance.setPageRect(rect).setPageNumber(noOfPages);
you'd do
appearance.setPageRect(rect).setPageNumber(noOfPages);
PdfFormXObject layer2 = getLayer2();
[...shape the layer2 contents as you desire...]
Of course you can use the source of the PdfSignatureAppearance method getAppearance for inspiration, in particular if you don't want your design to deviate much from the default.
Thus, YES, you can completely customize the signature appearance.
For example
An example customized layer2 content might be shaped like this:
PdfFormXObject layer2 = appearance.getLayer2();
PdfCanvas canvas = new PdfCanvas(layer2, signer.getDocument());
float MARGIN = 2;
PdfFont font = PdfFontFactory.createFont();
String name = null;
CertificateInfo.X500Name x500name = CertificateInfo.getSubjectFields((X509Certificate)chain[0]);
if (x500name != null) {
name = x500name.getField("CN");
if (name == null)
name = x500name.getField("E");
}
if (name == null)
name = "";
Rectangle dataRect = new Rectangle(rect.getWidth() / 2 + MARGIN / 2, MARGIN, rect.getWidth() / 2 - MARGIN, rect.getHeight() - 2 * MARGIN);
Rectangle signatureRect = new Rectangle(MARGIN, MARGIN, rect.getWidth() / 2 - 2 * MARGIN, rect.getHeight() - 2 * MARGIN);
try (Canvas layoutCanvas = new Canvas(canvas, signer.getDocument(), signatureRect);) {
Paragraph paragraph = new Paragraph(name).setFont(font).setMargin(0).setMultipliedLeading(0.9f).setFontSize(20);
layoutCanvas.add(paragraph);
}
try (Canvas layoutCanvas = new Canvas(canvas, signer.getDocument(), dataRect);) {
Paragraph paragraph = new Paragraph().setFont(font).setMargin(0).setMultipliedLeading(0.9f);
paragraph.add(new Text("Digitally signed by ").setFontSize(6));
paragraph.add(new Text(name + '\n').setFontSize(9));
paragraph.add(new Text("Date: " + new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z").format(signer.getSignDate().getTime()) + '\n').setFontSize(6));
paragraph.add(new Text("Reason: " + appearance.getReason() + '\n').setFontSize(6));
paragraph.add(new Text("Location: " + appearance.getLocation()).setFontSize(6));
layoutCanvas.add(paragraph);
}
This essentially is a copy&paste&refactoring of the iText code creating its default appearance with different font sizes for different text parts of it.
I'm using itext to generate editable Calendar pdf.
Im trying to add TextField to the PdfPCell using this code,
//To create PdfPCell for a specific day
public PdfPCell getDayCell(Calendar calendar, Locale locale) {
PdfPCell cell = new PdfPCell();
cell.setPadding(3);
// set the background color, based on the type of day
if (isSunday(calendar))
cell.setBackgroundColor(BaseColor.GRAY);
else if (isSpecialDay(calendar))
cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
else
cell.setBackgroundColor(BaseColor.WHITE);
// set the content in the language of the locale
Chunk chunk = new Chunk(String.format(locale, "%1$ta", calendar), small);
chunk.setTextRise(5);
// a paragraph with the day
Paragraph p = new Paragraph(chunk);
// a separator
p.add(new Chunk(new VerticalPositionMark()));
// and the number of the day
p.add(new Chunk(String.format(locale, "%1$te", calendar), normal));
cell.addElement(p);
cell.setCellEvent(new MyCellField(locale+""+calendar));
cell.setFixedHeight(80);
return cell;
}
// Adding TextField to the cellEvent
class MyCellField implements PdfPCellEvent {
protected String fieldname;
public MyCellField(String fieldname) {
this.fieldname = fieldname;
}
public void cellLayout(PdfPCell cell, Rectangle rectangle, PdfContentByte[] canvases) {
final PdfWriter writer = canvases[0].getPdfWriter();
final TextField textField = new TextField(writer, rectangle, fieldname);
textField.setAlignment(Element.ALIGN_TOP);
textField.setOptions(TextField.MULTILINE);
try {
final PdfFormField field = textField.getTextField();
writer.addAnnotation(field);
} catch (final IOException ioe) {
throw new ExceptionConverter(ioe);
} catch (final DocumentException de) {
throw new ExceptionConverter(de);
}
}
}
When I render the calendar pdf, the Cell focus is vertical not horizontal.
Kindly help me to find out what I'm missing.
NOTE: Please don't negative vote, I really want to figure out how to solve this. I referred other links like ITextSharp - text field in PdfPCell which where not helpful.
I tried adding
float textboxheight = 12f;
Rectangle rect = rectangle;
rect.Bottom = rect.Top - textboxheight;
rect.Bottom is showing error "The final field Rectangle.BOTTOM cannot be assigned".
Im using iText5
I think it's odd, because the behavior you describe isn't supposed to happen in the official iText version. (That makes me wonder: where did you get your version?)
However, you could try replacing this line:
Document document = new Document(PageSize.A4);
With this line:
Document document = new Document(new Rectangle(842, 595));
I have to make a PDF with a Table. So far it work fine, but now I want to add a wrapping feature. So I need to insert a Linefeed.
contentStream.beginText();
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString("Some text to insert into a table.");
contentStream.endText();
I want to add a "\n" before "insert". I tried "\u000A" which is the hex value for linefeed, but Eclipse shows me an error.
Is it possible to add linefeed with drawString?
The PDF format allows line breaks, but PDFBox has no build in feature for line breaks.
To use line breaks in PDF you have to define the leading you want to use with the TL-operator. The T*-operator makes a line break. The '-operator writes the given text into the next line. (See PDF-spec for more details, chapter "Text". It´s not that much.)
Here are two code snippets. Both do the same, but the first snippet uses ' and the second snippet uses T*.
private void printMultipleLines(
PDPageContentStream contentStream,
List<String> lines,
float x,
float y) throws IOException {
if (lines.size() == 0) {
return;
}
final int numberOfLines = lines.size();
final float fontHeight = getFontHeight();
contentStream.beginText();
contentStream.appendRawCommands(fontHeight + " TL\n");
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(lines.get(0));
for (int i = 1; i < numberOfLines; i++) {
contentStream.appendRawCommands(escapeString(lines.get(i)));
contentStream.appendRawCommands(" \'\n");
}
contentStream.endText();
}
private String escapeString(String text) throws IOException {
try {
COSString string = new COSString(text);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
string.writePDF(buffer);
return new String(buffer.toByteArray(), "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
// every JVM must know ISO-8859-1
throw new RuntimeException(e);
}
}
Use T* for line break:
private void printMultipleLines(
PDPageContentStream contentStream,
List<String> lines,
float x,
float y) throws IOException {
if (lines.size() == 0) {
return;
}
final int numberOfLines = lines.size();
final float fontHeight = getFontHeight();
contentStream.beginText();
contentStream.appendRawCommands(fontHeight + " TL\n");
contentStream.moveTextPositionByAmount( x, y);
for (int i = 0; i < numberOfLines; i++) {
contentStream.drawString(lines.get(i));
if (i < numberOfLines - 1) {
contentStream.appendRawCommands("T*\n");
}
}
contentStream.endText();
}
To get the height of the font you can use this command:
fontHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;
You might want to multiply it whit some line pitch factor.
The pdf format doesn't know line breaks. You have to split the string and move the text position to the next line, using moveTextPositionByAmount.
This is not a special "pdfbox-feature", it is due to the pdf format definition; so there is no way for drawString and there are also no other methods to be called that support linefeeds.
Because the PDF model often isn't the best model for the task at hand, it often makes sense to write a wrapper for it that adds support for whatever's "missing" in your case. This is true for both reading and writing.
Does anyone know an easy way to include the greater than or equal symbol in an iText PDF document without resorting to custom fonts?
You need to add the unicode for this character. Also make sure that the char is included in the font you use.
document.add( new Paragraph("\u2265"));
Use the below code for the same. It's work for me.
//symbol for greater or equal then
public void process() throws DocumentException, IOException {
String dest = "/home/ashok/ashok/tmp/hello.pdf";
BaseFont bfont = BaseFont.createFont(
"/home/ashok/ashok/fonts/Cardo-Regular.ttf",
BaseFont.IDENTITY_H,
BaseFont.EMBEDDED);
Font font = new Font(bfont, 12);
try {
Document document=new Document();
PdfWriter.getInstance(document,new
FileOutputStream("/home/ashok/ashok/tmp/hello.pdf"));
document.open();
Chunk chunk = new Chunk("\u2265");
Paragraph p = new Paragraph("Mathematical Operators are ",font);
p.add(chunk);
document.add(p);
p = new Paragraph(" ",font);
chunk = new Chunk("\u2264");
p.add(chunk);
document.add(p);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}