我有一个表单,允许用户创建自定义"邮票"放在PDF上。表单显示pdf第一页的图像,我希望用户基本上点击他们想要印章的屏幕,并能够预览它的样子。不要担心任何PDF内容,我已经处理好了。
为了使事情变得时髦,我有两个图像副本,正常的一个和一个亮度降低的副本。我显示低亮度图像,当用户将鼠标移动过来时,会显示或突出显示原始图像的一大块。然后,我在该区域中显示用户将要放在PDF上的文本。
我允许用户使用鼠标滚轮滚动并更改它们放置的文本的角度(从-45度到+45度)。
这是我的问题:我无法计算出正确的矩形/坐标。有时一切看起来都很棒,有时候(字体大小改变)它们并不合适。
如何计算x和y坐标: 放置旋转的文本 AND一个边界矩形,用10px
填充文本的宽度和高度下面的代码有效,直到我开始调整字体大小,然后一切都没有偏差。
前两张图片以较小的字体显示文字+边界矩形。看起来不错:
下一张图片显示,随着文字尺寸变大,我的像素四处移动并被切断。在更大的文本中,宽度/高度也会结束。
对不起,示例图片没有显示太多细节。我有实际的数据,我无法分享。
Private Sub PanelMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) '// handles the mouse move (handler added elsehwere)
With CType(sender, PictureBox)
.Image.Dispose() '// get rid of old image
Dim b As Bitmap = _curGray.Clone '// the low brightness image as the base image
'// stamp font and text values are initiated from another form
Using g As Graphics = Graphics.FromImage(b),
f As New Font(DefaultFont.FontFamily, CSng(_stmpTools.StampTextSize), If(_stmpTools.StampBold, FontStyle.Bold, FontStyle.Regular))
Const borderWidth As Integer = 10
Const borderPadding As Integer = 5
'// measure the string
Dim szx As SizeF = g.MeasureString(_stmpTools.StampText, f, Integer.MaxValue, StringFormat.GenericDefault)
Dim strLength As Single = szx.Width
Dim strHeight As Single = szx.Height
Dim x As Single = e.X - borderWidth - borderPadding,
y As Single = e.Y
Dim w As Double, h As Double
If Math.Abs(_angle) > Double.Epsilon Then
h = CDbl(strLength) * Math.Sin(CDbl(Math.Abs(_angle)) * Math.PI / 180.0F)
w = Math.Sqrt(CDbl(strLength) * CDbl(strLength) - h * h)
Else
'// its zero. so use calculated values
h = strHeight
w = strLength
End If
'// add space for the 10px border plus 5px of padding
Dim r As New Rectangle(0, 0, w, h)
r.Inflate(borderWidth + borderPadding, borderWidth + borderPadding)
h = r.Height
w = r.Width
'// keep box from moving off the left
If x < .Location.X Then
x = .Location.X
End If
'// keep box from moving off the right
If x > .Location.X + .Width - w Then
x = .Location.X + .Width - w
End If
'// I don't know, but these values work for most smaller fonts, but
'// it has got to be a fluke
If _angle > 0 Then
y = y - h + borderWidth + borderWidth
Else
y = y - borderWidth
End If
'// can't go off the top
If y < .Location.Y Then
y = .Location.Y
End If
'// can't go off the bottom
If y > .Location.Y + .Height - h Then
y = .Location.Y + .Height - h
End If
Dim rect As New Rectangle(x, y, w, h)
g.DrawImage(_curImg, rect, rect, GraphicsUnit.Pixel)
Using br As New SolidBrush(_stmpTools.StampTextColor)
RotateString(_stmpTools.StampText, _angle, e.X, e.Y, f, g, br)
End Using
'//draw bounding rectangle
Using p As New Pen(Color.Black, borderWidth)
g.DrawRectangle(p, rect)
End Using
End Using
'// set the picture box to show the new image
.Image = b
End With
End Sub
Private Sub RotateString(ByVal Text As String, ByVal angle As Integer, _
ByVal x As Integer, ByVal y As Integer, myfont As Font, mydrawing As Graphics, myColor As Brush)
Dim myMatrix As New Matrix
myMatrix.RotateAt(angle * -1, New Point(x, y)) 'Rotate drawing
mydrawing.Transform = myMatrix
mydrawing.DrawString(Text, myFont, myColor, x, y) 'Draw the text string
myMatrix.RotateAt(angle, New Point(x, y)) 'Rotate back
mydrawing.Transform = myMatrix
End Sub
在绘画方面,我并不是最伟大的。所以任何帮助都会很棒
使用@LarsTech的以下解决方案。我将g.FillRectangle替换为:
g.DrawImage(_curImg, r, r, GraphicsUnit.Pixel)
_curImg是原始图像的副本,亮度调高。当我从下面更改代码时,我最终得到:
注意双线。它们随着印章旋转,即使它们充当背景图像并且应该是未旋转的
根据建议,我将DrawStamp从@LarsTech更改为以下内容:
Private Sub DrawStamp(g As Graphics, text As String,
f As Font, center As Point, angle As Integer, backImg As Image)
Dim s As Size = g.MeasureString(text, f).ToSize
Dim r As New Rectangle(center.X - (s.Width / 2) - 16,
center.Y - (s.Height / 2) - 16,
s.Width + 32,
s.Height + 32)
g.DrawImage(backImg, r, r, GraphicsUnit.Pixel)
Using m As New Matrix
m.RotateAt(angle, center)
g.Transform = m
Using p As New Pen(Color.Black, 6)
g.DrawRectangle(p, r)
End Using
Using sf As New StringFormat
sf.LineAlignment = StringAlignment.Center
sf.Alignment = StringAlignment.Center
g.DrawString(text, f, Brushes.Black, r, sf)
End Using
g.ResetTransform()
End Using
End Sub
但是,我现在离开了
注意它绘制了背景,然后进行了旋转并绘制了标记。它几乎可以工作。在这个例子中,直线显示了预期的行为......但是我希望用背景填充整个图章。两侧多余的白色将是旋转到邮票背景中的东西。我很困惑,因为灰色&#39;然后,我会怀疑是要剪掉图像的一部分,但它们不是(如果我把它移到其他区域,我很遗憾不能在这里张贴),除了事实之外,通知没有偏差矩形的边是这样画的。
另一个编辑希望有更多信息
希望能够更好地解释我想要做的事情。我使用第三方PDF查看器,我需要允许用户将图像添加到PDF。观众不允许我在其上提出点击事件,因此为了抓住用户点击鼠标,我会执行以下操作:
按钮单击事件:
Dim bds As Rectangle = AxDPVActiveX1.Bounds
Dim pt As Point = AxDPVActiveX1.PointToScreen(bds.Location)
Using bit As Bitmap = New Bitmap(bds.Width, bds.Height)
Using g As Graphics = Graphics.FromImage(bit)
g.CopyFromScreen(New Point(pt.X - AxDPVActiveX1.Location.X, pt.Y - AxDPVActiveX1.Location.Y), Point.Empty, bds.Size)
End Using
_angle = 0
_curImg = bit.Clone
_curGray = Utils.CopyImageAndAdjustBrightness(bit, -100)
End Using
Dim p As New PictureBox
Utils.SetControlDoubleBuffered(p)
p.Dock = DockStyle.Fill
p.BackColor = Color.Transparent
AxDPVActiveX1.Visible = False
p.Image = _curImg.Clone
AddHandler p.MouseClick, AddressOf PanelDownMouse
AddHandler p.MouseMove, AddressOf PanelMouseMove
AddHandler p.MouseWheel, Sub(s As Object, ee As MouseEventArgs)
_angle = Math.Max(Math.Min(_angle + (ee.Delta / 30), 45), -45)
PanelMouseMove(s, ee)
End Sub
AddHandler p.MouseEnter, Sub(s As Object, ee As EventArgs)
CType(s, Control).Focus()
End Sub
AxDPVActiveX1.Parent.Controls.Add(p)
在那段代码之后我最终得到了两张图片。 _curgray是一个调整亮度的图像,_curImg是我原来的屏幕抓取。
_curGray: _curImg:
mouseMove
移动事件将应用于我的新图片框。这是问题前面的所有代码发挥作用的地方。
使用上面的代码,我的mouseMove事件会不断创建一个新图像以显示在我的图片框中。如果没有涉及轮换,我会得到我正在寻找的东西。请注意下图中邮票的背景比一切更亮。蓝色方块上方的部分略轻。我正在用这种方式吸引观众关注这个领域......这对我正在做的事情很重要。
然而,当对它应用旋转时,我似乎无法从原始图像复制。看下面的图像,背景与它一起旋转。我需要从ORIGINAL图像中抓取一个旋转的矩形。
http://msdn.microsoft.com/en-us/library/ms142040(v=vs.110).aspx Graphics.DrawImage()
接受
Public Sub DrawImage ( _
image As Image, _
destRect As Rectangle, _
srcRect As Rectangle, _
srcUnit As GraphicsUnit _
)
我可以指定从源图像中复制此源矩形(在本例中为_curImg)并放置到我的新图形上。它不允许我将变换应用于源矩形。基本上我想从源图像中复制相当于旋转矩形的区域(基于@larstech的转换)
我不知道如何更清楚地表达这个概念。如果它仍然没有意义,我会接受LarsTech的答案作为最佳答案并废弃我的想法。
答案 0 :(得分:1)
这只是三角学:
您知道c
,因为您知道原始文字的宽度,并且您知道h
,因为您知道文字的高度。您还需要知道alpha,它是您旋转文本的角度。
现在你需要运用数学的力量:首先在最后取小矩形。在左下角你可以看到,x的右角实际上是180°-90°-α,或90°-α。所以alpha也在对面的网站上找到。所以你可以找到x:
x = h * sin(alpha)
同样适用于y,但它是sin(90°-alpha)或cos(alpha)
y = h * cos(alpha)
接下来,您需要找到a和b来完成矩形。大三角形给你
a = w * cos(alpha)
和
b = w * sin(alpha)
然后将这些部分加在一起:
NewWidth = a + x
NewHeight = b + y
这样你就可以得到边界框的大小。至于坐标,它取决于打印旋转文本时实际定义的点。
答案 1 :(得分:1)
我会尝试将矩形和文本一起绘制:
Private Sub DrawStamp(g As Graphics, text As String,
f As Font, center As Point, angle As Integer)
Using m As New Matrix
g.SmoothingMode = SmoothingMode.AntiAlias
g.TextRenderingHint = TextRenderingHint.AntiAlias
m.RotateAt(angle, center)
g.Transform = m
Dim s As Size = g.MeasureString(text, f).ToSize
Dim r As New Rectangle(center.X - (s.Width / 2) - 16,
center.Y - (s.Height / 2) - 16,
s.Width + 32,
s.Height + 32)
g.FillRectangle(Brushes.White, r)
Using p As New Pen(Color.Black, 6)
g.DrawRectangle(p, r)
End Using
Using sf As New StringFormat
sf.LineAlignment = StringAlignment.Center
sf.Alignment = StringAlignment.Center
g.DrawString(text, f, Brushes.Black, r, sf)
End Using
g.ResetTransform()
End Using
End Sub
绘画示例:
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
e.Graphics.Clear(Color.LightGray)
Using f As New Font("Calibri", 16, FontStyle.Bold)
DrawStamp(e.Graphics,
"Reviewed By Doctor Papa",
f,
New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2),
-25)
End Using
End Sub
结果:
在这里,我更新了代码以“剪切”旋转的矩形,以便我可以在应用文本和边框之前从原始图像复制相同的区域:
Private Sub DrawStamp(g As Graphics, text As String,
f As Font, center As Point, angle As Integer)
Dim s As Size = g.MeasureString(text, f).ToSize
Dim r As New Rectangle(center.X - (s.Width / 2) - 16,
center.Y - (s.Height / 2) - 16,
s.Width + 32,
s.Height + 32)
Using bmp As New Bitmap(_curImg.Width, _curImg.Height)
Using gx As Graphics = Graphics.FromImage(bmp)
Using m As New Matrix
m.RotateAt(angle, center)
gx.Transform = m
gx.SetClip(r)
gx.ResetTransform()
End Using
gx.DrawImage(_curImg, Point.Empty)
End Using
g.DrawImage(bmp, Point.Empty)
End Using
Using m As New Matrix
g.SmoothingMode = SmoothingMode.AntiAlias
g.TextRenderingHint = TextRenderingHint.AntiAlias
m.RotateAt(angle, center)
g.Transform = m
Using p As New Pen(Color.Black, 6)
g.DrawRectangle(p, r)
End Using
Using sf As New StringFormat
sf.LineAlignment = StringAlignment.Center
sf.Alignment = StringAlignment.Center
g.DrawString(text, f, Brushes.Black, r, sf)
End Using
g.ResetTransform()
End Using
End Sub
新结果: