用于 Web 的文本到图像(带旋转)
本文将解释如何创建一个 HttpHandler,它能够接受一个文本字符串,可选地对其进行旋转,并返回旋转后的文本的图像。
引言
本文将解释如何创建一个 HttpHandler,它能够接受一个文本字符串,可选地对其进行旋转,并返回旋转后的文本的图像。
背景
我最近有一个在网页上旋转文本的愿望。 我原以为这可以通过 CSS 轻松实现,但事实并非如此。 虽然有一些 CSS 属性允许文本旋转,但它们因浏览器而异,而且我找到的都仅限于 90 度增量。 我想要能够任意角度旋转文本。
不久之后,我发现唯一似乎可行的办法是动态生成旋转文本后的图像。
开发环境
我认为此代码中使用的 C# 功能不要求必须使用 C# 3.0。 我还认为我使用的唯一 C# 2.0 功能是合并运算符。 我用它来初始化一些变量,如果您确实需要它与 C# 1.0 兼容,可以很容易地重写这部分。 但是,我使用 Visual Studio 2008 开发了此代码,这可能比旧版本的 Visual Studio 更容易创建 HttpHandler。
挑战
为了编写此代码,我需要克服几个挑战。
HttpHandler
首先,我意识到将其实现为 HttpHandler 是最好的方法。 这将允许我在网站的任何图像的 src
属性中嵌入 HttpHandler 的 URL。 事实上,如果我让 HttpHandler 更具全局可用性,我就可以将其嵌入到任何能够访问该 URL 的网页中,而不管网页的性质如何。 我的意思是,该页面不需要由 ASP.NET 生成。 它可以是托管在基于 Java 的 Web 服务器上的 JSP 页面,甚至可以是任何 Web 服务器上的静态 HTML 页面。 我以前从未写过 HttpHandler,但使用 Visual Studio 2008 发现它相当简单,而且网上有很多这方面的例子。
以编程方式生成图像
此外,我还需要以编程方式生成文本图像。 我以前从未这样做过,但它也相当简单。 网上有很多这方面的例子。
流式传输图像
我知道我不想实际创建图像的物理文件,所以将图像流式传输到浏览器是最好的方法。 我知道这可以做到,因为我使用了一个条形码库,它做的事情也是一样的。 它也是作为 HttpHandler 实现的,网上有很多关于将图像流式传输到浏览器的例子。
图像透明度
就我而言,我需要能够叠加多个文本图像,并且它们不会相互遮挡。 这意味着我需要能够创建具有透明背景的图像。 透明度立即将我们限制在创建 GIF 和 PNG 文件,这两者都存在问题。 旧的浏览器,特别是 Internet Explorer 6.0 及更早版本,无法正确支持 PNG 透明度。 用于以编程方式创建 GIF 图像的 .NET 库也无法很好地处理透明度。 这两种情况都带来了问题。
我能找到一些关于如何修复 GIF 透明度问题的文章,但大多数都很复杂,需要大量代码。 我终于找到了一篇文章,它用很少的代码就解决了这个问题,我采用了这种方法。 网上也有一些关于 Internet Explorer PNG 透明度问题的解决方法。 我几年前尝试过一种,但没有成功。 我想那可能是我的问题,因为有足够多的文章讨论这个解决方法,它应该是有用的。 我在这里提到这一点,以防您想生成 PNG 图像并想知道如何解决旧浏览器的问题。
这个问题最终非常具有挑战性,但我们的 HttpHandler 将能够根据传递给它的查询字符串参数来创建 GIF 或 PNG 文件。
文本旋转
此代码的根本原因在于需要旋转文本,而这似乎应该是小事一桩。 有一个简单的 .NET API 调用可以旋转文本,网上有很多例子。 确实,旋转文本很容易。 但困难的是在旋转后实际看到文本。 这是因为使用 .NET API 旋转时的旋转点。 事实证明,旋转点始终是左上角。 在我看来,这非常不直观,使得使用 API 旋转事物非常困难。 问题是,如果您围绕左上角旋转,您旋转的内容在进入第三象限时会完全离开图像,并且在第二象限和第一象限的大部分以及第四象限的一部分中完全隐藏。
在上图所示的图表中,实际文本图像由“Text @0”文本周围的黑框表示。 如您所见,文本在旋转 90 度及以上时完全离开了图像。 实际上,它在仅旋转几度后就开始离开图像,但在早期阶段您仍然可以看到大部分文本,因此您知道它在那里。 但一旦达到 90 度,文本就完全消失了。 这直到您知道发生了什么才是一个很大的谜。
这显然是一个常见问题,因为在网上找到类似“我刚刚旋转了我的东西,但它消失了。 我做错了什么?”这样的问题并不难。 遗憾的是,这最终成为最困难的挑战,需要我重新投入一些时间进行三角测量!
三角测量
我上一次做三角测量是在高中和大学时期,已经 20 多年了。 谁能想到在我职业生涯的这个晚期,它竟然变得实用且有用? 当然不是我!
所以我的文本离开了图像,但我发现有一个 API 可以让我移动旋转点。 我以为我抓住了机会。 我只是想将旋转点移动到图像中心,并让文本围绕该点旋转。 这似乎很简单。 不行。 没用。 还记得我之前说的,旋转始终围绕左上角发生。 所以我可以将旋转点移动到图像中心,但文本仍然围绕该点作为其左上角旋转,结果甚至更糟。 文本离开图像的幅度更大!
我在确定解决方案时遇到了很多困难。 我尝试猜测数字直到能看到文本,然后尝试确定模式、数学方程来预测所需的数字。 这似乎并不系统……至少不像我这种线性思维的人。
灵光一闪
在电影《灵感闪现》中,由演员格雷格·金尼尔饰演的角色向陪审团讲述了他的故事。 他引用了之前一个法院的判决,即发明常常是灵光一闪而产生的,金尼尔接着解释了他发明间歇性挡风玻璃刮水器的灵光一闪。 我解决这个问题的灵光一闪是当我意识到对于第四象限(旋转角度 0 到 89 度),我只想沿着 x 轴滑动旋转点,保持旋转点在 y = 0。 在这次讨论中,我将文本高度称为 th,文本宽度称为 tw。 这些都可以使用 .NET API 来确定。
在上图所示的图表中,我只是沿着 x 轴滑动图像的左上角,保持 y = 0。 这使得文本始终保持在图像内。 这也清楚地表明 x 坐标的范围是从 0 到文本高度 th。 现在我只需要确定三角测量来确定 x 坐标。
在上图所示的图表中,我可以看到旋转角度(30 度)也是内三角形与 y 轴的旋转角度。 我已经知道我的文本高度 th,可以使用 .NET API 来获取。 由于三角学指出 SIN θ = 对边/斜边,我可以确定 X。 使用代数,我现在知道 X = SIN 30 * th。 我必须先将 30 度转换为弧度,以便 .NET 数学 API 使用。 所以,我现在有了第四象限的方程!
Math.Sin(radians) * th
然后我发现,对于第一象限(旋转角度 271 到 359 度),我只需要沿着 y 轴向下滑动,同时保持 x = 0。 使用与上图类似的图表,我意识到 y 的变化范围是从 0 到文本宽度 tw,我也知道这个值来自 .NET API。 同样,一个简单的三角公式为我提供了 y 坐标
Math.Sin(radians) * tw
我本以为确定第二象限和第三象限的公式会很简单,只是第一象限和第四象限的镜像,但我没那么幸运。 对于这两个象限,我的 x 和 y 坐标都必须移动。 不过,到这个时候,我的三角学头脑已经开始运转了! 感觉就像回到了 11 年级。 实际上,虽然我那时候对三角学的掌握不如现在,但我不太确定我是否能像现在一样应用数学。
观察上图所示的图表,您可以看到情况开始变得更复杂。 在第三象限中,文本的左上角将沿着一条曲线路径从 x = th, y = 0 移动到 x = tw, y = th。 要确定公式,需要仔细观察。
在上图所示的图表中,我将文本旋转了 120 度。 现在是时候提一下,对于某些象限,我计算了与实际旋转角度相关的参考角度。 对于第三象限,我从旋转角度中减去 90。 所以对于 120 度的旋转,我的参考角度变为 30,这正是在上图中标记为 θ 的角度。 我知道我的 x 坐标需要是 x1 + x2。 对于这个计算,我还必须知道 COS 的定义。 如果您不记得三角学中的 COS,COS θ = 邻边/斜边。 因此
x1 = Math.Sin(radians) * tw
x2 = Math.Cos(radians) * th
所以我的新 x 坐标是
(Math.Sin(radians) * tw) + (Math.Cos(radians) * th)
我的 y 坐标是
Math.Sin(radians) * th
确定第二象限的新 x 和 y 坐标与第三象限类似,除了我最终需要确定 y1 和 y2 以及一个 x。
图像尺寸调整
当您查看这些图表时,您可能会注意到图像尺寸也需要改变。 当文本旋转 90 度时,图像的宽度需要设置为文本的高度,而图像的高度需要设置为文本的宽度。 在两者之间,还有更多的三角测量,但远不如确定新的旋转点那么复杂。 事实上,在我开发代码时,我实际上先写了这部分,发现它并不难。 因此,即使在使用三角测量确定旋转后的新图像尺寸之后,确定新的坐标仍然是一个挑战。 同样,如果不是“灵感闪现”,我也不确定会走向何方。
代码分析
让我们开始编写代码。 第一步是在新项目或现有项目中添加一个 HttpHandler。 在某些方面,我非常喜欢创建独立的 ASP.NET 网站来托管这个 HttpHandler,因为不要忘记,一旦它运行起来,任何能够访问托管 handler 的网站的 HTML 页面都可以调用它并将文本转换为图像。
创建 HttpHandler
在本篇文章中,我将假设您已经拥有一个 ASP.NET 网站或 Web 应用程序,并且只想向其中添加一个 HttpHandler。 在 Visual Studio 2008 中,右键单击解决方案资源管理器中的项目,选择“添加新项”菜单,然后选择“通用处理程序”模板。 将“名称”字段编辑为 TextImage.ashx。 这将为您创建一个名为 TextImage.ashx 的文件,其中包含一个名为 Handler 的存根类,该类实现了 IHttpHandler
接口。 它还会为您生成一个 ProcessRequest
方法和一个 IsReusable
只读属性,因为这些是 IHttpHandler
接口所必需的。
Using 指令
对于此代码,您需要以下 using
指令
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Web;
私有静态变量
由于我将在多个地方为同一个参数使用默认值,而不是在所有需要的地方提供字面默认值,因此我想为每个参数的默认值创建一些私有静态变量,并在整个代码中重用它们,以确保默认值的一致性。
public class TextImage : IHttpHandler {
private static string d_fontFamily = "Arial";
private static int d_fontSize = 10;
private static bool d_bold = false;
private static bool d_italic = false;
private static string d_color = "Black";
private static string d_bgColor = "Transparent";
private static int d_rotation = 0;
private static int d_underlineWidth = 0;
private static string d_underlineColor = "Black";
private static bool d_usePng = false;
到目前为止,这段代码并没有太多内容。 我只是定义了一些私有静态变量,并为每个参数分配了默认值。 这是为了防止要求传递每个参数。
ProcessRequest
让我们看一下实现 ProcessRequest 方法的代码。 它不会执行任何实际工作。 它只会收集传递的参数,处理它们的默认值,并在适当时返回一个模板(以图像形式,因为这是调用者期望返回的内容)。
public void ProcessRequest (HttpContext context) {
string fontFamily = context.Request.QueryString["FontFamily"] ?? d_fontFamily;
int fontSize = Int32.Parse(context.Request.QueryString["FontSize"] ?? d_fontSize.ToString());
bool bold = Boolean.Parse(context.Request.QueryString["Bold"] ?? d_bold.ToString());
bool italic = Boolean.Parse(context.Request.QueryString["Italic"] ?? d_italic.ToString());
string color = context.Request.QueryString["TextColor"] ?? d_color;
string bgColor = context.Request.QueryString["BackgroundColor"] ?? d_bgColor;
int rotation = Int32.Parse(context.Request.QueryString["Rotation"] ?? d_rotation.ToString());
int underlineWidth = Int32.Parse(context.Request.QueryString["UnderlineWidth"] ?? d_underlineWidth.ToString());
string underlineColor = context.Request.QueryString["UnderlineColor"] ?? d_underlineColor;
bool usePng = Boolean.Parse(context.Request.QueryString["UsePng"] ?? d_usePng.ToString());
到目前为止,我所做的只是为参数创建了一些局部变量,并将它们初始化为传递的参数或默认值。 我在这里使用了 C# 2.0 合并运算符 (??
),所以如果您需要 C# 1.0,您可以重写此初始化部分。
string text = "";
if (context.Request.QueryString["Text"] == null)
{
// Since no query string parameters have been passed, I assume the user
// doesn't know how to call this handler, so let's output the parameters.
text = "Supported parameters: Text,FontFamily,FontSize,Bold,Italic,TextColor,BackgroundColor,Rotation,UnderlineWidth,UnderlineColor,UsePng\nExample: TextImage.ashx?Text=LINQ%20Rocks!&FontFamily=Arial&FontSize=18&Bold=True&Italic=False&TextColor=Black&BackgroundColor=Transparent&Rotation=90&UnderlineWidth=0&UnderlineColor=%23FF0000&UsePng=False";
fontFamily = d_fontFamily;
fontSize = d_fontSize;
bold = d_bold;
italic = d_italic;
color = d_color;
bgColor = d_bgColor;
rotation = d_rotation;
underlineWidth = d_underlineWidth;
underlineColor = d_underlineColor;
usePng = d_usePng;
}
else
{
text = context.Request.QueryString["Text"];
}
在上面的代码中,我检查 Text 参数,如果未传递,则将其视为请求帮助文本,并将所有参数设置为其默认值。 因此,即使传递了一些值,我也已将它们恢复为默认值。 这是为了防止可能难以阅读参数帮助的参数干扰。 例如,如果字体大小传递为 4,或旋转角度传递为 180,我已将其恢复为更易读的值,如字体大小 10 和旋转角度 0。
Byte[] buffer =
CreateTextImage(text, fontFamily, fontSize, bold, italic, color, bgColor, rotation, underlineWidth, underlineColor, usePng);
context.Response.ContentType = usePng ? "image/png" : "image/gif";
context.Response.BinaryWrite(buffer);
context.Response.Flush();
}
在上面的代码中,我只是调用 CreateTextImage
方法,该方法执行所有工作。 它将返回一个字节数组,然后我将其写入响应。 请注意,我根据传递的 UsePng 参数设置 ContentType
,该参数默认为 false。 所以默认情况下,将生成 GIF 文件,但如果您传递 UsePng=True,ContentType
将被设置为 png。 最后,我刷新缓冲区。
现在让我们看看 CreateTextImage
方法,所有工作都在那里完成。
private byte[] CreateTextImage(string text, string fontFamily, int fontSize, bool bold, bool italic, string color, string bgColor, int rotation, int underlineWidth, string underlineColor, bool usePng)
{
float textWidth = 0;
float textHeight = 0;
FontStyle fontStyle = FontStyle.Regular;
if (bold)
fontStyle |= FontStyle.Bold;
if (italic)
fontStyle |= FontStyle.Italic;
首先,我创建一些 textWidth
和 textHeight
变量,并将它们初始化为 0。 然后我创建一个 FontStyle
对象,并根据传递给处理程序的查询字符串中的粗体和斜体设置进行设置。
using (Font font = new Font(fontFamily, fontSize, fontStyle))
{
// Get the text measurements.
// I need to create a dummy bitmap so that I can get a Graphics object to get the text measurements.
using (Bitmap bitmap = new Bitmap(1, 1))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
textWidth = graphics.MeasureString(text, font).Width;
textHeight = graphics.MeasureString(text, font).Height;
}
}
接下来,我使用传递的字体系列、字体大小和字体样式创建一个 Font
。 然后我创建一个虚拟位图以从中获取 Graphics
对象,然后使用 MeasureString
方法来确定文本的高度和宽度。 这是我在三角测量部分提到的用于确定文本高度和宽度的 .NET API。
// Calculate the needed bitmap measurements based on the text measurements.
int bitmapWidth = GetRotatedRectangleWidth(textWidth, textHeight, rotation);
int bitmapHeight = GetRotatedRectangleHeight(textWidth, textHeight, rotation);
然后,我使用我编写的几个本地方法来确定旋转后的新图像尺寸。 这些方法使用三角学,但远不如确定新的旋转点位置的方法复杂。
// Now I create the real bitmap of the necessary size to fit the text.
using (Bitmap bitmap = new Bitmap(bitmapWidth, bitmapHeight))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
if (!usePng)
{
graphics.Clear(Color.FromArgb(255, 255, 255, 204));
}
接下来,我创建一个新的 Bitmap
对象,这次是真正的对象,使用计算出的宽度和高度。 我从中获取一个 Graphics
对象,然后如果请求的是 GIF 图像,我会执行 GIF 透明度 hack 的一部分。
// Since I will be rotating text, I need to move the rotation point using the TranslateTransform.
int x, y;
// But first I need to know the location of the rotation point.
GetXY(rotation, textWidth, textHeight, out x, out y);
graphics.TranslateTransform(x, y);
接下来,我调用 GetXY
方法来获取新的旋转点。 这就是所有复杂的三角学发生的地方。 然后我调用 TranslateTransform
方法来移动旋转点。
// Now rotate and draw the text.
graphics.RotateTransform(rotation);
RotateTransform
方法是实际旋转图像的 .NET API。
// Fill in the background color
using (Brush brush = new SolidBrush(ColorTranslator.FromHtml(bgColor)))
{
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.FillRectangle(brush, 0, 0, textWidth, textHeight);
}
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
using (Brush brush = new SolidBrush(ColorTranslator.FromHtml(color)))
{
graphics.DrawString(text, font, brush, 0, 0);
}
我填充背景颜色,然后使用 DrawString
方法和前面创建的字体写入文本。
if (underlineWidth > 0)
{
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
using (Pen pen = new Pen(ColorTranslator.FromHtml(underlineColor), underlineWidth))
{
graphics.DrawLine(pen, 0, textHeight, textWidth, textHeight);
}
}
graphics.Flush();
然后,如果请求了下划线,我将绘制它并刷新 Graphics
对象。
MemoryStream m = new MemoryStream();
bitmap.Save(m, usePng ? ImageFormat.Png : ImageFormat.Gif);
接下来,我创建一个 MemoryStream
,并将新生成的 Bitmap
对象保存到其中。
if (usePng)
{
return m.ToArray();
}
else
{
// transparency hack.
byte[] n = { };
n = m.ToArray();
n[787] = 254;
return n;
}
如果我们要生成 PNG 文件,我可以简单地返回 MemoryStream
对象中的字节数组。 但是,如果请求的是 GIF,那么就是时候进行 GIF 透明度 hack 了。
} // using graphics
} // using bitmap
} // using font
}
这完成了主要代码。 现在剩下的只是处理数学的一些辅助方法。
private void GetXY(int rotation, float tw, float th, out int xT, out int yT)
{
xT = 0;
yT = 0;
double radians = GetRadians(GetReferenceAngleForPositioning(rotation));
if (rotation >= 0 && rotation <= 90)
{
xT = Convert.ToInt32(Math.Sin(radians) * th);
}
else if (rotation > 90 && rotation <= 180)
{
xT = Convert.ToInt32((Math.Sin(radians) * tw) + (Math.Cos(radians) * th));
yT = Convert.ToInt32(Math.Sin(radians) * th);
}
else if (rotation > 180 && rotation <= 270)
{
xT = Convert.ToInt32(Math.Cos(radians) * tw);
yT = Convert.ToInt32((Math.Cos(radians) * th) + (Math.Sin(radians) * tw));
}
else
{
yT = Convert.ToInt32(Math.Sin(radians) * tw);
}
}
我认为上面的 GetXY
方法是这段代码真正的精华所在。 这就是我前面讨论的三角测量执行的地方。 由于我已经在本节的“三角测量”部分讨论了大部分逻辑,我将继续进行。
private int GetRotatedRectangleWidth(float width, float height, int rotation)
{
return GetRotatedRectangleWidth(width, height, GetRadians(GetReferenceAngleForSizing(rotation)));
}
private int GetRotatedRectangleWidth(float width, float height, double rotationInRadians)
{
double w1 = width * Math.Cos(rotationInRadians);
double w2 = height * Math.Sin(rotationInRadians);
return Convert.ToInt32(Math.Ceiling(w1 + w2));
}
上面是一些用于确定旋转后图像宽度的方法。 在经历了所有这些三角测量之后,这些都相当简单。
private int GetRotatedRectangleHeight(float width, float height, int rotation)
{
return GetRotatedRectangleHeight(width, height, GetRadians(GetReferenceAngleForSizing(rotation)));
}
private int GetRotatedRectangleHeight(float width, float height, double rotationInRadians)
{
double h1 = width * Math.Sin(rotationInRadians);
double h2 = height * Math.Cos(rotationInRadians);
return Convert.ToInt32(Math.Ceiling(h1 + h2));
}
上面是一些用于确定旋转后图像高度的方法。
private double GetRadians(double referenceAngle)
{
return Math.PI * referenceAngle / 180.0;
}
GetRadians
方法只是将旋转角度转换为弧度,这是 .NET 数学 API 所需要的。
// The only difference between the reference angles for sizing and positioning is in the 3rd quadrant (> 90 <= 180).
private double GetReferenceAngleForSizing(int rotationInDegrees)
{
if (rotationInDegrees >= 0 && rotationInDegrees <= 90)
return rotationInDegrees;
else if (rotationInDegrees > 90 && rotationInDegrees <= 180)
return 180 - rotationInDegrees;
else if (rotationInDegrees > 180 && rotationInDegrees <= 270)
return rotationInDegrees - 180;
else if (rotationInDegrees > 270 && rotationInDegrees <= 360)
return 360 - rotationInDegrees;
else
return rotationInDegrees;
}
private double GetReferenceAngleForPositioning(int rotationInDegrees)
{
if (rotationInDegrees >= 0 && rotationInDegrees <= 90)
return rotationInDegrees;
else if (rotationInDegrees > 90 && rotationInDegrees <= 180)
return rotationInDegrees - 90;
else if (rotationInDegrees > 180 && rotationInDegrees <= 270)
return rotationInDegrees - 180;
else if (rotationInDegrees > 270 && rotationInDegrees <= 360)
return 360 - rotationInDegrees;
else
return rotationInDegrees;
}
上面的两个方法分别用于确定新的旋转后图像尺寸和旋转点定位的参考角度。 也许一个更懂几何和三角学的人可以将它们简化为一个方法。
不过,我在这里有一个警告,关于这样做。 在我之前的某些测试中,在我得出确定新的旋转点的当前计算结果之前,我有一个不同的计算使用了 TAN 三角函数。 小心,因为 TAN 在边界处会爆炸(趋近于无穷大和负无穷大)。 我不得不编写特殊处理程序来处理旋转接近 90 度间隔的情况。 后来我才意识到 SIN 和 COS 的行为要好得多。 您可以在维基百科上通过查找三角学来查看 TAN 的这种行为。 您可以在那里看到 SIN、TAN 和 CSC 的一些动画图。 注意 TAN 的图表向无穷大方向超出图表,并在从负无穷大返回的过程中重新出现。 与此形成对比的是,SIN 的动画保持在 -1 到 +1 的明确定义的范围内。
Using the Code
使用 HttpHandler 是这种方法的最佳部分,因为它可以用在任何 HTML img
标签的 src
属性中,如下所示
<img src="https:///TextToImageCS/TextImage.ashx?Text=joe@rattz.com&FontFamily=Calibri&FontSize=12&TextColor=Black&BackgroundColor=Transparent&Rotation=0&UnderlineWidth=0&UnderlineColor=Black&UsePng=False" />
这是该调用创建的图像
我将通过更改一些参数(例如旋转、下划线、字体系列、字体大小和颜色)来让下一个变得更花哨。 这是 img
标签
<img src="https:///TextToImageCS/TextImage.ashx?Text=LINQ%20Rulez!&FontFamily=Arial&FontSize=18&TextColor=Blue&BackgroundColor=Transparent&Rotation=270&UnderlineWidth=2&UnderlineColor=Black&UsePng=False" />
请注意,我在 LINQ 和 Rulez 两个单词之间的空格使用了 %20
,以便 URL 被正确编码。 这是生成的图像
最后一个例子,包含所有花哨的选项和 135 度旋转
<img src="https:///TextToImageCS/TextImage.ashx?Text=Code%20Project%20Rocks!&FontFamily=TimesNewRoman&FontSize=32&TextColor=Red&BackgroundColor=Blue&Rotation=135&UnderlineWidth=4&UnderlineColor=Red&UsePng=False" />
这是它生成的图像
当然,您需要更改 URL 以指向您的 HttpHandler。
完整的模块作为 TextImage.zip 附加在本文的顶部。
关注点
显然,在编写了这段代码之后,我对三角学有了新的认识,在我职业生涯的这个晚期,我从未想过会有这种事。 我从每个挑战中学到了很多东西。 我希望您能从中找到一些有益的东西。
用途
到这个时候,您可能会想,这是为了什么? 就我而言,我需要旋转文本,以便我可以有一个表格,其列标题以一定的角度显示,如下面的图像所示。
在该图像中,水平的东西只是一个生成的 HTML 表格,但列标题是旋转的图像。 而且,现在您可以看到我使用下划线功能的目的是什么! 测试代码生成了上面的 HTML,但它演示了该目的。
此代码也可以用来创建文本的验证码图像。 有一个人写了一些将文本转换为图像的代码,是为了防止网络机器人抓取电子邮件地址。 他没有在页面中嵌入文本电子邮件地址,而是创建了一个文本图像。
通过一些修改,此代码可用于动态地将标题或水印文本添加到现有图像中。
致谢
由于这段代码需要克服许多我以前从未做过的挑战,所以我访问了许多网站并阅读了许多文章。 如果您写过关于这些内容的文章,我很有可能读过您的文章。 遗憾的是,我访问了太多,我不知道该感谢谁的具体部分,除了一个人,Salman Arshad。 我从他博客 http://911-need-code-help.blogspot.com 上获得了 GIF 透明度 hack。