www.pudn.com > CPPstar.rar > star-d1.40.cpp
#include#include #include /* 关闭以下两行,需要单独编译的时候再打开。*/ #include "../linux/abds.h" #include "../abds4win/driver_printstr.h" /* ------------- Star NX100+、NX350系列打印机驱动程序:开始行 ------------- */ /* 版本变化记录: V1.00:07-07-06,王宇涛完成。 1、根据AHA中的printer.c中的AR3240打印机驱动重新编写。 2、改变了aux_device_config结构的意义。 3、增加1b 08 n按英寸设置页长。 4、Star打印机倍高行距原则:在本行打印字符的上半部分,然后向下多移动24/180英寸(就是一个字符的高度), 打印字符的下半部分,行与行之间的空隙不变,这与PR2的倍高原则完全不同,PR2倍高时在本行打印字符的下 半部分,然后逆向上移24/180英寸打印字符的上半部分,这样被告行可能会延伸到上一行的区域,这需要应用 特别注意。另外倍高的空行与有打印内容的行高度是一样的。举例:star打印机设置行距36/180英寸,非倍高 行的行高为36/180英寸,其中字符24/180英寸,本行与下一行的空隙12/180英寸;倍高行的行高为60/180英寸 (不是72/180英寸),其中字符24*2/180=48/180英寸,本行与下一行的空隙仍为12/180英寸。 5、Star打印机未提供逆向换行指令,逆向走纸只能依靠专门指令1b 6a n,所以模拟逆向换行需要记录当前行距。 V1.10:07-07-20,王宇涛完成。 1、一个重大的问题,此类打印机一般连续打印,打印完成后没有0x0c进纸很正常。V1.00中如果在打印的最后 有若干空行,则这些空行被缓存在ps.line_buf里不输出(因为这时ps.real_line标志为0),而语句被写成 如:“line_processor(pdo, "\x0d" , 1 , ps.real_line ) ;”。 使用者在撕走打印内容后重新装填纸张并 打印,这时缓存的内容(如空行)输出在当前页,即上一行的末尾加到了本行的行首,现象很可能是出现几个 莫名的空行(如果是有空行缓存的话)。 2、现在对回车(0x0d)函数carriage_return、垂直定位函数row_location(包括换行0x0a)、退纸函数 paper_eject,都采用立即向打印机输出的方式。这样问题就得到了解决。 3、如果最后一行有打印内容,但没有回车、换行等指令,那么数据仍会缓存在ps.line_buf里不输出,后果 很可能会合并打印在新一页的首行,不过就算是立即输出道打印机,效果也是一样的,没有回车作为结束的 一行字符同样不会被打印机打印。 V1.20:07-07-31,王宇涛完成。 1、因为要设置诸如11/3英寸的页长(如北京银行,一张11英寸的标准打印纸折叠成3页),所以以整数为页长 的设置指令 1b 08 n(n的单位英寸)并不合适。这种情况需要打印机按行来设置页长,页长=行数 X 当前行距。 所以从本版起对伪指令1b 08 n的n重新解释为行数而非英寸,并据此向打印机发送指令"1b 43 n"而非原来的 "1b 43 n";伪指令1b 09 n的含义仍为打印完第n行后退纸,但不再向打印机发送页长指令"1b 43 n"。 2、以前的版本没有考虑在有多个列距的行中进行绝对列定位,现在就此进行了专门的处理。如果在一行中出现 过多个列距,那么在绝对列定位时必须先回车,因为绝对列定位n的含义就是,以当前列距从最左边打印n个空格。 3、由于这个系列的打印机是没有绝对行定位的物理指令,所以所有行定位都是通过相对定位实现的,如果期间 发生过行距改变,那么绝对行定位将不准确。必须先发送”回车“,将列位置归0,然后再进行定位,更过细节 见1b 01 n指令出的说明。 V1.30:07-08-10,王宇涛完成。 1、改为C++的类方式表达。 2、把所有short型变量改为int型。 3、去掉函数明前的数字,如print_str7(),改为print_str7()。 4、纸张状态的引用由指针变成对象方式,ps原来是指针,对象是ps1,通过ps=&ps1方式赋值,现在取消ps1对象, 变ps为对象,原来ps->avp=0;变成ps.avp=0; 5、函数int print_str( PPDO pdo, char *buf, int control_mode )增加了int control_mode参数,但保留没有 使用。 V1.31:07-08-31,王宇涛完成。 1、取消了PRT_star类中的ps.width_inch成员,因为即得不到也用不到“纸宽”这个信息。 2、在PRT_star::PRT_star()中不再对adc.port_mode进行初始化。 3、改变超过逻辑页长的处理方法。原来超过逻辑页长的行数不输出到下一页,现在则输出到下一页。这样处理更 有利于应用的设计,因为逻辑上很清晰,PR2也是这样处理,只不过因为PR2不是连续走纸,所以用“模除”来直接 获得剩余行数,并在一次进纸时输出,而star驱动如果定位的值很大可能要走好几页。如逻辑页长60,行定位到 第210行,那么star驱动要发3次进纸(其中两个是空白页),而PR2只退一次纸,但两者最后都定位到行29,也 就是第30行(210-1) % 60=29。 V1.40:07-09-07,王宇涛完成。 1、引进的汉字GBK汉字编码体系,原来GB2312已经不适用了。 2、把“重打宏指令”由原来的0x11/0x12改为 1b 04 30/31,因为0x11/0x12在ASCII码中都有特定含义。 3、把“设置页顶宏指令”由原来的 1b 07 n 改为 1b 2c n。 4、新增“打印机复位”指令:1b 3a 30。 */ /* 以下是Star NX100、NX350系列打印机与”赞同外设宏指令“相关的指令。 */ #define SET_DOUBLE_WIDTH "\x1b\x57\x31" /* 设置倍宽打印。 */ #define CLEAR_DOUBLE_WIDTH "\x1b\x57\x30" /* 取消倍宽打印。 */ #define SET_DOUBLE_HEIGHT "\x1b\x77\x31" /* 设置倍高打印。 */ #define CLEAR_DOUBLE_HEIGHT "\x1b\x77\x30" /* 取消倍高打印。 */ #define SET_DOUBLE_HEIGHT_STAR "\x1b\x49\x43" /* 设置倍高打印。Star NX系列专用。 */ #define CLEAR_DOUBLE_HEIGHT_STAR "\x1b\x49\x41" /* 取消倍高打印。Star NX系列专用。 */ #define SET_BOLD "\x1b\x47" /* 设置加重打印。 */ #define CLEAR_BOLD "\x1b\x48" /* 取消加重打印。 */ #define SET_ENGLISH_CS "\x1c\x54" /* 设置西文列距 n/180英寸:1c 54 00 n。 */ #define SET_CHINESE_CS "\x1c\x53" /* 设置汉字列距 n/180英寸:1c 53 00 n。*/ #define SET_LS "\x1b\x33" /* 设置行距 n/180英寸:1b 33 n。 */ #define SET_LS360 "\x1b\x2b" /* 设置行距 n/360英寸:1b 2b n。 */ #define BF "\x1b\x6a" /* 反向走纸(Back Feed) n/180英寸:1b 6a n。 */ #define SET_PL "\x1b\x43" /* 设置页长为n行:1b 43 n,进纸时要根据这个页长定位下页的位置。 */ #define SET_LM "\x1b\x6c" /* 设置左边界:1b 6c n,n字符列数。 */ #define LINE_PROCESSOR_DISABLE 1 /* 行缓存无效,所有字符立即输出。 */ #define WESTERN_MIN 0x20 /* 西文可见字符编码最小值。 */ #define WESTERN_MAX 0x7f /* 西文可见字符编码最大值。 */ #define CHN_MSB_MIN 0x81 /* GBK汉字编码,汉字高字节最小值。 */ #define CHN_MSB_MAX 0xfe /* GBK汉字编码,汉字高字节最大值。 */ #define CHN_LSB_MIN 0x40 /* GBK汉字编码,汉字低字节最小值。 */ #define CHN_LSB_MAX 0xfe /* GBK汉字编码,汉字低字节最大值。 */ #define LINE_BUF_MAX 1024 /* 行缓存最大值。 */ #define REPLACE_STR_MAX 256 /* 替换字符串的最大长度。 */ /* STAR与EPSON LQ系列打印机类 */ class PRT_star { public: /* 定义公有数据,可以在类外使用,通过这个结构可以传递对本外设的配置信息。 */ /* 本结构在AHA的驱动程序printer.c中,最初是用来保存辅口连接的外设以及外设的配置信息。 在congfig.ini中的信息与此结构对应。现在将结构中有关端口的部分去除,只储存外设的配置信息, 但结构的名字保持不变,仍叫“aux_device_config”。与原结构最大的差别就是结构不再是各类外 设配置信息的汇总,而是仅用来描述本设备的配置,不同的外设,本结构的内容也会有所不同。 */ struct aux_device_config { int port_mode ; /* 端口模式,保存连接本设备的控制端口的信息(如终端辅口)。 */ /* device_type=设备类型,用来描述本设备: Bit0=0 通用倍高指令 1b 77 n,针对LQ1600系列。 Bit0=1 专用倍高指令 1b 49 n,针对Star NX系列。 Bit1=0 不支持逆向走纸指令1b 6a n,针对LQ1600系列。 bit1=1 支持逆向走纸指令1b 6a n,针对Star NX系列。 Bit4 换行(0a)指令如何执行。Bit4=1,换行不回车;Bit4=0,换行+回车。 LQ1600系列打印机典型设置: device_type=0x0000,高字节任意。 Star NX系列打印机典型设置:device_type=0x0003,高字节任意。 */ int device_type ; int top_margin ; /* 物理页顶区域,一般以最小步进距离为单位。 */ int bottom_margin ; /* 物理页尾区域,一般以最小步进距离为单位。 */ int left_margin ; /* 物理左边界区域,以当前列距下的字符为单位。 */ int ht ; /* 横向制表位,以当前列距下的字符为单位。 */ /* 有时在输出时,需要特定的字符换成另一个字符,如把$换成¥等。下面的两个替换字符串 包括源字符串和目的字符串,它们可以来自配置文件,也可以使用驱动定义的初值。 replace_source_str中放置的是源字符串,即要被替换的字符串,replace_target_str 中放置的是目的字符串,即替换字符串。两个字符串需要严格的一一对应,例如: replace_source_str="A王李",replace_target_str="B赵钱",表示输出时,A将背换成B; “王”将背换成“赵”;“李”将背换成“钱”;这个功能是在本版新增的,原来也有类似的 功能,但只能做有限的货币符号的替换。 */ char replace_source_str[REPLACE_STR_MAX] ; /* 替换源字符串。 */ char replace_target_str[REPLACE_STR_MAX] ; /* 替换目的字符串。 */ } adc ; protected: /* 定义受保护的数据,只能在本类和继承类中使用。 */ /* 以下结构记录当前纸张和打印的状态。 */ struct paper_status { int paper ; /* 纸张是否在打印机内。 */ int avp ; /* 以最小步进距离定位绝对垂直位置。 */ int row ; /* 当前行位置。 */ int col ; /* 当前列位置。 */ int ls ; /* 当前行距。 */ int cs ; /* 当前列距。 */ int last_cs ; /* 上一个打印字符或相对列定位所使用的列距。 */ /* 符合以下条件既是多列距行: 1、本行是已经按照某个列距移动过(ps.col>0)。即至少打印过一个字符或做过一次列定位,其实打印字符本身 就相当于相对列定位。这个条件首先确定在一行(ps.col>0)之前的多次列距改变,不作为判断多列距行的依据。 另外还保证了不会混淆不是同一行的列距改变。举例:如果上一行列距10CPI,本行列距12CPI,如果不判断 ps.col>0,那么程序会误以为本行是多列距行,因为 10CPI != 12CPI。 2、本次打印(相对列定位)与上次打印(相对列定位)所使用的列距不同(ps.last_cs != ps.cs)。 3、绝对列定位后,本行就一定不是多列距行,因为绝对列定位的意义就是从列的最开始,以当前列距 定位到指定位置,绝对定位后就相当于回复到单列距行,所以要清除多列距标志(ps.multi_cs=0)。 4、本驱动需要multi_cs功能。 ps.multi_cs是针对没有绝对列定位指令的打印机(如LQ1600、Star等)设计的,这些打印机的多列距行绝对列 定位需要特殊处理,即在定位前应先发“回车”然后再定位。如果不回车,而是从当前位置转换为相对列定位则 肯定不对,因为绝对列定位n的含义就是以当前列距从位置0开始,打印n个空格到指定的位置。 */ int multi_cs; /* 多列距标志,即一行同时有多个列距存在,V1.20引进。 */ int left_col; /* 当前的逻辑左边界-以列为单位,相对与物理左边界。 */ int top_line; /* 当前的逻辑顶边界-以行为单位,相对于物理顶边界。 */ int length_row ; /* 以行为单位的页长,打印到最大的行就退纸 2001-03-11。 */ int length_inch ; /* 以英寸为单位的页长,打印到页尾就退纸 2001-03-11。 */ int paper_type ; /* 纸张类型。 */ int first_print ; /* “第一次打印”标志。 */ /* real_line,真行标志,用来描述本打印行的状态: Bit0 本逻辑行包含至少一个可打印字符0x20-0x7f以及汉字。Bit0=1,是;Bit0=0,否。 Bit1 一次明确的列定位(包含左边界的设定,左边界是列的物理坐标的一部分)已经执行。 Bit1=1,是;Bit1=0,否。 Bit2 本行已经被物理行定位(本驱动保留)。Bit2=1,是;Bit2=0,否。 */ int real_line ; /* ff_type,退纸类型: Bit0 超过逻辑页长引发的退纸。Bit0=1,是;Bit0=0,否。 Bit1 超过物理页长引发的退纸。Bit1=1,是;Bit1=0,否。 */ int ff_type ; int double_height ; /* 倍高标志。 */ int double_width ; /* 倍宽标志。 */ int line_buf_counter ; /* 行缓存计数器。 */ char line_buf[LINE_BUF_MAX] ; /* 行缓存。 */ } ps ; public: /* 定义公有方法,可以在类外使用。 */ PRT_star( void ) ; /* 构造函数。 */ int printer_exchange( PPDO pdo, char *buf, int buf_length, int control_mode ) ; protected: /* 定义受保护的方法,只能在本类和继承类中使用。 */ int get_answer( PPDO pdo, int ans_type ) ; int line_processor( PPDO pdo, char *buf, int total, int control_mode ) ; int paper_eject( PPDO pdo ) ; int carriage_return( PPDO pdo, int mode ) ; int row_location( PPDO pdo, int row, int mode ) ; int col_location( PPDO pdo, int col, int mode ) ; } ; /* Star NX系列打印机驱动:PRT_star类构造函数。 功能:初始化PRT_star类中的对象和变量。 入口参数: 无 返回: 无 */ PRT_star::PRT_star( ) { /* 初时化“aux_device_config结构”的对象:adc。 */ adc.port_mode=0 ; adc.device_type=0x0703 ; adc.top_margin=0 ; adc.bottom_margin=0 ; adc.left_margin=0 ; adc.ht=8 ; adc.replace_source_str[0]=0 ; adc.replace_target_str[0]=0 ; /* 初时化“paper_status结构”的对象:ps。 */ ps.paper=0 ; ps.avp=0 ; ps.row=0 ; ps.col=0 ; ps.ls=0 ; ps.cs=0 ; ps.last_cs=0 ; ps.multi_cs=0 ; ps.left_col=0 ; ps.top_line=0 ; ps.length_row=0 ; ps.length_inch=0 ; ps.paper_type=0 ; ps.first_print=0 ; ps.real_line=0 ; ps.ff_type=0 ; ps.double_height=0 ; ps.double_width=0 ; ps.line_buf_counter=0 ; } /* Star NX系列打印机驱动:取打印机状态函数。 功能:取得连接打印机的并口的状态字节。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 2、int ans_type: 请求类型,保留未使用。 返回: >0: 状态值。 -9: 非法状态请求。 */ int PRT_star::get_answer( PPDO pdo, int ans_type ) { int value ; /* Star NX系列打印机是通过并口的状态字节来报告自己的状态的,如果是其它端口则无法返回状态。 */ value=( (PPRINTSTR_PDO) pdo )->getPortType( pdo ) ; /* 取当前所用端口的类型。 */ if ( value == PARALLPORT ) value=( (PPRINTSTR_PDO) pdo )->checkPrinterStatus( pdo ) ; /* 取并口的状态字节。 */ else value=-9 ; /* 非并口则报错。 */ return( value ) ; } /* Star NX系列打印机驱动:行处理函数。 功能:将应用要打印的数据放到行缓存ps.line_buf里,并根据control_mode的指示确定是否需要输出到打印机。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 2、char *buf: 等待接收的数据。 3、int total: 等待接收的数据的长度。 4、int control_mode: 控制模式。 1:先把buf里的内容放到行缓存ps.line_buf里,然后将行缓存里的全部内容向设备输出。 0:仅把buf里的内容放到行缓存ps.line_buf里。 返回:本次接收的字符串的长度。 */ int PRT_star::line_processor( PPDO pdo, char *buf, int total, int control_mode ) { int i=0 ; if ( LINE_PROCESSOR_DISABLE ) { /* 关闭“行处理”过程,buf立即输出到打印机。 */ i=pdo->auxComm->write( pdo->item.port, buf, total ) ; return( i ) ; } /* “行处理”过程。“行处理”一般在利用图形方式打印字符时才会使用到,早期不支持汉字打印的PR50,就只能使用图形 来打印汉字。用图形打汉字,就必须完整地接收到一行后才可以打印,这就象打印机内部的处理程序一样。“行处理”技 术来自HPR4905汉卡时代,在PR50图形打汉字时也得到应用,不过时代在进步,不支持汉字的设备基本绝迹。不过现在 依然保留了“行处理”概念,以备不时之需。“行处理”不只是本函数,还需要调用者明确告知,当前发送的buf是行的一 部分control_mode=0,还是最后一部分control_mode=1。 */ while( 1 ) { if ( i < total ) { ps.line_buf[ps.line_buf_counter]=buf[i] ; ps.line_buf_counter++ ; i++ ; /* 如果小于行缓存的最大值则继续接收,否则先强行输出已接收的数据,然后再继续接收。 */ if ( ps.line_buf_counter < LINE_BUF_MAX ) continue ; } else { if ( ps.line_buf_counter==0 || control_mode!=1 ) return( i ) ; /* 返回本次接收的字符串的长度。 */ } pdo->auxComm->write( pdo->item.port, ps.line_buf, ps.line_buf_counter ) ; ps.line_buf_counter=0 ; } } /* Star NX系列打印机驱动:进纸(FF)函数。 功能:执行进纸操作。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 返回: 0: 正常。 */ int PRT_star::paper_eject( PPDO pdo ) { line_processor( pdo, "\x0d\x0c", 2, 1 ) ; /* 打印当前行并退(进)纸。 */ ps.paper=0 ; ps.row=0 ; ps.avp=0 ; ps.col=0 ; ps.first_print=0 ; ps.real_line=0 ; ps.multi_cs=0 ; return( 0 ) ; } /* Star NX系列打印机驱动:回车(CR)函数。 功能:执行回车操作。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 返回: 0: 正常。 */ int PRT_star::carriage_return( PPDO pdo, int mode ) { /* “回车”必然导致本行的输出。另外只要行的位置发生改变,就要求打印机将本行的内容立即打印,否则可能导致数据 大量积压,不利于提高打印效率。列坐标如果是“回车”就归0,如果是“换行”就保持不变。 */ if ( mode ) { /* 列坐标归0,“回车”效果。 */ line_processor( pdo, "\x0d", 1, 1 ) ; /* 立即输出到打印机,防止堵塞在ps.line_buf里。 */ ps.col=0 ; /* 当前列位置。 */ ps.multi_cs=0 ; /* 多列距标志,即一行同时有多个列距存在。 */ ps.real_line=0 ; /* 清空(真实)行标志。 */ } else { /* 列坐标不变,“换行”效果。 */ /* Bit2=0,这样再次遇到可打印字符时,首先要执行物理的行定位。Bit0=0,一个逻辑意义的新行。 */ ps.real_line &= 0xfffa ; } return( 0 ) ; } /* Star NX系列打印机驱动:行定位函数。 功能:根据mode的指示,定位到第row+1行。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 2、int row: 要定位到的绝对行坐标,从0开始计算。 3、int mode: 定位模式。 Bit0: 定位类型,只在逻辑定位(Bit1=1)时使用。Bit0=1,绝对垂直定位;Bit0=1,相对垂直定位。 Bit1: 是否执行逻辑定位,本功能保留。Bit1=1,是;Bit1=0,否。 Bit2: 是否执行物理定位,本功能保留。Bit2=1,是;Bit2=0,否。 返回: 0: 正常。 -1: 要定位的行坐标小于第一行,忽略这次定位。 */ int PRT_star::row_location( PPDO pdo, int row, int mode ) { int temp ; unsigned char value ; if ( row < 0 ) return( -1 ) ; /* 如果要定位的行坐标小于第一行,那就忽略这次定位。 */ while( row>=ps.length_row && ps.length_row && mode ) { /* 定义了页长,且要定位的行坐标超过了页长,且mode=1时,直接进纸并返回。 */ paper_eject( pdo ) ; ps.ff_type |= 0x01 ; /* 因超过逻辑页长而引发的退纸。 */ /* 超出逻辑页长的行数,要在下一页输出,如页长是60行,当前要定位的是第80行,那么超出的 80-60=20行要在下一页里输出,之所以要输出这20行是为了应用程序设计起更方便。如果是140行 则需要连走2页纸,在第3页输出剩余的20行。*/ row-=ps.length_row ; } if ( row == ps.row ) return ( 0 ) ; /* 如果要定位的行就是当前行,退出。 */ if ( row > ps.row ) /* 行定位。 */ { /* 对于正向进纸,以打印空行(0x0a)的方式完成行的定位。空行打印永远以非倍高的方式进行, 无论本打印机是否是这样处理,为了让程序更通用,都要对倍高的情况进行了处理。 */ if ( ps.double_height & 0x01 ) /* 倍高生效中 */ { if ( adc.device_type & 0x01 ) /* 如果是Star NX系列,则用专用指令临时关闭倍高。 */ line_processor( pdo, CLEAR_DOUBLE_HEIGHT_STAR, strlen(CLEAR_DOUBLE_HEIGHT_STAR), 1 ) ; else /* 其它,则用通用指令临时关闭倍高。 */ line_processor( pdo, CLEAR_DOUBLE_HEIGHT, strlen(CLEAR_DOUBLE_HEIGHT), 1 ) ; } while( row > ps.row ) { line_processor( pdo, "\x0a", 1, 1 ) ; /* 按当前行距正向走纸一行。 */ ps.row++ ; } if ( ps.double_height & 0x0001 ) /* 倍高生效中。 */ { if ( adc.device_type & 0x01 ) /* 如果是Star NX系列,则用专用指令恢复倍高。 */ line_processor( pdo, SET_DOUBLE_HEIGHT_STAR, strlen(SET_DOUBLE_HEIGHT_STAR), 1 ) ; else /* 其它,则用通用指令恢复倍高。 */ line_processor( pdo, SET_DOUBLE_HEIGHT, strlen(SET_DOUBLE_HEIGHT), 1 ) ; } } else { if ( (adc.device_type & 0x02) == 0 ) return ( 0 ) ; /* 不支持逆向走纸指令,退出。 */ /* 对于逆向进纸,没有逆向打印空行的指令。只能依靠基于最小步进单位的逆向走纸指令来完成。 如果应用没有设置过行距,将导致实际打印就在当前行进行,行坐标也不变。ps.ls=0说明目前行 距是缺省行距,具体多少不知,0导致行原地不动。 */ temp=ps.ls*(ps.row-row) ; /* 按最小步进单位,计算逆向走纸的量。 */ while ( temp ) { line_processor( pdo, BF, strlen(BF), 1 ) ; /* 逆向走纸。 */ /* 为了提高走纸效率,充分利用打印机最大逆向走纸255/180英寸这一功能。 */ value=( temp > 0xff )? 0xff : (unsigned char) temp ; line_processor( pdo, (char *) &value, 1, 1 ) ; /* 逆向走纸value/180英寸。 */ temp-=value ; } if ( ps.ls ) ps.row=row ; /* 只有ps.ls不等于0,行定位才进行,行坐标才能等于设定数值。 */ } return( 0 ) ; } /* Star NX系列打印机驱动:列定位函数。 功能:根据mode的指示,定位到第col+1列。每次都是物理定位,没有逻辑定位。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 2、int col: 要定位到的绝对列坐标,从0开始计算。 3、int mode: 定位模式。 Bit0: 定位类型。Bit0=1,绝对垂直定位;Bit0=0,相对垂直定位。 Bit1: 是否执行逻辑定位,本功能保留。Bit1=1,是;Bit1=0,否。 Bit2: 是否执行物理定位,本功能保留。Bit2=1,是;Bit2=0,否。 返回: 0: 正常。 -1: 要定位的列坐标小于第一列,忽略这次定位。 */ int PRT_star::col_location( PPDO pdo, int col, int mode ) { /* 绝对列定位:定位到“当前列距*绝对列坐标”所指定的地方;相对列定位:从当前位置向右或向左(负数)移动 “当前列距*需移动的相对列数”。 Star既没有列的绝对定位指令也没有相对定位指令,对于应用的绝对和相对定位需求,驱动都是通过转化为打印等量 的“空格”的方法来实现的,这实际上等同与相对定位。所有这些定位的单位都是当前列距下的字符,而非打印机的 最小步进单位。 */ int space_counter ; if ( col < ps.left_col ) return( -1 ) ; /* 如果要定位的列坐标小于左边界,那就忽略这次定位。 */ if ( col 0 ) { if ( ps.col>0 && (ps.last_cs != ps.cs) ) /* 判断是否为多列距行? */ ps.multi_cs |= 0x01 ; /* 空格打印永远以非倍宽的方式进行。 */ if ( ps.double_width & 0x01 ) /* 倍宽生效中。 */ line_processor( pdo, CLEAR_DOUBLE_WIDTH, strlen(CLEAR_DOUBLE_WIDTH), 0 ) ; /* 临时关闭倍宽。 */ while( space_counter > 0 ) { line_processor( pdo, " ", 1, 0 ) ; /* 用打印“空格”来实现列定位。 */ space_counter-- ; } if ( ps.double_width & 0x01 ) /* 倍宽生效中。 */ line_processor( pdo, SET_DOUBLE_WIDTH, strlen(SET_DOUBLE_WIDTH), 0 ) ; /* 恢复倍宽。 */ ps.last_cs=ps.cs ; /* 保存刚使用过的列距。 */ ps.real_line |= 0x01 ; /* 至少有一个“空格”输出(列定位),所以这是一个有真实内容的行。 */ } ps.col=col ; /* 更新当前列坐标为设定值。 */ ps.real_line |= 0x02 ; /* 一次明确的列定位(包含左边界)已经执行。 */ return( 0 ) ; } /* Star NX系列打印机驱动:打印函数。 功能:取得连接打印机的并口的状态字节。 入口参数: 1、PPDO pdo: 输入、输出的端口对象。 2、char *buf: 调用者(应用)传过来要打印的内容。 3、int buf_length: 要打印内容的字节长度。 4、int control_mode: 控制模式,保留未使用。 返回: 0: 正常 */ int PRT_star::printer_exchange( PPDO pdo, char *buf, int buf_length, int control_mode ) { int i, j, return_value, status, command_flag=0 ; unsigned char value, temp, temp_msb=0 ; static unsigned char chn_msb=0 ; /* 存放汉字的高字节,以便以后可能的字符转换,如货币符号等。 */ return_value=pdo->auxComm->open( pdo->item.port, 2 ) ; /* 打开连接外设的通讯端口。 */ if ( return_value < 0 ) return( -11 ) ; /* 打开连接外设的通讯端口失败。 */ /* 输入过程:返回状态请求... */ /* status,状态请求的返回值: 1、Bit1-0 有纸无纸标志。Bit1-0,00=打印机内无纸;01=有纸;10=纸已对齐;11=已经进纸(如执行过1b 4c 30 30 30)。 2、Bit2 最后一个打印行标志。Bit2=1 是;Bit2=0 否。 */ status=0 ; /* 将所有返回状态位清0。 */ if ( control_mode & 0x8000 ) /* 返回打印机状态。 */ { switch( control_mode & 0xff ) { case 0x04: return_value=get_answer( pdo, 4 ) ; if ( return_value > 0 ) /* <=0 就认为是无纸。 */ status=( return_value & 0x20 )? 0x03 : 0x00 ; /* 取得纸张状态。 */ break ; } pdo->auxComm->close( pdo->item.port ) ; /* 关闭连接外设的通讯端口。 */ return( status ) ; } /* 输出过程:打印... */ for ( i=0; i =CHN_LSB_MIN && value<=CHN_LSB_MAX ) /* 当前接收的字符是否是汉字低字节? */ { /* 要输出的是一个汉字,查找“替换源字符串”,看是否是一个需要被替换的汉字? */ temp_msb=0 ; for( j=0; j =CHN_MSB_MIN && temp<=CHN_MSB_MAX ) /* 是否是汉字的高字节? */ { temp_msb=temp ; /* 如果不存在汉字高字节,那么当前字节就是汉字的高字节。 */ continue ; /* 继续循环,取下一个字节。 */ } if ( temp_msb && temp>=CHN_LSB_MIN && temp<=CHN_LSB_MAX ) /* 是否是汉字的低字节? */ { /* 在“替换源字符串”中获得一个汉字,比较要输出的汉字是否需要被替换。 */ if ( temp_msb==chn_msb && temp==value ) { /* 这个汉字需要被替换 */ temp_msb=(unsigned char) adc.replace_target_str[j-1] ; temp=(unsigned char) adc.replace_target_str[j] ; if ( temp_msb>=CHN_MSB_MIN && temp_msb<=CHN_MSB_MAX && temp>=CHN_LSB_MIN && temp<=CHN_LSB_MAX ) { /* 如果“替换目的字符串”中的对应位置确实也是汉字,那么就执行替换,否则就忽略。 这个检查主要是防止“替换目的字符串”的对应位置是非法字符或是两个西文。 */ chn_msb=(unsigned char) adc.replace_target_str[j-1] ; value=(unsigned char) adc.replace_target_str[j] ; } break ; /* 无论是否替换,都结束循环,退出。 */ } } temp_msb=0 ; /* 本字节不是汉字字节,或者一个完整汉字已经处理。 */ } } else chn_msb=0 ; /* 如果不是汉字字节,上个接收字节无论是什么都不能算做汉字的高字节。 */ switch( command_flag ) { case 0x00: /* 处理单字符指令或单个可打印的字符。 */ switch( value ) { case 0x09: /* 横向制表位置。 */ col_location( pdo, ps.col+adc.ht, 0x04 ) ; /* 转换为绝对列坐标后再定位、物理定位。 */ break ; case 0x0d: /* 回车。 */ carriage_return( pdo, 1 ) ; break ; case 0x0a: /* 回车+换行。 */ if ( (adc.device_type & 0x10) == 0 ) /* 是否在“换行”时执行“回车”? */ carriage_return( pdo, 1 ) ; /* 执行“回车”,未打印的内容输出,列坐标归0。 */ else carriage_return( pdo, 0 ) ; /* 类似“回车”,未打印的内容输出,但列坐标不变。 */ row_location( pdo, ps.row+1, 0x04 ) ; /* 转换为绝对行坐标后再定位、物理定位。 */ break ; case 0x0c: /* 进(退)纸。 */ paper_eject( pdo ) ; ps.ff_type=0 ; /* 正常指令退纸,非因超过逻辑或物理页长而引发的退纸。 */ break ; case 0x1b: /* 1B命令,需等待后续命令字符。 */ command_flag=0xff ; break ; default: /* 处理需要打印的单个字符。 */ if ( chn_msb==0 && value>=CHN_MSB_MIN && value<=CHN_MSB_MAX ) { chn_msb=value ; /* 是汉字的高字节,退出,准备接收汉字低字节。 */ break ; /* 不做后续处理,退出,继续接收下一个字节。 */ } if ( chn_msb == 0 ) { /* 要输出的是一个西文,下面进行西文替换和是否为可见字符检查。首先查找”替换源字符串“, 看是否需要被替换。西文字符之所以选择在这里做替换检查,是因为着这里都是要打印的字符而 非指令,指令中的内容是不能被替换的,要替换一定是可打印的字符(包括汉字和西文)。 */ temp_msb=0 ; for( j=0; j =CHN_MSB_MIN && temp<=CHN_MSB_MAX ) /* 是否是汉字的高字节? */ { temp_msb=temp ; /* 如果不存在汉字高字节,那么当前字节就是汉字的高字节。 */ continue ; /* 继续循环,取下一个字节。 */ } if ( temp_msb && temp>=CHN_LSB_MIN && temp<=CHN_LSB_MAX ) /* 是否是汉字的低字节? */ { temp_msb=0 ; /* 一个完整的汉字已获得,但要被忽略,因为现在要找的是西文。 */ continue ; /* 继续循环,取下一个字节。 */ } temp_msb=0 ; /* 本字节不是汉字字节,或者一个完整汉字已经处理。 */ if ( temp == value ) { /* 这个字符需要被替换。 */ value=(unsigned char) adc.replace_target_str[j] ; break ; } } /* 过滤西文不可打印字符。汉字之所以不用特殊的检查,是因为在前面已经针对汉字的高低 字节,在不同的地方进行了检查,不会有不符合编码规则的汉字出现在这里。 */ if ( value WESTERN_MAX ) break ; /* 本字节是不可打印字符,丢弃。不做后续处理,退出,继续接收下一个字节。*/ } /* 如果在打印一行中第一个字符之前,没有执行过列定位,并且左边界大于0,那么现在必须进行 列定位,否则左边界将无法设置,没有左边界的打印,格式也必然是错误的。左边界是个逻辑值, 驱动并不物理设置打印机的左边界,只有通过列定位,才能把左边界真实地打印出来。 */ if ( (ps.real_line & 0x02)==0 && ps.left_col>0 ) col_location( pdo, ps.col, 0x05 ) ; /* 执行绝对列定位。 */ if ( ps.col>0 && (ps.last_cs != ps.cs) ) /* 判断是否为多列距行? */ ps.multi_cs |= 0x01 ; if ( chn_msb ) /* 如果有未输出的汉字高字节,则先输出这个字节。 */ { line_processor( pdo, (char *) &chn_msb, 1, 0 ) ; ps.col++ ; if ( ps.double_width & 0x01 ) /* 倍宽,则列位置再加1。 */ ps.col++ ; chn_msb=0 ; /* 一个完整的汉字被处理,汉字高字节标志被清除。 */ } line_processor(pdo, (char *) &value, 1, 0 ) ; /* 输出这个字符。 */ ps.col++ ; /* 列位置加1。 */ if ( ps.double_width & 0x01 ) /* 倍宽,则列位置再加1。 */ ps.col++ ; ps.last_cs=ps.cs ; /* 保存刚使用过的列距。 */ ps.real_line |= 0x01 ; /* 至少有一个可打印字符已输出,所以这是一个有真实内容的行。 */ } break ; case 0xff: /* 处理指令"1B"的第二个字符。 */ switch( value ) { case 0x01: /* 设置列距,单位:1/180英寸。 */ case 0x02: /* 设置行距,单位:1/180英寸。 */ case 0x03: /* 设置行距,单位:1/240英寸。 */ case 0x04: /* 设置重打和取消重打。 */ case 0x05: /* 设置倍高和取消倍高。 */ case 0x06: /* 设置倍宽和取消倍宽。 */ case 0x08: /* 设置页长,单位:英寸。 */ case 0x09: /* 设置页长,单位:行。 */ case 0x2a: /* 设置左边界,单位:列。 */ case 0x2c: /* 设置页顶(保留)。 */ case 0x3a: /* 系统设置。 */ case 0x4a: /* 绝对行定位,单位:行。 */ case 0x4b: /* 相对行定位,单位:行。 */ case 0x4c: /* 绝对列定位,单位:列。 */ case 0x4d: /* 相对列定位,单位:列。 */ command_flag=value ; /* 等待接收指令"1B"的第三个字符。 */ break ; default: command_flag=0 ; /* 如果是不支持的指令,则下个字符恢复为接收单字符状态。 */ } break ; /* 以下处理指令"1B"的第三个字符。 */ case 0x01: /* 设置列距,单位:1/180英寸。 */ command_flag=0 ; if ( value < 12 ) /* 西文字符的点阵为12*24,宽度最小为12/180英寸。 */ value=12 ; ps.cs=value ; /* 保存列距。 */ value-=12 ; line_processor( pdo, SET_ENGLISH_CS , strlen(SET_ENGLISH_CS)+1 , 0 ) ; /* 包括字符串结尾的"00"。 */ line_processor( pdo, (char *) &value , 1 , 0 ) ; /* 设置西文、半角列距。 */ value*=2 ; line_processor( pdo, SET_CHINESE_CS , strlen(SET_CHINESE_CS)+1 , 0 ) ; /* 包括字符串结尾的"00"。 */ line_processor( pdo, (char *) &value , 1 , 0 ) ; /* 设置中文列距。 */ break ; case 0x02: /* 设置行距,单位:1/180英寸。 */ command_flag=0 ; ps.ls=value ; /* 保存行距。 */ line_processor( pdo, SET_LS, strlen(SET_LS), 0 ) ; line_processor( pdo, (char *) &value, 1, 0 ) ; break ; case 0x03: /* 设置行距,单位:1/240英寸。 */ /* ps-ls保存的是以1/180英寸为单位的行距,逆向换行时使用,因为本打印机不支持以1/360英寸逆向走纸。 打印机不支持1/240英寸的行距,但支持1/180、1/360英寸两种行距,所以在设行距时使用更细腻的1/360英寸 单位来实现1/240英寸单位。*/ command_flag=0 ; ps.ls=value*3/4 ; /* 单位转换:1/240->1/180英寸。 */ line_processor( pdo, SET_LS360, strlen(SET_LS360), 0 ) ; value=value*3/2 ; /* 单位转换:1/240->1/360英寸。 */ line_processor( pdo, (char *) &value, 1, 0 ) ; break ; case 0x04: /* 设置重打和取消重打 1B 04 n,n的含义见下面代码。*/ command_flag=0 ; switch( value ) { case 0x30: /* 取消重打。 */ line_processor( pdo, CLEAR_BOLD , strlen(CLEAR_BOLD) , 0 ) ; break ; case 0x31: /* 设置重打。 */ line_processor( pdo, SET_BOLD , strlen(SET_BOLD) , 0 ) ; break ; } break ; case 0x05: /* 设置倍高和取消倍高 1B 05 n,n的含义见下面代码。*/ command_flag=0 ; switch( value ) { case 0x30: /* 取消倍高。 */ if ( adc.device_type & 0x01 ) /* 如果是Star NX系列,则用专用指令临时关闭倍高。 */ line_processor( pdo, CLEAR_DOUBLE_HEIGHT_STAR, strlen(CLEAR_DOUBLE_HEIGHT_STAR), 0 ) ; else /* 其它,则用通用指令临时关闭倍高。 */ line_processor( pdo, CLEAR_DOUBLE_HEIGHT, strlen(CLEAR_DOUBLE_HEIGHT), 0 ) ; ps.double_height &= 0xfffe ; break ; case 0x31: /* 设置倍高 */ if ( adc.device_type & 0x01 ) /* 如果是Star NX系列,则用专用指令设置倍高。 */ line_processor( pdo, SET_DOUBLE_HEIGHT_STAR, strlen(SET_DOUBLE_HEIGHT_STAR), 0 ) ; else /* 其它,则用通用指令设置倍高。 */ line_processor( pdo, SET_DOUBLE_HEIGHT, strlen(SET_DOUBLE_HEIGHT), 0 ) ; ps.double_height |= 0x0001 ; break ; } break ; case 0x06: /* 设置倍宽和取消倍宽 1B 06 n,n的含义见下面代码。*/ command_flag=0 ; switch( value ) { case 0x30: /* 取消倍宽。 */ line_processor( pdo, CLEAR_DOUBLE_WIDTH, strlen(CLEAR_DOUBLE_WIDTH), 0 ) ; ps.double_width &= 0xfffe ; break ; case 0x31: /* 设置倍宽。 */ line_processor( pdo, SET_DOUBLE_WIDTH, strlen(SET_DOUBLE_WIDTH), 0 ) ; ps.double_width |= 0x0001 ; break ; } break ; case 0x08: /* 设置页长-以英寸为单位,取值0(255) ~ 254。 */ /* 以英寸为页长单位的指令。与1b 09 n指令不同,本驱动程序在打印到指定 页长时不自动退纸,这是因为驱动不保存以英寸为单位的行位置。 */ command_flag=0 ; /* 对于为0的设置,伪指令无法表达,因为0代表包含着伪指令的字符串的结束。 对于必须为0的伪指令参数,用0xff代替。*/ value=( value == 0xff )? 0 : value ; ps.length_inch=ps.ls * value ; /* 设置打印机的物理页长,进纸(0x0c)时要根据这个页长定位下页的位置。 */ line_processor( pdo, SET_PL, strlen(SET_PL), 0 ) ; line_processor( pdo, (char *) &value, 1, 0 ) ; break ; case 0x09: /* 设置页长-以行为单位,取值0(255) ~ 254。 */ command_flag=0 ; /* 对于为0的设置,伪指令无法表达,因为0代表包含着伪指令的字符串的结束。 对于必须为0的伪指令参数,用0xff代替。*/ value=( value == 0xff )? 0 : value ; ps.length_row=value ; break ; case 0x2a: /* 左边界-以字符为单位,取值0(255) ~ 254。 */ command_flag=0 ; /* 对于为0的设置,伪指令无法表达,因为0代表包含着伪指令的字符串的结束。 对于必须为0的伪指令参数,用0xff代替。*/ value=( value == 0xff )? 0 : value ; ps.left_col=value ; /* 以列为单位设置左边界 */ /* 左边界具体的设置在驱动中完成,不再需要设置物理的打印机左边界,因为不同的打印机设置方法 差别很大,因此还是放到驱动程序里做比较容易做到统一,以下代码封闭。 2007-07-07 王宇涛。 line_processor( pdo, SET_LM, strlen(SET_LM), 0 ) ; line_processor( pdo, &value , 1 , 0 ) ; */ break ; case 0x2c: /* 设置页顶。未提供此功能,保留。*/ command_flag=0 ; break ; case 0x3a: /* 系统设置。 */ command_flag=0 ; switch( value ) { case 0x30: /* 打印机复位。 */ line_processor( pdo, "\x1b\x40", 2, 1 ) ; break ; } break ; case 0x4a: /* 绝对行定位,取值1 ~ 255。 */ command_flag=0 ; carriage_return( pdo, 0 ) ; /* 类似“回车”,未打印的内容输出,但列坐标不变。 */ /* 伪指令中的行从1开始,而ps.row是从0开始的,减1以统一起始量。 */ row_location( pdo, value-1, 0x05 ) ; /* 绝对行定位、物理定位。 */ break ; case 0x4b: /* 相对行定位,取值-127 ~ +127。 */ command_flag=0 ; carriage_return( pdo, 0 ) ; /* 类似“回车”,未打印的内容输出,但列坐标不变。 */ /* 0意味着原地不动,伪指令的参数可以为负数,负数意味着逆向走纸。*/ row_location( pdo, ps.row + (signed char) value, 0x04 ) ; /* 转换为绝对行坐标后再定位、物理定位。 */ break ; case 0x4c: /* 绝对列定位,取值1 ~ 255。 */ command_flag=0 ; /* 伪指令中的列从1开始,而ps.col是从0开始的,减1以统一起始量。定位时要加上左边界。 */ col_location( pdo, ps.left_col+value-1, 0x05 ) ; /* 绝对列定位、物理定位。 */ break ; case 0x4d: /* 相对列定位,取值0 ~ 255。 */ /* 列相对定位不取负数,有以下原因: 1、负数代表向左移动打印头,一般打印机没有左移打印头的命令,所以一般都是通过转换为绝对列定位来模拟 这个动作。如果在左移动前变动了列距,那么这种模拟会产生错误的格式。如果打印机有左移打印头的命令, 那么在极端的情况下,左移还可能超过打印机的物理左边界。举例:以15CPI绝对定位到第3列(1b 4c 03),然 后变动列距为10CPI,在向左移动3列(1b 4c -03),对于用绝对列定位来模拟的,格式是错误的;对于使用左移 打印头的,定位会在打印机物理左边界的左边,这是更大的错误。 2、对于宽行打印机来说,一行的列完全可能超过127,所以相对列定位的n应大于127,那么n就只能是无符号数。 */ command_flag=0 ; /* 不要使用 if ( ps.col < ps.left_col ),因为ps.left_col在一行的打印过程有可能会改变,这种改变应 在下一行生效,否则有可能在行中就再次设置左边界。 */ if ( (ps.real_line & 0x02) == 0 ) /* 本行的第一次列定位,使用左边界的值,再加偏移量,然后转换为绝对列坐标后再定位、物理定位。 */ col_location( pdo, ps.left_col+value, 0x04 ) ; else /* 本行的非第一次列定位,使用当前列坐标,再加偏移量,然后转换为绝对列坐标后再定位、物理定位。 */ col_location( pdo, ps.col+value, 0x04 ) ; /* 转换为绝对列坐标后再定位、物理定位。 */ break ; } } pdo->auxComm->close( pdo->item.port ) ; /* 关闭连接外设的通讯端口。 */ return( i ) ; } /* ------------- Star NX100+、NX350系列打印机驱动程序:结束行 ------------- */