Extracting portion of a PDF - pdf

I'm trying to extract a portion of a pdf (the coordinates of the section will always remain constant) using PDF Sharp. Then I will resize the portion to 4" x 6" for printing onto a sticky back label. How would I extract the portion of the PDF? This is in a console application, C#.

There is no easy way to extract parts from a PDF file.
A possible workaround: create a new page in label size, then draw the existing page onto the new page so that the required rectangle is visible on the new page.
If needed, draw white rectangles to hide information that is not part of the section you need, but that is visible on the new page.

So here was how I managed to do this, not a perfect solution (you do loose some quality). This uses Spire.PDF, not PDF Sharp as I originally planned. I got fairly lucky in that the output size was nearly 4" X 6". So I just used shrink to fit on print options.
static void Main(string[] args)
{
ConvertPDFToBmp("FilePathOfPDF");
CropAtRect("FilePathOfConvertedImage");
ConvertToPDF("FilePathOfCroppedImage");
}
public static void ConvertPDFToBmp(string filePath)
{
PdfDocument document = new PdfDocument();
document.LoadFromFile(filePath);
Image emf = document.SaveAsImage(0, Spire.Pdf.Graphics.PdfImageType.Bitmap, 400, 400);
emf.Save("FilePath", ImageFormat.Jpeg);
}
public static void CropAtRect(string filePath)
{
Bitmap b = (Bitmap)Bitmap.FromFile(filePath);
Rectangle r = new Rectangle(new /*Where the rectangle starts*/Point(/*Width*/, /*Height*/), (new /*How big is the rectangle*/Size(/*Width*/, /*Height*/)));
Bitmap nb = new Bitmap(r.Width, r.Height);
nb.SetResolution(400, 400); //Scale to keep quality
Graphics g = Graphics.FromImage(nb);
g.DrawImage(b, -r.X, -r.Y);
nb.Save("FilePath", ImageFormat.Jpeg);
}
public static void ConvertToPDF(string filePath)
{
Bitmap b = (Bitmap)Bitmap.FromFile(filePath);
PdfDocument doc = new PdfDocument();
PdfImage pdfImage = PdfImage.FromImage(b);
PdfUnitConvertor uinit = new PdfUnitConvertor();
PdfPageBase page = doc.Pages.Add(new /*Size of PDF Page*/SizeF(585, 365), new PdfMargins(0f));
page.Canvas.DrawImage(pdfImage, new /*Where the image starts*/PointF(0, 0));
doc.SaveToFile("FilePath");
}

Related

Winforms Itext Ghost Script Rectangular coordinates selection

Using C# and Winforms, I want to display a PDF, select a rectangular region, and then extract that area of text from a number of PDFs. For displaying the PDF, I have a number of options...
Use an "Adobe PDF Reader" control to display the PDF - However, I cant use mouseover events and according to https://forums.adobe.com/thread/1640606 its just not possible to select a region.
Use a "WebBrowser" control to display the PDF, but it appears I have the same issue with mouseover events and cannot select a region.
Convert the PDF to an image (using ghostscript in my case) and displaying it in a picturebox. I'm finding the most success here, as I can now generate and record the coordinates of a rectangular region. When I take these coordinates and apply them to the PDF using Itext, I don't think my rectangular region translates correctly.
My question is, How do I render the GhostScripted image in a picture box maintaining the same ratios so that my coordinates will line up with the PDF?
Thank you in advance for the down votes!!
Here is the current state of my code... Everything works with the exception that my units are off in space somewhere. The action DOES return text, but it's never the text I selected. Im sure its a combination of the coordinate system / units and I will continue to try to understand this.
---- update
With a PDF at 0 deg rotation (portrait), I think the following holds true, or is at least working for me right now... User Units having not been changed, the coordinates taken from selecting in the picturebox need adjusting. The Y coordinates need to be subtracted from the overall height while the X coordinate remains the same.
iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(first.X, 3024-first.Y, last.X, 3024-last.Y);
This is picking text up exactly as expected on 0 deg rotated PDFs. On 90 deg rotated PDFs, the X and Y coordinates just need to be swapped.I am updating the code snippet below to show my working example.
using System;
using System.Drawing;
using System.Windows.Forms;
using Ghostscript.NET.Rasterizer;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
namespace formPdf
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileName; // The filename of the pdf
float width; // The width of the PDF in pixels
float hight; // the Height of the PDF in pixels
float rotation; // the Rotation of the PDF 0 or 90
float llx = 0; // The Lower Left X value for applying to the PDF
float lly = 0; // the Lower Left Y value for applying to the PDF
float urx = 0; // the Upper Right X value for applying to the PDF
float ury = 0; // the Upper Right Y value for applying to the PDF
// OnCLick event to open the file browser and select a file... The Width, Height and rotation values are set and the program
// is directed to render the First page of the pdf by calling the setPicture function
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
fileName = openFileDialog1.FileName;
PdfReader reader = new PdfReader(fileName);
iTextSharp.text.Rectangle dim = reader.GetPageSizeWithRotation(1);
width = dim.Width;
hight = dim.Height;
rotation = dim.Rotation;
setPicture(openFileDialog1.FileName);
} catch
{
// do nothing for now
}
}
}
// Using Ghostscript, the image is rendered to a picturebox. DPIs are set assuming the PDF default value is used
private void setPicture(string fileName)
{
GhostscriptRasterizer rasterizer = new GhostscriptRasterizer();
rasterizer.Open(fileName);
Image img = rasterizer.GetPage(72, 72, 1);
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
pictureBox1.Image = img;
}
// Declare point variables for the user defined rectangle indicating the locatoin of the PDF to be searched...
Point first = new Point();
Point last = new Point();
// The first point is collected on the MouseDown event
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
first = e.Location;
}
// The second point is collected on the mouse down event. Points to be applied to the PDF are adjusted based on the rotation of the PDF.
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
last = e.Location;
if (rotation == 0)
{
llx = first.X;
lly = hight - first.Y;
urx = last.X;
ury = hight - last.Y;
} else if(rotation == 90) {
llx = first.Y;
lly = first.X;
urx = last.Y;
ury = last.X;
}
gettext();
}
// the original PDF is opened with Itext and the text is extracted from t he defined location...
private void gettext()
{
PdfReader reader = new PdfReader(fileName);
iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(llx, lly, urx, ury);
RenderFilter[] renderfilter = new RenderFilter[1];
renderfilter[0] = new RegionTextRenderFilter(rect);
ITextExtractionStrategy textExtractionStrategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), renderfilter);
string text = PdfTextExtractor.GetTextFromPage(reader, 1, textExtractionStrategy);
iTextSharp.text.Rectangle mediabox = reader.GetPageSizeWithRotation(1);
MessageBox.Show("", text+" "+mediabox+" "+first+" "+last);
}
// Image Controls....
}
}

iTextSharp rotated PDF page reverts orientation when file is rasterized at print house

Using iTextSharp I am creating a PDF composed of a collection of existing PDFs, some of the included PDFs are landscape orientation and need to be rotated. So, I do the following:
private static void AdjustRotationIfNeeded(PdfImportedPage pdfImportedPage, PdfReader reader, int documentPage)
{
float width = pdfImportedPage.Width;
float height = pdfImportedPage.Height;
if (pdfImportedPage.Rotation != 0)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(0));
}
if (width > height)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(270));
}
}
This works great. The included PDFs rotated to portrait orientation if needed. The PDF prints correctly on my local printer.
This file is sent to a fulfillment house, and unfortunately, the landscape included files do not print properly when going through their printer and rasterization process. They use Kodak (Creo) NexRip 11.01 or Kodak (Creo) Prinergy 6.1. machines. The fulfillment house's suggestion is to: "generate a new PDF file after we rotate pages or make any changes to a PDF. It is as easy as exporting out to a PostScript and distilling back to a PDF."
I know iTextSharp doesn't support PostScript. Is there another way iTextSharp can rotate included PDFs to hold the orientation when rasterized?
First let me assure you that changing the rotation in the page dictionary is the correct procedure to achieve what you want. As far as I can see your code, there's nothing wrong with it. You are doing the right thing.
Unfortunately, you are faced with a third party product over which you have no control that is not doing the right thing. How to solve this?
I have written an example called IncorrectExample. I have named it that way because I don't want it to be used in a context that is different from yours. You can safely ignore all the warnings I added: they are not meant for you. This example is very specific to your problem.
Please try the following code:
public void manipulatePdf(String src, String dest)
throws IOException, DocumentException {
// Creating a reader
PdfReader reader = new PdfReader(src);
// step 1
Rectangle pagesize = getPageSize(reader, 1);
Document document = new Document(pagesize);
// step 2
PdfWriter writer
= PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte cb = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
pagesize = getPageSize(reader, i);
document.setPageSize(pagesize);
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
if (isPortrait(reader, i)) {
cb.addTemplate(page, 0, 0);
}
else {
cb.addTemplate(page, 0, 1, -1, 0, pagesize.getWidth(), 0);
}
}
// step 4
document.close();
reader.close();
}
public Rectangle getPageSize(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSizeWithRotation(pagenumber);
return new Rectangle(
Math.min(pagesize.getWidth(), pagesize.getHeight()),
Math.max(pagesize.getWidth(), pagesize.getHeight()));
}
public boolean isPortrait(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSize(pagenumber);
return pagesize.getHeight() > pagesize.getWidth();
}
I have taken the pages.pdf file as an example. This file is special in the sense that it has two pages in landscape that are created in a different way:
one page is a page of which the width is smaller than the height (sounds like it's a page in portrait), but as there's a /Rotate value of 90 added to the page dictionary, it is shown in landscape.
the other page isn't rotated, but it has a height that is smaller than the width.
In my example, I am using the classes Document and PdfWriter to create a copy of the original document. This is wrong in general because it throws away all interaction. I should use PdfStamper or PdfCopy instead, but it is right in your specific case because you don't need the interactivity: the final purpose of the PDF is to be printed.
With Document, I create new pages using a new Rectangle that uses the lowest value of the dimensions of the existing page as the width and the highest value as the height. This way, the page will always be in portrait. Note that I use the method getPageSizeWithRotation() to make sure I get the correct width and height, taking into account any possible rotation.
I then add a PdfImportedPage to the direct content of the writer. I use the isPortrait() method to find out if I need to rotate the page or not. Observe that the isPortrait() method looks at the page size without taking into account the rotation. If we did take into account the rotation, we'd rotate pages that don't need rotating.
The resulting PDF can be found here: pages_changed.pdf
As you can see, some information got lost: there was an annotation on the final page: it's gone. There were specific viewer preferences defined for the original document: they're gone. But that shouldn't matter in your specific case, because all that matters for you is that the pages are printed correctly.

Add links to PDF programmatically

I have about 180 PDF files that are generated from a geodatabase. I would like to programmatically add links as hot spots (no text) at the top, bottom, left and right as needed to navigate to the adjoining page files. I would also like to add links over a 3x3 grid in the lower left corner of the page for additional navigation. The grid is already in the existing PDF just no links. Total there will be a possible 14 links added to each page
I am open to suggestions as to how to go about this. I am using Acrobat Pro XI, and I am familiar with various programing languages python, vb.net, C#... Just no experience working directly with PDF files.
This is very late answer. Actually I was searching for free alternative to above paid libraries. I found the following links which can be helpful to others.
Apache PDFBox is a vast java library to create pdf programmatically.
TomRoush/PdfBox-Android is it's android implementation. You can find the sample project with this implementation.
I have added the code for creating clickable links in pdf by using above android library and sample project.
public void createPdf(View v) {
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
// Create a new font object selecting one of the PDF base fonts
PDFont font = PDType1Font.HELVETICA;
// Or a custom font
//try {
// PDType0Font font = PDType0Font.load(document, assetManager.open("MyFontFile.TTF"));
//} catch(IOException e) {
// e.printStackTrace();
//}
PDPageContentStream contentStream;
try {
// Define a content stream for adding to the PDF
contentStream = new PDPageContentStream(document, page);
String preText = "Icons made by ";
String linkText = "My_Site";
float upperRightX = page.getMediaBox().getUpperRightX();
float upperRightY = page.getMediaBox().getUpperRightY();
// Write linkText in blue text
contentStream.beginText();
contentStream.setNonStrokingColor(15, 38, 192);
contentStream.setFont(font, 18);
contentStream.moveTextPositionByAmount( 0, upperRightY-20);
contentStream.drawString(preText + linkText);
contentStream.endText();
// create a link annotation
PDAnnotationLink txtLink = new PDAnnotationLink();
// set up the markup area
float offset = (font.getStringWidth(preText) / 1000) * 18;
float textWidth = (font.getStringWidth(linkText) / 1000) * 18;
PDRectangle position = new PDRectangle();
position.setLowerLeftX(offset);
position.setLowerLeftY(upperRightY - 24f);
position.setUpperRightX(offset + textWidth);
position.setUpperRightY(upperRightY -4);
txtLink.setRectangle(position);
// add an action
PDActionURI action = new PDActionURI();
action.setURI("https://www.**********.com/");
txtLink.setAction(action);
// and that's all ;-)
page.getAnnotations().add(txtLink);
// load 'Social media' icons from 'vector' resources.
float padding = 5, startX = 5, startY = upperRightY-100, width = 25, height=25;
loadVectorIconWithLink(document, page, contentStream, R.drawable.ic_facebook,
"https://www.facebook.com/My_Name/", startX, startY, width, height);
startX += (width + padding);
loadVectorIconWithLink(document, page, contentStream, R.drawable.ic_instagram,
"https://www.instagram.com/My_Name", startX, startY, width, height);
// Make sure that the content stream is closed:
contentStream.close();
// Save the final pdf document to a file
String path = root.getAbsolutePath() + "/Download/Created.pdf";
document.save(path);
document.close();
tv.setText("Successfully wrote PDF to " + path);
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadVectorIconWithLink( PDDocument theDocument,
PDPage thePage,
PDPageContentStream theContentStream,
#DrawableRes int theDrawableId,
String theUriString,
float x, float y, float width, float height
) throws IOException
{
Bitmap alphaImage = getBitmapFromDrawable(this, theDrawableId);
PDImageXObject alphaXimage = LosslessFactory.createFromImage(theDocument, alphaImage);
theContentStream.drawImage(alphaXimage, x, y, width, height );
// create a link annotation
PDAnnotationLink iconLink = new PDAnnotationLink();
PDRectangle position = new PDRectangle( x, y, width, height );
iconLink.setRectangle(position);
// add an action
PDActionURI action1 = new PDActionURI();
action1.setURI(theUriString);
iconLink.setAction(action1);
// and that's all ;-)
thePage.getAnnotations().add(iconLink);
}
public static Bitmap getBitmapFromDrawable(Context context, #DrawableRes int drawableId) {
Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof VectorDrawableCompat || drawable instanceof VectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} else {
throw new IllegalArgumentException("unsupported drawable type");
}
}
There are at least three types of links you might want to add: links to pages within the same document, links to pages in other PDF document, links to URLs on the web.
Docotic.Pdf library can add links of any of these types (please note that I am on of the developers of this library). Here are two relevant examples:
Create link to page
Create hyperlink
There are no examples for how to create links to pages in an other PDF document published online, but you can always contact support if you need such an example.
After continuing to search and not finding any other promising open source solutions I went with Debenu Quick PDF Library. The specific functions I used are are noted below:
AddLinkToFile
AddLinkToPage
Other annotations and hotspot links
The time that the two library functions are going to save me weekly is worth the cost alone. I am sure I will find other use for the other 900+ PDF functions

Image Opacity in PdfSharp.NET

I'm using PDFSharp.NET library to watermark a list of PDFs file. Everything works fine, the website has a lot of samples.
http://www.pdfsharp.net/wiki/Graphics-sample.ashx
The last thing I need is to add the Company Logo, which is big, in the middle of the PDF Page.
I can use a PNG, so that areas which are set as transparent do not "cover" the PDF page".
The pdf is not generated using PDFSharp, but is an "Image" PDF.
For this reason, what I need, is, in addition to the transparency, which works, be able some how to set the Image Opacity!
The code to place the image is this one:
XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append);
XImage image = XImage.FromFile(mypath);
gfx.DrawImage(image, pwidth/2-image.PixelWidth/2, pheight/2 image.PixelHeight/2);
gfx.Dispose();
Anyone has already faced with that?
I don't know how to alter the opacity of an image while drawing it using PDFsharp (and I'm afraid this can't be done).
So instead I would just open the logo (PNG) with an image processor and set the opacity there.
I was looking in to this aswell now for making a watermark (companyLogo) to place over pdf sheets. The code below lets you change the opacity.
PDFSharp can not change the image opacity. What you can do is change the image you feed to PDF sharp. This has already been answered so I am just sharing my code of doing so.
private void DrawGraphics()
{
XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append);
Image myTransparenImage = SetImageOpacity(Image.FromFile("MyPath"), (float)opacityYouwant); // opacityYouWant has to be a value between 0.0 and 1.0
XImage image = XImage.FromBitmapSource(Convert(myTransparenImage));
gfx.DrawImage(image, pwidth / 2 - image.PixelWidth / 2, pheight / 2 image.PixelHeight / 2);
gfx.Dispose();
}
public Image SetImageOpacity(Image image, float opacity)
{
try
{
//create a Bitmap the size of the image provided
Bitmap bmp = new Bitmap(image.Width, image.Height);
//create a graphics object from the image
using (Graphics gfx = Graphics.FromImage(bmp))
{
//create a color matrix object
ColorMatrix matrix = new ColorMatrix();
//set the opacity
matrix.Matrix33 = opacity;
//create image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color(opacity) of the image
attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
//now draw the image
gfx.DrawImage(image, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
return bmp;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
public BitmapImage Convert(Image img)
{
using (var memory = new MemoryStream())
{
img.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
return bitmapImage;
}
}

Have 2 bitmap resolutions in a PDF

Is there a way to place 2 instances of a bitmap in a PDF for a single image? One to display when it is viewed on the screen and another when it prints?
The problem we have is rendering a chart to a bitmap. If we do 300 dpi then axis lines, borders, etc. disappear. If we do 96 dpi, then printing it looks bad.
thanks - dave
You can use Optional Content to do this. Supplying the usage application dictionaries with a 'Print' event causes the content to be appropriate for printing. (Note, not all printing applications will honour this).
See The PDF Reference Manual, in my 1.7 edition section 4.10 'Optional Content' beginning on page 364.
You can add an Alternate Image Dictionary (PDF Spec, section 8.9.5.4) which can specify an image to be used for printing.
Yes, there is a way, although I do not know it. We used it as a prank on a coworker, when printing a document, some totally other pictures appeared
You can also use 2 readonly textbox fields and draw the images on the field's appearance. Then for one field you set its visibility to VisibleNonPrintable and for the other HiddenButPrintable.
I implemented this (using iText). For anyone else who needs these here's the code. And you can download the source at my blog.
static void Main(string[] args)
{
Document document = new Document(new Rectangle(0, 0, 8.5f * 72.0f, 11 * 72));
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(Path.GetFullPath(#"..\..\test_dotnet.pdf"), FileMode.OpenOrCreate, FileAccess.ReadWrite));
document.Open();
document.Add(new Paragraph("Visibility test"));
// not displayed on printer
PdfLayer layer = new PdfLayer("screen", writer);
layer.OnPanel = false;
layer.SetPrint("Print", false);
layer.View = true;
PdfContentByte cb = writer.DirectContent;
cb.BeginLayer(layer);
Image img = Image.GetInstance(Path.GetFullPath(#"..\..\building_01.png"));
img.SetAbsolutePosition(72, 72 * 7);
cb.AddImage(img);
cb.EndLayer();
// not displayed on screen
layer = new PdfLayer("print", writer);
layer.OnPanel = false;
layer.SetPrint("Print", true);
layer.View = false;
cb = writer.DirectContent;
cb.BeginLayer(layer);
img = Image.GetInstance(Path.GetFullPath(#"..\..\building_02.png"));
img.SetAbsolutePosition(72, 72 * 3);
cb.AddImage(img);
cb.EndLayer();
document.Close();
Console.Out.WriteLine("all done");
}