How to fill a shape with the pattern when generating PDF? - c#

I am using PDFsharp to draw shapes in PDF. Sadly, I can't find any information on filling shapes with a custom pattern, 45-degree stripes in this case.
Stripes
I assume, there is no tool available to do that. How can I achieve the same effect?
Possible solution might be cropping manually drawn lines or using a library that is not PDFsharp, but I am just guessing at this point.

Create a path for the shape. Then just fill a rectangle that surrounds the shape with the pattern you need.
See "Clip through path" in the Graphics sample:
http://www.pdfsharp.net/wiki/Graphics-sample.ashx#Clip_through_path_16
The sample uses text as a shape, but the path can be anything you can draw.
XGraphicsPath path = new XGraphicsPath();
path.AddString("Clip!", new XFontFamily("Verdana"), XFontStyle.Bold, 90,
new XRect(0, 0, 250, 140), XStringFormats.Center);
gfx.IntersectClip(path);
// Draw a beam of dotted lines
XPen pen = XPens.DarkRed.Clone();
pen.DashStyle = XDashStyle.Dot;
for (double r = 0; r <= 90; r += 0.5)
gfx.DrawLine(pen, 0, 0,
250 * Math.Cos(r / 90 * Math.PI), 250 * Math.Sin(r / 90 Math.PI));

Related

Drawing Ellipses to a Bitmap. Works in .NET, does not work in Mono

I'm having some issues with drawing ellipses to a bitmap in a .NET 4.0 C# WinForms application running on mono 4.6.2 on Raspbian 9 (stretch).
The method in question generates a bitmap of 5 white circles of a specified diameter on a black screen. One circle per corner of the screen, and one in the center. The bitmap is meant to encompass the entire screen, so I'm getting the size for the bitmap as a rectangle via Screen.Bounds(), which in turn gives me Res.Width and Res.Height.
This code works as expected in Windows in .NET (which is what it was originally written for and tested on) but running the exe through mono on Raspbian, simply fills the bitmap with white. I have also found that running in mono for Windows (v5.10.0.160), the circles generated end up being smaller than when run in .NET.
private Bitmap GenerateCirclesBitmap(int diam)
{
Bitmap bmp = GenerateColorBitmap(Color.Black);
Graphics g = Graphics.FromImage(bmp);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
Brush whiteBrush = new SolidBrush(Color.White);
g.FillEllipse(whiteBrush, diam/7, diam/7, diam, diam); //Top Left Corner
g.FillEllipse(whiteBrush, diam/7, (Res.Height - diam)-(diam/7), diam, diam); //Bottom Left Corner
g.FillEllipse(whiteBrush, (Res.Width - diam) - (diam / 7), (Res.Height - diam) - (diam / 7), diam, diam); //Bottom Right Corner
g.FillEllipse(whiteBrush, (Res.Width - diam) - (diam / 7), diam/7, diam, diam); //Top Right Corner
g.FillEllipse(whiteBrush, (Res.Width / 2) - (diam / 2), (Res.Height / 2) - (diam / 2), diam, diam); //Center
g.DrawImage(bmp, Res.Width, Res.Height);
return bmp;
}
I was thinking maybe it was an issue with the dimensions of the screen, Res.Width and/or Res.Height, but if that were the case I would expect my method to generate color bars would have problems as well. However this code works as expected (8 color bars each 1/8 of total screen width) on both versions of mono as well as in .NET, which leads me to think that Res.Width is OK:
private Bitmap GenerateBarsBitmap(Color bar1, Color bar2, Color bar3, Color bar4, Color bar5, Color bar6)
{
Bitmap bmp = GenerateColorBitmap(Color.Black);
int barwidth = Res.Width / 8;
Color[] colors = new Color[8];
Brush b = new SolidBrush(Color.White);
Graphics g = Graphics.FromImage(bmp);
colors[0] = Color.White;
colors[1] = bar1;
colors[2] = bar2;
colors[3] = bar3;
colors[4] = bar4;
colors[5] = bar5;
colors[6] = bar6;
colors[7] = Color.Black;
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
for (int colornum = 0; colornum < colors.Length; colornum++)
{
b = new SolidBrush(colors[colornum]);
g.FillRectangle(b, barwidth * colornum, 0, barwidth, Res.Height);
b.Dispose();
}
return bmp;
}
Any ideas or insight would be appreciated. Thanks!
One difference between your two routines is the line
g.DrawImage(bmp, Res.Width, Res.Height);
It comes after you draw the circles. So in a fully compatible world the Mono framework should do the same as .NET does, i.e. draw the bitmap with all the new content over itself.
But fully compatible is not just hard. Given all the weirdness in the long history of GDI, .NET etc, it is in fact out of the question..
Mono seems to use the original version of the bitmap without the newly drawn circles and by drawing it over itself it deletes them.
So remove the unnessesary line and all should be well..

MonoGame - proper SpriteFont scaling

I'm making a GUI for my game and now I'm stuck on animations. I need to scale a font up when the mouse hovers over it and scale it down when it's not. Here's my code:
// Update()
if (!IsDisabled)
{
elapsedSecondsFast = (float)gameTime.ElapsedGameTime.TotalSeconds * 3;
if (Size.Contains(InputManager.MouseRect))
{
scale += elapsedSecondsFast;
if (scale >= 1.05f) scale = 1.05f;
}
else
{
scale -= elapsedSecondsFast;
if (scale <= 1.0f) scale = 1.0f;
}
}
// Draw()
if ((PrimaryFont != null) && (SecondaryFont != null) && (!string.IsNullOrEmpty(Text)))
{
if (IsHovered) TextOutliner.DrawBorderedText(spriteBatch, SecondaryFont, Text, new Vector2(TextRectangle.X, TextRectangle.Y), ForeColor, 0.0f, new Vector2((SecondaryFont.MeasureString(Text).X / 2 * scale - SecondaryFont.MeasureString(Text).X / 2), (SecondaryFont.MeasureString(Text).Y / 2 * scale - SecondaryFont.MeasureString(Text).Y / 2)), scale);
else TextOutliner.DrawBorderedText(spriteBatch, PrimaryFont, Text, new Vector2(TextRectangle.X, TextRectangle.Y), ForeColor, 0.0f, new Vector2(PrimaryFont.MeasureString(Text).X / 2 * scale - PrimaryFont.MeasureString(Text).X / 2, (PrimaryFont.MeasureString(Text).Y / 2 * scale - PrimaryFont.MeasureString(Text).Y / 2)), scale);
}
The above is a GUIElement class which is inherited by my Button class. Let me explain my code briefly:
PrimaryFont and SecondaryFont are 2 SpriteFonts that use the same
font but a different size. This gives me the scale up/down animation I need without blurring my PrimaryFont.
TextRectangle and Size are 2 different Rectangles. Since my button has a texture and text I decided not to draw text on the texture file but have the game position my text over the texture to "fake" the effect. So TextRectangle is the size and location of button text and Size is size and location of button texture. TextRectangle has its center point in the center of the Button texture. So far I have been using magic numbers to achieve this. This is the core of the problem here.
You can see my origin, I passed it to the DrawBorderedText method of my TextOutliner class. The attributes are in the same order as if it were a spriteBatch.DrawString() call, only without SpriteEffects and layerDepth.
The Problem
Since I'm scaling the font (origin = center I think) it will no longer be in the center of the button. And since I have been using magic numbers to position the un-scaled text over the center of the button texture, I don't want to be forced to do the same thing for scaled text. I'm looking for an algorithm that would always position the text in the middle of my 270x72 texture, no matter if the text is scaled or not, while keeping the scale animation shown above, for each instance of the Button class. Preferably to have its origin point in the center.
Edit
So should I draw like this:
if (IsHovered) TextOutliner.DrawBorderedText(spriteBatch, SecondaryFont, Text, new Vector2(TextRectangle.X, TextRectangle.Y), ForeColor, 0.0f, new Vector2((SecondaryFont.MeasureString(Text).X / 2), (SecondaryFont.MeasureString(Text).Y / 2)), scale);
else TextOutliner.DrawBorderedText(spriteBatch, PrimaryFont, Text, new Vector2(TextRectangle.X, TextRectangle.Y), ForeColor, 0.0f, new Vector2(PrimaryFont.MeasureString(Text).X / 2, (PrimaryFont.MeasureString(Text).Y / 2)), scale);
and then draw the button's text at btn.Size.Width / 2, btn.Size.Height / 2, (int)MainGame.GameFontLarge.MeasureString("Play").X / 2, (int)MainGame.GameFontLarge.MeasureString("Play").Y / 2
So I eventually found the algorithm by myself and finally eliminated the need of using magic numbers for position of the text. Here's my technique:
TextOutliner.DrawBorderedText(spriteBatch, Font, Text, new Vector2(Size.X + ((Size.Width - Font.MeasureString(Text).X) / 2), Size.Y + ((Size.Height - Font.MeasureString(Text).Y)) / 2), ForeColor, 0.0f, new Vector2((Font.MeasureString(Text).X / 2 * scale - Font.MeasureString(Text).X / 2), (Font.MeasureString(Text).Y / 2 * scale - Font.MeasureString(Text).Y / 2)), scale);
I couldn't take the scale out of the origin equation as #LibertyLocked suggested, because the font was scaling from top-left point upon Mouse Hover and not the center as I want it to.

Draw Lines on Image in PDF using ItextSharp

I am trying to draw lines on image that needs to be loaded on pdf document, just like we draw graphics on paint event of any control, but it fails to do so.
Any Suggestions ?
Document pdfDoc = new Document(PageSize.A2, 10f, 10f, 10f, 0f);
pdfDoc.AddHeader("Batting Report - ", txtSearchBox.Text);
iTextSharp.text.Image pic = iTextSharp.text.Image.GetInstance(Properties.Resources.bgWW
, System.Drawing.Imaging.ImageFormat.Jpeg);
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, stream);
pdfDoc.Open();
pdfDoc.Add(pic);
So, how do I modify the pic object of ItextSharpImage so that it can draw lines on the image?
Please take a look at the WatermarkedImages4 example. It is based on the WatermarkedImages1 example I referred to in the comments. The only different between the two examples is that we add text in the example written in answer to How to add text to an image?
whereas we add lines in the example written in answer to your question.
We add images like this:
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE1)));
The getWatermarkedImage() method looks like this:
public Image getWatermarkedImage(PdfContentByte cb, Image img) throws DocumentException {
float width = img.getScaledWidth();
float height = img.getScaledHeight();
PdfTemplate template = cb.createTemplate(width, height);
template.addImage(img, width, 0, 0, height, 0, 0);
template.saveState();
template.setColorStroke(BaseColor.GREEN);
template.setLineWidth(3);
template.moveTo(width * .25f, height * .25f);
template.lineTo(width * .75f, height * .75f);
template.moveTo(width * .25f, height * .75f);
template.lineTo(width * .25f, height * .25f);
template.stroke();
template.setColorStroke(BaseColor.WHITE);
template.ellipse(0, 0, width, height);
template.stroke();
template.restoreState();
return Image.getInstance(template);
}
As you can see, I add two green lines using moveTo(), lineTo() and stroke(). I also add a white ellipse using the ellipse() and stroke() method.
This results in a PDF that looks like this:
As you can see, the shape of the ellipse and the position of the lines are different for the different images because I defined my coordinates based on the width and the height of 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..

Border in DrawRectangle

Well, I'm coding the OnPaint event for my own control and it is very nescessary for me to make it pixel-accurate.
I've got a little problem with borders of rectangles.
See picture:
removed dead ImageShack link
These two rectangles were drawn with the same location and size parameters, but using different size of the pen. See what happend? When border became larger it has eaten the free space before the rectangle (on the left).
I wonder if there is some kind of property which makes border be drawn inside of the rectangle, so that the distance to rectangle will always be the same. Thanks.
You can do this by specifying PenAlignment
Pen pen = new Pen(Color.Black, 2);
pen.Alignment = PenAlignment.Inset; //<-- this
g.DrawRectangle(pen, rect);
If you want the outer bounds of the rectangle to be constrained in all directions you will need to recalculate it in relation to the pen width:
private void DrawRectangle(Graphics g, Rectangle rect, float penWidth)
{
using (Pen pen = new Pen(SystemColors.ControlDark, penWidth))
{
float shrinkAmount = pen.Width / 2;
g.DrawRectangle(
pen,
rect.X + shrinkAmount, // move half a pen-width to the right
rect.Y + shrinkAmount, // move half a pen-width to the down
rect.Width - penWidth, // shrink width with one pen-width
rect.Height - penWidth); // shrink height with one pen-width
}
}
This isn't a direct answer to the question, but you might want to consider using the ControlPaint.DrawBorder method. You can specify the border style, colour, and various other properties. I also believe it handles adjusting the margins for you.
I guess not... but you may move the drawing position half the pen size to the bottom right

Categories