使用.DrawString时,字体显示得太小或太大

时间:2014-10-23 22:06:42

标签: vb.net graphics fonts bitmap drawstring

我需要在图像的底部添加半英寸的空白区域,并在左下角和右下角(在新添加的空白区域内)绘制一个字符串。一切似乎工作正常,但字体有时看起来太小或太大。

我想我需要以某种方式将抽绳字体缩放到图像的大小?我已经筋疲力尽,试图解决这个问题......请帮助!!

见下面的代码----

Imports System.IO
Imports System.Drawing.Graphics
Imports System.Drawing
Imports System.Drawing.Bitmap
Imports System.Drawing.Imaging
Public Class Form1
    Dim ofilepath As String = "C:\temp\20141022\TEST0000001.tif"
    Dim nfilepath As String = "C:\temp\20141022\new.tif"

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Dim newbm As New Bitmap(AddBorderAndStamp(Bitmap.FromFile(ofilepath), Color.White, 50, Path.GetFileNameWithoutExtension(ofilepath), "CONFIDENTIAL"))
        newbm.Save(nfilepath, Imaging.ImageFormat.Tiff)
        Me.Close()
    End Sub

    Public Function AddBorderAndStamp(ByVal bm As Bitmap, ByVal borderColor As System.Drawing.Color, ByVal borderWidthInPixels As Integer, ByVal bates As String, ByVal designation As String) As Bitmap
        Dim voffset As Integer = 75
        Dim hoffset As Integer = 15
        Dim newBitmap As New Bitmap(bm.Width, bm.Height + (borderWidthInPixels * 2))
        Dim mfont As Font = New Font("Arial", 32, FontStyle.Bold)

        For x As Integer = 0 To newBitmap.Width - 1
            For y As Integer = newBitmap.Height - 1 To (newBitmap.Height - 1) - borderWidthInPixels Step -1
                newBitmap.SetPixel(x, y, borderColor)
            Next
        Next
        Dim gr As System.Drawing.Graphics = Graphics.FromImage(newBitmap)
        gr.Clear(Color.White)
        gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)


        Dim textSize As SizeF = gr.MeasureString(bates, mfont)
        gr.DrawString(bates, mfont, Brushes.Black, bm.Width - textSize.Width - hoffset, newBitmap.Height - voffset)
        gr.DrawString(designation, mfont, Brushes.Black, hoffset, newBitmap.Height - voffset)
        gr.Dispose()

        Return newBitmap

    End Function
End Class

1 个答案:

答案 0 :(得分:0)

如果您想知道此行bates字符串的大小(宽度):

    Dim textSize As SizeF = gr.MeasureString(bates, mfont)

在将位图绘制到位图上之前,您必须计算要在font size上执行的缩放,以确保文本缩小(如果太长)或拉伸(如果太窄)虽然没有过度拉伸高度 - 意味着要在图像上绘制。

然后, 只有当您拥有有效的字体 时,才能像使用以下行一样绘制文字

    gr.DrawString(bates, mfont, Brushes.Black, bm.Width - textSize.Width - hoffset, newBitmap.Height - voffset)
    gr.DrawString(designation, mfont, Brushes.Black, hoffset, newBitmap.Height - voffset)

要了解要使用的字体大小,请查看this SO Question Page 我知道它是C#,但这基本上是你在绘制贝茨和指定文字之前必须做的事情。区别在于两点:

  • 你有两个String缩放在一个剪辑矩形(bates和指定)而不是一个。
  • 您必须考虑左侧designation和右侧bates之间的(默认)分隔。

但这两点可以很容易地解决。这也意味着您的问题可能重复(StackOverflow不推荐同一问题的多个变体,因为SO 的目的 直接将您带到一个问题和答案的页面 - 如果已经回答 - 但是不要用相同主题的数十个副本充斥您的身份)

但恕我直言,两个字符串(名称和贝茨)足以将此视为一个新问题,因为其他主题未涵盖的代码处理。

saeed的功能转换为vb.Net

Private Function FindFont( _
    ByRef rg As Graphics, _
    ByVal testString as String, _
    ByVal clipRoom As Size, _
    ByVal preferedFont As Font) As Font

    ' You should perform some scale functions...
    Dim realSize As SizeF = rg.MeasureString(testString, PreferedFont)
    Dim heightScaleRatio As Double = clipRoom.Height / realSize.Height
    Dim widthScaleRatio As Double = clipRoom.Width / realSize.Width
    Dim scaleRatio As Double
    Dim newFontSize As Single ' I'm used to declare everything on top.

    If heightScaleRatio < widthScaleRatio Then
        scaleRatio = heightScaleRatio
    Else
        scaleRatio = widthScaleRatio
    End If
    newFontSize = CSng(preferedFont.Size * ScaleRatio)

    Return New Font(preferedFont.FontFamily, newFontSize, preferedFont.Style)
End Function

为了提供代码,Credits首先使用saeed,但如果saeed使用了我不知道的其他人的代码,原作者的信用将取代saeed&#39>。 < / p>

该功能的必要参数是:

  • rg As Graphics。您已经拥有它,即rg功能中的AddBorderAndStamp
  • testString As String。你也拥有它,它只是testString = designation + bates
  • clipRoom As Size。你还没有那个变量。您必须在AddBorderAndStamp函数中声明它,并使用一些逻辑来定义其.Width.Height值。
  • preferedFont As Font。你也已经有了,mfont As Font = New Font("Arial", 32, FontStyle.Bold)

要添加到AddBorderAndStamp功能的声明是:

    Dim clipRoom As Size ' Declare it.
    Dim stampSeparation As Integer = 80 ' why 80 ? dunno ! it's an arbitrary value..
  

stampSeparation是一个任意变量,表示宽度IN   指定和贝茨之间的PIXEL。基本上,它看起来像这样:

'< - - - - - - - total Bitmap width  - - - - - - - >
'|        |designation|_____________|bates|        |

'|        |           |             |     |        ^ Right Image boder
'|        |           |             |     ^ Right margin
'|        |           |             ^ Left plot of your bates String
'|        |           <-------------> Length or Width of stampSeparation
'|        ^ Left plot of your designation String
'^ Left image border

您的可用空间来写文字是

的总和
  • designation.Width
  • bates.Width
  • stampSeparation

但是因为你想要通过stampSeparation分隔指定和bates beeing, stampSeparation必须从可用的剪辑宽度中减去。所以:

    clipRoom.Width = newBitmap.Width - (hOffset * 2) - stampSeparation
                   ' newBitmap.Width - 30            - 80
                   ' newBitmap.Width - 110
    ' CAREFULL !!! clipRoom.Width MUST be positive !!!
    ' Check your bm is wide enough, say at least 200 pixels Width...

您在底部的房间是一个不同的故事:您的AddBorderAndStamp函数有一个borderWidthInPixels(作为整数)参数,用于定义您在顶部/底部平均值添加的房间。然后使用voffset(作为整数)变量将图章向上移动..这意味着您在图像底部的高度可用空间来绘制文本:

    clipRoom.Height = vOffset ' ?
                    ' 75 ?
    ' (CAREFULL !!! clipRoom.Height MUST be positive !!!)

如果我是你,我会根据clipRoom.Height的一小部分动态定义AddBorderAndStamp,并在之后动态计算vOffset 使用designation知道batesMeasureString高度的最终高度......但那将是一种过度杀伤......

    ' perhaps one simple logic like the following would suffice
    ' clipRoom.Height = CInt(vOffset * 4 / 5)

现在您有clipRoom以及调用FindFont(...)功能所需的所有其他内容。


所以:

    Public Function AddBorderAndStamp(...) As Bitmap
        ' Dim vOffset ' ...
        ' Dim hOffset ' ...
        ' ...

        ' add the following declarations :
        Dim clipRoom As Size
        Dim stampSeparation As Integer = 80 ' or 60 as you like but small enough

        ' ...
        ' ... your function block code goes here until the following line :
        gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)

        ' Calculate the available clip room...
        clipRoom.Width = newBitmap.Width - (hOffset * 2) - stampSeparation
        clipRoom.Height = vOffset
        ' ^^ remember : the logic to handle valid Width and Height is up to you
        ' I know how to handle that, but you should aswell, or try at least.

        ' Now, update the size of your font...
        mFont = FindFont(rg, designation + bates, clipRoom, mFont)

        ' then continue with the rest of the code...
        Dim textSize As SizeF = gr.MeasureString(bates, mfont)
        gr.DrawString(bates, mfont, Brushes.Black, bm.Width - textSize.Width - hoffset, newBitmap.Height - voffset)
        gr.DrawString(designation, mfont, Brushes.Black, hoffset, newBitmap.Height - voffset)
        gr.Dispose()

        Return newBitmap

    End Function

顺便说一下

  

您好。您应该在帖子中添加[vb.Net]标记,然后移除   [image][bitmap],因为这些是同义词。

如果没有格式化,很难发现代码拼写错误。但是,由于你是StackOverflow(SO)的新手,我不想向你投票或标记你,但我坚持:更新你问题的标签。


备注1:

您写道:

        For x As Integer = 0 To newBitmap.Width - 1
            For y As Integer = newBitmap.Height - 1 To (newBitmap.Height - 1) - borderWidthInPixels Step -1
                newBitmap.SetPixel(x, y, borderColor)
            Next
        Next
        Dim gr As System.Drawing.Graphics = Graphics.FromImage(newBitmap)
        gr.Clear(Color.White)
  1. 您从底部逐个像素地更改newBitmap的颜色。我没关系,但有更好的方法来实现这一点。

  2. 但是,你要声明一个Graphics来处理newBitmap上的绘图,然后你调用gr.Clear(Color.White) ??? 是不是 完全 用白色绘制你的newBitmap,从而破坏了上面的SetPixel循环的目的?

  3. 我不明白。也许你希望这个功能浪费时间,所以只需使用

    System.Threading.Thread.Sleep(X) ' where X is an Integer variable in miliseconds
    

    否则,我建议你摆脱SetPixel及其包围For循环,然后使用你的gr Graphics填充一个Rectangle:

            ' ...
            Dim gr As System.Drawing.Graphics = Graphics.FromImage(newBitmap)
            gr.Clear(Color.White)
            gr.FillRectangle( _
                New SolidBrush(borderColor), _
                0, newBitmap.Height - borderWidthInPixels, _
                newBitmap.Width, borderWidthInPixels)
            gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)
            ' ...
    

    备注2:

            ' you've set newBitmap to have 
            ' bm.Heigth expanded by two times the value of borderWidthInPixels
            Dim newBitmap As New Bitmap(bm.Width, bm.Height + (borderWidthInPixels * 2))
    
            ' ...
    
            ' Then you plot bm at the very top of newBitmap
            gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)
            ' that means you have a blank space at the bottom
            ' that height twice the value of borderWidthInPixels
    
            ' ...
    
            ' But you plot bates and designation 
            ' at a fixed vOffset from the bottom of newBitmap
            gr.DrawString(bates, , , , newBitmap.Height - voffset)
            gr.DrawString(designation, , , , newBitmap.Height - voffset)
    

    borderWidthInPixelsvoffset之间的关系对我来说仍然不清楚,这可能会误导上述clipRoom.Height的表述。

    但是,我认为我已经给出了足够的材料来帮助你,即使上面的clipRoom.Height公式错误,也很容易修复它。

    你还必须明白我默认使用 saeed 方法,但是通过阅读我链接的帖子,你会发现其他方法,迭代方法,我不会这样做很多因为它们是迭代 CPU重而不总是更精确。你甚至可以找到关于TextToBitmap的提示;尝试更适合你,但它也更复杂..对于你正在做的事情,我认为一个或两个像素取代输出不是问题。

    对于垂直展示位置,您说&#34; ..但有时候字体看起来太小或太大了。&#34; 这不够准确全面了解这个问题。您应该发布&#34;太小&#34; 案例和&#34;太大&#34; 案例的两个图像样本。由于您没有更改代码中的字体大小,我认为太小问题是指定和bates之间的空间太大,太大重叠文本。没有一个参考可能的垂直布局问题,所以我的答案并没有详细阐述。这就是为什么我不在这里引入多线逻辑,这将代表另一个问题(如果没有可用的话)