I am trying to run Countrychunks example from the Chapter 2. The example works but the line: document.Add(Chunk.NEWLINE); does not produce the new line and the loop overwrites the first line. I am posting my code here in case I am doing anything wrong:
public void createCountryChunks(String fileName)
{
iTextSharp.text.Font font;
Document document = new iTextSharp.text.Document();
//PdfWriter.GetInstance(document, new FileStream(fileName)).setInitialLeading(16);
PdfWriter.GetInstance(document, new FileStream(fileName, FileMode.Create));
document.Open();
font = new iTextSharp.text.Font(iTextSharp.text.Font.FontFamily.HELVETICA, 6, iTextSharp.text.Font.BOLD, iTextSharp.text.BaseColor.WHITE);
foreach (var p in myProducts)
{
// add a country to the document as a Chunk
document.Add(new Chunk(p.pr_name));
document.Add(new Chunk(" "));
Chunk id = new Chunk(p.pr_URN.ToString(), font);
// with a background color
id.SetBackground(BaseColor.BLACK, 1f, 0.5f, 1f, 1.5f);
// and a text rise
id.SetTextRise(6);
document.Add(id);
document.Add(Chunk.NEWLINE);
}
document.Close();
}
As you can see the example is a bit different because of the data but the rest is almost the same as the original Java example.
Any suggestions please?
The setInitialLeading call that you weren't able to bring over and was commented out was actually very important. Adding that back in will solve your problems. I really don't like to add properties directly onto my constructed objects so I'm going to do it in two lines:
var w = PdfWriter.GetInstance(document, new FileStream(fileName, FileMode.Create));
w.InitialLeading = 16;
Related
Only last slide is getting convert means last slide overlapping every slides. Can anyone suggest how to combine it in one PDF?
I have tried with different approach but they are first creating image and then PDF.
FileInputStream inputStream = new FileInputStream(in);
SlideShow ppt = new SlideShow(inputStream);
inputStream.close();
Dimension pgsize = ppt.getPageSize();
Document document = new Document();
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(out));
document.setPageSize(new Rectangle(
(float)pgsize.getWidth(), (float)pgsize.getHeight()));
document.open();
PdfGraphics2D graphics= null;
for (int i = 0; i < ppt.getSlides().length; i++) {
Slide slide = ppt.getSlides()[i];
graphics = (PdfGraphics2D) pdfWriter.getDirectContent()
.createGraphics((float)pgsize.getWidth(), (float)pgsize.getHeight());
slide.draw(graphics);
}
graphics.dispose();
document.close();
I have this code to get field positions
PdfReader reader = new PdfReader(ssPdf);
var output = new MemoryStream();
var stamper = new PdfStamper(reader, output);
IList<AcroFields.FieldPosition> positions =stamper.AcroFields.GetFieldPositions(ssName);
and this to remove one position rect
if (positions.count>0)
positions.RemoveAt(1);
But then i dont have any method to set the field positions list to the stamper.
If i call the GetFieldPositions again it will return all the fields and not without the one i deleted
IList<AcroFields.FieldPosition> positions2 = stamper.AcroFields.GetFieldPositions(ssName);
stamper.Close();
reader.Close();
Used itextpdf-5.5.9 and itext-xtra-5.5.9
I am trying to applying redaction on Partly text string but after applied redaction whole string is removed from document.Please find attached screenshot.
PdfReader reader = new PdfReader(src);
PdfCleanUpProcessor cleaner= null;
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(targetPdf));
stamper.setRotateContents(false);
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
Rectangle rectangle = new Rectangle(380, 640, 430, 665);
cleanUpLocations.add(new PdfCleanUpLocation(1, rectangle, BaseColor.BLACK));
cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
The OP clarified in comments that it is indeed his expectation that redaction only removes text which is completely contained in the redaction area; text, though, whose bounding box is even partially outside that area, is expected to remain.
This expectation may be unwise as far as security of redaction is concerned because this way text a casual redactor does not see anymore due to the colored redaction area may still remain in the PDF content available to text extraction or even simple copy&paste.
If in spite of such reservations one still wants to tweak PdfCleanup to work like expected by the OP, one essentially merely has to change the PdfCleanUpRegionFilter used by the PdfCleanUpProcessor: The filter implementation used by default rejects a glyph (and so marks it for removal) if its bounding box intersects the redaction area. To fulfill the OP's expectations, this behavior has to be replaced by a check whether the bounding box is completely contained in the redaction area.
This sounds simple. Unfortunately it is not as simple as it sounds because the clean-up code is not designed for easy replacement of the region filter implementation, many relevant objects or methods are private or at best package protected.
Thus, to achieve the OP's desired behavior I simply copied all of the classes from the com.itextpdf.text.pdf package into an own package, therein added a new filter class derived from my copy of PdfCleanUpRegionFilter with the different text rejection algorithm mentioned above, and then changed the copy of PdfCleanUpProcessor to use this other filter class:
/**
* In contrast to the base class {#link PdfCleanUpRegionFilter}, this filter
* only rejects text <b>completely</b> inside the redaction zone. The original
* also rejects text located merely <b>partially</b> inside the redaction zone.
*/
public class StrictPdfCleanUpRegionFilter extends PdfCleanUpRegionFilter
{
public StrictPdfCleanUpRegionFilter(List<Rectangle> rectangles)
{
super(rectangles);
this.rectangles = rectangles;
}
/**
* Checks if the text is COMPLETELY inside render filter region.
*/
#Override
public boolean allowText(TextRenderInfo renderInfo) {
LineSegment ascent = renderInfo.getAscentLine();
LineSegment descent = renderInfo.getDescentLine();
Point2D[] glyphRect = new Point2D[] {
new Point2D.Float(ascent.getStartPoint().get(0), ascent.getStartPoint().get(1)),
new Point2D.Float(ascent.getEndPoint().get(0), ascent.getEndPoint().get(1)),
new Point2D.Float(descent.getEndPoint().get(0), descent.getEndPoint().get(1)),
new Point2D.Float(descent.getStartPoint().get(0), descent.getStartPoint().get(1)),
};
for (Rectangle rectangle : rectangles)
{
boolean glyphInRectangle = true;
for (Point2D point2d : glyphRect)
{
glyphInRectangle &= rectangle.getLeft() <= point2d.getX();
glyphInRectangle &= point2d.getX() <= rectangle.getRight();
glyphInRectangle &= rectangle.getBottom() <= point2d.getY();
glyphInRectangle &= point2d.getY() <= rectangle.getTop();
}
if (glyphInRectangle)
return false;
}
return true;
}
List<Rectangle> rectangles;
}
(StrictPdfCleanUpRegionFilter)
public class StrictPdfCleanUpProcessor {
...
private PdfCleanUpRegionFilter createFilter(List<PdfCleanUpLocation> cleanUpLocations) {
List<Rectangle> regions = new ArrayList<Rectangle>(cleanUpLocations.size());
for (PdfCleanUpLocation location : cleanUpLocations) {
regions.add(location.getRegion());
}
return new StrictPdfCleanUpRegionFilter(regions);
}
...
}
(StrictPdfCleanUpProcessor, my copy of PdfCleanUpProcessor)
All classes can be found here.
It can be used just like the original cleanup implementation, one merely has to remember using the copied classes, not the original ones:
try ( InputStream resource = getClass().getResourceAsStream("Document.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "Document-redacted-strict.pdf")) )
{
PdfReader reader = new PdfReader(resource);
StrictPdfCleanUpProcessor cleaner= null;
PdfStamper stamper = new PdfStamper(reader, result);
stamper.setRotateContents(false);
List<mkl.testarea.itext5.pdfcleanup.PdfCleanUpLocation> cleanUpLocations = new ArrayList<>();
Rectangle rectangle = new Rectangle(380, 640, 430, 665);
cleanUpLocations.add(new mkl.testarea.itext5.pdfcleanup.PdfCleanUpLocation(1, rectangle, BaseColor.BLACK));
cleaner = new StrictPdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
(RedactText test method testRedactStrictForMayankPandey)
The example PDF provided by the OP
After redaction using the original classes
After redaction using the tweaked classes
A sanity check with the tweaked classes
To be sure the tweaked classes still removed any text at all, I enlarged the redaction area so that "heet", the last characters of "Document Submission Sheet", were completely contained in the redaction area:
try ( InputStream resource = getClass().getResourceAsStream("Document.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "Document-redacted-strict-large.pdf")) )
{
PdfReader reader = new PdfReader(resource);
StrictPdfCleanUpProcessor cleaner= null;
PdfStamper stamper = new PdfStamper(reader, result);
stamper.setRotateContents(false);
List<mkl.testarea.itext5.pdfcleanup.PdfCleanUpLocation> cleanUpLocations = new ArrayList<>();
Rectangle rectangle = new Rectangle(380, 640, 430, 680);
cleanUpLocations.add(new mkl.testarea.itext5.pdfcleanup.PdfCleanUpLocation(1, rectangle, BaseColor.BLACK));
cleaner = new StrictPdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
(RedactText test method testRedactStrictForMayankPandeyLarge)
And indeed, copy&paste (and other text extraction methods, too) now only render
"Document Submission S".
I want to add a PDF file using iTextSharp but if PDF file contains bookmarks then they should also be added.
Currently I'm using following code
Document document = new Document();
//Step 2: we create a writer that listens to the document
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(outputFileName, FileMode.Create));
writer.ViewerPreferences = PdfWriter.PageModeUseOutlines;
//Step 3: Open the document
document.Open();
PdfContentByte cb = writer.DirectContent;
//The current file path
string filename = "D:\\rtf\\2.pdf";
// we create a reader for the document
PdfReader reader = new PdfReader(filename);
//Chapter ch = new Chapter("", 1);
for (int pageNumber = 1; pageNumber < reader.NumberOfPages + 1; pageNumber++)
{
document.SetPageSize(reader.GetPageSizeWithRotation(1));
document.NewPage();
// Insert to Destination on the first page
if (pageNumber == 1)
{
Chunk fileRef = new Chunk(" ");
fileRef.SetLocalDestination(filename);
document.Add(fileRef);
}
PdfImportedPage page = writer.GetImportedPage(reader, pageNumber);
int rotation = reader.GetPageRotation(pageNumber);
if (rotation == 90 || rotation == 270)
{
cb.Add(page);
}
else
{
cb.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);
}
}
document.Close();
Please read Chapter 6 of my book. In table 6.1, you'll read:
Can import pages from other PDF documents. The major downside is that all interactive features of the imported page (annotations, bookmarks, fields, and so forth) are lost in the process.
This is exactly what you experience. However, if you look at the other classes listed in that table, you'll discover PdfStamper, PdfCopy, etc... which are classes that do preserve interactive features.
PdfStamper will keep the bookmarks. If you want to use PdfCopy (or PdfSmartCopy), you need to read chapter 7 to find out how to keep them. Chapter 7 isn't available for free, but you can consult the examples here: Java / C#. You need the ConcatenateBookmarks example.
Note that you're code currently looks convoluted because you're not using the correct classes. Using PdfStamper should significantly reduce the number of lines of code.
The code below illustrates a problem I have with iTextSharp. Everything works perfectly. The pdf file is created and appears correct on the screen. When I print the pdf from Adobe Reader X, it looks exactly right but Adobe reports "An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem."
Unfortunately, the file has to be attached to an email and sent to clients. The error message is not a good look and I want to fix it. It happens in all versions of Reader that I have tried, including 10.1.15 installed today.
I have iTextSharp 5.3.4.0 under Windows 7 Pro SP1
private void writeTestDoc()
{
string fname = "test.pdf";
float textWidth = 500;
float leftMgn = 60;
float rubricTop = 720;
float leftPad = 5;
float topPad = 12;
float leading = 0;
BaseFont baseCalibri = BaseFont.CreateFont("c:/windows/fonts/calibri.ttf", BaseFont.WINANSI, true);
Font plainFont = new Font(baseCalibri, 11, Font.NORMAL);
Document document = new Document();
try
{
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(fname, FileMode.Create));
document.Open();
PdfContentByte cb = writer.DirectContent;
cb.BeginText();
ColumnText ct = new ColumnText(cb);
float boxTop = rubricTop;
ct.SetSimpleColumn(leftMgn + leftPad, boxTop - topPad, leftMgn + textWidth, boxTop, leading, Element.ALIGN_CENTER);
ct.AddText(new Phrase("A test message", plainFont));
ct.Go();
cb.EndText();
document.Close();
}
catch (Exception ex)
{
writeFile("ERROR in writeTestDoc " + ex.Message);
}
}
Remove cb.BeginText(); and cb.EndText();. It's illegal for BT/ET text objects to be nested. Report the place where you've found the documentation that told you to use BeginText()/EndText in combination with ColumnText, so that we can ask the author to correct it from his or her documentation.