Im editing a pdf. The client wants the image inside pdf to be resize and rotated.
so what i did is to extract the image inside the pdf to be able to manipulate the image then insert it again to the the pdf(replacing the old one)
here is the code where i got the code for extracting image
https://psycodedeveloper.wordpress.com/2013/01/10/how-to-extract-images-from-pdf-files-using-c-and-itextsharp/
but when i extract the image to image is rotated 180 degree
i even used the free Spire.PDF to extract the image but the extracted image of the spire.pdf is rotated 90 degree. so how can i get the image orientation of the pdf. so that i can make the image to its original orientation. thank you
There are two relevant factors deciding on the effective rotation of an image, the current transformation matrix at the time the image is drawn (which also fixes the dimensions of the image) and the page rotation.
You can determine these values as shown below in the code you refer to:
...
public static Dictionary<string, System.Drawing.Image> ExtractImages(string filename)
{
var images = new Dictionary<string, System.Drawing.Image>();
using (var reader = new PdfReader(filename))
{
var parser = new PdfReaderContentParser(reader);
ImageRenderListener listener = null;
for (var i = 1; i <= reader.NumberOfPages; i++)
{
// v-- Determine clockwise rotation of page
Console.WriteLine("Page {1} is rotated by {0}°.\n", reader.GetPageRotation(i), i);
// ^-- Determine clockwise rotation of page
parser.ProcessContent(i, (listener = new ImageRenderListener()));
var index = 1;
[...]
}
return images;
}
}
...
public void RenderImage(ImageRenderInfo renderInfo)
{
// v-- Determine transformation matrix of image
Matrix ctm = renderInfo.GetImageCTM();
Console.WriteLine("Found image with transformation matrix:\n{0}\n", ctm);
// ^-- Determine transformation matrix of image
PdfImageObject image = renderInfo.GetImage();
PdfName filter = (PdfName)image.Get(PdfName.FILTER);
[...]
}
...
The output in your case:
Page 1 is rotated by 270°.
Found image with transformation matrix:
792,0001 0 0
0 612 0
0 0 1
Found 1 images on page 1.
Thus, the transformation matrix obviously only scales the image to the appropriate dimensions without rotating it but the page itself is defined to be shown rotated by 270°.
This corresponds to my observations. In particular in contrast to what you said:
but when i extract the image to image is rotated 180 degree
I get an image from your code which has to be rotated by 270° clockwise to be upright.
If you indeed get an image rotated by 180°, you should check the version of iTextSharp you use. The archive on the web site you refer to contains a fairly old version, 5.3.5.0, and bugs might have been fixed in the meantime.
Related
I looking for a way to calculate a rectangle (x,y,width & height) which can be used for cropping an image around the coordinates of a selected face.
I have an image 995x1000 (https://tourspider.blob.core.windows.net/img/artists/original/947a0903-9b64-42a1-8179-108bab2a9e46.jpg) by which the center of the face is located at 492x325. I can find this information using various services so even for multiple faces in an image I'm ableto find the most prominent - hence a single coordinate.
Now i need to make various sized cropped images from the source image (200x150, 200x200 & 750x250). Now I can't seem to solve how to best calculate a rectangle around the center coordinates while taking into account the edges of the images. The face should be as central as possible in the image.
Even after experimenting with various services (https://www.microsoft.com/cognitive-services/en-us/computer-vision-api) the result are pretty poor as the face, mainly in the 750x250, is sometimes not even present.
I'm also experimenting with the ImageProcessor (http://imageprocessor.org/) library with which you can use anchors for resizing but can't get the desired result.
Does anybody has an idea on how best crop around predefined coordinates?
Using Imageprocessor I created the following solution. It is not yet perfect but goes a long way ;)
public static void StoreImage(byte[] image, int destinationWidth, int destinationHeight, Point anchor)
{
using (var inStream = new MemoryStream(image))
using (var imageFactory = new ImageFactory())
{
// Load the image in the image factory
imageFactory.Load(inStream);
var originalSourceWidth = imageFactory.Image.Width;
var originalSourceHeight = imageFactory.Image.Height;
// Resizes the image until the shortest side reaches the set given dimension.
// This will maintain the aspect ratio of the original image.
imageFactory.Resize(new ResizeLayer(new Size(destinationWidth, destinationHeight), ResizeMode.Min));
var resizedSourceWidth = imageFactory.Image.Width;
var resizedSourceHeight = imageFactory.Image.Height;
//Adjust anchor position
var resizedAnchorX = anchor.X/(originalSourceWidth / resizedSourceWidth);
var resizedAnchorY = anchor.Y/(originalSourceHeight/resizedSourceHeight);
if (anchor.X > originalSourceWidth || anchor.Y > originalSourceHeight)
{
throw new Exception($"Invalid anchor point. Image: {originalSourceWidth}x{originalSourceHeight}. Anchor: {anchor.X}x{anchor.Y}.");
}
var cropX = resizedAnchorX - destinationWidth/2;
if (cropX < 0)
cropX = 0;
var cropY = resizedAnchorY - destinationHeight/2;
if (cropY < 0)
cropY = 0;
if (cropY > resizedSourceHeight)
cropY = resizedSourceHeight;
imageFactory
.Crop(new Rectangle(cropX, cropY, destinationWidth, destinationHeight))
.Save($#"{Guid.NewGuid()}.jpg");
}
}
Got template docx with image placeholder which replaced by correct picture.
private void SetImagePartData(ImagePart imagePart, byte[] data)
{
if (imagePart != null)
{
using (var writer = new BinaryWriter(imagePart.GetStream()))
{
writer.Write(data);
}
}
}
but it preserves placeholder size. How to change it to actual image size? Byte array is aqquared from image on server, so size is known.
If you mean a content control with your placeholder you can use following code I once needed:
//Get SdtElement (can be a block, run... so I use the base class) with corresponding Tag
SdtElement block = doc.MainDocumentPart.Document.Body.Descendants<SdtElement>()
.FirstOrDefault(sdt => sdt.SdtProperties.GetFirstChild<Tag>()?.Val == contentControlTag);
//Get First drawing Element and get the original sizes of placeholder SDT
//I use SDT placeholder size as maximum size to calculate picture size with correct ratios
Drawing sdtImage = block.Descendants<Drawing>().First();
double sdtWidth = sdtImage.Inline.Extent.Cx;
double sdtHeight = sdtImage.Inline.Extent.Cy;
double sdtRatio = sdtWidth / sdtHeight;
*Calculate final width/height of image*
//Resize picture placeholder
sdtImage.Inline.Extent.Cx = finalWidth;
sdtImage.Inline.Extent.Cy = finalHeight;
//Change width/height of picture shapeproperties Transform
//This will override above height/width until you manually drag image for example
sdtImage.Inline.Graphic.GraphicData
.GetFirstChild<DocumentFormat.OpenXml.Drawing.Pictures.Picture>()
.ShapeProperties.Transform2D.Extents.Cx = finalWidth;
sdtImage.Inline.Graphic.GraphicData
.GetFirstChild<DocumentFormat.OpenXml.Drawing.Pictures.Picture>()
.ShapeProperties.Transform2D.Extents.Cy = finalHeight;
But you can use it if you are just using an image in your word document too. You just need to locate the correct Drawing element which contains the reference to your imagepart. Then you can use the bottom part of the code to adjust the image size. It's important you adjust both the Transform2D x and y as well as the Inline x and y or the image size won't be changed.
I have a function which is cropping the specific part of the pdf file and adding it into the new Pdf file but the main problem that i am getting is that it is showing the cropped part of the page into the bottom (footer) of the newly created pdf file.
Here is the code..
public static void CropPdfFile(string sourceFilePath, string outputFilePath)
{
// Allows PdfReader to read a pdf document without the owner's password
PdfReader.unethicalreading = true;
// Reads the PDF document
using (PdfReader pdfReader = new PdfReader(sourceFilePath))
{
// Set which part of the source document will be copied.
// PdfRectangel(bottom-left-x, bottom-left-y, upper-right-x, upper-right-y)
PdfRectangle rect = new PdfRectangle(0f, 9049.172f, 594.0195f, 700.3f);
using (var output = new FileStream(outputFilePath, FileMode.CreateNew, FileAccess.Write))
{
// Create a new document
using (Document doc = new Document())
{
// Make a copy of the document
PdfSmartCopy smartCopy = new PdfSmartCopy(doc, output);
// Open the newly created document
doc.Open();
// Loop through all pages of the source document
for (int i = 4; i <= pdfReader.NumberOfPages; i++)
{
// Get a page
var page = pdfReader.GetPageN(i);
// Apply the rectangle filter we created
page.Put(PdfName.CROPBOX, rect);
page.Put(PdfName.MEDIABOX, rect);
// Copy the content and insert into the new document
smartCopy.SetLinearPageMode();
var copiedPage = smartCopy.GetImportedPage(pdfReader, i);
smartCopy.AddPage(copiedPage);
}
// Close the output document
doc.Close();
}
}
}
Please help me to solve this..
The size of a PDF page (expressed in user units) depends on the value of the mediabox. For instance: The media box of an A4 page is usually defined like this [0 0 595 842]
In this case, the origin of the coordinate system (0, 0) coincides with the lower-left corner. The coordinate of the upper right corner is (595, 842).
Another possible value for an A4 page would be [0 842 595 1684]. Same width (595 user units), same height (1684 - 842 = 842 user units), but the lower-left corner now has the coordinate (0, 842) and the coordinate of the upper-right corner is (595, 1684).
You write that you create a PdfRectangle using these parameters: (bottom-left-x, bottom-left-y, upper-right-x, upper-right-y). However, you're using these hard-coded values: 0f, 9049.172f, 594.0195f, 700.3f.
Your lower-left-y (9049.172) is at a higher position than your upper-right-y (700.3). This doesn't really make sense. Hence: you should consider changing that value to something that does make sense. What value that should be, is a question only you can answer since only you know the value of the MediaBox of the file you want to crop.
In your comment, you explain that your PDF is an A4 page. You can check this by using the PdfReader method named getPageSize(). If you want to crop the page so that you only see the header of your document, you need to use something like this:
PdfRectangle rect = new PdfRectangle(0f, 842 - x, 595, 842);
Where x is the height of the header. For instance, if the header is 100 user units, then you'd need:
PdfRectangle rect = new PdfRectangle(0f, 742, 595, 842);
It is unclear why you're always talking about 100px. If you want to convert pixels to points, please read Convert Pixels to Points
Using the formula mentioned there 100 pixels equals 75 points.
Using GDI+ I am attempting to make a simple square that consists of an image. This rectangle will be moved. There are a few issues I've been running into. First of all, how to locally refer to the image (it is set to always copy), how to get the image centered in the square, and how to keep the image stationary when the square moves?
Bitmap runnerImage = (Bitmap)Image.FromFile(#"newRunner.bmp", true);//this results in an error without full path
TextureBrush imageBrush = new TextureBrush(runnerImage);
imageBrush.WrapMode = System.Drawing.Drawing2D.WrapMode.Clamp;//causes the image to get smaller/larger if movement is tried
Graphics.FillRectangle(imageBrush, displayArea);
Without using wrapMode.clamp it defaults to tiling, which looks like the image is tiled and moving the square moves from one image to the next
how to locally refer to the image (it is set to always copy)
You can add the image to a resource file and then reference that Image from there within the code. (See link http://msdn.microsoft.com/en-us/library/7k989cfy%28v=vs.90%29.aspx)
How to get the image centered in the square, and how to keep the image
stationary when the square moves?
This can be achieved using TranslateTransform with displayArea's location
(See link http://msdn.microsoft.com/en-us/library/13fy233f%28v=vs.110%29.aspx)
TextureBrush imageBrush = new TextureBrush(runnerImage);
imageBrush.WrapMode = WrapMode.Clamp;//causes the image to get smaller/larger if movement is tried
Rectangle displayArea = new Rectangle(25, 25, 100, 200); //Random values I assigned
Point xDisplayCenterRelative = new Point(displayArea.Width / 2, displayArea.Height / 2); //Find the relative center location of DisplayArea
Point xImageCenterRelative = new Point(runnerImage.Width / 2, runnerImage.Height / 2); //Find the relative center location of Image
Point xOffSetRelative = new Point(xDisplayCenterRelative.X - xImageCenterRelative.X, xDisplayCenterRelative.Y - xImageCenterRelative.Y); //Find the relative offset
Point xAbsolutePixel = xOffSetRelative + new Size(displayArea.Location); //Find the absolute location
imageBrush.TranslateTransform(xAbsolutePixel.X, xAbsolutePixel.Y);
e.Graphics.FillRectangle(imageBrush, displayArea);
e.Graphics.DrawRectangle(Pens.Black, displayArea); //I'm using PaintEventArgs graphics
Edit: I assumed that Image Size is always <= Square Size
I'm using the following code to get a picture from the the MediaLibrary on the phone and resize it. In the emulator it is working fine but it is rotating it -90 degrees when I try it on a real phone.
The 4th parameter for SaveJpeg is orientation and the tooltip says
"This parameter is not currently used by this method. Use a value of 0 as a placeholder."
The same thing happens if I pass 0,1,-1. Seems like it might actually be implemented on the phone and not in the emulator, but I don't know what to pass.
public byte[] GetPhoto(string photoName, int width, int height)
{
using (var ml = new Microsoft.Xna.Framework.Media.MediaLibrary())
{
using(Stream stream = (from p in ml.Pictures where p.Name == photoName select p).FirstOrDefault().GetImage())
{
//load the stream into a WriteableBitmap so it can be resized
using(MemoryStream outstream = new MemoryStream())
{
PictureDecoder.DecodeJpeg(stream).SaveJpeg(outstream, width, height, 0, 85);
return outstream.ToArray();
}
}
}
}
Also I just noticed that the sample pictures on the phone are not having this problem, just the ones I've taken.
I don't think the EXIF data for orientation is read by WP7 (happy to be corrected as I've only tried when the CTP SDK was out). However, you can manually rotate your picture using this method. An alternative, which I haven't tried, could be to get the image's rotate transform and rotate it 90 degrees. Transform rotations may work out to be quicker than manually shifting all the pixels of the writeable bitmap.