Java 实现Freemarker动态HTML转PDF 内带条形码

Java 实现Freemarker动态HTML转PDF 内带条形码

maven jar 准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>html2pdf</artifactId>
            <version>2.1.7</version>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
        </dependency>
        <dependency>
            <groupId>net.sf.barcode4j</groupId>
            <artifactId>barcode4j-light</artifactId>
            <version>2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>xml-apis</groupId>
                    <artifactId>xml-apis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

准备好字体文件ttf,要不会出现中文乱码,最好放在resource里面

在这里插入图片描述

由于我的project是关于香港的业务,要用到繁体字。

PMingLiU-CN.ttf 是新细明体
MSYHBD.TTF 是微软雅黑,因为标题要加粗,所有要加入一些粗体,不然CSS的font-weight: 600;不能起到加粗的效果。

html 转换pdf PDFUtils.java

由于我的项目是关于信封的内容,因此我本身不知道html显示的内容的高度进行分页,而且业务里要求需要页数和总页数,页脚。所有我只能在生成pdf之后再用itext加入PDF文件的页数及其他。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.styledxmlparser.css.media.MediaDeviceDescription;
import com.itextpdf.styledxmlparser.css.media.MediaType;

import hk.com.hkbn.itbss.sys.service.helper.SftpHelper;
import hk.com.hkbn.ordletter.bo.GenPdfParam;
import hk.com.hkbn.ordletter.exception.OrdLetterException;

/**
 * HTML  to PDF
 *
 */
public class PDFUtils {
   
    private static final Logger logger = LoggerFactory.getLogger(PDFUtils.class);
   
    public static void htmlFileToPDFStream(GenPdfParam param) throws OrdLetterException  {
        try {
            if (param.getHtmlFile() == null) {
                throw new OrdLetterException("htmlFile is null");
            }
            if (param.getPdfPath() == null) {
                throw new OrdLetterException("output path is null");
            }
            // import font ttf resource
            ConverterProperties converter = new ConverterProperties();
            DefaultFontProvider defaultFontProvider = new DefaultFontProvider(false, false, false);
            defaultFontProvider.addFont(param.getFontBasePath()+"/PMingLiU-CN.ttf");
            defaultFontProvider.addFont(param.getFontBasePath()+"/MSYHBD.ttf");
            converter.setFontProvider(defaultFontProvider);
            converter.setCharset("UTF-8");
            //  设置pdf的显示方式,用screen代替print
            MediaDeviceDescription mediaDeviceDescription = new MediaDeviceDescription(MediaType.PRINT);
            converter.setMediaDeviceDescription(mediaDeviceDescription);
           
            //make html to pdf
            HtmlConverter.convertToPdf(new FileInputStream(param.getHtmlFile()), new FileOutputStream(param.getPdfPath()), converter);
            logger.info("make pdf finish");
            //add barcode page number
            AddBarCodePageNum.addBarCodePageNum(param);
//          uploadOrdLetter(param.getNewpdfPath(),"kim_uploap_test.pdf");
            logger.info("upload sftp success");
        } catch (Exception e) {
            e.printStackTrace();
            throw new OrdLetterException("create html to pdf error");
        }
       
    }
   
    public static void uploadOrdLetter(String filepath ,String filename)  {
        try {
            InputStream inputStream = new FileInputStream(filepath);
            SftpHelper sftpHelper = new SftpHelper();
            sftpHelper.uploadFileToOutputPath(inputStream, filename);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

加入页码和页脚以及pdf中的条形码AddBarCodePageNum.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;

import hk.com.hkbn.ordletter.bo.GenPdfParam;
import hk.com.hkbn.ordletter.entity.LetterObj;
import hk.com.hkbn.ordletter.util.Assert;

/**
 * @description PDF add bar code \ page number \ page footer
 */
public class AddBarCodePageNum {

    /**
     * add bar code and page footer
     *
     * @param orgPdfPath
     * @param outputPdfPath
     * @param barcodepath
     * @return
     */
    private static final Logger logger = LoggerFactory.getLogger(AddBarCodePageNum.class);
       
    public static void addBarCodePageNum(GenPdfParam param) {
       
        try (// ouput file io
            FileOutputStream fos = new FileOutputStream(param.getNewpdfPath());) {
            // make new file , default size A4
            Document document = new Document(PageSize.A4);
            PdfWriter writer = PdfWriter.getInstance(document,fos);
            document.open();
           
            // PDF content
            PdfContentByte pdfContent = writer.getDirectContent();
            // input pdf file content io steam
            PdfReader reader = new PdfReader(param.getPdfPath());
            // Get resource file total pages
            int num = reader.getNumberOfPages();
            List<LetterObj> nextLetterList = new ArrayList<LetterObj>();
            // keyword
            for (int page = 1; page <= num; page++) {
                LetterObj letterObj = matchPage(reader, page, "@next_Letter_Flag@");
                if (Assert.isNotNull(letterObj)&&letterObj.isNextLetterNumber()) {
                    letterObj.setPageNumber(page);
                    nextLetterList.add(letterObj);
                }
            }
            logger.info("total page " + num);
            if (num <= 0 || nextLetterList.size() <= 0) {
                logger.info("no page data");
                return;
            }
            Integer grobalInteger = 1;
            Integer prePosition = 0;
            for (LetterObj letterObj : nextLetterList) {
                Integer preNumber = 0;
                if (prePosition > 0) {
                    preNumber = nextLetterList.get(prePosition - 1).getPageNumber();
                }
                String barcodevalue = param.getBarCodeValueMap().get(prePosition);
                for (int i = 1; i <= letterObj.getPageNumber() - preNumber; i++) {
                    document.newPage();
                    // set empty footer
                    writer.setPageEmpty(false);
                    // add page num and page footer
                    onEndPage(writer, document, i, letterObj.getPageNumber() - preNumber,
                            letterObj.getLetterLangFlag(),barcodevalue,prePosition,param);
                    PdfImportedPage page = writer.getImportedPage(reader, grobalInteger);
                    pdfContent.addTemplate(page, 0, 0);
                    if (grobalInteger < nextLetterList.get(nextLetterList.size() - 1).getPageNumber())
                        grobalInteger++;
                }
                prePosition++;
            }
            /*
            for (int i = 1; i <= num; i++) {
                  document.newPage();
                  // set empty
                  writer.setPageEmpty(false);
                  //add page num and page footer
                  onEndPage(writer, document,i,num,language,fontBasePath,barcodepath);
                  PdfImportedPage page =writer.getImportedPage(reader, i);
                  pdfContent.addTemplate(page, 0, 0);
                }
            */
            document.close();
            reader.close();
            writer.close();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Find next letter pageNumber by keyword
     *
     * @param reader
     * @param pageNumber
     * @param keyword    
     * @return
     * @throws Exception
     */
    public static LetterObj matchPage(PdfReader reader, Integer pageNumber, String keyword) throws Exception {
        PdfReaderContentParser parse = new PdfReaderContentParser(reader);
        // listen
        KeyWordListener renderListener = new KeyWordListener();
        renderListener.setKeyword(keyword);
        renderListener.setPageNumber(pageNumber);
        parse.processContent(pageNumber, renderListener);
        return renderListener.getListenObj();
    }

    /**
     * make left bar code
     *
     * @param document
     * @param BarcodePath
     * @throws DocumentException
     */
    public static void addBarcode(Document document, String BarcodePath) throws DocumentException {
        try {
            Image image = Image.getInstance(BarcodePath);
            image.setRotationDegrees(90);// image 90
            image.setAbsolutePosition(10, 70);
//          image.scaleAbsolute(200,30);
            // setting image size
             image.scaleToFit(30,300);
            document.add(image);
        } catch (BadElementException | IOException e) {
            e.printStackTrace();
        }

    }
   
    /**
     * make first page bar code
     *
     * @param document
     * @param BarcodePath
     * @throws DocumentException
     */
    public static void addFirstPageBarcode(Document document, String BarcodePath) throws DocumentException {
        try {
            Image image = Image.getInstance(BarcodePath);
            image.setAbsolutePosition(52, 595);
            // setting image size
            image.scaleAbsolute(250,30);
//          image.scaleToFit(200,300);
            document.add(image);
        } catch (BadElementException | IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * make page number
     *
     * @param writer
     * @param document
     * @param maxPage
     */
    public static void onEndPage(PdfWriter writer, Document document, Integer currentPage, Integer maxPage,
            String language,  String bracodevalue,Integer LetterRecordCount,GenPdfParam param) {

        try {
            // PDF content
            PdfContentByte pdfContent = writer.getDirectContent();

            pdfContent.saveState();
            pdfContent.beginText();

            int footerFontSize = 8;
            String footerNum = null;
            String fontBasePath = null;
            if ("C".equals(language) || language == null) {
                footerNum = String.format("第 %d 頁, 共 %d 頁", currentPage, maxPage);
                fontBasePath = param.getFontBasePath() + "/PMingLiU-CN.ttf";
            } else if ("E".equals(language)) {
                footerNum = String.format("Page %d , total %d pages", currentPage, maxPage);
                fontBasePath = param.getFontBasePath() + "/PMingLiU-CN.ttf";
            }
            if (currentPage % 2 != 0) {
                // each bar code
                File barCodeFile = new File(param.getBarCodePath());
                String barcode = genLeftBarCodeValue(currentPage,maxPage,LetterRecordCount);
                BarcodeUtil.genBarCode39(barcode, BarcodeUtil.IMG_TYPE_PNG, barCodeFile);
                // add left bar code
                addBarcode(document, param.getBarCodePath());
            }
            if(currentPage==1 && Assert.isNotNull(bracodevalue)) {
                // bar code
                File barCodeFile = new File(param.getBarCodePath());
                BarcodeUtil.genBarCode128(bracodevalue, BarcodeUtil.IMG_TYPE_PNG, barCodeFile);
                // add first page bar code
                addFirstPageBarcode(document, param.getBarCodePath());
            }
            // setting font type
            BaseFont baseFont = BaseFont.createFont(fontBasePath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            Font fontDetail = new Font(baseFont, footerFontSize, Font.NORMAL);

            pdfContent.setFontAndSize(baseFont, footerFontSize);
            Phrase phrase = new Phrase(footerNum, fontDetail);
            // get file x pod
            float x = (document.left() + document.right()) / 2;
            // get file y pod
            float y = document.bottom(-10);
            // make page footer
            ColumnText.showTextAligned(pdfContent, Element.ALIGN_CENTER, phrase, x, y - 5, 0);
            // AC-Retention-C content
            Calendar calendar = Calendar.getInstance();
            Date now = calendar.getTime();
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
            String footer = "AC-Retention-C-" + format.format(now);
            phrase = new Phrase(footer, fontDetail);
            ColumnText.showTextAligned(pdfContent, Element.ALIGN_CENTER, phrase, x + 200, y - 10, 0);
            pdfContent.endText();
            pdfContent.restoreState();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    /**
     * get left bar code value
     * @param currentPage
     * @param totalPage
     * @param LetterRecordCount
     * @return
     */
    public static String genLeftBarCodeValue(Integer currentPage,Integer totalPage,Integer LetterRecordCount) {
        StringBuilder barcode = new StringBuilder();
        if (currentPage.equals(totalPage)&&currentPage==1) {
            barcode.append("3");
        }else if (currentPage==1) {
            barcode.append("2");
        }else if(currentPage.equals(totalPage)) {
            barcode.append("1");
        }else {
            barcode.append("0");
        }
        //add field 2 (Letter Program Code (001-999))
        barcode.append(String.format("%03d", 6));
        //add field 3 (Letter records count (0001-9999))
        barcode.append(String.format("%04d", LetterRecordCount+1));
        //add field 4 (Insert value *Please check below table)
        barcode.append("1");
        //add field 5 (Current Page Number)
        barcode.append(String.format("%02d", currentPage));
        //add field 6 (total page)
        barcode.append(String.format("%02d", totalPage));
        return barcode.toString();
    }

}

多封pdf信封,设置关键之监听来重置PDF的页数KeyWordListener.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;

import hk.com.hkbn.ordletter.entity.LetterObj;

/**
 * use to listener PDF file keywords
 */
public class KeyWordListener implements RenderListener {




    /**
     *match keyword
     */
    private String keyword;
    /**
     * match current page
     */
    private Integer pageNumber;
   
    /**
     * has match keyword object
     */
    private LetterObj listenObj = new LetterObj();
   

    @Override
    public void beginTextBlock() {
        //do nothing
    }

    @Override
    public void renderText(TextRenderInfo renderInfo) {
        //get char
        String content =renderInfo.getText();
        if(content.contains(keyword)) {
            listenObj.setNextLetterNumber(true);
            listenObj.setLetterLangFlag(content.substring(content.length()-1));
        }  
    }

    @Override
    public void endTextBlock() {
        //do nothing
    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) {
        //do nothing
    }

    /**
     * setting need match keyword current page
     * @param pageNumber
     */
    public void setPageNumber(Integer pageNumber) {
        this.pageNumber = pageNumber;
    }

    /**
     * setting match keyword
     * @param keyword
     */
    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }

    public LetterObj getListenObj() {
        return listenObj;
    }

    public void setListenObj(LetterObj listenObj) {
        this.listenObj = listenObj;
    }

    public String getKeyword() {
        return keyword;
    }

    public Integer getPageNumber() {
        return pageNumber;
    }

   

}

freemarker 通过模板文件生成html FreemarkerTemplate.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Locale;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * freemarker make HTML
 *
 */
public class FreemarkerTemplate {

    private final Configuration configuration = new Configuration(
            Configuration.VERSION_2_3_23);
    private String charset;

    public FreemarkerTemplate(String charset) {
        this.charset = charset;
        configuration.setEncoding(Locale.CHINA, charset);
        configuration.setClassicCompatible(true);//处理空值未空字符串
    }

    public void setTemplateClassPath(Class resourceLoaderClass,
            String basePackagePath) {
        configuration.setClassForTemplateLoading(resourceLoaderClass,
                basePackagePath);
    }

    public void setTemplateDirectoryPath(String templatePath)
            throws IOException {
        configuration.setDirectoryForTemplateLoading(new File(templatePath));
    }

    public void processToStream(String templateFileName,
            Map<String, Object> dataMap, Writer writer) throws Throwable {
        Template template = configuration.getTemplate(templateFileName);
        template.process(dataMap, writer);
    }

    public void processToFile(String templateFileName,
            Map<String, Object> dataMap, File outFile) throws Throwable {
        Writer writer = new OutputStreamWriter(new FileOutputStream(outFile),
                charset);
        try {
            processToStream(templateFileName, dataMap, writer);
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    public String processToString(String templateFileName,
            Map<String, Object> dataMap) throws Throwable {
        Writer writer = new StringWriter(2048);
        try {
            processToStream(templateFileName, dataMap, writer);
            return writer.toString();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}

java制作barcode39 和 barcode128 ,生活中比较常见的两种条形码BarcodeUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.krysalis.barcode4j.HumanReadablePlacement;
import org.krysalis.barcode4j.impl.code128.Code128Bean;
import org.krysalis.barcode4j.impl.code39.Code39Bean;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
import org.krysalis.barcode4j.tools.UnitConv;
/**
 * make bar code
 *
 */
public class BarcodeUtil {
   
    public static final String IMG_TYPE_PNG="image/png";
   
    public static final String IMG_TYPE_GIF="image/gif";
   
    public static final String IMG_TYPE_JPEG="image/jpeg";


    public static File genBarCode39(String msg,String imgType, File file)
            throws IOException {
        FileOutputStream out = new FileOutputStream(file);
        try {
            generateBarCode39(msg,imgType, out);
        } finally {
            if (out != null) {
                out.close();
            }
        }
        return file;
    }

    public static byte[] generateToByte(String msg,String imgType) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            generateBarCode39(msg,imgType, out);
            return out.toByteArray();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }
   
    public static File genBarCode128(String msg,String imgType, File file)
            throws IOException {
        FileOutputStream out = new FileOutputStream(file);
        try {
            generateBarCode128(msg,imgType, out);
        } finally {
            if (out != null) {
                out.close();
            }
        }
        return file;
    }

    public static void generateBarCode39(String msg, String imgType,OutputStream out)
            throws IOException {
        if (msg == null || out == null) {
            return;
        }
        Code39Bean bean = new Code39Bean();
        int dpi = 150;
        // module width
        double moduleWidth = UnitConv.in2mm(1.0f / dpi);
        bean.setModuleWidth(moduleWidth);
        bean.setMsgPosition(HumanReadablePlacement.HRP_NONE);
        bean.setHeight(5);
        bean.setWideFactor(3);
        bean.doQuietZone(false);
   
        BitmapCanvasProvider canvas = new BitmapCanvasProvider(out, imgType,
                dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
        bean.generateBarcode(canvas, msg);
        canvas.finish();
    }
   
    public static void generateBarCode128(String msg, String imgType,OutputStream out)
            throws IOException {
        if (msg == null || out == null) {
            return;
        }
        Code128Bean bean = new Code128Bean();
        int dpi = 100;
        // module宽度 UnitConv.in2mm(1.f / dpi);
        double moduleWidth = 0.4;
        bean.setModuleWidth(moduleWidth);
        bean.setMsgPosition(HumanReadablePlacement.HRP_NONE);
        bean.setHeight(10);
        bean.doQuietZone(false);

   
        BitmapCanvasProvider canvas = new BitmapCanvasProvider(out, imgType,
                dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
        bean.generateBarcode(canvas, msg);
        canvas.finish();
    }
}

分页的时候会出现的问题,内容断开,或者分页之后内容不对齐。

可以在css 中给div添加以下属性

1
2
3
4
.gitf-name{
    clear: both;
    display:inline-block;/*防止内容断开,将外层div设置为一个block*/
}

部分freemarker 代码(有需要可以自己百度查一下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!--first part-->
            <div class="container">
                    <div class="give_name">${letterPlan.customerName}</div>
                    <div class="addr-first">
                        <div class="addtitle">BADD1</div>
                        <div style="float: left;">${letterPlan.add1}</div>
                    </div>
                    <div class="addr">
                        <div class="addtitle">BADD2</div>
                        <div style="float: left;">${letterPlan.add2}</div>
                    </div>
                    <div class="addr">
                        <div class="addtitle">BADD3</div>
                        <div style="float: left;">${letterPlan.add3}</div>
                    </div>
                    <div class="addr">
                        <div class="addtitle">BADD4</div>
                        <div style="float: left;">${letterPlan.add4}</div>
                    </div>
                    <div class="pps">
                        <div style="clear: both;display:inline-block;height: 5px;">
                            <div class="pps-left-div">${letterPlan.barcodeID}</div>
                            <div class="pps-right-div">${letterPlan.customerBarcode}</div>
                        </div>
                        <div class="bar_code_div">
                            <!-- <img class="barcode" src="${barcodeImg}"></img> -->
                        </div>
                        <div class="send-data">
                            <div class="left-div">發信日期(年/月/日)</div>
                            <div class="middle">:</div>  
                            <div class="right-div">${letterPlan.dateOfDispatch}</div>
                        </div>
                        <div class="useraccount">
                            <div class="left-div">賬戶號碼</div>
                            <div class="middle">:</div>  
                            <div class="right-div">${letterPlan.accountPPS}</div>
                        </div>
                    </div>
             </div>

最终效果图

在这里插入图片描述
在这里插入图片描述