欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 浅析,生成OFD文件

程序员文章站 2022-04-28 15:15:00
摘要:这几天遇到个需要,需要提供用户下载电子证照,文件格式为OFD,一上网查瞬间懵了,几乎没有相关的,Google上也没有(咱们国家的国标文件没有相关资料也正常),时间紧,选择最简单的方法来实现,我将需要生成的文件用word做了一份模板,利用网页工具转成OFD文件,这个网站非常良心,不干coder  ......

摘要:这几天遇到个需要,需要提供用户下载电子证照,文件格式为ofd,一上网查瞬间懵了,几乎没有相关的,google上也没有(咱们国家的国标文件没有相关资料也正常),时间紧,选择最简单的方法来实现,我将需要生成的文件用word做了一份模板,利用网页工具转成ofd文件,这个网站非常良心,不干coder blackmail coder的事儿,这

是地址(因为ofd文件没有专业软件帮助几乎很难代码实现),(用7z工具解压ofd文件,压缩使用winzip,压缩的时候不能带根路径)得到文件目录结构如下:

首先我们来看ofd文件的目录结构,有助于我们用代码操作:

Java 浅析,生成OFD文件

doc_1文件夹下面是主要内容

Java 浅析,生成OFD文件

pages下面存放ofd每个页面

下面简单说明pages下面页面信息xml文件的标签及属性作用

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ofd:page xmlns:ofd="http://www.ofdspec.org/2016">
    <ofd:area>
        <ofd:physicalbox>0 0 209.90291 296.68628</ofd:physicalbox>
        <ofd:applicationbox>0 0 209.90291 296.68628</ofd:applicationbox>
        <ofd:contentbox>0 0 209.90291 296.68628</ofd:contentbox>
    </ofd:area>
    <ofd:content>
        <ofd:layer id="0">
            <ofd:imageobject id="7" resourceid="6" boundary="0 -0.3528 210.2557 297.0391" ctm="210.2557 0 0 297.0391 0 0"/>
            <ofd:textobject id="9" font="8" size="7.761115550994873" boundary="47.2723 67.0278 261.0557 15.5222">
                <ofd:cgtransform codeposition="0" codecount="17" glyphcount="17">
                    <ofd:glyphs>11738 12862 7255 17457 12235 14764 13842 11859 12078 11931 15952 14729 11465 7256 11113 11465 13610</ofd:glyphs>
                </ofd:cgtransform>
                <ofd:textcode x="0.0" y="7.7611" deltax="7.7611 7.4083 7.4083 7.7611 7.7611 7.7611 7.7611 7.7611 7.7611 7.7611 7.7611 7.7611 8.1139 7.0556 7.7611 7.7611">填充字符填充字符填充字符填充字符填</ofd:textcode>
            </ofd:textobject>
        </ofd:layer>
    </ofd:content>
</ofd:page>

 

ofd:cgtransform:标签内的内容,为网站生成模板内,res文件夹下的字体文件内的检索码,这里重点说明,我们通过网站得到的模板文件,在res文件加下面存放的字体文件只包含你ofd文件内存在的文字,所以你会发现这个字体文件特别小,你需要到你电脑的c:\windows\fonts下面找到你需要的字体文件,或者自己准备也行,这样可以保证ofd文件在各种环境下查看,打印字体的统一!!!然后在publicres.xml文件内设置你自己准备的字体,(自己准备的字体需要放到res文件夹下面),ofd:cgtransform标签内存在内容(字符码),则去字体文件内寻找匹配的文字,没有则显示ofd:textcode标签内的内容。这我选择使用ofd:textcode显示内容,ofd:cgtransform标签则全部删掉。

 

字体文件设置文件 publicres.xml 如下

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ofd:res baseloc="res" xmlns:ofd="http://www.ofdspec.org/2016">
    <ofd:fonts>
        <ofd:font id="" fontname="cbmowr+stkaiti" familyname="cbmowr+stkaiti">
            <ofd:fontfile>font_1.otf</ofd:fontfile>
        </ofd:font>
        <ofd:font id="8" fontname="stkaiti" familyname="stkaiti">
            <fontfile>font.ttf</fontfile>
        </ofd:font>
    </ofd:fonts>
</ofd:res>

注意 ofd:font 标签内的id属性,页面信息xml文件内 ofd:textobject 标签的font属性和此处的id一致即可引用字体。至于ofd:font标签内的fontname属性则需要填入字体正确

的头文件信息内的名称.

 重点说明标签 ofd:textobject,这也是ofd文件为何如此dt的地方:

ofd:textobject 标签下的 boundary 属性:将ofd:textobject看作一个box,前两个值分别为定位点的坐标,后两个值为x轴和y轴相对与该定位点的偏移量,由此在页面上确定该box的位置,
        也因为该属性,ofd:textobject标签显示内容位置与xml文件内的顺序无关
ofd:textcode 标签下的 deltax 属性:为字符x轴偏移量,第一个值为ofd:textcode标签内第一个字符和第二个字符的偏移量,有n个字符就有n-1个偏移量,多个用空格隔开,如果你显示的内容
        可能存在折行,你需要在模板文件内将显示内容填充满,通过上一层ofd:textobject标签的boundary属性的第二个值获取折行y轴偏移量,折行内容还放不下,你需要调整 size 属性来
        调整字体大小,(这里建议再弄一份小字体文件再需要缩小字体的时候设置对应的size,deltax属性),显示的内容有10个,偏移量最少得设置9个,如果显示内容字符数-偏移量数 > 1,
        则会发生字符重叠现象;

现在有了模板文件,知道了基本的属性作用,我使用dom4j来操作xml文件,上代码-->
package code;

import java.awt.font;
import java.awt.fontformatexception;
import java.io.file;
import java.io.fileinputstream;
import java.io.filewriter;
import java.io.ioexception;
import java.io.objectinputstream.getfield;
import java.util.arraylist;
import java.util.hashmap;
import java.util.iterator;
import java.util.list;
import java.util.map;
import java.util.regex.matcher;
import java.util.regex.pattern;

import org.apache.poi.xslf.model.geom.path;
import org.dom4j.document;
import org.dom4j.documentexception;
import org.dom4j.documenthelper;
import org.dom4j.element;
import org.dom4j.node;
import org.dom4j.xpath;
import org.dom4j.io.outputformat;
import org.dom4j.io.saxreader;
import org.dom4j.io.xmlwriter;

import util.oprationfile;
import util.ziputil;

public class createelectroniccertification {
    //ofd文件迭代id,不能重复,数据折行新增节点设置id
    private static int index;
    
    public static void main(string[] args) throws documentexception, ioexception {
        map<string, object> map = new hashmap<string, object>();
        generateofd(map);
        //oprationfile.deletefiles(new file(newofdfilesys));//删除生成的文件。这里自己操作参数,备份好文件,删除不可恢复,
    }
    
    public static boolean generateofd(map<string,object> map) throws ioexception, documentexception{
        //ofd文件模板文件夹位置
        string resourcesaddress = "e:/ofd_model";//map.get("resourcesaddress");
        //不折行最大字符长度
        int linewide = 19;//map.get("linewide");
        //强制换行符
        string linebreak = "/r";//map.get("linebreak");
        //折行y轴偏移量
        double y_offset = 7.7611;//map.get("y_offset");
        //生成ofd文件存放文件夹
        string ofdaddress = "e:/";//map.get("ofdaddress");
        
        string modelforidfileaddress = "/doc_1/document.xml";//map.get("modelforidfileaddress");
        
        //string newfilename = common.createguid();
        string newfilename = "ofd";//随机ofd文件名
        string maxidandpagesfile = modelforidfileaddress;//ofd模板存放maxid,pageaddress地址的document位置
        string newofdfilesys = copymodelfilesys(newfilename,resourcesaddress);//拷贝模板文件,返回新文件路径
        if(newofdfilesys.equals("0")){
            return false;
        }
        getmaxid(newofdfilesys+maxidandpagesfile);
        //获取页面xml文件的地址
        list<string> pageaddresslist = getpagesaddress(newofdfilesys+maxidandpagesfile);
        for(string pageaddress: pageaddresslist){
            pageaddress = newofdfilesys+"/doc_1/"+pageaddress;
            oprationxmlfordom4j(pageaddress, map, linebreak, linewide, y_offset);
        }
        string ofdname = newfilename+".ofd";
        
        //电子签章,二维码等图片在此覆盖模板内图片
        //save(string str);
        
        //去根目录压缩(ofd文件压缩不能带根目录)
        ziputil.docompress(newofdfilesys, ofdaddress+ofdname,newfilename);
        return true;
    }
    
    //获取page页面存放地址
    public static list<string> getpagesaddress(string maxidandpagesfile) throws documentexception{
        saxreader reader = new saxreader();
        document document = reader.read(new file(maxidandpagesfile));
        list<element> list =document.selectnodes("//ofd:page");//xpath
        list<string> pageaddresslist = new arraylist<string>();
        for(element elment:list){
            pageaddresslist.add(elment.attributevalue("baseloc"));
        }
        pageaddresslist.remove(0);
        return pageaddresslist;
    }
    
    public static string copymodelfilesys(string newfilename,string resourcesaddress) throws ioexception{
        string oldfilename = new file(resourcesaddress).getname();
        string newofdfilesys = resourcesaddress.replaceall(oldfilename, newfilename);
        if(oprationfile.copydir(resourcesaddress, newofdfilesys)){
            return newofdfilesys;
        }
        return "0";
    }
    
    //获取最大id,ofd文件,xml内的id属性类似前端页面的元素id,不可重复,这里取最大的在有新增节点时设置id用
    public static void getmaxid(string address) throws documentexception{
        saxreader reader = new saxreader();
        document document = reader.read(new file(address));
        @suppresswarnings("unchecked")
        list<node>list=document.selectnodes("//ofd:maxunitid");
        index = integer.parseint(list.get(0).gettext())+1;
    }
    
    
    public static void oprationxmlfordom4j(string fileaddress,map<string, object> map,string linebreak,int linewide,double y_offset) throws documentexception, ioexception {
        file file = new file(fileaddress);
        saxreader reader = new saxreader();
        document document = reader.read(file);
        element root = document.getrootelement();
        element layerelement = root.element("content").element("layer");
        iterator iterator = document.selectnodes("//ofd:textobject").iterator();
        list<element> foldlinelsit = new arraylist<element>();
        
        while (iterator.hasnext()) {
            element textobjectelement = (element) iterator.next();
            for(string key : map.keyset()){
                if((textobjectelement.element("textcode").gettext()).equals(key)){
                    if(key.equals("fj")){
                        linewide = 27;//附记页最大行长度
                    }
                    //--------------------------------------
                    system.out.println("匹配参数:"+textobjectelement.element("textcode").gettext());
                    list<string> paramlist = new arraylist<string>();
                    string boundary= textobjectelement.attributevalue("boundary");
                    string[] b_split = boundary.split(" ");
                    string boundarystr = "";
                    double at_y_offset = double.parsedouble(b_split[1]);
                    string params  = ((string)map.get(key)).trim();
                    string[] paramgroup = params.split(linebreak);
                    //自动折行
                    for(int i = 0 ; i<paramgroup.length ; i++){
                        if(paramgroup[i].length()>linewide){
                            int flag = 0;
                            int g_len = (paramgroup[i].length()+linewide-1)/linewide;
                            for(int li = 0;li<g_len;li++){
                                if(li+1 == g_len){
                                    paramlist.add(paramgroup[i].substring(flag,paramgroup[i].length()));
                                }else{
                                    paramlist.add((paramgroup[i].substring(flag,flag+linewide)));
                                }
                                flag+=linewide;
                            }
                        }else{
                            paramlist.add(paramgroup[i]);
                        }
                    }
                    //配置y轴偏移量,首行直接替换参数,折行依次添加节点()
                    for(int i = 0; i < paramlist.size(); i++){
                        string param = paramlist.get(i);
                        if(i==0){
                            element atelem = textobjectelement.element("textcode");
                            atelem.settext(param);
                        }else{
                            at_y_offset = double.parsedouble(string.format("%.4f", at_y_offset+y_offset));
                            boundarystr = b_split[0]+" "+ at_y_offset + " " + b_split[2]+ " "+b_split[3];
                            //setparam(textobjectelement,param,boundarystr);
                            element newelement = (element) textobjectelement.clone();//此处必须使用clone()方法,底层调用object的clone();
                            element elem = newelement.element("textcode");
                            //第三页模板平均折行y轴偏移量7.7611,boundary第三,四个参数可忽略不设置,文件解析根据deltax偏移量自动布局boundary宽度,高度为定植不用修改
                            index = index+1;
                            elem.settext(param);
                            newelement.attribute("id").setvalue(index+"");
                            newelement.attribute("boundary").setvalue(boundarystr.trim());
                            foldlinelsit.add(newelement);
                        }
                    }
                }
            }
        }
        if(foldlinelsit.size()>0){
            for(element e : foldlinelsit){
                layerelement.add((element)e.clone());
            }
        }
        save(document, fileaddress);
        foldlinelsit.clear();
    }
    public static void save(document doc, string address) throws ioexception {
        outputformat format = outputformat.createprettyprint();
        format.setencoding("utf-8");
        file file=new file(address);
        file.getparentfile().mkdirs();
        xmlwriter writer = new xmlwriter(new filewriter(file), format);
        writer.write(doc);
        writer.close();
    }
}

 

 工具类

package util;

import java.io.file;
import java.io.fileinputstream;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.util.scanner;

public class oprationfile {
    @suppresswarnings("static-access")
    public static boolean copydir(string sourcepath, string newpath)
            throws ioexception {
        file file = new file(sourcepath);
        string[] filepath = file.list();

        if (!(new file(newpath)).exists()) {
            (new file(newpath)).mkdir();
        }

        for (int i = 0; i < filepath.length; i++) {
            if ((new file(sourcepath + file.separator + filepath[i]))
                    .isdirectory()) {
                copydir(sourcepath + file.separator + filepath[i], newpath
                        + file.separator + filepath[i]);
            }

            if (new file(sourcepath + file.separator + filepath[i]).isfile()) {
                copyfile(sourcepath + file.separator + filepath[i], newpath
                        + file.separator + filepath[i]);
            }

        }
        return true;
    }

    public static void copyfile(string oldpath, string newpath) throws ioexception {
        file oldfile = new file(oldpath);
        file file = new file(newpath);
        fileinputstream in = new fileinputstream(oldfile);
        fileoutputstream out = new fileoutputstream(file);;

        byte[] buffer=new byte[2097152];
        int readbyte = 0;
        while((readbyte = in.read(buffer)) != -1){
            out.write(buffer, 0, readbyte);
        }
    
        in.close();
        out.close();
    }
    
    public static void deletefiles(file file){
        if (file.isdirectory()) {
            file[] files=file.listfiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isdirectory()) {
                    deletefiles(files[i]);
                }else {
                    files[i].delete();
                }
            }          
        }
        file.delete();
    }

    public static void main(string[] args) throws ioexception {
        scanner sc = new scanner(system.in);
        string filepath = sc.nextline();
        
        deletefiles(new file(filepath));
        
    }
}

下面这个方法来自大佬的分享,建议自己写,重载太多不方便传参,这里需要重点说明,ofd文件压缩采用zip,一定注意不能有根目录,以我的为例,直接将doc_1文件夹和ofd.xml文件压缩,用电脑操作也是一样,

package util;

import java.io.file;
import java.io.fileinputstream;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.util.zip.zipentry;
import java.util.zip.zipoutputstream;

public class ziputil {

    private ziputil() {
    }

    public static void docompress(string srcfile, string zipfile,string rootddirectory)
            throws ioexception {
        docompress(new file(srcfile), new file(zipfile),rootddirectory);
    }

    /**
     * 文件压缩
     * 
     * @param srcfile
     *            目录或者单个文件
     * @param zipfile
     *            压缩后的zip文件
     */
    public static void docompress(file srcfile, file zipfile,string rootddirectory)
            throws ioexception {
        zipoutputstream out = null;
        try {
            out = new zipoutputstream(new fileoutputstream(zipfile));
            docompress(srcfile, out,rootddirectory);
        } catch (exception e) {
            throw e;
        } finally {
            out.close();// 记得关闭资源
        }
    }

    public static void docompress(file file, zipoutputstream out,string rootddirectory)
            throws ioexception {
        docompress(file, out, "",rootddirectory);
    }

    public static void docompress(file infile, zipoutputstream out, string dir,string rootddirectory)
            throws ioexception {
        if (infile.isdirectory()) {
            file[] files = infile.listfiles();
            if (files != null && files.length > 0) {
                for (file file : files) {
                    string name = infile.getname();
                    if (!"".equals(dir)) {
                        name = dir + "/" + name;
                    }
                    ziputil.docompress(file, out, name,rootddirectory);
                }
            }
        } else {
        //将根目录干掉,否则无法打开ofd文件 dir = dir.replaceall(rootddirectory, ""); ziputil.dozip(infile, out, dir); } } public static void dozip(file infile, zipoutputstream out, string dir) throws ioexception { string entryname = null; if (!"".equals(dir)) { entryname = dir + "/" + infile.getname(); } else { entryname = infile.getname(); } zipentry entry = new zipentry(entryname); out.putnextentry(entry); int len = 0; byte[] buffer = new byte[1024]; fileinputstream fis = new fileinputstream(infile); while ((len = fis.read(buffer)) > 0) { out.write(buffer, 0, len); out.flush(); } out.closeentry(); fis.close(); } public static void main(string[] args) throws ioexception { string rootddirectory = "ofd"; docompress("e:/ofd/", "e:/ofd.ofd",rootddirectory); } }

 

没了。。。