How to center Image inside a merged cell range in ClosedXML - c#

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);

Related

bitmap merge different amount of images together every time c#

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.

Set text width component via script

I am instantiating the leader board on the image (https://imgur.com/a/I0aVSaR), the problem is, I can't align properly the numbers on the center row.
I think that if I set the width of the numbers of the right row, the center row will be aligned. Right now the elements on the right row have different widths.
My problem now is how to set the width via script, I already tried rectransform.deltaSize, SetSizeWithCurrentAnchors and other similar things with no luck.
I've also tried to set a preferred width on the inspector on Layout Element but is ignored (https://imgur.com/a/O3hDeGN).
This is how the elements are created:
for (int i = 0; i < leaderboardEntries.Count; i++)
{
Transform newEntry = Instantiate(entryPrefab);
newEntry.SetParent(leaderboardEntriesPanel);
Text entryName = newEntry.GetChild(0).GetComponent<Text>();
Text entryScore = newEntry.GetChild(1).GetComponent<Text>();
Text entryRank = newEntry.GetChild(2).GetComponent<Text>();
entryName.text = leaderboardEntries[i].getName();
entryScore.text = leaderboardEntries[i].getScore().ToString();
entryRank.text = (i+1).ToString();
entryName.alignment = TextAnchor.UpperLeft;
entryScore.alignment = TextAnchor.UpperRight;
entryRank.alignment = TextAnchor.UpperRight;
}
The center row is the score, the left row the Name and the right row the rank.
Thanks!
if you want to change the width and height of a RectTransform you can try this:
rectTransform.sizeDelta = new Vector2( width, height);
but fixing their alignment need something else, first of all, all alignments should be same, UpperLeft or MiddleLeft for example.
then set anchor point of all your entries to the same value also.
then something like this should work properly:
// in this example I considered all anchor points are middle left and all
// alignments are middle left too
float width = leaderboardEntriesPanel.GetComponent<RectTransform>().rect.width;
for (int i = 0; i < leaderboardEntries.Count; i++)
{
Transform newEntry = Instantiate(entryPrefab);
newEntry.SetParent(leaderboardEntriesPanel);
Text entryName = newEntry.GetChild(0).GetComponent<Text>();
Text entryScore = newEntry.GetChild(1).GetComponent<Text>();
Text entryRank = newEntry.GetChild(2).GetComponent<Text>();
entryName.text = leaderboardEntries[i].getName();
entryScore.text = leaderboardEntries[i].getScore().ToString();
entryRank.text = (i+1).ToString();
entryName.GetComponent<RectTransform>().anchoredPosition = new Vector2(width / 15, entryName.GetComponent<RectTransform>().anchoredPosition.y);
entryScore.GetComponent<RectTransform>().anchoredPosition = new Vector2(width / 2, entryScore.GetComponent<RectTransform>().anchoredPosition.y);
entryRank.GetComponent<RectTransform>().anchoredPosition = new Vector2(width * 4 / 5, entryRank.GetComponent<RectTransform>().anchoredPosition.y);
}

EPPlus: Position image in a cell

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

Excel resize cell according to image size

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);

Issue Aligning Text with the Image Using MigraDoc

I am having header in this format --
"Heading" "Image" "Title"
Below is the code snippet i am using to achieve this -
Paragraph header = section.Headers.Primary.AddParagraph("Heading");
header.Format.Font.Bold = true;
header.AddTab();
Image image = header.AddImage("../../Images/logo.png");
image.Height = Unit.FromMillimeter(6);
header.AddFormattedText("Title", TextFormat.NotBold);
I need to align my "Image" and "Title" in such a way that title is vertically centrally aligned with respect to image's height, how can i achieve this ?
Any pointers/code snippet is much appreciated.
You could use a table to fit all the information in a certain structure:
// create document
Document MigraDokument = new Document();
// create section.
Section section = MigraDokument.AddSection();
section.PageSetup.PageFormat = PageFormat.A4;
// create a table
Table t = section.AddTable();
// size to use for the image and the image cell in the table
int size = 6;
// create 3 columns
Column column_header = t.AddColumn("6cm");
column_header.Format.Alignment = ParagraphAlignment.Center;
Column column_image = t.AddColumn(Unit.FromMillimeter(size));
column_image.Format.Alignment = ParagraphAlignment.Center;
Column column_text = t.AddColumn("4cm");
column_text.Format.Alignment = ParagraphAlignment.Center;
// Add 1 row to fill it with the content
Row r = t.AddRow();
// add you Header
Paragraph header = r.Cells[0].AddParagraph("Heading");
header.Format.Font.Bold = true;
header.AddTab();
// add the image
Image image = r.Cells[1].AddImage("../../logo.png");
image.Height = Unit.FromMillimeter(size);
// Add your Title
r.Cells[2].AddParagraph("Title");
// allign all of them
r.Cells[0].VerticalAlignment = VerticalAlignment.Center;
r.Cells[1].VerticalAlignment = VerticalAlignment.Center;
r.Cells[2].VerticalAlignment = VerticalAlignment.Center;
In my document the result looks the following:
Thanks to #MongZhu for suggesting the table way , posting the code snippet that i am using now just for future reference.
Table table = section.Headers.Primary.AddTable();
table.AddColumn("11cm");
table.AddColumn("2cm");
table.AddColumn("8cm");
Row row = table.AddRow();
row.VerticalAlignment = VerticalAlignment.Center;
Paragraph header = row.Cells[0].AddParagraph("Heading");
header.Format.Font.Bold = true;
Image image = row.Cells[1].AddImage("../../Images/logo.png");
image.Height = Unit.FromMillimeter(6);
row.Cells[2].AddParagraph("Title");

Categories

Resources