简介:围绕 SAP Smart Forms 中文打印时字体重叠、长文本被压缩这两类典型问题整理的排错文档,面向已具备一定 ABAP 表单开发基础、需要在多语言环境下输出中文字段的开发者与顾问。压缩包共 1 个 doc 文件,约 28KB,内容集中,属于可直接查阅的经验记录:先区分英文环境与中文环境下打印预览的差异,再落到 Smart Forms 生成功能模块中 SSFRT_SET_LANGUAGES 的调用逻辑,说明 %header-langu 如何决定打印的语言版本,以及为何补充 control_parameters-langu = 1 后重叠消失;同时提到 Note 808326、908442 与亚洲字符超过 31 字符被压缩问题对应的 Note 1007575,便于读者据此核对系统补丁状态。已有 1088 人学习,适合作为中文字体异常与打印预览异常的对照排查参考。
1. 环境先对齐:SAP Smart Forms 中文字体重叠这件事,打补丁不一定管用
有个场景我印象很深:系统里 Unicode 相关补丁已经打到最新,SNOTE 里那几个 Note 一条不落全绿,程序跑起来也不报任何错。结果业务同事在 login language 为E的环境下点一下打印预览,中文全叠在一起,字符之间相互压着,预览里看着像一条黑带,真打到纸上更糟——超长的部分会挤出去和后面的字段交杂。这就是 SAP Smart Forms 中文字体重叠最典型的形态,也是很多人第一反应去搜 Note、打完补丁发现没用、然后开始怀疑系统 bug 的原因。
这套环境不复杂,一套 ECC 系统、一个 Smart Forms、一个打印程序,但它的坑集中在「语言版本」这一层:表单本身是中文的,打印请求却按英文在排。装不好不是功能缺失,是输出错乱,而且只在跨语言打印时复现,测试环境很容易漏掉。这篇写给正在处理 SAP Smart Forms / SAPscript 中文打印问题的 ABAP 和 Basis,从环境三查开始,一路到代码落地、补丁基线、验证跑通。
2. 环境与版本核对:三件事没查清就别动代码
动手之前先把底摸清。跨语言打印这个问题,很多次「改了没效果」都是因为环境前提没对齐——你在中文环境复现不出来,或者在非 Unicode 系统上照着 Unicode 的方案改,自然对不上号。这一章给的是可以直接抄的核查顺序,跑完这三步,后面每一步判断才有依据。
2.1 三个必须先确认的系统前提
第一个是登录语言。系统菜单里「系统 → 状态」能看到当前会话的登录语言,中文是1,英文是E。要注意的是登录语言会跟着用户主数据走,同一台机器换个用户登录,sy-langu就变了,所以复现问题时别只看自己的会话,要问清楚报问题的人是哪个账号、哪种语言登的。SU3 里可以查默认登录语言,改之前记得让用户确认,别顺手给人改了。
第二个是 Unicode 判定。Note 808326、908442 的适用范围都写死在 Unicode 系统上,非 Unicode 系统套这两条 Note 的逻辑是白费功夫。判断方法不用去翻系统参数,跑一个极短的报表就行:
report zcheck_print_env. data: lv_charsize type i. " 1 = 非 Unicode 系统,2 = Unicode 系统 lv_charsize = cl_abap_char_utilities=>charsize. write: / '当前登录语言 sy-langu :', sy-langu. write: / '字符宽度 charsize :', lv_charsize. write: / '系统日期 / 时间 :', sy-datum, sy-uzeit.cl_abap_char_utilities=>charsize返回 1 就是单字节系统,返回 2 就是 Unicode。这个判断比记参数名靠谱,也不会因为系统升级换代失效。第三个是补丁级别,SE38 跑不了这个,得去「系统 → 状态」看 SAP Basis 组件的 Support Package 层级,或者 SPAM 里看已导入的队列。补丁级别要记下来,后面查 Note 是否适用时,这是唯一的判断依据。
再补一个容易漏的:输出设备类型。SPAD 里看目标打印机的设备类型,设备类型决定了字符集和字体映射,中文字符走一条不认双字节的设备类型,预览和真打都会出问题。常见做法是确认设备类型面向中文场景(比如 CNSAPWIN 这一类),不确定就先在 SPAD 里看它的字符集定义,别直接改生产设备。
2.2 版本与 Note 基线核对表
原文那套系统是 ECC6.4,已经打过两条 Note,问题仍然存在。这说明一件事:Note 是必要条件,不是充分条件。做基线核对的时候,把下面三条逐个查一遍,别凭印象。
| Note 编号 | 标题 | 症状描述 | 核对方式 |
|---|---|---|---|
| 808326 | SCR: Unicode print preview | Unicode 系统下 SAPscript 表单预览排版错误,字符重叠或间距过大 | SNOTE 注释浏览器按编号查实施状态 |
| 908442 | Overlapping Asian texts in Unicode system | Unicode 系统预览时亚洲文字重叠 | 同上 |
| 1007575 | Compressed Asian characters in the print preview | Unicode 系统下亚洲字符超过 31 个字符被压缩 | 同上,注意这条是后发现的 |
SNOTE 里查已实施注释,用「注释浏览器」按编号检索比翻列表快,能直接看到是「已应用」还是「可应用」。有一点要提醒:不同 Basis 版本上 Note 的修正说明(Corrections Instructions)代码片段不完全一样,原文提到的判断方法是拿系统里的代码和 Note 里的修正代码逐段对比,对上了才算真打了。只看状态灯是绿的,不够。
三条 Note 里,前两条管重叠,第三条管压缩,是两个不同的问题,别混着查。原文的经历是先解决了重叠,多次测试后又发现长字符串被压成一坨,回头才找到 1007575。这个顺序很典型——重叠解决之后,长文本的压缩问题才会浮出来,因为之前全叠在一起,根本看不出还有压缩。
3. 定位语言参数:从 SSFRT_SET_LANGUAGES 追到 %header-langu
把环境和补丁排除掉之后,剩下的可能就是程序本身。这一章讲的是怎么从「预览重叠」这个现象,一路追到决定表单语言版本的那个变量。追的过程不复杂,但要知道去哪找、看哪个值。
3.1 在生成的 FM 里找那段赋值代码
Smart Forms 激活之后会生成一个功能模块,名字形如/1BCDWB/SF00000xxxxx,在 SE37 里直接打开就能看源码。这个 FM 是系统生成的,一般不建议在里面改,但它是排查问题最好的黑匣子——排版逻辑、语言判定、字段输出全在里面。
生成 FM 里往下翻,一般在 350 行以后,能找到这么一段:
call function 'SSFRT_SET_LANGUAGES' exporting i_control_parameters = control_parameters i_masterlanguage = '1' i_thrulang = ' ' i_install_lang = ' ' importing o_languages = %languages. %header-langu = %languages-langu1.逐个参数说清楚:i_control_parameters是从调用方传进来的控制结构,类型是SSFCTRLOP,里面装着输出语言、是否弹对话框、是否返回 OTF 这些开关;i_masterlanguage = '1'是主语言,写死成中文;i_thrulang是翻译语言,空表示不指定;i_install_lang是安装语言,同样为空;返回的%languages是一个语言列表结构,langu1是其中第一个有效语言。
真正决定成败的是下一行%header-langu = %languages-langu1。%header的类型就是SSFCTRLOP,所以%header-langu这个字段的含义是「本次打印按哪个语言版本的表单来排版」。它决定了系统去取哪个语言版本的表单定义、按哪套字符宽度算换行。如果它是E,系统就会找英文版表单;找不到,就会把中文版当英文版处理,中文字符的宽度按单字节算,于是该占两格的字符只占一格,后面的内容顶上来,重叠就出现了。
3.2 读懂 control_parameters 为 initial 时的回退逻辑
SSFRT_SET_LANGUAGES里给%header-langu赋值的逻辑,原文写得很直接:
" 伪代码,还原 FM 内部的判定顺序 if control_parameters is initial. langu = sy-langu. " 回退:用当前登录语言 else. langu = control_parameters-langu. endif.这就是整件事的根。control_parameters是初始值(is initial)的时候,语言直接退回sy-langu——也就是当前会话的登录语言。中文环境登录,sy-langu = '1',一切正常,换行按双字节算;英文环境登录,sy-langu = 'E',系统里没有 E 版的中文表单,于是拿中文版硬套英文排版规则,中文数据字段(语法为&...&的那些)一长就叠。
再往深一层:为什么control_parameters是初始值?因为调用这个 FM 的 FORM routine 程序里压根没给control_parameters-langu赋过值。程序只传了数据、传了输出设备,语言字段是空的。系统不会替你猜,它只会回退到sy-langu,而这个回退在单语言场景下永远正确,在跨语言场景下永远错。
到这里逻辑闭环了:Note 打全 → 系统层面的排版缺陷已经修掉 → 但你的程序还在用错误的语言版本请求排版 → 重叠依旧。这也解释了为什么原文一开始怀疑是自己的程序写错了,方向是对的。
提示:调试的时候不要只盯着
sy-langu看,那个值永远是对的。断点要打在SSFRT_SET_LANGUAGES调用之后,看%header-langu被赋成了什么,这才是判断依据。
4. 动手改配置:control_parameters-langu = 1 的传参落地
定位到了,改起来其实只有一行。但这一行放在哪里、怎么传、能不能经得住重新激活和传输,决定了改完之后是稳定运行还是下次激活又翻车。这一章按调用方程序、SAPscript 两套场景分别落地,最后说样式和设备的收尾。
4.1 在调用程序里补赋值
正确的做法是在调用打印 FM 的程序里,把SSFCTRLOP结构的langu字段显式赋成'1',然后传给表单 FM。别去改生成的 FM,那是系统生成的,表单重新激活一次你的改动就没了,这个坑后文还会说。
data: ls_ctrlop type ssfctrlop, ls_output type ssfcompop, ls_jobout type ssfcrltd. " 核心一行:把输出语言钉死在中文 ls_ctrlop-langu = '1'. " 后台打印不弹对话框,避免无人值守时卡住 ls_ctrlop-no_dialog = 'X'. " 取回 OTF,便于排查和存底 ls_ctrlop-getotf = 'X'. ls_output-tddest = 'LP01'. " 目标输出设备,按实际设备改 ls_output-tdimmed = 'X'. " 立即打印,不留在假脱机里等 ls_output-tdcopies = 1. call function '/1BCDWB/SF00000123' exporting control_parameters = ls_ctrlop output_options = ls_output user_settings = space importing job_output_info = ls_jobout tables it_data = lt_data exceptions formatting_error = 1 internal_error = 2 send_error = 3 user_canceled = 4. if sy-subrc <> 0. message id sy-msgid type 'E' number sy-msgno with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. endif.参数取舍说几个。langu = '1'是唯一的必需项,其他都是配套。no_dialog = 'X'建议加上,跨语言打印往往出现在后台作业里,弹对话框等于卡死。getotf = 'X'是我个人习惯,拿到 OTF 之后能在调试器里直接看排版结果,比反复点预览快得多。tddest要按实际设备填,别照抄LP01,设备不存在会抛send_error。异常处理那段别省,四个异常里formatting_error和internal_error是最常见的两个,不捕获的话报错信息会被吞掉,排查时又是一轮玄学。
改完之后有一个部署环节别漏:如果这个程序在生产环境,改动要走传输请求。SE09/SE10 里建请求、释放、导入,目标系统再跑一遍验证。SAPscript 和 Smart Forms 的样式、表单定义也是对象,同样要挂传输,别只传程序不传表单,到了生产环境语言设置对了但字体没跟过去,问题会以另一种形态出现。
4.2 SAPscript 场景下 OPEN_FORM 的 language 参数
同一套系统里如果还有 SAPscript 表单在跑,处理思路完全一致,只是参数位置不同。OPEN_FORM有独立的language参数,类型跟sy-langu一致。
data: ls_ctrlop type ssfctrlop, ls_comp type ssfcompop. " 与 Smart Forms 保持一致的取值 ls_ctrlop-langu = '1'. call function 'OPEN_FORM' exporting form = 'ZFORM_001' language = ls_ctrlop-langu " 关键:表单语言版本 device = 'PRINTER' dialog = 'X' options = ls_comp exceptions canceled = 1 device = 2 form = 3 unclosed = 4 others = 5. if sy-subrc <> 0. " 分别处理,别一句 others 糊过去 case sy-subrc. when 2. message '输出设备不可用' type 'E'. when 3. message '表单不存在或语言版本缺失' type 'E'. when others. message '打开表单失败' type 'E'. endcase. endif.language参数不传的话,OPEN_FORM同样会退回登录语言,症状和 Smart Forms 一模一样。所以一个系统里两种表单混用的时候,两处都要改,只改一处会出现「有的单据正常有的重叠」这种更让人头疼的现象。dialog参数在后台场景记得改成'N'或者配合options里的tdimmed,不然一样会卡。
4.3 样式和输出设备里那两个容易被忽略的中文字体设置
语言传对了,还有两级设置会影响最终效果。第一级是 Smart Forms 的样式(Style),样式里的字符格式定义了字体族、字号、加粗这些。中文内容建议在样式中明确指定中文字体族(如宋体一类),不要留空依赖系统默认——系统默认在不同前端、不同设备类型下解释不一样,预览正常真打跑偏的情况我见过不止一次。样式是独立对象,改完要激活并挂传输。
第二级是 SPAD 里的设备类型。设备类型的字符集定义决定了双字节字符怎么送给打印机。设备类型不支持中文,前面语言设置得再对也没用,输出会变成方框或者乱码。常见做法是找 Basis 确认目标设备类型的字符集,或者先在测试设备上打一张含长中文的测试页,比对预览和纸面是否一致。这两步都不需要改代码,但属于「装完必须过一遍」的收尾动作。
5. 避坑与常见问题排查:五条中文字体重叠的血泪记录
下面五条是我在不同系统上真金白银踩出来的,每一条都按「现象 → 原因 → 解决」写。看的时候对照自己的系统过一遍,能省掉不少来回。
5.1 现象:Note 全打了,预览还是叠
原因:这是最典型的一条。Notes 修的是系统排版层的缺陷,但你的调用程序没给control_parameters-langu赋值,%header-langu依旧退回sy-langu。系统层修好了,请求层还在传错语言,结果不变。解决:在调用程序里补ls_ctrlop-langu = '1',然后断点确认%header-langu的实际取值,别只看代码改没改。
5.2 现象:改完当天好了,过几天又叠了
原因:直接改了/1BCDWB/SF...这个生成的 FM。表单在 SMARTFORMS 里重新激活时,整个 FM 会被重新生成,手改的代码全部覆盖。这种翻车最难查,因为改动记录在版本管理里看不出来。解决:所有改动只放在调用方程序里,生成 FM 只读不改。已经改过的,把生成 FM 里的手工代码删掉,改到调用程序,再激活表单验证一次。
5.3 现象:中文描述在英文环境里变成乱码,不是重叠
原因:表单是在英文环境(login language 为E)下编辑的,中文文本在保存时字符编码处理不当。原文提到这个成因和解决方法当时没找出来,我的处理习惯是绕开:中文内容一律在中文环境下编辑和保存。解决:切到1语言登录,重新编辑受影响的中文文本节点,保存激活。已经乱码的文本没法自动还原,只能重录,所以这条要提前避,别等出了问题再补。
5.4 现象:重叠修好了,但长中文被压成一坨
原因:Note 1007575 没打,或者打了没生效。这条的症状很明确——超过 31 个字符才开始压缩,31 以内正常。判断方法就是造一条刚好 31 字符和一条 32 字符的数据,对比预览。解决:SNOTE 实施 1007575,实施完清缓存再测。测试时注意别用中英混排的数据,混排的字符计数容易看走眼。
5.5 现象:测试机全对,生产机换了台打印机又出问题
原因:设备类型不一致。测试机用的设备类型字符集支持中文,生产机那台映射到了另一个设备类型,双字节字符送不过去。语言的设置是对的,问题出在最后一段链路上。解决:SPAD 里对比两边设备类型的字符集定义,统一成支持中文的类型;短期可以先在应用层指定tddest走一台已知正常的设备验证,确认是设备问题之后再让 Basis 调整生产配置。
6. 装完怎么验证:三屏一次对比,别只看预览
改完代码、打完 Note、传完请求,最怕的是「预览看着对了」就收工。预览和真打之间还隔着设备类型、字体映射、分页计算三层,任何一层不对,纸面上照样出问题。我的验证方法是三屏一次对比:中文环境预览、英文环境预览、英文环境真实打印,三份结果放在一起看。下面这张表是我每次必过的检查项。
| 验证项 | 操作方式 | 通过标准 |
|---|---|---|
| 语言版本是否正确 | 在SSFRT_SET_LANGUAGES调用后打断点看%header-langu | 值为1,与sy-langu无关 |
| 中文环境预览 | 用1登录,跑同一张表单 | 中文按预留空间自动换行,无重叠 |
| 英文环境预览 | 用E登录,跑同一张表单 | 与中文环境预览结果一致 |
| 31 字符边界 | 造 31 字符、32 字符两条数据 | 31 正常显示,32 不被压缩 |
| 真实打印 | 指定生产设备类型,打一张含长中文的测试页 | 纸面与预览一致,无交杂、无方框 |
边界测试那条别省。31 和 32 两个用例是区分「语言问题」和「压缩问题」的分水岭:31 正常 32 压缩,说明语言设置已经生效,问题在 Note 1007575;两个都叠,说明语言还没传对,回去看第 4 章。这个判断顺序能省掉一大半来回试的时间。
调试阶段还有一个技巧:把getotf = 'X'打开,从job_output_info里把 OTF 取出来,直接在调试器里看输出流的语言标记和字符位置。预览界面是渲染后的结果,OTF 是渲染前的原始数据,重叠是渲染前就注定的还是渲染时算错的,看 OTF 一眼就分得清。我一般在改完代码的第一轮测试都会走这一步,比反复点预览按钮快。
最后说个习惯。从那以后我每接一个跨语言打印的问题,都强制先跑一遍环境三查——sy-langu是多少、cl_abap_char_utilities=>charsize返回几、三条 Note 的实施状态是什么——查完再动一行代码。这套顺序看着笨,但它把「系统层」「请求层」「设备层」三个可能出问题的地方一次性摊开,不会出现改了半天发现方向错了的情况。希望帮到你。
本文还有配套的精品资源,点击获取