iText7 - How to obtain image object data - c#

I need to add a BarcodeQRCode image to a PdfCanvas but I have not found how to do it.
I have the following code
BarcodeQRCode qr;
PdfFormXObject xObject;
Image im;
PdfCanvas canvas = new PdfCanvas(page);
qr = new BarcodeQRCode(msg);
xObject = qr.CreateFormXObject(Color.BLACK, _pdfDoc);
im = new Image(xObject);
im.SetFixedPosition(10f, 10f);
At this point I need to add the image object to the canvas, but in iText7 I need to pass the image object like a iText.IO.Image.ImageData, and that's the problem, because to make this I need the Byte[] of the image.
My question is, How to obtain the Byte[] of the Image object?
I have also tried to do this to obtain the ImageData
PdfImageXObject xoi;
xoi = new PdfImageXObject(xObject.GetPdfObject());
iText.IO.Image.ImageData id = iText.IO.Image.ImageDataFactory.Create(xoi.GetImageBytes());
but I take an error, because xoi.GetImageBytes() throws a null reference exception. Is like the PdfImageXObject is not created correctly.
I am a Little crazy and I need help, please.
Thanks.

You don't have to pass the ImageData object in iText7 in order to draw on PdfCanvas. ImageData is for common images (PNG, JPG), but for barcodes you don't want to lose quality and there are other ways of adding a barcode to the canvas.
In your case, you have already created a PdfFormXObject instance and you can add it to the canvas with PdfCanvas#addXObject as follows:
BarcodeQRCode barcode = new BarcodeQRCode(messageText);
PdfFormXObject barcodeFormXObject = barcode.createFormXObject(Color.BLACK, document);
float scale = 5;
float x = 10;
float y = 10;
canvas.addXObject(barcodeFormXObject, scale, 0, 0, scale, x, y);
UPD: In order for the barcode to be of fixed size regardless of its contents, you can use the following code:
// Tweak this value for fixed width and height
float fixedWidthAndHeight = 300;
float formXobjectWidthAndHeight = barcodeFormXObject.getHeight();
float scale = fixedWidthAndHeight / formXobjectWidthAndHeight;
float x = 10;
float y = 10;
canvas.addXObject(barcodeFormXObject, scale, 0, 0, scale, x, y);
Please note that some padding is added to the barcode, so its width/height will not be equal to fixedWidthAndHeight if you measure the distance between two opposite black squares, but it will be proportional to fixedWidthAndHeight, so you can tweak this variable as needed.

Related

Cropping an image to a "perfect" square like native Photos app

Whilst working on a functionality for viewing embedded images that get fetched from one of our servers, one of the requirements is to make an overview page, where all images are displayed, in a fasion like the iOS Native Photos App (square tiles, in rows of 4/5 and when tapped, displays the image screen-wide.
This has been successfully implemented, except that the images were morphed (image aspects were never taken into account). Thus, they have asked me to devise a method to crop the image, so the original aspect stays unchanged.
In order to do this, I was thinking of first resizing the image until the width reaches the max width I want it to be ((screenwidth - 10) / 4 - 5).
If the height of the image is smaller than the width, I set the height to be equal to the width, and crop the width accordingly (with aspect unchanged).
If the height of the image is greater than the width, I just want to crop the height.
This is the method I am currently using:
//Images is an array/List with base64 strings
foreach (var image in Images)
{
UIImage imageToAdd = UIImage.LoadFromData(NSData.FromArray(Convert.FromBase64String(image.Content)));
var width = imageToAdd.Size.Width;
var height = imageToAdd.Size.Height;
//GlobalSupport.ScreenWidth is a static variable that contains the actual screensize
var newWidth = (GlobalSupport.ScreenWidth - 10) / 4 - 5;
var newHeight = height * newWidth / width;
var widthToCrop = 0.0f;
var heightToCrop = 0.0f;
//If the new height is smaller than the new width, make the new height equal to the new width, and modify the new width accordingly.
if (newHeight < newWidth)
{
newHeight = newWidth;
newWidth = width * newHeight / height;
widthToCrop = newWidth - newHeight;
}
//Or, if the new height is greater than the new width, just crop the height.
else if (newHeight > newWidth)
{
heightToCrop = newHeight - newWidth;
}
UIGraphics.BeginImageContext(new SizeF(newWidth, newHeight));
var cropRectangle = new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight);
imageToAdd.Draw(cropRectangle);
imageToAdd = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
UIButton btnToAdd = new UIButton(new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight));
btnToAdd.SetBackgroundImage(imageToAdd, UIControlState.Normal);
btnToAdd.TouchUpInside += (sender, e) =>
{
Task.Factory.StartNew(() =>
{
//Displays a loading spinner
GlobalSupport.EnableLoadingOverlay(true, NSBundle.MainBundle.LocalizedString("txtOneMoment", "", ""));
}).ContinueWith(task => InvokeOnMainThread(() =>
{
//Navigate to the full-screen image viewer
this.NavigationController.PushViewController(
new VCDCMPhotoDetails(
UIImage.LoadFromData(
NSData.FromArray(
Convert.FromBase64String(image.Content)))), true);
GlobalSupport.EnableLoadingOverlay(false, "");
}));
};
//Add btn to the scrollable view's children
scrollView.AddSubview(btnToAdd);
}
This method works great, except for one thing: if the new height is smaller than the new width, the new height and new width successfully get modified, but after the ImageToAdd.Draw(RectangleF) the Size of the ImageToAdd still has the new width value, instead of the cropped width (that is, if I can assume that, if I draw an image with -20 as its x-value, the width gets modified with -20 as well).
I don't know if this is the right way to do it. If it isn't, then all help is welcome! If this is the right way to do it, but I'm missing something, please let me know! Thanks in advance!
When cropping the image, the x-coordinate is equal to the width of the image, minus the max width of a thumbnail: http://i.stack.imgur.com/uNm7k.png
After cropping, the image still had the original new width, instead of the cropped width: http://i.stack.imgur.com/rabeY.png
The resulting grid of images: http://i.stack.imgur.com/opo2f.jpg
Let me just show you a proper procedure on how to crop the image with a FILL operation. The code is in Swift but you should have no trouble porting it:
public static func resampleImageToSize(image: UIImage!, size: CGSize) -> UIImage {
let originalWidth = image.size.width
let originalHeight = image.size.height
let originalRatio = originalWidth/originalHeight
let targetRatio = size.width/size.height
var targetFrame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
if originalRatio>targetRatio {
// crop width
let targetHeight = size.height
let targetWidth = targetHeight * originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
} else if originalRatio<targetRatio {
// crop height
let targetWidth = size.width
let targetHeight = targetWidth / originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
}
UIGraphicsBeginImageContext(size)
image.drawInRect(targetFrame)
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage
}
So this is a general method that takes in an original image and returns an image with a size of your choice. In your case that is the size of the icons you want to display. The operation here is fill, if you need to fit the image all you need to do is swap < and > in both if statements.
What you are doing is easiest to achieve with creating an image view with the size of your target image size. Then set your image to it and set the content mode to scale aspect fill and simply create a screenshot of the view (you can find very easy solutions for that on the web).
From your question I can not see if you have an issue at all or what it is. But one thing is bothering me. The line
RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight)
Will crop it the way you will always get the bottom right part of the image. You should most likely center it:
RectangleF(-widthToCrop*0.5, -heightToCrop*0.5, newWidth, newHeight)
Ok I think I found your problem. The frame you compute is the frame on which you should draw the image to put it on the square. But when you begin image context you should set the size to be a square. So if the width is larger the size should be (newHeight, newHeight) otherwise (newWidth, newWidth). The button frame makes no sense to me either. Why are you using the rectangle that should be used only to redraw the image?

How to Draw a given Character in exact height?

I am drawing the text using Graphics.DrawString() method, But the text height drawn is not same as which i gave.
For Eg:
Font F=new Font("Arial", 1f,GraphicUnit.Inch);
g.DrawString("M", F,Brushes.red,new Point(0,0));
By using the above code, i'm drawing the text with height 1 inch, but the text drawn is not exactly in 1 inch.
I need to Draw the text in Exact height which i'm giving. Thanks in advance..
The simplest solution will be to use a GraphicsPath. Here are the steps necessary:
Calculate the height you want in pixels: To get 1.0f inches at, say 150 dpi you need 150 pixels.
Then create a GraphicsPath and add the character or string in the font and font style you want to use, using the calculated height
Now measure the resulting height, using GetBounds.
Then scale the height up to the necessary number of pixels
Finally clear the path and add the string again with the new height
Now you can use FillPath to output the pixels..
Here is a code example. It writes the test string to a file. If you want to write it to a printer or a control using their Graphics objects, you can do it the same way; just get/set the dpi before you calculate the first estimate of the height..
The code below creates this file; the Consolas 'x' is 150 pixels tall as is the 2nd character (ox95) from the Wingdings font. (Note that I did not center the output):
// we are using these test data:
int Dpi = 150;
float targetHeight = 1.00f;
FontFamily ff = new FontFamily("Consolas");
int fs = (int) FontStyle.Regular;
string targetString = "X";
// this would be the height without the white space
int targetPixels = (int) targetHeight * Dpi;
// we write to a Btimpap. I make it large enough..
// Instead you can write to a printer or a Control surface..
using (Bitmap bmp = new Bitmap(targetPixels * 2, targetPixels * 2))
{
// either set the resolution here
// or get and use it above from the Graphics!
bmp.SetResolution(Dpi, Dpi);
using (Graphics G = Graphics.FromImage(bmp))
{
// good quality, please!
G.SmoothingMode = SmoothingMode.AntiAlias;
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
// target position (in pixels)
PointF p0 = new PointF(0, 0);
GraphicsPath gp = new GraphicsPath();
// first try:
gp.AddString(targetString, ff, fs, targetPixels, p0,
StringFormat.GenericDefault);
// this is the 1st result
RectangleF gbBounds = gp.GetBounds();
// now we correct the height:
float tSize = targetPixels * targetPixels / gbBounds.Height;
// and if needed the location:
p0 = new PointF(p0.X - gbBounds.X, p0.X - gbBounds.Y);
// and retry
gp.Reset();
gp.AddString(targetString, ff, fs, tSize, p0, StringFormat.GenericDefault);
// this should be good
G.Clear(Color.White);
G.FillPath(Brushes.Black, gp);
}
//now we save the image
bmp.Save("D:\\testString.png", ImageFormat.Png);
}
You may want to try using the correction factor to scale up a Font size and use DrawString after all.
There is also a way to calculate the numbers ahead using FontMetrics, but I understand the link to mean that such an approach could be font-dependent..

WPF Image Collison Detection

I have some code which detects collision ;
public bool DetectCollision(ContentControl ctrl1, ContentControl ctrl2)
{
Rect ctrl1Rect = new Rect(
new Point(Convert.ToDouble(ctrl1.GetValue(Canvas.LeftProperty)),
Convert.ToDouble(ctrl1.GetValue(Canvas.TopProperty))),
new Point((Convert.ToDouble(ctrl1.GetValue(Canvas.LeftProperty)) + ctrl1.ActualWidth),
(Convert.ToDouble(ctrl1.GetValue(Canvas.TopProperty)) + ctrl1.ActualHeight)));
Rect ctrl2Rect = new Rect(
new Point(Convert.ToDouble(ctrl2.GetValue(Canvas.LeftProperty)),
Convert.ToDouble(ctrl2.GetValue(Canvas.TopProperty))),
new Point((Convert.ToDouble(ctrl2.GetValue(Canvas.LeftProperty)) + ctrl2.ActualWidth),
(Convert.ToDouble(ctrl2.GetValue(Canvas.TopProperty)) + ctrl2.ActualHeight)));
ctrl1Rect.Intersect(ctrl2Rect);
return !(ctrl1Rect == Rect.Empty);
}
It detects when 2 rectangles are over. There are images in the given parameter ContentControls. I want to be able to detect if those images intersects not the rectangels. Following images shows whatn I want ;
Then you are not looking for rectangular collision detection but actually pixel-level collision detection and that is going to be much more processing intensive.
On top of the rectangular collision detection that you already have implemented you will have to examine each pixel of both images in the overlapping rectangular region.
In the simplest case, if both of two overlapping pixels have non transparent color then you have a collision.
If you want to complicate things you may want to add thresholds such as: requiring a percentage of overlapping pixels in order to trigger a collision; or setting a threshold for the combined alpha level of the pixels instead of using any non zero value.
You can try converting your images as a geometry object and then you can check if they are colliding correctly. But these images should be as a vector image. To convert images to a vector image, you can check this open source project.
public static Point[] GetIntersectionPoints(Geometry g1, Geometry g2)
{
Geometry og1 = g1.GetWidenedPathGeometry(new Pen(Brushes.Black, 1.0));
Geometry og2 = g2.GetWidenedPathGeometry(new Pen(Brushes.Black, 1.0));
CombinedGeometry cg = new CombinedGeometry(GeometryCombineMode.Intersect, og1, og2);
PathGeometry pg = cg.GetFlattenedPathGeometry();
Point[] result = new Point[pg.Figures.Count];
for (int i = 0; i < pg.Figures.Count; i++)
{
Rect fig = new PathGeometry(new PathFigure[] { pg.Figures[i] }).Bounds;
result[i] = new Point(fig.Left + fig.Width / 2.0, fig.Top + fig.Height / 2.0);
}
return result;
}

Create thumbnail in .NET C# by defining width

I am using the following code in my website, for thumbnail creation:
string furl = "~/images/thumbs/" + matchString;
lBlogThumb.ImageUrl = GetThumbnailView(furl, 200, 200);
private string GetThumbnailView(string originalImagePath, int height, int width)
{
//Consider Image is stored at path like "ProductImage\\Product1.jpg"
//Now we have created one another folder ProductThumbnail to store thumbnail image of product.
//So let name of image be same, just change the FolderName while storing image.
string thumbnailImagePath = originalImagePath.Replace("thumbs", "thumbs2");
//If thumbnail Image is not available, generate it.
if (!System.IO.File.Exists(Server.MapPath(thumbnailImagePath)))
{
System.Drawing.Image imThumbnailImage;
System.Drawing.Image OriginalImage = System.Drawing.Image.FromFile(Server.MapPath(originalImagePath));
imThumbnailImage = OriginalImage.GetThumbnailImage(width, height,
new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
imThumbnailImage.Save(Server.MapPath(thumbnailImagePath), System.Drawing.Imaging.ImageFormat.Jpeg);
imThumbnailImage.Dispose();
OriginalImage.Dispose();
}
return thumbnailImagePath;
}
public bool ThumbnailCallback() { return false; }
I would like to change this code, and be able to create a thumbnail defining width ONLY. What I have in mind is actually something like cropping/resizing image, using a static width, maintaining it's ratio. Is that possible;
You mention resizing and cropping. If you want the thumbnail heights to vary with a fixed width, the answers provided already will work for you.
The mention of cropping makes me think that you may want a fixed thumbnail size, with the width filled and any overflowing vertical portion cropped off. If that is the case, you'll need to do a bit more work. I needed something similar recently, and this is what I came up with.
This will create a thumbnail of the original that is sized and cropped in such a way that the source image completely fills the target thumbnail, cropping any overflow. There will be no borders within the thumbnail, even if the original and thumbnail aspect ratios are different.
public System.Drawing.Image CreateThumbnail(System.Drawing.Image image, Size thumbnailSize)
{
float scalingRatio = CalculateScalingRatio(image.Size, thumbnailSize);
int scaledWidth = (int)Math.Round((float)image.Size.Width * scalingRatio);
int scaledHeight = (int)Math.Round((float)image.Size.Height * scalingRatio);
int scaledLeft = (thumbnailSize.Width - scaledWidth) / 2;
int scaledTop = (thumbnailSize.Height - scaledHeight) / 2;
// For portrait mode, adjust the vertical top of the crop area so that we get more of the top area
if (scaledWidth < scaledHeight && scaledHeight > thumbnailSize.Height)
{
scaledTop = (thumbnailSize.Height - scaledHeight) / 4;
}
Rectangle cropArea = new Rectangle(scaledLeft, scaledTop, scaledWidth, scaledHeight);
System.Drawing.Image thumbnail = new Bitmap(thumbnailSize.Width, thumbnailSize.Height);
using (Graphics thumbnailGraphics = Graphics.FromImage(thumbnail))
{
thumbnailGraphics.CompositingQuality = CompositingQuality.HighQuality;
thumbnailGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
thumbnailGraphics.SmoothingMode = SmoothingMode.HighQuality;
thumbnailGraphics.DrawImage(image, cropArea);
}
return thumbnail;
}
private float CalculateScalingRatio(Size originalSize, Size targetSize)
{
float originalAspectRatio = (float)originalSize.Width / (float)originalSize.Height;
float targetAspectRatio = (float)targetSize.Width / (float)targetSize.Height;
float scalingRatio = 0;
if (targetAspectRatio >= originalAspectRatio)
{
scalingRatio = (float)targetSize.Width / (float)originalSize.Width;
}
else
{
scalingRatio = (float)targetSize.Height / (float)originalSize.Height;
}
return scalingRatio;
}
To use with your code, you could replace your call to OriginalImage.GetThumbnailImage with this:
imThumbnailImage = CreateThumbnail(OriginalImage, new Size(width, height));
Note that for portrait images, this code will actually shift the thumbnail's viewport slightly higher on the original image. This was done so that portrait shots of people wouldn't result in headless torsos when the thumbnails were created. If you don't want that logic, simply remove the if block following the "portrait mode" comment.
let's have originalWidth=the original image width and thumbWidth. You can simply choose the thumbWidth to your desired value, and calculate the thumbHeigth=originalHeigth*thumbWidth/originalWidth
I got sick of needed to do this and created a lib that does this easily: Link To Documentation & Download
A basic example without rounding with a thumbnail width of 140, below the 'file' is a HttpPostedFile uploaded from an ASP.Net FileUpload control, the HttpPostedFile exposes a stream.
// Save images to disk.
using (System.Drawing.Image image = System.Drawing.Image.FromStream(file.InputStream))
using (System.Drawing.Image thumbnailImage = image.GetThumbnailImage(140, Convert.ToInt32((image.Height / (image.Width / 140))), null, IntPtr.Zero))
{
if (image != null)
{
image.Save(imageFilePath);
thumbnailImage.Save(thumbnailImagePath);
}
else
throw new ArgumentNullException("Image stream is null");
}

Huge Whitespace exists to right after drawing image. Want to get rid of it

I'm using the following codeproject to build an asp.net website and so far everything is good. My only problem is after the barcode is generated, a huge whitespace exist to the right of the barcode. I've been playing with this and am unable to resolve it.
Details below:
Link to Code Project Article: http://www.codeproject.com/KB/aspnet/AspBarCodes.aspx?msg=3543809
Copy of the Font is here: http://trussvillemethodist.web01.appliedi-labs.net/IDAutomationHC39M.ttf
//Working Path
string sWorkPath = "";
sWorkPath = this.Context.Server.MapPath("");
//Fonts
PrivateFontCollection fnts = new PrivateFontCollection();
fnts.AddFontFile(sWorkPath + #"\IDAutomationHC39M.ttf");
FontFamily fntfam = new FontFamily("IDAutomationHC39M", fnts);
Font oFont = new Font(fntfam, 18);
// Get the Requested code sent from the previous page.
string strCode = Request["code"].ToString();
//Graphics
//I don't know what to set the width to as I can't call the MeasureString without creating the Graphics object.
Bitmap oBitmaptemp = new Bitmap(40, 100);
Graphics oGraphicstemp = Graphics.FromImage(oBitmaptemp);
int w = (int)oGraphicstemp.MeasureString(strCode, oFont).Width + 4;
// Create a bitmap object of the width that we calculated and height of 100
Bitmap oBitmap = new Bitmap(w, 100);
// then create a Graphic object for the bitmap we just created.
Graphics oGraphics = Graphics.FromImage(oBitmap);
// Let's create the Point and Brushes for the barcode
PointF oPoint = new PointF(2f, 2f);
SolidBrush oBrushWrite = new SolidBrush(Color.Black);
SolidBrush oBrush = new SolidBrush(Color.White);
// Now lets create the actual barcode image
// with a rectangle filled with white color
oGraphics.FillRectangle(oBrush, 0, 0, w, 100);
// We have to put prefix and sufix of an asterisk (*),
// in order to be a valid barcode
oGraphics.DrawString("*" + strCode + "*", oFont, oBrushWrite, oPoint);
// Then we send the Graphics with the actual barcode
Response.ContentType = "image/gif";
oBitmap.Save(Response.OutputStream, ImageFormat.Gif);
oBitmap.Dispose();
oGraphics.Dispose();
oBrush.Dispose();
oFont.Dispose();
The code just assumes 40 pixels per character, which is why you get a lot of image left on the right of the text. You can use the MeasureString method to measure the size of the text, and use that to create an image of the correct size:
int w = (int)oGraphics.MeasureString("*123$10.00*", oFont).Width + 4;
I noticed that you don't dispose any of the objects that you are using. The Graphics, Bitmap, SolidBrush and Font objects need to be disposed.
You might also want to consider using a GIF image instead of JPEG, it's more suited for this kind of graphics.

Categories