近期做了一套打印的功能,主要实现模板套打。因为模板中每个信息项的位置和尺寸都是固定的,所有有些不定长的文本就需要使用图片来实现等比例缩放
我的技术解决方案就是:
根据要求的文本信息得出需要的画布大小,在根据模板区域的大小缩放生成的图片以适应模板
参数:文本内容、字体、字号、字体样式(枚举)、字体方向、模板区域大小
代码如下:
/// <summary>
/// 根据原画布尺寸和内容获取适应的字体
/// </summary>
/// <param name="width">原画宽</param>
/// <param name="height">原画高</param>
/// <param name="content">内容</param>
/// <param name="isVertical">文字方向</param>
/// <param name="fontName">字体</param>
/// <param name="fontSize">字号</param>
/// <param name="fstyle">字体样式</param>
/// <returns>调整后的字体</returns>
public Font GetAdaptiveFont(int width, int height, string content, bool isVertical, string fontName, int fontSize, FontStyle fstyle) {
Font myFont = new Font(fontName, fontSize, FontStyle.Bold);
Image imSource = new Bitmap(width, height);
Graphics gSource = Graphics.FromImage(imSource);
Size iNeed = gSource.MeasureString(content, myFont).ToSize();
int iLen = content.Length;
if (!isVertical) {
if ((int)iNeed.Height > imSource.Height) {
while (myFont.Size > 1) {
myFont = new Font(fontName, fontSize, fstyle);
iNeed = gSource.MeasureString(content, myFont).ToSize();
if ((int)iNeed.Height <= imSource.Height) {
break;
}
fontSize -= 2;
}
}
} else {
iNeed = gSource.MeasureString(content.Substring(0, 1), myFont).ToSize();
if ((int)iNeed.Width > imSource.Width) {
while (myFont.Size > 1) {
myFont = new Font(fontName, fontSize, fstyle);
iNeed = gSource.MeasureString(content.Substring(0, 1), myFont).ToSize();
if ((int)iNeed.Width <= imSource.Width) {
break;
}
fontSize -= 2;
}
}
}
return myFont;
}
此方法根据以上参数调整字体以适应画布,之后根据调整后的字体生成图片:
/// <summary>
/// 画图
/// </summary>
/// <param name="myFont">字体</param>
/// <param name="myBrush">字体颜色</param>
/// <param name="content">内容</param>
/// <returns>画好的图片</returns>
private Image DrawImage(Font myFont, SolidBrush myBrush, string content) {
Image image = new Bitmap(1, 1);
Graphics g = Graphics.FromImage(image);
Size iNeed = g.MeasureString(content, myFont).ToSize();
image = new Bitmap(iNeed.Width, iNeed.Height);
g = Graphics.FromImage(image);
g.Clear(Color.White);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawString(content, myFont, myBrush, 0f, 0f);
return image;
}
下一步要做的是根据模板区域大小对图片进行缩放处理:
/// <summary>
/// 获得缩放后的图片
/// </summary>
/// <param name="isVertical">文字方向</param>
/// <param name="imSource">原画尺寸</param>
/// <param name="imNeed">实际尺寸</param>
/// <param name="content">内容</param>
/// <returns></returns>
public Image GetStrukImage(bool isVertical, Image imSource, Image imNeed, string content) {
Graphics gSource = Graphics.FromImage(imSource);
if (!isVertical) {
//字体高度 = 画布高度,宽度拉伸
if ((int)imNeed.Width > imSource.Width) {
gSource.DrawImage(imNeed, new Rectangle(0, 0, imSource.Width, imSource.Height), new Rectangle(0, 0, imNeed.Width, imNeed.Height), GraphicsUnit.Pixel);
} else {
imSource = imNeed;
}
} else {
int iLen = content.Length;
//字体宽度 = 画布看度,高度拉伸
if ((int)imNeed.Height > imSource.Height) {
gSource.DrawImage(imNeed, new Rectangle(0, 0, imSource.Width, imSource.Height), new Rectangle(0, 0, imNeed.Width, imNeed.Height), GraphicsUnit.Pixel);
} else {
imSource = imNeed;
}
}
return imSource;
}
因为是套打,打印的纸张有背景图,所以生成的图片就要做透明处理,去掉图片的底色,以免覆盖套打模板:
/// <summary>
/// 返回一个透明背景的位图
/// </summary>
/// <param name="image">要设置透明的位图</param>
/// <param name="color">要设置透明的颜色</param>
/// <returns>透明背景的位图</returns>
public Image MakeTransparentGif(Image image, Color color) {
byte R = color.R;
byte G = color.G;
byte B = color.B;
MemoryStream fin = new MemoryStream();
image.Save(fin, System.Drawing.Imaging.ImageFormat.Gif);
MemoryStream fout = new MemoryStream((int)fin.Length);
int count = 0;
byte[] buf = new byte[256];
byte transparentIdx = 0;
fin.Seek(0, SeekOrigin.Begin);
//header
count = fin.Read(buf, 0, 13);
if ((buf[0] != 71) || (buf[1] != 73) || (buf[2] != 70))
return null; //GIF
fout.Write(buf, 0, 13);
int i = 0;
if ((buf[10] & 0x80) > 0) {
i = 1 << ((buf[10] & 7) + 1) == 256 ? 256 : 0;
}
for (; i != 0; i--) {
fin.Read(buf, 0, 3);
if ((buf[0] == R) && (buf[1] == G) && (buf[2] == B)) {
transparentIdx = (byte)(256 - i);
}
fout.Write(buf, 0, 3);
}
bool gcePresent = false;
while (true) {
fin.Read(buf, 0, 1);
fout.Write(buf, 0, 1);
if (buf[0] != 0x21)
break;
fin.Read(buf, 0, 1);
fout.Write(buf, 0, 1);
gcePresent = (buf[0] == 0xf9);
while (true) {
fin.Read(buf, 0, 1);
fout.Write(buf, 0, 1);
if (buf[0] == 0)
break;
count = buf[0];
if (fin.Read(buf, 0, count) != count)
return null;
if (gcePresent) {
if (count == 4) {
buf[0] |= 0x01;
buf[3] = transparentIdx;
}
}
fout.Write(buf, 0, count);
}
}
while (count > 0) {
count = fin.Read(buf, 0, 1);
fout.Write(buf, 0, 1);
}
fin.Close();
fout.Flush();
return new Bitmap(fout);
}
接下来就是逻辑部分了,将图片做数据流返回:
MemoryStream ms = new MemoryStream();
imNeed.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
imNeed.Dispose();
Byte[] fileBytes = ms.GetBuffer();
ms.Close();
Context.Response.BinaryWrite(fileBytes);
在这里还要做一下说明:有些图片格式是不能够做透明处理的,我试过了好像只有png和bmp可以
接下来要说一下Ext.Net打印的部分了,这部门主要是页面的处理,Ext.Net本身有样式,在打印的时候布局很难调整,如果使用windows的ifreame且页面不带Ext.Net的任何控件,则一点问题没有,我直接贴代码了,没什么好说的,我现在的问题就是会多打出来一份空白页,不知道该怎么解决,还请高手指导一下:
<ext:Viewport runat="server" Layout="FitLayout">
<Items>
<ext:Panel ID="pnlMain" runat="server" Layout="FitLayout">
<TopBar>
<ext:Toolbar ID="tbrTool" runat="server" LabelWidth="30">
<Items>
<ext:ComboBox ID="cmbTemplate" runat="server" FieldLabel="模板">
<Listeners>
<Select Handler="GetTemplate()" />
</Listeners>
</ext:ComboBox>
<ext:CompositeField ID="CompositeField1" runat="server" FieldLabel="缩放" Width="200" Flex="1">
<Items>
<ext:SliderField ID="ZoomSlider" runat="server">
<Slider ID="sdZoom" runat="server" Width="100" MinValue="50" MaxValue="150" Increment="10" Value="100">
<Listeners>
<Change Fn="PageZoom" />
<AfterRender Handler="txtZoom.setValue(this.value);" />
</Listeners>
<Plugins>
<ext:SliderTip ID="SliderTip1" runat="server">
<GetText Handler="return thumb.value.toString()+'%';" />
</ext:SliderTip>
</Plugins>
</Slider>
</ext:SliderField>
<ext:SpinnerField runat="server" ID="txtZoom" Width="50" MinValue="50" MaxValue="150" IncrementValue="10" AnchorHorizontal="100%">
<Listeners>
<Change Handler="zoom();" />
<SpinDown Handler="zoom();" />
<SpinUp Handler="zoom();" />
</Listeners>
</ext:SpinnerField>
</Items>
</ext:CompositeField>
<ext:ToolbarSeparator />
<ext:Button ID="btnPrint" runat="server" Text="打印" Icon="Printer" OnClientClick="Print()" />
</Items>
</ext:Toolbar>
</TopBar>
<AutoLoad Url="about:blank" Mode="IFrame" />
</ext:Panel>
</Items>
</ext:Viewport>
打印之前需要调整缩放,打印之后要还原回来,OK