最近抽空学习了一下使用c语言来生成pdf文件的库-libHaru,利用该库你可以不必关心pdf内部复杂的结构来生成你所需要的pdf文档,跟着libHaru库中的示例敲了一遍代码,对libHaru库有了一些认知(当然目前还没有完全看完示例代码),在此做出一些笔记顺便记录一下遇到的一些API接口。
1. 首先是libHaru的坐标系统,跟我们平时所使用的坐标系统不同,平时使用c语言时默认的坐标原点都是在左上角,而在libHaru中坐标原点是在左下角,如下所示:
对于pdf文档中的任何一个目标其位置坐标都是指的是目标的左下角,例如我们要在(x,y)处显示字符'Ag',则效果是下面这样的
2. 图形模式,在libHaru中有三种图形模式:HPDF_GMODE_PAGE_DESCRIPTION、HPDF_GMODE_TEXT_OBJECT、HPDF_GMODE_PATH_OBJECT,三者的转换关系如下所示:
默认情况下page对象处于HPDF_GMODE_PAGE_DESCRIPTION模式,通过调用相应的API接口来切换到HPDF_GMODE_PATH_OBJECT或HPDF_GMODE_TEXT_OBJECT模式,HPDF_GMODE_PATH_OBJECT和HPDF_GMODE_TEXT_OBJECT之间是不能直接切换的,必须要通过HPDF_GMODE_PAGE_DESCRIPTION来切换,有点儿类似VIM的几种模式的切换。之所以要熟悉page对象的几种模式是因为libHaru中的一些API接口只能在所需的模式下调用,如果不在所需的模式下调用就会报错,举个例子,我们要设置字体类型以及字体大小是调用HPDF_PAGE_SetFontAndSize()接口,该接口的说明如下:
可知该接口需要在HPDF_GMODE_PAGE_DESCRIPTION或HPDF_GMODE_TEXT_OBJECT模式下调用,如果当前是在HPDF_GMODE_PATH_OBJECT模式下调用该接口就会报出0x1051错误--HPDF_PAGE_INVALID_GMODE。
3. 文本坐标,在绘制文本信息时libHaru内部会维持着一个文本坐标的信息,而每次调用HPDF_Page_BeginText()接口进入文本模式时该坐标信息就会清零,因此在示例中经常会遇到需要绘制不同的文本信息时就重新调用一次HPDF_Page_BeginText()接口,显示文本信息的API接口有如下几个:
前面三个接口都是在当前位置显示文本信息,后两个接口是在指定位置显示文本,有一点区别是前三者不会更新当前文本坐标信息,而后两者会更新当前文本坐标信息,而移动当前文本坐标的API接口有如下几个:
需要注意的是在1、2接口中的x,y参数指的是偏移量,如下:
再来说下前面的文本显示操作的接口HPDF_Page_ShowText()接口不会更新当前文本坐标信息的含义是什么,举个例子,连续调用两次该接口显示文本信息,可以看到最后的结果是第二次的文本是追加在前者的后面,主要代码如下:
HPDF_Page_ShowText(page,"123");
HPDF_Page_ShowText(page,"456");
在生成的pdf文档中我们可以看到"123456"文本
由于是在HPDF_Page_BeginText()后直接显示的文本,因此该文本信息是出于pdf文档的左下角,当我们看到"456"是追加在"123"后面时想当然的认为当前文本坐标是向右移动了,其实不然当前文本坐标依然是在"123"的起始处(0,0),而不是在字符6的右面,如果此时我们想在(x,y)处显示另一个文本信息,有两种方式1:调用HPDF_Page_MoveTextPos()接口移动当前文本坐标至(x,y)处,然后再调用HPDF_Page_ShowText()接口显示文本;2:直接调用HPDF_Page_TextOut()接口直接显示。后者也会更新当前文本坐标至(x,y)处。在多次使用这些接口后就会明白了,如果不想太复杂直接重新调用HPDF_Page_BeginText()接口清零当前文本坐标信息即可。
4. 设置虚线--HPDF_Page_SetDash()接口,这是我遇到的第一个不太明了的API接口,是在line_demo中所使用的接口,用于设置虚线的模式,参数说明如下:
一开始对这个接口不是太明白,后来反复测试了几组参数才逐渐清晰起来,主要是文档说的太简洁,dash_pattern是一个数组存放虚线的长度和间隙的长度信息,num_elem用于指示dash_pattern数组中元素的个数,其范围是(0<= num_param <= 8),phase则是指虚线的位移量或者是平移量。
现在来详细说明下各个参数,当dash_pattern=NULL,num_ele=0,phase=0时,绘制出的是实线,也是默认情况下的设置。一般来说dash_pattern数组中的元素是成对存在的,如果不是成对存在则dash_pattern中就只能有一个数据且num_elem为1,dash_pattern中最多存放4组值,每组中第一个值是指虚线中实体线段的长度,每组中第二个值是指虚线中空白实体线段的长度,当只有一个数值时表明实体线段的长度和空白实体线段的长度相同,且长度为所指定的值。phase则是指所画虚线向左位移的长度,现在来看下示例:
HPDF_Page_SetDash (page, NULL, 0, 0);
HPDF_Page_MoveTo (page, 60, height - 170);
HPDF_Page_LineTo (page, 220, height - 170);
HPDF_Page_Stroke (page);
HPDF_Page_SetDash (page, dashMode, 1, 0);
HPDF_Page_MoveTo (page, 60, height - 175);
HPDF_Page_LineTo (page, 220, height - 175);
HPDF_Page_Stroke (page);
dashMode[0] = 4;
HPDF_Page_SetDash (page, dashMode, 1, 0);
HPDF_Page_MoveTo (page, 60, height - 180);
HPDF_Page_LineTo (page, 220, height - 180);
HPDF_Page_Stroke (page);
dashMode[1] = 4;
HPDF_Page_SetDash (page, dashMode, 2, 4);
HPDF_Page_MoveTo (page, 60, height - 185);
HPDF_Page_LineTo (page, 220, height - 185);
HPDF_Page_Stroke (page);
dashMode[1] = 8;
HPDF_Page_SetDash (page, dashMode, 2, 0);
HPDF_Page_MoveTo (page, 60, height - 190);
HPDF_Page_LineTo (page, 220, height - 190);
HPDF_Page_Stroke (page);
HPDF_Page_SetDash (page, dashMode, 2, 8);
HPDF_Page_MoveTo (page, 60, height - 192);
HPDF_Page_LineTo (page, 220, height - 192);
HPDF_Page_Stroke (page);
dashMode[2] = 8;
dashMode[3] = 4;
HPDF_Page_SetDash (page, dashMode, 4, 0);
HPDF_Page_MoveTo (page, 60, height - 195);
HPDF_Page_LineTo (page, 220, height - 195);
HPDF_Page_Stroke (page);
这里绘制了7条线,效果如下所示:
第一条是一条长度为160的实线,第二条是dashMode[0]=3,phase=0的虚线,可以看到这是一条均匀的虚线,黑色线段的长度和空白线段的长度均为3,第三条跟第二条类似,只是长度变为了4,可以更好地跟第二条对比,第四条跟第三条类似只是增加了phase=4,可以看到第四条是第三条线向左位移了4个长度得到的,需要注意的是对于第三条线来说phase的取值相同但所得到的效果不同,第三条线无论phase为几,其位移效果总是跟phase%4的效果相同。第五条线可以和第六条线一起看,把空白线段长度改为了8,效果就是一段长度为4的黑色线段后面跟着一段长度为8的空白线段依次循环。最后一条线放置了两组参数,可以看到效果是先是一段长度为dashMode[0]=4的黑色线段后面跟着一段长度为dashMode[1]的空白线段,接着是一段长度为dashMode[2]=8的黑色线段,最后是一段长度为dashMode[3]=4的空白线段,依次循环。
5. 设置文本的转换矩阵,HPDF_Page_SetTextMatrix()接口,参数说明如下:
该接口的文档说明也不是很详细,导致我也纠结了半天,先看下示例的效果图,
即实现文本信息的特殊显示方式,a,b两个参数还是很好理解的,控制文本旋转的角度一般描述为a=cosine(angle),b=sine(angle).
c,d参数文档也没有描述清楚,但根据示例中放大x轴方向和放大y轴方向的示例可以帮助我们了解,主要代码如下:
无论是关于x轴放大还是y轴放大,文本信息均没有旋转,因此angle=0,a=cosine(0),b=sine(0),在关于x轴放大示例中a=1.5,b=0,可以清楚知道文本关于x轴放大了1.5倍,关于y轴放大也类似,a=1,b=0,可知文本关于x轴放大了1倍,而c,d参数根据这两个示例可知,c=0时d即为关于y轴放大的倍数。经过查看libHaru的HPDF_Page_ShowText()源码可以看到有这么一段代码,
即可知a,b参数是跟文本显示长度紧密相关的,就像画圆一般文本显示长度为圆的半径,a,b分别为圆上一点所对应的x轴和y轴上的投影跟文本显示长度的比值,即cosine(angle),sine(angle),既然a,b是跟文本显示长度紧密相关,结合上面关于x轴放大和y轴放大的示例,c,d应该是跟文本显示高度相关,再查看源码中HPDF_ShowTextNextLine()接口中有这么一段代码:
果然是这样text_leading变量存储的是文本的线宽也就是行高信息,c即为所显示高度跟正常高度的比值,正常情况下文本如果不旋转则文本显示的高度即和text_leading相同,而如果文本有了旋转则高度肯定和text_leading不同了,c,d参数即时描述的该信息,再结合前面文本倾斜显示的示例就可知一二了,正常情况下文本显示的外形是一个矩形,经过a,b参数旋转过后文本显示的外形就会变成一个菱形(而非矩形),如下:
而对于示例中,文本信息还是正着显示即垂直于下面的红色线段显示就需要继续调整红色菱形图使之再次成为矩形,最后结果如下:
显示文本显示图形式红色矩形,此时行高已经变了,不再是长度h了,而是BD线段的长度,对y轴的偏移量为cosine(angle),而对于x轴的偏移则为-sine(angle),因此,c=-sine(angle),d=cosine(angle)。HPDF_Page_SetTextMatrix()接口可以和HPDF_Page_Concat()接口对比来看,两者有很多共同之处。
6. 中文字体
libHaru是支持日文、韩文、中文多字节编码的,通过调用HPDF_UseCNSFonts(),HPDF_UseCNSEncodings()接口来使能简体汉字的输入,结果生成的汉字是乱码,代码中输入的汉字是"简体中文",生成的文档是下面这样的
虽然也是简体中文,但结果不是我们想要的,根据character_map示例可以看到是能够正常生成简体中文的,因此是编码的问题了,度娘了一下使用mingw32编译器如何正确显示中文,有一个链接http://blog.csdn.net/softman11/article/details/6121538 该链接有解决办法,因此尝试了一下更改libHaru中的makefile.mingw文件,修改LDFLAGS_DEMO2选项,如下:
再次编译,即可看到正确的结果,如下:
上述的汉字是libHaru内部自带的,libHaru中还提供了TTF字体的使用,去网上下载了一个新蒂小丸子的字体来尝试,代码示例如下:
生成的结果如下:
可以看到,使用外部的中文字体也是成功的。
目前只总结了这几点,如果后续有新的心得再来更新吧。