I am inserting an image in an excel cell, but usually that image will be larger, both in height and in width than the original cell. So after I have inserted my image I would like to: resize the specific excel row and column according to the image's size. This is what I have:
Microsoft.Office.Interop.Excel.Application xlApp;
Microsoft.Office.Interop.Excel.Worksheet ws;
object misValue = System.Reflection.Missing.Value;
xlApp = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = xlApp.Workbooks.Open(#".../MyExcelFile.xlsx");
ws = wb.Sheets[1];
string picPath = #"..../MyPic.png";
System.Drawing.Image img = System.Drawing.Image.FromFile(picPath);
var size = img.Size;
Microsoft.Office.Interop.Excel.Range oRange = (Microsoft.Office.Interop.Excel.Range)ws.Cells[1, 5];
float Left = (float)((double)oRange.Left);
float Top = (float)((double)oRange.Top);
const float ImageSize = 32;
ws.Shapes.AddPicture(picPath, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, Left, Top, ImageSize, ImageSize);
So here I am inserting my image in the 5th cell of the 1st row, but setting its size to 32.However, I have access to this image's height and width. How can I set row 1 to that height and column 5 to that width?
EDIT: With the current code from Richard Mneyan, it looks like this, so the image doesn't fit in the cell:
The below would set height and width of your cell to respective picture height and width if you apply ratio of Column widths to Row Heights:
var sh = ws.Shapes.AddPicture(picPath, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, Left, Top, ImageSize, ImageSize);
oRange.Rows[1].RowHeight = sh.Height;
oRange.Columns[1].ColumnWidth = sh.Width;
This is not perfect answer because it is really hard to set Excel Column Widths to exact number. For instance the default Excel Column width is 8.43, the next increment would be 8.57. Why?; because it is based on number of the Normal font
characters that can fit in a cell: https://support.microsoft.com/en-us/help/214123/description-of-how-column-widths-are-determined-in-excel
Here applying approximate ratios (which is not perfect):
double rColRow = 6; // Ratio of units of measure: columns widths to row heights
double rImgColWidth = 5.9; // Ratio of units of measure: image size and column widths
oRange.Rows[1].RowHeight = (sh.Width * rColRow / rImgColWidth);
oRange.Columns[1].ColumnWidth = (sh.Height / rImgColWidth);
Related
When i run the program, i get a different amount of images(from 20 all the way up to 2000) and i would like to merge all of these images into one image which would preferably be a square.
This is the code i have for getting the file images(the images are in URL format)
int ximg = 1;
int totalImgs = richTextBox1.Lines.Count();
while (ximg < totalImgs)
{
System.Net.WebRequest request = System.Net.WebRequest.Create(richTextBox1.Lines[ximg]);
System.Net.WebResponse response = request.GetResponse();
System.IO.Stream responseStream =
response.GetResponseStream();
Bitmap image = new Bitmap(responseStream);
List<Image> fileList = new List<Image>();
fileList.Add(image);
ximg++;
}
Also every single image has a title in a different richtextbox which i would like to know if it is possible to add a title under the image (richtextbox1.lines[1] (image) = richtextbox2.lines[1] (title)). Is it possible to add a picture as a background picture when merging(to the square image I want to generate)? Is it possible to add a border on every single image picture and merge them with the border? How can the code calculate when its time to change line and start adding images in the next row?
I've tried this code, but it works only if you know the amount of images you want to merge.
Bitmap bitmap = new Bitmap(image1.Width + image2.Width, Math.Max(image1.Height, image2.Height));
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(image1, 0, 0);
g.DrawImage(image2, image1.Width, 0);
}
bitmap.Save("merged.bmp");
I would do it with photoshop but when there are 2000 images to merge together, I just do not have the time.
Is there any way to accomplish such task? Any references would be appreciated!
I have mentioned it above :) !
This solution will create a mosaic of the images from left to right, top to bottom. It does not do anything to maximize the available space. It also assumes that you have a max width and height for the finished image, since it's not realistic to support an arbitrary size.
// where we store the finished mosaic
var mosaic = new Bitmap(maxWidth, maxHeight);
// track the location where we are drawing each image
var imageCorner = new Point(0,0);
// track the height of the current row
var currentRowHeight = 0;
// track the width and height of the mosaic as it grows.
var mosaicWidth = 0;
var mosaicHeight = 0;
using (var g = Graphics.FromImage(bitmap))
{
var borderPen = new Pen(Brushes.Black) { Width = 2 };
var labelFont = new Font("Arial", 10);
var labelBrush = new SolidBrush(Color.Black);
foreach (var image in imageList)
{
if (imageCorner.X + image.Width > maxWidth)
{
// if adding the image to the current row would make it too wide,
// move to the next row by resetting X to zero and adding the
// height of the tallest image to Y
imageCorner.X = 0;
imageCorner.Y += currentRowHeight;
// since this is a new row, it's current height is zero
currentRowHeight = 0;
}
// if adding this image would put us past the
// height of the image, then we're out of room.
if (imageCorner.Y + image.Height > maxHeight)
{
// this skips images if there's no room for them in
// the mosaic, you may want to do something different
Trace.WriteLine($"Image is {image.Height} pixels tall, but only {maxHeight - mosaicHeight} pixels available.");
continue;
}
// draw the image
g.DrawImage(image, imageCorner);
// draw the border
g.DrawRectangle(borderPen,
imageCorner.X, imageCorner.Y,
image.Width, image.Height)
// draw the label
g.DrawText("Image Label", labelBrush, imageCorner.X, imageCorner.Y)
// now that we've drawn the image, we need to shift to the right
imageCorner.X += image.Width;
// row height is the height of the tallest image so far in this row
currentRowHeight = Math.Max(image.Height, currentRowHeight);
// track the total height of the mosaic
mosaicHeight = imageCorner.Y + currentRowHeight;
// mosaic width is just the widest row in the mosaic
mosaicWidth = Math.Max(imageCorner.X, widthOfWidestRow);
}
}
// trim off the parts of the mosaic we didn't fill
mosaic = mosaic.Clone(new Rectangle(0, 0, mosaicWidth, mosaicHeight);
mosaic.Save("merged.bmp");
If you wanted to minimize wasted space, you could sort your list of images in different ways, or calculate ahead of time what a good width and height for the mosaic would be.
I am using ClosedXML (https://github.com/ClosedXML/ClosedXML) for creating an excel file in my C# MVC Controller. As per the documentation in https://github.com/closedxml/closedxml/wiki/How-can-I-insert-an-image, I have inserted an image in a cell and merged that cell with to cells on the right side.My code is as follows:
For adding image
var imagePath = #"c:\myFolder\image.jpg";
var image = MyWorkSheet.AddPicture(imagePath ) .MoveTo((MyWorkSheet.Cell(3,1).Address)) .Scale(0.2);
image.Width = 50;
image.Height = 50;
For merging cell
MyWorkSheet.Range(MyWorkSheet.Cell(3,1).Address, MyWorkSheet.Cell(3, 3).Address).Merge();
But the image lies on the upper left corner of the cell. I cant find any web source explaining how to center the image in the cell range. Anyone please help me.
You have to move your image with an offset from the cell. To do that, you have to calculate that offset.
Column width is returned in point (not in pixel). You have to convert it to pixel to compare with image pixel width.
So you can do :
int iColumnWidth = (MyWorkSheet.Column(1).Width - 1) * 7 + 12; // To convert column width in pixel unit.
int xOffset = (iColumnWidth - image.Width) / 2;
int yOffset = 0;
image.MoveTo(MyWorkSheet.Cell(3,1), New Point(xOffset, yOffset));
You need to do some manual calculation based on the with respect to image and cell size.
Workbook wb = new Workbook();
Worksheet sheet = wb.Worksheets[0];
sheet.Range["A1"].Text = "Align Picture Within A Cell:";
sheet.Range["A1"].Style.VerticalAlignment = VerticalAlignType.Top;
string picPath = #"C:\Users\Administrator\Desktop\scenery.jpg";
ExcelPicture picture = sheet.Pictures.Add(1, 1, picPath);
sheet.Columns[0].ColumnWidth = 50;
sheet.Rows[0].RowHeight = 150;
picture.LeftColumnOffset =100;
picture.TopRowOffset = 25;
wb.SaveToFile("AlignPicture.xlsx", ExcelVersion.Version2013);
I am trying to insert images "into" a cell in excel using Epplus.
using the following code
private static void SetImage(ExcelWorksheet sheet, ExcelRange cell)
{
using (WebClient client = new WebClient())
using (Stream stream = client.OpenRead("https://..."))
using (Bitmap bitmap = new Bitmap(stream))
{
var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), bitmap);
picture.From.Column = cell.Start.Column - 1;
picture.From.Row = cell.Start.Row - 1;
picture.To.Column = cell.Start.Column;
picture.To.Row = cell.Start.Row;
}
}
-
var cell = sheet.Cells[2, 2];
SetImage(sheet, cell);
cell = sheet.Cells[3, 2];
SetImage(sheet, cell);
However it always seems to have an overlap to the right.
If I adjust the cell widths and heights the overlap changes but never disappears
So I abandoned the
picture.To.Column = cell.Start.Column;
picture.To.Row = cell.Start.Row;
since I just could not get it to work and decided to calculated my own dimensions using:
picture.SetSize(width, height);
The trick is to understand how Excel actually calculates widths and heights.
Height of a cell: Its measured in points, but we want pixels. There are 72 points in an inch. One can convert points to pixel using the following formula points* (1/72.0) * DPI. DPI is dots per inch and can be found using the following method:
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
float dpiY = graphics.DpiY;
}
So to calculate the height of a cell in pixels I used
private static int GetHeightInPixels(ExcelRange cell)
{
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
float dpiY = graphics.DpiY;
return (int)(cell.Worksheet.Row(cell.Start.Row).Height * (1 / 72.0) * dpiY);
}
}
Width of a cell: This is a bit trickier. Basically the width of a cell in excel is equal to the number of characters (formatted using the default font) that a cell can contain horizontally.
For example
This colum is of length 12 and can contain 12 numbers in the Calibri(11) font.
That is also my excel default since my body default is calibri(11)
Here is an article explaining it in more depth.
The next question is how on earth does one translate that to pixels.
Firstly we need to discover what the length of a character is in the default font. One could use TextRenderer.MeasureText in the System.Windows.Forms namespace. However I am using .Net Core and needed another way. Another way is to use the the System.Drawing.Common core lib which is now in preview.
public static float MeasureString(string s, Font font)
{
using (var g = Graphics.FromHwnd(IntPtr.Zero))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
return g.MeasureString(s, font, int.MaxValue, StringFormat.GenericTypographic).Width;
}
}
I then used that method to calculate the width in pixels as follows
private static int GetWidthInPixels(ExcelRange cell)
{
double columnWidth = cell.Worksheet.Column(cell.Start.Column).Width;
Font font = new Font(cell.Style.Font.Name, cell.Style.Font.Size, FontStyle.Regular);
double pxBaseline = Math.Round(MeasureString("1234567890", font) / 10);
return (int)(columnWidth * pxBaseline);
}
Edit: Please note that overlap still happens when the zoom factor is set to more than 100% under display settings
I have the following code in my Windows Forms application:
x = Convert.ToInt32(textBox1.Text);
tableLayoutPanel1.RowCount = x;
tableLayoutPanel1.ColumnCount = x;
I have a textbox which determines the number of the rows and columns of a table layout panel. The problem is, that the cells look like this:
the picture
What can I do to make the size of the cells the same?
Have you tried using RowStyles and ColumnStyles properties?
TableLayoutRowStyleCollection styles =
tableLayoutPanel1.RowStyles;
foreach (RowStyle style in styles){
// Set the row height to 20 pixels.
style.SizeType = SizeType.Absolute;
style.Height = 20;
}
and also:
TableLayoutColumnStyleCollection styles =
tableLayoutPanel1.ColumnStyles;
foreach (ColumnStyle style in styles){
// Set the column width to 20 pixels.
style.SizeType = SizeType.Absolute;
style.Width = 20;
}
You can just use RowStyles if you want by adding the width of the row.
I am creating a single pdf page with 6 images in a table in separate cells, even though I am setting the images height and width on the server side exactly the same with ScaleToFit the images sizes are not the same on the pdf page.
Is there anyway to get all the images the exact same size?
PdfPTable table = new PdfPTable(3);
table.HorizontalAlignment = Element.ALIGN_CENTER;
table.WidthPercentage = 100;
table.TotalWidth = 698.5f;
table.LockedWidth = true;
table.SetWidths(new float [] {1,1,1});
iTextSharp.text.Image img1 = iTextSharp.text.Image.GetInstance("C:\\Users\\DaNet\\Downloads\\image.jpg");
img1.Alignment = iTextSharp.text.Image.ALIGN_CENTER;
img1.ScaleToFit(120f, 155.25f);
iTextSharp.text.pdf.PdfPCell imgCell1 = new iTextSharp.text.pdf.PdfPCell(img1);
imgCell1.HorizontalAlignment = Element.ALIGN_CENTER;
imgCell1.BackgroundColor = new BaseColor(255, 255, 255);
imgCell1.Border = iTextSharp.text.Rectangle.NO_BORDER;
table.AddCell(imgCell1);
Two things.
First, see this post about wrapping the Image in a Chunk. Basically:
iTextSharp.text.pdf.PdfPCell imgCell1 = new iTextSharp.text.pdf.PdfPCell();
imgCell1.AddElement(new Chunk(img1, 0, 0));
Second, if you want the exact same size then you want to use ScaleAbsolute instead of ScaleToFit. The latter keeps the aspect ratio of the image so a 100x200 image scaled to fit 50x50 would come out as 25x50.
img1.ScaleAbsolute(120f, 155.25f);