不用 OLE、不用前端 Word纯 ABAP 生成
不用 OLE、不用前端 Word
纯 ABAP 生成
配套示例 ZDEMO_ABAP2DOCX
一、起因
做 SAP 每日巡检报告,最终交付物客户要 Word。
第一版的路子是:ABAP 导出 CSV → 人工/AI 转 Word。跑通了,但很快就发现不对劲:
· 多一道人工,没法进后台作业自动发;
· CSV 里没有的字段(Kernel 版本、Basis SP),转出来的 Word 就是空格子;
· 格式每次都得重调,不稳定。
于是回到根上:能不能让 ABAP 直接吐 Word?
二、几条路的取舍
|
方案 |
能用吗 |
问题 |
|
OLE 自动化(CALL METHOD OF) |
能 |
前端必须装 Word;慢;后台作业彻底没戏 |
|
DOI(Desktop Office Integration) |
能 |
同样依赖前端 Office,控件老旧 |
|
Smart Forms / Adobe Forms |
能 |
只出 PDF,不是 Word |
|
HTML 存成 .doc |
能 |
最省事,但不是真 docx,Word 会弹「格式与扩展名不符」 |
|
OOXML + CL_ABAP_ZIP |
能 |
纯后台,无依赖,产出真 .docx |
最后一条是正解。理由很简单——后台作业跑不了任何前端东西。只要你想定时发邮件,OLE / DOI / GUI_DOWNLOAD 这些全部出局。
三、.docx 到底是什么
一句话:.docx 就是个 zip,里面装 XML。
把任意一个 .docx 改名成 .zip 解开,你会看到一堆文件。但最小可用的骨架只要三个:
[Content_Types].xml声明包里有哪些类型的部件
_rels/.rels包级关系:告诉 Word「主文档是谁」
word/document.xml正文(WordprocessingML)
真的只要三个。styles.xml、fontTable.xml、theme1.xml 全都可以不要——只要你把格式写成内联属性。
[Content_Types].xml
<Default </DefaultExtension=”rels”
ContentType=”application/vnd.openxmlformats-package.relationships+xml”/>
<Default </DefaultExtension=”xml” ContentType=”application/xml”/>
<Override </OverridePartName=”/word/document.xml”
ContentType=”application/vnd.openxmlformats-officedocument
.wordprocessingml.document.main+xml”/>
_rels/.rels
<Relationship </RelationshipId=”rId1″
Type=”http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument”
Target=”word/document.xml”/>
这两个文件是死的,照抄即可。所有活儿都在 document.xml。
四、WordprocessingML 速成
只要记住四个标签,就能写 90% 的报告:
w:p段落(paragraph)
w:r文本块(run)—— 一个段落里格式相同的一截
w:t真正的文字(text)
w:tbl表格 → w:tr 行 → w:tc 单元格
嵌套关系是固定的:段落套 run,run 套 text。
一个加粗深蓝、10.5pt 的段落:
<w:rFonts </w:rFontsw:ascii=”Microsoft YaHei” w:eastAsia=”Microsoft YaHei”/>
<w:color </w:colorw:val=”1F4E79″/>
<w:sz </w:szw:val=”21″/>
<w:t </w:txml:space=”preserve”>一、系统概览
一个带底纹的单元格:
<w:tcW </w:tcWw:w=”1400″ w:type=”dxa”/>
<w:shd </w:shdw:val=”clear” w:color=”auto” w:fill=”FFC7CE”/>
<w:vAlign </w:vAlignw:val=”center”/>
单位速记
· twip:1 pt = 20 twip,1 cm ≈ 567 twip。表格宽度、页边距用它。
· 半磅:w:sz 用半磅。10.5pt 写 21,16pt 写 32。第一次用必踩。
· A4:纵向 w:w=”11906″ w:h=”16838″;横向把两个数对调,再加 w:orient=”landscape”。
五、四个坑(都是我真踩过的)
坑 1:w:tblGrid 不能省 ⚠
这个是最坑的。表格必须在
<w:gridCol </w:gridColw:w=”1200″/>
<w:gridCol </w:gridColw:w=”4000″/>
<w:gridCol </w:gridColw:w=”1400″/>
不写会怎样?我第一版就没写,Word 打开提示「文档需要修复」。用 python-docx 校验直接抛:
InvalidXmlError: required
gridCol 的个数和宽度必须跟实际单元格对得上,否则列宽会乱。
坑 2:XML 转义,且 & 必须第一个换
巡检的「检查条件」里全是 > 和 <:CPU<80%=Normal;>90%=Error。不转义,Word 直接打不开。
REPLACE ALL OCCURRENCES OF ‘&’ IN rv WITH ‘&’.” 必须第一个
REPLACE ALL OCCURRENCES OF ‘<‘ IN rv WITH ‘<‘.
REPLACE ALL OCCURRENCES OF ‘>’ IN rv WITH ‘>’.
顺序反了的话,先换出来的 < 会被后面的 & 规则再转成 <,页面上就显示成 < 了。
坑 3:中文字体要设 w:eastAsia
只设 w:ascii 中文不生效,会掉回默认宋体。三个都写上:
坑 4:必须 UTF-8
XML 声明写的是 encoding=”UTF-8″,那 xstring 就必须真的是 UTF-8,不能用系统默认代码页:
DATA lo TYPE REF TO cl_abap_conv_out_ce.
lo = cl_abap_conv_out_ce=>create( encoding = ‘UTF-8’ ).
lo->convert( EXPORTING data = iv IMPORTING buffer = rv ).
另外每个
,空单元格也得给个空段落,否则同样报「需要修复」。
六、打包:CL_ABAP_ZIP
到这一步就没难度了:
DATA lo_zip TYPE REF TO cl_abap_zip.
CREATE OBJECT lo_zip.
lo_zip->add( name = ‘[Content_Types].xml’ content = s2x( lv_ct) ).
lo_zip->add( name = ‘_rels/.rels’content = s2x( lv_rel ) ).
lo_zip->add( name = ‘word/document.xml’content = s2x( lv_doc ) ).
rv_x = lo_zip->save( ).” → xstring,这就是 .docx 了
拿到 xstring 之后,三种出口随便挑:
” 出口 1:前台下载
CALL FUNCTION ‘SCMS_XSTRING_TO_BINARY’
EXPORTINGbuffer=lv_x
IMPORTINGoutput_length= lv_len
TABLESbinary_tab=lt_bin.
cl_gui_frontend_services=>gui_download(
EXPORTINGbin_filesize = lv_len filename = lv_path filetype = ‘BIN’
CHANGINGdata_tab=lt_bin ).
” 出口 2:邮件附件(后台作业走这条)
lo_doc->add_attachment( i_attachment_type= ‘BIN’
i_attachment_subject= ‘巡检报告’
i_att_content_hex=lt_bin
i_attachment_header=lt_head” &SO_FILENAME=xxx.docx
i_attachment_size=lv_size ).
” 出口 3:存服务器
OPEN DATASET lv_srv FOR OUTPUT IN BINARY MODE.
TRANSFER lv_x TO lv_srv.
CLOSE DATASET lv_srv.
关键点:整条链路没有一处碰前端。所以 SM36 排个定时作业,每天早上自动生成 Word 发邮件,完全没问题。
七、封装成类
实战里别裸拼 XML,封一层就好用了:
CLASS lcl_docx DEFINITION.
PUBLICSECTION.
METHODSparaIMPORTING iv_txt TYPE string iv_sz TYPE i DEFAULT 21
iv_boldTYPE abap_bool DEFAULT abap_false
iv_colorTYPE string DEFAULT ‘000000’.
METHODStbl_begin IMPORTING it_w TYPE tt_i.” 传列宽 → 自动生成 tblGrid
METHODSrow_begin.
METHODScellIMPORTING iv_txt TYPE string iv_w TYPE i
iv_fillTYPE string DEFAULT ‘FFFFFF’
iv_boldTYPE abap_bool DEFAULT abap_false.
METHODSrow_end.
METHODStbl_end.
METHODSsaveRETURNING VALUE(rv_x) TYPE xstring.
PRIVATESECTION.
DATAmv_body TYPE string.” 只是个字符串缓冲
METHODSesc IMPORTING iv TYPE string RETURNING VALUE(rv) TYPE string.
METHODSs2x IMPORTING iv TYPE string RETURNING VALUE(rv) TYPE xstring.
ENDCLASS.
调用起来就跟写文档一样自然:
lo->para( iv_txt = ‘ABAP 直接生成 Word 演示’ iv_sz = 36
iv_bold= abap_true iv_color = ‘1F4E79’ ).
lo->tbl_begin( lt_w ).
lo->row_begin( ).
lo->cell( iv_txt = ‘ST22’ iv_w = 1200 ).
lo->cell( iv_txt = ‘ABAP Dump 检查’ iv_w = 4000 ).
lo->cell( iv_txt = ‘警告’ iv_w = 1400 iv_fill = ‘FFEB9C’ iv_bold = abap_true ).
lo->row_end( ).
lo->tbl_end( ).
lv_x = lo->save( ).
注意:mv_body 用 && 拼,不要用 CONCATENATE——CONCATENATE 的操作数不允许写方法调用,&& 可以。这个我也踩过,报的还是个很误导的错(「cannot be created as FOR TESTING」,其实是符号解析不到)。
八、怎么验证
别等到 Word 里才发现「文档需要修复」。两个笨办法很管用:
1. 改名成 .zip 解开,看三个文件在不在、XML 是否 well-formed;
2. 拿 python-docx 打开一遍——它比 Word 严格得多,Word 会容错自动修复的东西(比如缺 tblGrid),python-docx 会直接抛异常。我那个 tblGrid 的坑就是这么揪出来的:
import docx
d = docx.Document(‘demo.docx’)
print([p.text for p in d.paragraphs])
for t in d.tables:
print(len(t.rows),‘x’, len(t.columns))
能过 python-docx,Word 基本就稳了。
九、小结
· .docx = zip + XML,三个文件起步,CL_ABAP_ZIP 一把梭;
· 记住 w:p / w:r / w:t / w:tbl,单位是 twip 和半磅;
· 四个坑:tblGrid 必须有、& 先转义、中文设 eastAsia、必须 UTF-8;
· 最大价值:全程不碰前端,后台作业能自动生成+发邮件,这是 OLE/DOI 给不了的。
配套示例见 ZDEMO_ABAP2DOCX,一百多行、可直接激活运行,生成一份带标题、表格、状态底纹的 Word。
夜雨聆风