I am creating a PDF using iTextSharp 5.4.5 in .NET 4.0 like this:
class Program
{
static void Main(string[] args)
{
string html="<span style='transform: rotate(-90deg)'>Some Text</span>";
byte[] file=PDFGenerator.GeneratePDF(html);
string filename=#"C:\Users\myaccount\Desktop\myfile.pdf";
var v=System.IO.File.Create(filename);
v.Write(file, 0, file.Length);
}
}
public class PDFGenerator
{
public static byte[] GeneratePDF(string html)
{
MemoryStream msOutput = new MemoryStream();
TextReader reader = new StringReader(html);
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
PdfWriter writer = PdfWriter.GetInstance(document, msOutput);
HTMLWorker worker = new HTMLWorker(document);
document.Open();
worker.StartDocument();
worker.Parse(reader);
worker.EndDocument();
worker.Close();
document.Close();
return msOutput.ToArray();
}
}
However, the text in the PDF is not coming out rotated at all. I need the text to be rotated vertically. Is there any CSS or HTML I can enter so that it will rotate?
HTMLWorker has been deprecated for a very long time in favor of XMLWorker and zero work is being done in it. The former doesn't really support CSS while the latter does, too. However, the transform CSS property isn't supported anyway so that doesn't help you. (See this for a list of supported properties.)
Do you have an absolute need for HTML parsing? If not, you can just use the ColumnText class to do what you want. PDFs origins are in the bottom left corner instead of the top left so might need to adjust your math. Also, rotations are in the opposite direction so your -90 becomes just 90.
ColumnText.ShowTextAligned(writer.DirectContent, Element.ALIGN_CENTER, new Phrase("Hello World"), 50, 50, 90);
Related
I'm using iTextSharp to draw markup graphics in PDF documents, using PdfAnnotation.CreateInk. I'm trying to draw rectangles, passing in an array of five co-ordinates. I know iTextSharp has a dedicated function for drawing rectangles, but I'm trying to use just one method to draw the various kinds of markup lines I need, and it seems that CeateInk should be able to do that.
The problem is that CreateInk is drawing the rectangle with curves rather than corners. I haven't been able to figure out how to change this. This answer suggests that the solution might be to create a PdfAppearance; I haven't yet figured out whether that will work. Here's my code (the first bit converts a list of list of points to an array of arrays, which InkList requires):
public void MarkupInk(List<List<float>> InkList){
float[][] Coords = new float[InkList.Count][];
for (int i = 0; i < InkList.Count; i++) {
float[] thisarr = InkList [i].ToArray ();
Coords[i] = new float[InkList[i].Count];
System.Array.Copy (thisarr, Coords [i], InkList [i].Count);
}
using(MemoryStream ms = new MemoryStream ()){
PdfReader reader = new PdfReader (textmaster.pdfDocArr);
PdfStamper stamper = new PdfStamper (reader, ms);
pagerect = reader.GetPageSize (textmaster.currentfirstpage + 1);
PdfAnnotation an2 = PdfAnnotation.CreateInk (stamper.Writer, pagerect, "", Coords);
an2.Color = strokeColor;
an2.BorderStyle = new PdfBorderDictionary (strokeWeight, PdfBorderDictionary.STYLE_SOLID);
stamper.AddAnnotation (an2, textmaster.currentfirstpage+1);
stamper.Close ();
textmaster.pdfDocArr = ms.ToArray ();
reader.Close ();
}
}
Any suggestions are much appreciated. Thanks!
EDIT: following #mkl's code I now have code that creates PDFAnnotations with appearances. And most of those annotations show up correctly in the viewing applications I use. But there is one odd behavior that I have not been able to fix.
What's happening is that the most recently created annotation does not appear in the viewing applications until I've created another annotation. So if I create annotation A, it's invisible until I create annotation B, at which point annotation A appears and B does not. Creating annotation C causes annotation B to appear, and so on.
This behavior persists even if I close the pdf file and the viewing application and re-load from disk. So the data describing the last-created annotation exists as part of the pdf file, but it doesn't render until I've created a new annotation.
I suspect there's something I'm still missing in the code I'm using to create the annotations and pdfAppearances. Here's code that creates a single line annotation:
public void WriteLineAnnotation(List<float> polyCoords){
using (MemoryStream ms = new MemoryStream ()) {
PdfReader reader = new PdfReader (textmaster.pdfDocArr);
PdfStamper stamper = new PdfStamper (reader, ms) { AnnotationFlattening = true };
pagerect = reader.GetPageSize (textmaster.currentfirstpage + 1);
//Create the pdfAnnotation polyline
PdfAnnotation polyann = PdfAnnotation.CreatePolygonPolyline (stamper.Writer, pagerect, "", false, new PdfArray (polyCoords.ToArray ()));
polyann.Color = strokeColor;
polyann.BorderStyle = new PdfBorderDictionary (strokeWeight, PdfBorderDictionary.STYLE_SOLID);
polyann.Flags = iTextSharp.text.pdf.PdfAnnotation.FLAGS_PRINT;
//create the PdfAppearance and set attributes
PdfAppearance app = PdfAppearance.CreateAppearance (stamper.Writer, pagerect.Width, pagerect.Height);
app.SetColorStroke (strokeColor);
app.MoveTo (polyCoords [0], polyCoords [1]);
app.LineTo (polyCoords [2], polyCoords [3]);
app.Stroke ();
//set the annotation's appearance, add annotation, clean up
polyann.SetAppearance (PdfName.N, app);
stamper.AddAnnotation (polyann, textmaster.currentfirstpage + 1);
stamper.Close ();
reader.Close ();
//create bytearray from memorystream and send to pdf renderer
textmaster.pdfDocArr = ms.ToArray ();
}
}
[/code]
Is there something obvious that I'm missing? Thanks in advance for any help.
Which annotation type to use
I know iTextSharp has a dedicated function for drawing rectangles, but I'm trying to use just one method to draw the various kinds of markup lines I need, and it seems that CeateInk should be able to do that.
Please be aware that iText not merely has separate functions for those different forms, these different functions also create different types of PDF annotations.
In particular the Ink annotation -- which you would like to use for all forms -- is specified as
An ink annotation (PDF 1.3) represents a freehand “scribble” composed of one or more disjoint paths.
(Section 12.5.6.13 - Ink Annotations - ISO 32000-1)
As a freehand “scribble” commonly is not considered to be a sequence of straight lines and sharp corners but instead more soft and rounded; thus, it is only natural that PDF viewers will display an ink annotation given by coordinates of the corners of a rectangle with curves rather than corners.
Of course you can use an appearance stream to provide a visualization of the appearance but that would be a small misuse of this PDF feature.
Instead I would propose you use a different kind of annotation to draw the various kinds of markup lines you need: Polyline annotations. They are specified as:
Polygon annotations (PDF 1.5) display closed polygons on the page. Such polygons may have any number of vertices connected by straight lines. Polyline annotations (PDF 1.5) are similar to polygons, except that the first and last vertex are not implicitly connected.
(Section 12.5.6.9 - Polygon and Polyline Annotations - ISO 32000-1)
iText(Sharp) provides a method for this kind of annotations, too:
/**
* Creates a polygon or -line annotation
* #param writer the PdfWriter
* #param rect the annotation position
* #param contents the textual content of the annotation
* #param polygon if true, the we're creating a polygon annotation, if false, a polyline
* #param vertices an array with the vertices of the polygon or -line
* #since 5.0.2
*/
public static PdfAnnotation CreatePolygonPolyline(
PdfWriter writer, Rectangle rect, String contents, bool polygon, PdfArray vertices)
You might eventually still have to create annotations as not all PDF viewers, in particular so called "pre-viewers", generate appearances but instead count on appearances being provided in PDFs they pre-display...
Examples
Without own appearance
using (PdfReader pdfReader = new PdfReader(inputPath))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream))
{
PdfArray vertices = new PdfArray(new int[]{ 100, 100, 300, 300, 100, 300, 300, 100 });
PdfAnnotation polyLine = PdfAnnotation.CreatePolygonPolyline(pdfStamper.Writer, pdfReader.GetPageSize(1),
"", false, vertices);
polyLine.Color = BaseColor.GREEN;
pdfStamper.AddAnnotation(polyLine, 1);
}
adds this:
in PDF viewers supporting annotations including appearance generation according to the specification.
With own appearance
using (PdfReader pdfReader = new PdfReader(inputPath))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream))
{
Rectangle pageSize = pdfReader.GetPageSize(1);
PdfArray vertices = new PdfArray(new int[] { 100, 100, 300, 300, 100, 300, 300, 100 });
PdfAnnotation polyLine = PdfAnnotation.CreatePolygonPolyline(pdfStamper.Writer, pageSize,
"", false, vertices);
polyLine.Color = BaseColor.GREEN;
PdfAppearance appearance = PdfAppearance.CreateAppearance(pdfStamper.Writer, pageSize.Width, pageSize.Height);
appearance.SetColorStroke(BaseColor.RED);
appearance.MoveTo(100, 100);
appearance.LineTo(300, 300);
appearance.LineTo(100, 300);
appearance.LineTo(300, 100);
appearance.Stroke();
polyLine.SetAppearance(PdfName.N, appearance);
pdfStamper.AddAnnotation(polyLine, 1);
}
adds this
in PDF viewers supporting annotations which bring along their annotation according to the specification.
(I explicitly used a different color in my appearance to make sure the PDF viewer shows my appearance and does not create one itself.)
There is an existing table/image. When I write text using pdfcontentbyte, it is written behind this table/image.
I also want to write the text from the right side of this table/column.
The code I'm currently using to produce the image above:
// open the reader
PdfReader reader = new PdfReader(oldFile);
iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
Document document = new Document(size);
// open the writer
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
PdfContentByte cb = writer.DirectContent;
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.EMBEDDED);
string text = "WV0501";
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(2, text, 155, 655, 0);
cb.EndText();
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
document.Close();
fs.Close();
writer.Close();
You don't show us your code, so we have to guess what you are doing wrong.
The content isn't showing:
You probably have this line in your code:
PdfContentByte canvas = pdfStamper.GetUnderContent(page);
If so, you should replace it with this line:
PdfContentByte canvas = pdfStamper.GetOverContent(page);
Update after you showed us your code:
You want to add content to an existing document, yet you are using a combination of Document and PdfWriter. Why are you doing this? Please read chapter 6 of my book where you'll learn about PdfStamper.
Right now, you are adding the text first and then you are covering it with a page from an existing document. This means that the existing page will cover the text.
You can switch this around like this:
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.EMBEDDED);
string text = "WV0501";
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(2, text, 155, 655, 0);
cb.EndText();
Now the text will cover the page, but that doesn't mean your code is better. You should really use PdfStamper instead of PdfWriter:
PdfReader reader = new PdfReader(oldFile);
FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
PdfStamper stamper = new PdfStamper(reader, fs);
PdfContentByte canvas = stamper.GetOverContent(1);
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
ColumnText.ShowTextAligned(
canvas,
Element.ALIGN_RIGHT,
new Phrase("WV0501", new Font(bf, 9)),
155, 655, 0
);
stamper.Close();
Don't you agree that this is more elegant?
IMPORTANT:
In your code you use:
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.EMBEDDED);
However, that doesn't make much sense: COURIER_BOLD is one of the Standard Type 1 fonts and because of that the embedded parameter is ignored. I changed this into NOT_EMBEDDED because if you use EMBEDDED developers who read your code and who don't know anything about PDF and iText may get confused. They might ask: Why is the font not getting embedded when the parameter says it should be embedded?
BaseFont bf = BaseFont.CreateFont(BaseFont.COURIER_BOLD,BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
Writing from the right side:
You are defining the alignment using a number: 2. I suggest that you don't use a number in your code, but a constant: ALIGN_RIGHT. This way, we see that you want to right align the text.
The text is right aligned with respect to the coordinates you've defined:
x = 155
y = 655
If you are not happy with the position of your text, you should change these hard-coded coordinates. For instance: increase x and decrease y.
You probably want the text to be relative to the border of a table cell or an image. If that is the case, you should not hard-code the coordinates.
Retrieving the coordinates of an image is discussed in another question on SO. Retrieving the coordinates of a table might very well be impossible. It all depends on the nature of the original PDF.
is there any way to combine two b5 size pdf into single legal size pdf. after googling i did not find any solution. how to approach this problem. what i can use to do it. i am developing c# desktop application which will combine two b5 size pdf to single legal page. one left size and another is right side
e.g. input
1. b5first.pdf
1234
2. b5second
567
Output should be
3. legal.pdf
1234 567
You can get the text from the second pdf and put it in first, in the place you want to put.
If you are using iTextsharp you can do something like
String text += PdfTextExtractor.GetTextFromPage(reader, pageno ,new LocationTextExtractionStrategy());
Then can put the string wherever you want.
If you are looking for open-source solution then check this MetafileToEPSConverter to convert metafile into EPS and then convert EPS into PDF using epstopdf (included into LyX) or similar tools.
public static void somefunction(string oldFile,string oldFile1,string pathout)
{
// open the reader
PdfReader reader = new PdfReader(oldFile);
PdfReader reader1 = new PdfReader(oldFile1);
Document document = new Document(PageSize.LEGAL.Rotate());
// open the writer
FileStream fs = new FileStream(pathout, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
// the pdf content
PdfContentByte cb = writer.DirectContent;
// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
PdfImportedPage page1 = writer.GetImportedPage(reader1, 1);
cb.AddTemplate(page, 0, 0);
cb.AddTemplate(page1, 500, 0);
// close the streams and voilá the file should be changed :)
document.Close();
fs.Close();
writer.Close();
reader.Close();
reader1.Close();
}
I want to add variable text and thumbnail images on specific position.
Text with formatting like fontstyles,rotation etc.
iTextSharp.text.pdf.PdfReader reader = null;
Document document = new Document();
PdfWriter writer;
writer = PdfWriter.GetInstance(document,
new FileStream(temp,
FileMode.Create, FileAccess.Write));
reader = new iTextSharp.text.pdf.PdfReader(file);
size = reader.GetCropBox(1);
PdfContentByte cb = writer.DirectContent;
document.NewPage();
PdfImportedPage page = writer.GetImportedPage(reader,
pageNumber);
cb.AddTemplate(page, 0, 0);
document.Close();
This will copy first page to new document. I want to add text and images on new document. how to do that?
Many people will stop reading your question after your first sentence:
I want to Edit existing PDF
If you read the intro of chapter 6 of my book, you'll understand that the word Edit is not the right word to use.
I did read on, and that I saw that you were using Document and PdfWriter to stamp new content on an existing PDF. If you do the effort of reading chapter 6 of my book, you'll understand that this is wrong. You should use PdfStamper instead.
This is one of the examples provided online:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Phrase header = new Phrase("Copy", new Font(FontFamily.HELVETICA, 14));
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
float x = reader.getPageSize(i).getWidth() / 2;
float y = reader.getPageSize(i).getTop(20);
ColumnText.showTextAligned(
stamper.getOverContent(i), Element.ALIGN_CENTER,
header, x, y, 0);
}
stamper.close();
reader.close();
}
You can find these examples on the following URLs:
http://itextpdf.com/sandbox/stamper
http://itextpdf.com/book/chapter.php?id=6
If you need the examples from chapter 6 ported to C#, please visit this URL: http://tinyurl.com/itextsharpIIA2C06
Update
Does PdfStamper support:
font embedding: yes, when done correctly, fonts are embedded,
font size: yes, that's obvious,
font family: you provide the font program, the font program knows the font family,
alignments: for single lines: use showTextAligned(); for text blocks use ColumnText (caveat: read about the difference between text mode and composite mode)
rotation: for single lines, this is only a matter of providing a rotation angle; for text blocks, create a Form XObject and add this object using a rotation,
...
Wonder if this possible. Saw many posts on adding watermark after the pdf is created and saved in disk. But during creation of document how do i add a image watermark. I know how to add a image to document. But how do i position it such that it comes at the end of page.
For C#, use this code...
//new Document
Document DOC = new Document();
// open Document
DOC.Open();
//create New FileStream with image "WM.JPG"
FileStream fs1 = new FileStream("WM.JPG", FileMode.Open);
iTextSharp.text.Image JPG = iTextSharp.text.Image.GetInstance(System.Drawing.Image.FromStream(fs1), ImageFormat.Jpeg);
//Scale image
JPG.ScalePercent(35f);
//Set position
JPG.SetAbsolutePosition(130f,240f);
//Close Stream
fs1.Close();
DOC.Add(JPG);
This is essentially identical to adding a header or footer.
You need to create a class that implements PdfPageEvent, and in the OnPageEnd, grab the page's PdfContentByte, and draw your image there. Use an absolute position.
Note: You probably want to derive from PdfPageEventHelper, it has empty implementations of all the page events, so you just need to write the method you actually care about.
Note: Unless your image is mostly transparent, drawing it on top of your page will cover up Many Things. IIRC ("If I Recall Correctly"), PNG and GIF files added by iText will automatically be properly masked, allowing things under them to show through.
If you want to add an opaque image underneath everything, you should override OnStartPage() instead.
This is Java, but converting it is mostly a matter of capitalizing method names and swapping get/set calls for property access.
Image watermarkImage = new Image(imgPath);
watermarkImage.setAbsolutePosition(x, y);
writer.setPageEvent( new MyPageEvent(watermarkImage) );
public MyPageEvent extends PdfPageEventHelper {
private Image waterMark;
public MyPageEvent(Image img) {
waterMark = img;
}
public void OnEndPage/*OnStartPage*/(PdfWriter writer, Document doc) {
PdfContentByte content = writer.getContent();
content.addImage( waterMark );
}
}
This is the accepted answer's port to C#, and what worked for me. I'm using an A4 page size:
Define this BackgroundImagePdfPageEvent class:
public class BackgroundImagePdfPageEvent : PdfPageEventHelper
{
private readonly Image watermark;
public BackgroundImagePdfPageEvent(string imagePath)
{
using (var fs = new FileStream(imagePath, FileMode.Open))
{
watermark = Image.GetInstance(System.Drawing.Image.FromStream(fs), ImageFormat.Jpeg);
watermark.SetAbsolutePosition(0, 0);
watermark.ScaleAbsolute(PageSize.A4.Width, PageSize.A4.Height);
watermark.Alignment = Image.UNDERLYING;
}
}
public override void OnStartPage(PdfWriter writer, Document document)
{
document.Add(watermark);
}
}
Then, when creating your document:
var doc = new Document(PageSize.A4);
doc.SetMargins(60f, 60f, 120f, 60f);
var outputStream = new MemoryStream();
var writer = PdfWriter.GetInstance(doc, outputStream);
var imagePath = "PATH_TO_YOUR_IMAGE";
writer.PageEvent = new BackgroundImagePdfPageEvent(imagePath);