I am using an SVGDocument to convert a graph created using Mermaid to bitmap format so it can be copied to the clipboard, saved as a bitmaps to disk etc. The code I am using is shown below. I fill the bitmap with white first as if not the image has a light grey background when copied to the clipboard.
var HTMLDocument = new HtmlAgilityPack.HtmlDocument();
HTMLDocument.LoadHtml(HTML);
// Get the SVG representing the diagram
var SVGElement = HTMLDocument.DocumentNode.Descendants("svg").FirstOrDefault();
if (SVGElement == null)
{
return;
}
// Create a SVG document from the svg element in the HTML
var Document = SvgDocument.FromSvg<SvgDocument>(SVGElement.OuterHtml);
// Get the size of the SVG element so we can create a bitmap
// to match
var Bounds = Document.Bounds;
// Adjust the width and height to take account of the position
var Width = Bounds.Width + Bounds.Left + 10;
var Height = Bounds.Height + Bounds.Top + 100;
// Create a new bitmap of the correct size
var Bitmap = new Bitmap((int)Width, (int)Height);
// Fill it with white and then draw the SVG diagram
using (var BitMapGraphics = Graphics.FromImage(Bitmap))
{
BitMapGraphics.FillRectangle(new SolidBrush(Color.White), 0, 0, Width, Height);
Document.Draw(BitMapGraphics);
}
However, when pasted from the clipboard, the image does not show any text within the graph shapes. The original Mermaid diagram is:
When pasted from the clipboard using the bitmap created above becomes:
Any thoughts on why the text within the shapes is not being rendered would be much appreciated.
Related
I would like to be able to fill graphics objects using the tiling pattern functionality that pdf offers. For example, I would like to be able to draw something like this:
iText7 has a few objects related to patterns that could be useful, but I am having trouble figuring out how to use them and it is exceedingly difficult to find examples of similar code online.
iText7 provides the following classes that may be useful:
PdfPattern.Tiling
PatternColor
PdfPatternCanvas
It looks like you should be able to create a PdfPattern.Tiling object which references an image in some way and then create a PatternColor from that tiling object. Then you can set your canvas' fill color to the PatternColor you just created. An example of a function that does this is:
private void SetImageTilingFill(PdfCanvas canvas, PdfImageXObject img)
{
PdfPattern.Tiling tiling = new PdfPattern.Tiling((float)Inches2Points(img.GetHeight() / 96), (float)Inches2Points(img.GetWidth() / 96)); // create tiling object with width and height the size of the img
tiling.GetResources().AddImage(img);// add the image as a resource?
canvas.SetFillColor(new PatternColor(tiling)); // set fill color to PatternColor?
}
So far this approach has not been successful, my rectangle ends up solid black. Any suggestions would be much appreciated.
Using an image as a tile requires setting up the tile (it has a canvas to draw on). And then using it as a fill when drawing.
try (PdfWriter writer = new PdfWriter("tiling.pdf");
PdfDocument document = new PdfDocument(writer)) {
// creating image
ImageData imageData = ImageDataFactory.create("wavy.png");
PdfImageXObject imageXo = new PdfImageXObject(imageData);
// setting up the tile
PdfPattern.Tiling imageTile = new PdfPattern.Tiling(100, 100);
new PdfPatternCanvas(imageTile, document)
.addXObjectFittedIntoRectangle(imageXo, new Rectangle(0, 0, 100, 100))
.release();
PdfPage page = document.addNewPage();
PdfCanvas canvas = new PdfCanvas(page);
//using the tile
canvas.setFillColor(new PatternColor(imageTile));
canvas.rectangle(10, 10, 500, 900).fill();
canvas.release();
}
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 several paragraphs of text and couple of pictures between these paragraphs.
Now, I want to generate a picture using these materials, merging them vertically. But all the blocks of the text and pictures can not have bigger width than that of the generating picture, which means I have to zoom out the origin pictures, and fill each paragraph of text into a rectangle to fit the width.
Here is the tough thing:
To figure out the size of the rectangle to contain the text, I need use Graphics.MeasureString() method, which needs an instance of Graphics used to generate my picture(now, I'm using a blank template picture). But I do not know the exact size of this Graphics until I figure out all the sizes of rectangles and pictures.
Is there any method to get an instance of Graphics without source image?
Or is there any other method to do this work?
hope this could help dude .
http://chiragrdarji.wordpress.com/2008/05/09/generate-image-from-text-using-c-or-convert-text-in-to-image-using-c/
https://web.archive.org/web/20131231000000/http://tech.pro/tutorial/654/csharp-snippet-tutorial-how-to-draw-text-on-an-image
http://www.codeproject.com/Questions/388845/HOW-TO-MAKE-HIGH-QAULITY-IMAGE-WITH-TEXT-IN-Csharp
thank you
For people how are intrested in a WPF solution (as asked):
public static BitmapSource CreateImage(string text, double width, double heigth)
{
// create WPF control
var size = new Size(width, heigth);
var stackPanel = new StackPanel();
var header = new TextBlock();
header.Text = "Header";
header.FontWeight = FontWeights.Bold;
var content = new TextBlock();
content.TextWrapping = TextWrapping.Wrap;
content.Text = text;
stackPanel.Children.Add(header);
stackPanel.Children.Add(content);
// process layouting
stackPanel.Measure(size);
stackPanel.Arrange(new Rect(size));
// Render control to an image
RenderTargetBitmap rtb = new RenderTargetBitmap((int)stackPanel.ActualWidth, (int)stackPanel.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(stackPanel);
return rtb;
}
I have a .png image i wish to overlay on a base image.
My overlay image contains just a red slant line. I need to get the red line overlayed on the base image at the same co-ordinate as it is in overlay image.
The problem is I do not have the co-ordinates location.
I need to find it programmatically with C#. The overlay image will always be transparent or of white background. What code to find the line co-ordinates from overlay image?
You can create new image, render background image first and then render overlay image over it. Since overlay has alpha channel and line is placed where it should be (i mean there is opaque space on top and left side of line) you do not need coordinates. Illustration code:
Image imageBackground = Image.FromFile("bitmap1.png");
Image imageOverlay = Image.FromFile("bitmap2.png");
Image img = new Bitmap(imageBackground.Width, imageBackground.Height);
using (Graphics gr = Graphics.FromImage(img))
{
gr.DrawImage(imageBackground, new Point(0, 0));
gr.DrawImage(imageOverlay, new Point(0, 0));
}
img.Save("output.png", ImageFormat.Png);
If you are using WPF, why not use a path for your overlay instead of an image if it is a simple line? This would allow it to scale to any size, and has methods for manipulating its dimensions.
If you are using Winforms, there are some similar graphics drawing capabilities you might leverage. Getting the dimensions of the image should be easy, assuming you're using a PictureBox, the following properties should suffice:
myPictureBox.Top
myPictureBox.Bottom
myPictureBox.Left
myPictureBox.Right
Similarly, for a WPF Image:
myImage.Margin
I already needed to create a blank space around an image and I used the ImageFactory library to do that.
Here is the code. I guess you are capable to figure out how to adjust to your needs.
public static Image ResizedImage(Image image, int desiredWidth, int desiredHeight)
{
Image res = (Bitmap)image.Clone();
Image resizedImage;
ImageLayer imageLayer = new ImageLayer();
try
{
if (res != null)
{
//white background
res = new Bitmap(desiredWidth, desiredHeight, res.PixelFormat);
//image to handle
using (var imgf = new ImageFactory(true))
{
imgf
.Load(image)
.Resize(new ResizeLayer(new Size(desiredWidth, desiredHeight),
ResizeMode.Max,
AnchorPosition.Center,
false));
resizedImage = (Bitmap)imgf.Image.Clone();
}
//final image
if (resizedImage != null)
{
imageLayer.Image = resizedImage;
imageLayer.Size = new Size(resizedImage.Width, resizedImage.Height);
imageLayer.Opacity = 100;
using (var imgf = new ImageFactory(true))
{
imgf
.Load(res)
.BackgroundColor(Color.White)
.Overlay(imageLayer);
res = (Bitmap)imgf.Image.Clone();
}
}
}
}
catch (Exception ex)
{
ex.Message;
}
return res;
}
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.