位图字体显示系列一:显示 ASCII 字体
http://blog.****.net/subfate/article/details/6444578
起因:
早在阅读tslib源代码时就注意到里面有font_8x8.c和font_8x16.c两个文件(后来才得知,它们来自Linux内核,具体目录是./drivers/video/console),它们包含了在屏幕上显示所需的所有字符(当然,是可打印字符),由此也看了一下tslib显示字符的代码。对将来在触摸屏上显示汉字有所帮助,——这是后话,暂且按下不提。
上面所说的字符当然是ASCII码了,这种编码学过计算机的人可能会很熟悉,就不多讲了。本文所述者,就是通过ASCII码字库文件(网上有下载)和上面提到的其中之一的文件在终端上显示ASCII字符。这也为研究汉字显示打下基础(此为假话,写文章及做作所需也,实际上本文作者是先研究汉字显示再研究ASCII码显示的,勿模仿,切切)。
本文中使用的字库是8x16,每个字符占用内存空间为16字节(后面不再提及),文中显示字符所做工作有下面几步(显示汉字也差不多,当然,这又是后话 ):
1、打开字库文件(似乎是废话);
2、计算出要打印的字符在字库文件中的偏移量(亦即网上所说的“寻址”);
3、将这个偏移地址的数据(16字节)读取缓冲区中;
4、显示这个缓冲区,为1者打印,为0者留空。(由于是在终端上显示,因此,可以使用如“#”、“*”等字符代替。)
先说说如何找到某个字符在字库文件中的偏移量。字库里的字符排序是符合ASCII码顺序的,而每个字符占16字节,因此,将要打印的字符与16相乘,便得到这个字符在字库中的位置了,这个位置之后的16个字节的数据,就是这个字符了。比如大写字母“A”,十六进制是0x41(“A”还是十进制的65、八进制的101,其实都是一回事),它与0x10(十进制的16)相乘,得到0x410(计算机中使用十六进制十分方便,对于某些试题要求计算出某地址的十进制,我表示不理解,既不直观,又不方便,还容易出错)。使用十六进制编辑器(用UE相信会更方便些)查看这个偏移量,会发现有如下数据:
00000410 00 00 10 38 6c c6 c6 fe c6 c6 c6 c6 00 00 00 00 |...8l...........|
所谓的字库,里面就是一些二进制数据。我们看看上面得到“A”的16个字节数据显示效果是怎样的。将上面的十六进制数据按二进制输出,一个字节一行,共占16行,如下:
00000000
00010000
00111000
01101100
11000110
11000110
11111110
11000110
11000110
11000110
11000110
00000000
00000000
00000000
00000000
不直观,将出现0的地方留空,则变成如下形状(受网页影响,为美观起见进行调整,虽非实际中二进制所示,但形状是一致的):
1
111
11 11
11 11
11 11
1111111
11 11
11 11
11 11
11 11
上面的便是传说中的“A”了。
下面就用代码读取字库信息,并显示出来。完整代码如下:
源代码文件编码:ANSI
测试环境编码:zh_CN.UTF-8、zh_CN.gd2312
* ************************************************/
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#ifdef DEBUG
#define debug(fmt, ...) printf(fmt, #__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
void display_font_ascii(char *asc)
{
int i, j;
debug("=================/n");
for(i=0;i<16;i++)
{
for(j=0;j<8;j++)
{
/* 逐位相与,为1者打印“*”,否则打印空格 */
if(asc[i] & (0x80>>j))
printf("*");
else
printf(" ");
}
printf("/n");
}
debug("=================/n");
}
int main()
{
int i;
unsigned char *p;
unsigned char asciicode[] = "Ab";
unsigned long offset;
FILE *asc;
char ascii[16];
if((asc=fopen("ASC16","rb"))==NULL)
{
perror("Can't Open ASC16");
exit(0);
}
/* ASCII字库文件 */
for (i = 0; i < sizeof(asciicode)-1; i++)
{
debug("%c %x/n", asciicode[i], asciicode[i]); /* 打印数值 */
offset = asciicode[i]*16;
debug("offset: %x/n", offset); /* 打印偏移量 */
fseek(asc,offset,SEEK_SET);
fread(ascii,16,1,asc); /* 读取16字节 */
display_font_ascii(ascii); /* 显示 */
}
fclose(asc);
return 0;
}
由于是示意性代码,不必纠结于代码优化、代码风格等等问题。效果(同样进行了调整)如下:
*
***
** **
** **
** **
*******
** **
** **
** **
** **
***
**
**
****
** **
** **
** **
** **
** **
*****
还有另一种方法,不读取字库,而是将所有的字符存放于某个数组中,比如像font_8x16.c文件中的fontdata_8x16数组。这里假设数组为ascii_code,那么,上面的代码只需修改一小部分,如下:
{
debug("%c %x/n", asciicode[i], asciicode[i]);
offset = asciicode[i]* 16; // 寻址
p = ascii_code+offset; // 查找字符在ascii_code数组中的偏移量
debug("offset: %x/n", offset);
display_font_ascii(p);
}
效果是一样的,所不同的是,前一种方法需要读取字库文件,后一种方法直接在内存中读取,完整的ASCII码占用空间为4KB。不过,ASCII中可打印的字符共96个(经过认真数那些可打印的字符,发现实际上是95个(即32号到126号),这里将127号算上了,具体的请搜索一下ASCII),占用空间为96*16,即1536字节。由于前面0x20(即32)个字符在显示中没有用到,因此是可以去掉的,这样一来,寻址方式又稍有不同,如下:
{
debug("%c %x/n", asciicode[i], asciicode[i]);
offset = (asciicode[i] - 0x20 )* 16; // 寻址
p = ascii_code+offset; // 查找字符在ascii_code数组中的偏移量
debug("offset: %x/n", offset);
display_font_ascii(p);
}
由于文中涉及知识、代码、字库文件都可以在网络上找到,因此文中就不提供下载了。
资料:
1、ASCII码字库,有多种形式,如8*16、8*12,名称为ASC12、ASC16,搜索一下就能找到。
2、“字库数组”,可以在Linux内核源代码的./drivers/video/console目录下找到很多相关代码文件,如font_8x8.c、font_8x16.c、font_sun8x16.c等等。像font_8x16.c文件,代码中说是由cpi2fnt这个东西产生的,但搜索一下,没什么介绍,英文的又不想去看,对这个东西也就不了解了。
3、可以搜索一下字库生成工具,用这些工具可以生成需要的字库。
推荐阅读
-
关于 TFT LCD 模块的一些问题 (2) - LCD 显示屏字体、字型
-
位图字体显示系列一:显示 ASCII 字体
-
Adobe国际认证中文官方网站】Adobe中国摄影计划,免费安装正版激活--Adobe Creative Cloud中国摄影计划。与此同时,Adobe宣布天猫为Adobe Creative Cloud中国摄影计划的电商战略合作伙伴,并将与其合作上线Adobe天猫官方旗舰店。 此举无疑一方面扩大了Adobe在中国的影响力,另一方面也有助于国内用户更好地培养正版软件意识,推动Adobe软件在中国的正版化进程。 网络异常,图片无法显示 ||网络异常 Adobe Creative Cloud中国摄影计划包括Photoshop和Lightroom Classic两大桌面创意工具,以及iOS版Photoshop Express。 其中,Adobe Lightroom Classic和Adobe Photoshop作为两款常用的图像处理软件,对于那些玩摄影、后期修图的创意设计人群无疑有着巨大的帮助,而LR+PS套装对于摄影领域用户的重要性自不必说,正版产品的性能实时更新也可以放心!体验最新功能,对于新镜头(补偿)和机身(RAW 读取)都能第一时间适应。不信你看: Photoshop 图像合成 裁剪、移除对象、润饰合成照片、玩转色彩和特效,创建精美图片和艺术品! Lightroom Classic 照片编辑 轻松批量管理和编辑照片,内置专业创意控件和摄影师预设,让你的照片大放异彩。 手机 PS 便捷编辑 Photoshop Express 支持多种滤镜、贴纸,手机即可完成抠图、除雾等任务 人工智能编辑工具 神经滤镜、快速点击选区、自动选择主题等人工智能功能让图像编辑更轻松 创意画笔内容识别 定制艺术画笔工具,实现个性化效果;内容识别填充,智能去除无用物体。 Adobe Creative Cloud 中国摄影计划的推出,为中国的专业摄影师、摄影爱好者、后期修图和其他创意设计人员带来了全方位的内容和体验。 网络异常,图片无法显示 ||网络异常 当然,不可否认的是,"由于盗版软件缺乏开发、维护和升级成本,销售价格远低于正版软件。再加上很多普通人并不需要使用正版软件的复杂功能,版权观念较淡,还是有大量的创意设计人员会选择盗版软件"。 但事实上,当所有的软件都不再是单一的软件,而是变成一种服务时,单机版盗版的存在就逐渐成为鸡肋。因为有太多的服务让你即使是所谓的 "完美破解",也无法享受,Adobe Cloud 就是一个很好的例子,所谓的完美破解,你只能使用 "Adobe "的一半,对于更精彩的 "云",只能望云兴叹。更何况,越来越多的设计工具从免费走向付费,越来越多的设计师和企业已经接受了付费使用的模式。 其次,对于互联网时代的企业数字化转型而言,数字化合规至关重要。21年来,使用盗版PS和未经授权的方正字体被指侵权的事情闹得沸沸扬扬,虽然新闻真假难辨,但也给使用盗版工具的用户敲响了警钟。 付费使用正版工具,可以更放心地进行设计,不用担心版权风险!
-
Grid++Report 锐浪报表开发常见问题解答集锦-报表设计 问:怎样在设计时打印预览报表? 答:为了及时查看报表的设计效果,Grid++Report 报表设计应用程序提供了四种查看视图:普通视图、页面视图、预览视图与查询视图。通过窗口下边的 Tab 按钮可以在四种视图中任意切换。在预览视图中查看报表的打印预览效果,在查询视图中查看报表的查询显示效果。如果在报表的记录集提供了数据源连接串与查询 SQL,在进入预览视图与查询视图时会利用数据源连接串与查询 SQL 从数据源中自动取数,否则 Grid++Report 将自动生成模拟数据进行模拟打印预览与查询显示。注意:在预览视图与查询视图中看到的报表运行结果有可能与在你程序中的最终运行结果有差异,因为在报表的生成过程中我们可以在程序中对报表的生成行为进行一定的控制。 问:怎样用 Grid++Report 设计交叉表? 答:Grid++Report 没有提供专门实现交叉表的功能,其它的报表构件提供的交叉表功能一般也比较死板和功能有限。利用 Grid++Report 的编程接口可以做出灵活多变,功能丰富的交叉表。示例程序 CrossTab 就是一个实现交叉表的例子程序,认真领会此例子程序,你就可以做出自己想要各种交叉表,并能提取一些共用代码,便于重复使用。 问:怎样设置整个报表的缺省字体? 答:设置报表主对象的字体属性,也就是设置了整个报表的缺省字体。如果改变报表主对象的字体属性,则没有专门的设置字体属性的子对象的字体属性也跟随改变。同样每个报表节与明细网格也有字体属性,他们的字体属性也就是其拥有的子对象的缺省字体。 问:怎样在打印时限制一页的输出行数? 答:设定明细网格的内容行的‘每页行数(RowsPerPage)’属性即可。另外要注意‘调节行高(AdjustRowHeight)’属性值:为真时根据页面的输出高度自动调整行的高度,使整个页面的输出区域充满。为假时按设计时的高度输出行。 问:怎样显示中文大写金额? 答:将对象的“格式(Format)”属性设为 “$$” 及可,可以设置格式的对象有:字段(IGRField)、参数(IGRParameter)、系统变量(IGRSystemVarBox)与综合文字框(IGRMemoBox),其中综合文字框是在报表式上设格式。 问:能否实现自定义纸张与票据打印? 答:Grid++Report 完全支持自定义纸张的打印,只要在报表设定时在页面设置中选定自定义纸张,并指定准确的纸张尺寸。当然要在最终输出时得道合适的打印结果,输出打印机必须支持自定义纸张打印。Windows2000/XP/2003 操作系统上可以在打印机上定义自定义纸张,也可以采用这种方式实现自定义纸张打印。 问:怎样实现 0 值不打印? 答:直接设置格式串就可以,在“数字格式”设置对话框中选定“0 不显示”,就会得到合适的格式串。也可以通过直接录入格式串来指定 0 不显示,但格式串必须符合 Grid++Report 的规定格式。另一种实现办法是在报表获取明细记录数据时,在 BeforePostRecord 事件中将值为零的字段设为空,调用字段的 Clear 方法将字段置为空。 问:怎样实现多栏报表? 答:在明细网格上设‘页栏数(PageColumnCount)’属性值大于 1 即可。通过 Grid++Report 的“页栏输出顺序”还可以指定多栏报表的输出顺序是“先从上到下”还是“先从左到右”。 问:如何实现票据套打? 答:Grid++Report 为实现票据套打做了很多专门的安排:报表设计器提供了页面设计模式,按照设定的纸张尺寸显示设计面板,如果将空白票据的扫描图设为设计背景图,在定位报表内容的输出位置会非常方便。报表部件可以设定打印类别,非套打输出的内容在套打打印模式下就不会输出。 问:Grid++Report 有没有横向分页功能? 答:回答是肯定的,在列的总宽度超过打印页面的输出宽度时,Grid++Report 可以另起新页输出剩余的列,如果左边存在锁定列,锁定列可以在后面的新页中重复输出,这样可以保证关键数据列在每一页都有输出。仔细体会 Grid++Report 提供的多种打印适应策略,选用最合适的方式。Grid++Report 的多种打印适应策略为开发动态报表提供了很好的支持。 问:怎样实现报表本页小计功能? 答:定义一个报表分组,将本分组定义为页分组,在本分组的分组头与分组尾上定义统计。页分组就是在每页产生一个分组项,在每页的上端与下端都会分别显示页分组的分组头与分组尾,页分组不用定义分组依据字段。 报表运行 问:怎样与数据库建立连接? 答:如果在设计报表时指定了数据集的数据源连接串与查询 SQL 语句,Grid++Report 采用拉模式直接从数据源取得报表数据,Grid++Report 利用 OLE DB 从数据源取数,OLE DB 提供了广泛的数据源操作能力。如果 Grid++Report 的数据来源采用推模式,即 Grid++Report 不直接与数据库建立连接,各种编程语言/平台都提供了很好的数据库连接方式,并且易于操作,应用程序在报表主对象(IGridppReport)的 FetchRecord 事件中将数据传入,例子程序提供了各种编程语言填入数据的通用方法,对C++Builder 和 Delphi 还进行了专门的包装,直接关联 TDataSet 对象也可以将 TDataSet 对象中的数据传给报表。 问:打印时能否对打印纸张进行自适应?支持表格的折行打印吗? 答:Grid++Report 在打印时采用多种适应策略,通过设置明细网格(IGRDetailGrid)的‘打印策略(PrintAdaptMethod)’属性指定打印策略。(1)丢弃:按设计时列的宽度输出,超出范围的内容不显示。(2)绕行:按设计时列的宽度输出,如果在当前行不能完整输出,则另起新行进行输出。(3)缩放适应:对所有列的输出宽度进行按比例地缩放,使总宽度等于页面的输出宽度。(4)缩小适应:如果列的总宽度小于页面的输出宽度,对所有列的输出宽度进行按比例地缩小,使总宽度等于页面的输出宽度。(5)横向分页:超范围的列在新页中输出。(6)横向分页并重复锁定列。 问:如何改变缺省打印预览窗口的窗口标题? 答:改变报表主对象的‘标题(Title)’属性即可。 问:利用集合对象的编程接口取子对象的接口引用,但不是自己期望的结果。 答:Grid++Report中所有集合对象的下标索引都是从 1 开始,另按对象的名称查找对象的接口引用时,名称字符是不区分大小写的。 问:怎样在运行时控制报表中各个对象的可见性?即怎样在运行时显示或隐藏对象? 答:在报表主对象(GridppReport)的 SectionFormat 事件中设定相应报表子对象的可见(Visible)属性即可。 问:报表主对象重新载入数据,设计器中为什么没有反映新载入的数据? 答:应调用 IGRDesigner 的 Reload 方法。 问:怎样实现不进入打印预览界面,直接将报表打印出来?