Java tutorial
package com.masscustsoft.service; import java.awt.Color; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import javax.servlet.http.HttpServletResponse; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Chunk; import com.itextpdf.text.DocListener; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.ExceptionConverter; import com.itextpdf.text.Font; import com.itextpdf.text.FontFactoryImp; import com.itextpdf.text.FontProvider; import com.itextpdf.text.GreekList; import com.itextpdf.text.Image; import com.itextpdf.text.ListItem; import com.itextpdf.text.PageSize; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.Rectangle; import com.itextpdf.text.RomanList; import com.itextpdf.text.TextElementArray; import com.itextpdf.text.ZapfDingbatsList; import com.itextpdf.text.ZapfDingbatsNumberList; import com.itextpdf.text.html.HtmlTags; import com.itextpdf.text.html.HtmlUtilities; import com.itextpdf.text.html.simpleparser.CellWrapper; import com.itextpdf.text.html.simpleparser.ChainedProperties; import com.itextpdf.text.html.simpleparser.ElementFactory; import com.itextpdf.text.html.simpleparser.HTMLTagProcessor; import com.itextpdf.text.html.simpleparser.HTMLTagProcessors; import com.itextpdf.text.html.simpleparser.HTMLWorker; import com.itextpdf.text.html.simpleparser.ImageProcessor; import com.itextpdf.text.html.simpleparser.ImageProvider; import com.itextpdf.text.html.simpleparser.ImageStore; import com.itextpdf.text.html.simpleparser.LinkProcessor; import com.itextpdf.text.html.simpleparser.StyleSheet; import com.itextpdf.text.html.simpleparser.TableWrapper; import com.itextpdf.text.log.Logger; import com.itextpdf.text.log.LoggerFactory; import com.itextpdf.text.pdf.Barcode; import com.itextpdf.text.pdf.Barcode128; import com.itextpdf.text.pdf.Barcode39; import com.itextpdf.text.pdf.BarcodeCodabar; import com.itextpdf.text.pdf.BarcodeEAN; import com.itextpdf.text.pdf.BarcodeEANSUPP; import com.itextpdf.text.pdf.BarcodeInter25; import com.itextpdf.text.pdf.BarcodePDF417; import com.itextpdf.text.pdf.BarcodePostnet; import com.itextpdf.text.pdf.BarcodeQRCode; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfPageEvent; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.PdfStamper; import com.itextpdf.text.pdf.PdfTemplate; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.text.pdf.codec.Base64; import com.itextpdf.text.pdf.draw.LineSeparator; import com.itextpdf.text.xml.simpleparser.SimpleXMLParser; import com.masscustsoft.api.IRepository; import com.masscustsoft.helper.Upload; import com.masscustsoft.model.AbstractResult; import com.masscustsoft.model.ExternalFile; import com.masscustsoft.model.ReportFile; import com.masscustsoft.util.LightStr; import com.masscustsoft.util.LightUtil; import com.masscustsoft.util.MapUtil; import com.masscustsoft.util.StreamUtil; import com.masscustsoft.util.ThreadHelper; import com.masscustsoft.xml.BeanFactory; public class ToPdf extends DirectAction implements PdfPageEvent { protected Map getRpt() throws Exception { Map rpt = (Map) LightUtil.parseJson(getStr("rpt", "{}")); return rpt; } @Override protected void run(AbstractResult ret) throws Exception { Map<String, String> i18n = (Map) LightUtil.parseJson(getStr("i18n", "{}")); ThreadHelper.set("_i18n_", i18n); Map rpt = getRpt(); File temp = File.createTempFile("tmp", ".pdf"); String name = MapUtil.getStr(rpt, "title", "No-Name"); HttpServletResponse resp = Upload.getUpload().getResponse(); resp.setHeader("Content-Disposition", "attachment;filename=\"report.pdf\""); resp.setContentType("application/pdf"); LightUtil.doCache(resp); createPdf(rpt, i18n, temp); FileInputStream is = new FileInputStream(temp); PdfReader reader = new PdfReader(is); int pages = reader.getNumberOfPages(); File temp2 = File.createTempFile("pdf", ".pdf"); OutputStream out = new FileOutputStream(temp2); PdfStamper stamp = new PdfStamper(reader, out); for (int i = 1; i <= pages; i++) { PdfContentByte c = stamp.getOverContent(i); Rectangle ps = reader.getPageSize(i); ThreadHelper.set("pageNumber", i); ThreadHelper.set("pageCount", pages); c.saveState(); List<Map> items = (List) rpt.get("overlays"); if (items != null) { for (Map item : items) { getDirectContent(c, ps, item); } } c.restoreState(); } stamp.close(); ReportFile df = new ReportFile(); df.setOwnerId(getSession().getUserId()); df.setName(name); df.setCreateTime(LightUtil.longDate()); ExternalFile.newExternalFile(getDs(), getFs(), df.getFile(), temp2); getDs().insertBean(df); temp.delete(); temp2.delete(); Map m = new HashMap(); m.put("externalId", df.getFile().getExternalId()); ret.setResult(m); } private void createPdf(Map rpt, Map<String, String> i18n, File pdf) throws Exception { OutputStream out = new FileOutputStream(pdf); ThreadHelper.set("_fonts_", new HashMap<String, BaseFont>()); ThreadHelper.set("_defaultFont_", BaseFont.createFont()); String pageSize = MapUtil.getStr(rpt, "pageSize", "A4"); int defaultFontSize = MapUtil.getInt(rpt, "defaultFontSize", 6); ThreadHelper.set("_defaultFontSize_", defaultFontSize); ThreadHelper.set("_rpt_", rpt); int i = pageSize.indexOf(';'); String margins = "36 36 36 36"; if (i > 0) { margins = pageSize.substring(i + 1); pageSize = pageSize.substring(0, i); } boolean rotate = false; if (pageSize.startsWith("@")) { rotate = true; pageSize = pageSize.substring(1); } Rectangle pSize = PageSize.getRectangle(pageSize); if (rotate) pSize = pSize.rotate(); String mars[] = margins.split(" "); float ml = 0, mt = 0, mr = 0, mb = 0; mr = mt = mb = ml = LightUtil.decodeFloat(mars[0]); if (mars.length > 1) { mt = mb = LightUtil.decodeFloat(mars[1]); } if (mars.length > 2) { mr = LightUtil.decodeFloat(mars[2]); } if (mars.length > 3) { mb = LightUtil.decodeFloat(mars[3]); } Document doc = new Document(pSize, ml, mr, mt, mb); MapUtil.setIfStr(rpt, "author", doc, "addAuthor"); MapUtil.setIfStr(rpt, "creator", doc, "addCreator"); MapUtil.setIfStr(rpt, "title", doc, "addTitle"); MapUtil.setIfStr(rpt, "keyWords", doc, "addKeywords"); MapUtil.setIfStr(rpt, "subject", doc, "addSubject"); PdfWriter writer = PdfWriter.getInstance(doc, out); writer.setPageEvent(this); writer.setStrictImageSequence(true); ThreadHelper.set("_writer_", writer); ThreadHelper.set("_doc_", doc); doc.open(); List<Map> items = (List) rpt.get("items"); for (Map row : items) { Element el = getElement(row); if (el != null) doc.add(el); } doc.close(); out.close(); writer.close(); ThreadHelper.set("_writer_", null); ThreadHelper.set("_doc_", null); } private Map<String, BaseFont> getFonts() { return (Map) ThreadHelper.get("_fonts_"); } private BaseFont getDefaultFont() { return (BaseFont) ThreadHelper.get("_defaultFont_"); } @Override public void onOpenDocument(PdfWriter writer, Document document) { PdfTemplate pgTpl = writer.getDirectContent().createTemplate(100, 100); pgTpl.setBoundingBox(new Rectangle(-20, -20, 100, 100)); ThreadHelper.set("_pageTpl_", pgTpl); } private PdfTemplate getPgTpl() { return (PdfTemplate) ThreadHelper.get("_pageTpl_"); } @Override public void onCloseDocument(PdfWriter writer, Document document) { PdfTemplate tpl = getPgTpl(); tpl.beginText(); tpl.setFontAndSize(getDefaultFont(), 12); tpl.setTextMatrix(0, 0); tpl.showText(Integer.toString(writer.getPageNumber() - 1)); tpl.endText(); } @Override public void onStartPage(PdfWriter writer, Document document) { } @Override public void onEndPage(PdfWriter writer, Document document) { } @Override public void onParagraph(PdfWriter writer, Document document, float paragraphPosition) { } @Override public void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { } @Override public void onChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title) { } @Override public void onChapterEnd(PdfWriter writer, Document document, float paragraphPosition) { } @Override public void onSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title) { } @Override public void onSectionEnd(PdfWriter writer, Document document, float paragraphPosition) { } @Override public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) { } private PdfWriter getWriter() { return (PdfWriter) ThreadHelper.get("_writer_"); } private Document getDoc() { return (Document) ThreadHelper.get("_doc_"); } private Element getElement(Map it) throws DocumentException, Exception { String cls = MapUtil.getStr(it, "cls"); switch (cls) { case "separator": return getSeparator(it); case "image": return getImage(it); case "list": return getList(it); case "table": return getTable(it); case "barcode": return getBarcode(it); case "newpage": getDoc().newPage(); default: return getParagraph(it); } } private LineSeparator getSeparator(Map it) throws Exception { LineSeparator sep = new LineSeparator(); MapUtil.setIfFloat(it, "lineWidth", sep); MapUtil.setIfFloat(it, "offset", sep); MapUtil.setIfFloat(it, "percentage", sep); BaseColor color = getColor(it, "color"); if (color != null) sep.setLineColor(color); sep.setAlignment(getAlignment(it, "alignment")); return sep; } private Paragraph getParagraph(Map it) throws Exception { Paragraph p = new Paragraph(); String lb = MapUtil.getStr(it, "label", null); if (lb != null) { Chunk ch = new Chunk(); ch.setLocalDestination(lb); p.add(ch); } getChunks(p, it, null); List<Map> lst = (List) it.get("items"); if (lst != null) { for (Map item : lst) { Element el = getElement(item); if (el != null) p.add(el); } } applyFont(p, it); return p; } private void applyFont(Paragraph p, Map it) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (it.get("pushFontSize") != null) { Stack<Integer> stack = (Stack) ThreadHelper.get("_fontSizeStack_"); if (stack == null) { stack = new Stack(); ThreadHelper.set("_fontSizeStack_", stack); } int cur = (Integer) ThreadHelper.get("_defaultFontSize_"); int size = MapUtil.getInt(it, "pushFontSize", 6); stack.push(cur); ThreadHelper.set("_defaultFontSize_", size); } if (it.get("popFontSize") != null) { Stack<Integer> stack = (Stack) ThreadHelper.get("_fontSizeStack_"); if (stack != null && stack.size() > 0) { ThreadHelper.set("_defaultFontSize_", stack.pop()); } } MapUtil.setIfFloat(it, "leading", p); MapUtil.setIfFloat(it, "firstLineIndent", p); MapUtil.setIfFloat(it, "indentationLeft", p); MapUtil.setIfFloat(it, "indentationRight", p); MapUtil.setIfFloat(it, "spacingAfter", p); MapUtil.setIfFloat(it, "spacingBefore", p); p.setAlignment(getAlignment(it, "alignment")); for (Object a : p.getChunks()) { if (a instanceof Chunk) { Chunk ch = (Chunk) a; applyFont(ch, it); } } } private int getAlignment(Map it, String fld) { String alignment = MapUtil.getStr(it, fld); int align = Element.ALIGN_UNDEFINED; if ("right".equalsIgnoreCase(alignment)) align = Element.ALIGN_RIGHT; if ("left".equalsIgnoreCase(alignment)) align = Element.ALIGN_LEFT; if ("center".equalsIgnoreCase(alignment)) align = Element.ALIGN_CENTER; return align; } private int getVAlign(Map it, String fld) { String alignment = MapUtil.getStr(it, fld); int align = Element.ALIGN_UNDEFINED; if ("bottom".equalsIgnoreCase(alignment)) align = Element.ALIGN_BOTTOM; if ("top".equalsIgnoreCase(alignment)) align = Element.ALIGN_TOP; if ("middle".equalsIgnoreCase(alignment)) align = Element.ALIGN_MIDDLE; return align; } private void getChunks(Paragraph p, final Map it, String text) throws Exception { if (text == null) { text = (String) it.get("text"); } if (text != null) { if (text.startsWith("<") || text.endsWith(">")) { //treat as HTML Reader in = new StringReader(text); FontFactoryImp ffi = new FontFactoryImp() { @Override public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color, boolean cached) { if (size == Font.UNDEFINED) size = getSize(it); if (style == Font.UNDEFINED) style = getStyle(it); if (color == null) color = getColor(it, "color"); return super.getFont(fontname, encoding, embedded, size, style, color, cached); } }; HashMap map = new HashMap(); map.put("font_factory", ffi); List list = DirectHtmlWorker.parse2List(in, null, null, map); for (int i = 0; i < list.size(); i++) { Object el = list.get(i); if (el instanceof Paragraph) { Paragraph pp = (Paragraph) el; applyFont(pp, it); p.add(pp); } } return; } List<BaseFont> base = new ArrayList<BaseFont>(); String st = text; StringBuffer buf = new StringBuffer(); for (int i = 0; i < st.length(); i++) { char c = st.charAt(i); if (!compatible(base, c)) { processMacro(buf.toString(), base.get(0), it, p); buf.delete(0, buf.length()); base.clear(); } buf.append(c); } if (buf.length() > 0) { processMacro(buf.toString(), base.get(0), it, p); } } } private void processMacro(String buf, BaseFont base, Map it, Paragraph p) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { List<String> list = LightUtil.splitMacro(buf, '$'); for (String ss : list) { Chunk ch = null; if (ss.equals("${pageNumber}")) { PdfWriter writer = getWriter(); if (writer != null) ss = writer.getPageNumber() + ""; else ss = ThreadHelper.get("pageNumber") + ""; } else if (ss.equals("${pageCount}")) { ss = ThreadHelper.get("pageCount") + ""; } else if (ss.equals("${newPage}")) { ch = Chunk.NEXTPAGE; } else if (ss.equals("${newLine}")) { ch = Chunk.NEWLINE; } if (ch == null) ch = new Chunk(ss, new Font(base)); applyFont(ch, it); String gt = MapUtil.getStr(it, "goto", null); if (gt != null) { ch.setLocalGoto(gt); } p.add(ch); } } private void applyFont(Chunk ch, Map it) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { ch.getFont().setSize(getSize(it)); ch.getFont().setStyle(getStyle(it)); ch.getFont().setColor(getColor(it, "color")); MapUtil.setIfFloat(it, "textRise", ch); Float skewAlpha = MapUtil.getFloat(it, "skewAlpha", null); Float skewBeta = MapUtil.getFloat(it, "skewBeta", null); if (skewAlpha != null && skewBeta != null) ch.setSkew(skewAlpha.floatValue(), skewBeta.floatValue()); MapUtil.setIfStr(it, "localDestination", ch); MapUtil.setIfStr(it, "localGoto", ch); } private int getSize(Map it) { Integer sz = MapUtil.getInt(it, "fontSize", (Integer) ThreadHelper.get("_defaultFontSize_")); if (sz != null) return sz; return -1; } private BaseColor getColor(Map row, String id) { String color = (String) row.get(id); if (color == null) return null; Color c = Color.getColor(color); if (c == null) return null; return new BaseColor(c.getRed(), c.getBlue(), c.getGreen()); } private int getStyle(Map row) { Boolean bold = (Boolean) row.get("bold"); Boolean italic = (Boolean) row.get("italic"); Boolean underline = (Boolean) row.get("underline"); Boolean strike = (Boolean) row.get("strike"); Boolean normal = (Boolean) row.get("normal"); int style = 0; if (bold != null && bold) style |= 1; if (italic != null && italic) style |= 2; if (underline != null && underline) style |= 4; if (strike != null && strike) style |= 8; if (style == 0 && (normal == null || normal == false)) style = -1; return style; } private Image getImage(Map row) throws Exception { Image image = null; String url = (String) row.get("url"); if (url != null) { image = Image.getInstance(url); } String externalId = (String) row.get("externalId"); if (externalId != null) { File tmp = File.createTempFile("tmp", ".image"); InputStream is = getFs().getResource(externalId); FileOutputStream os = new FileOutputStream(tmp); StreamUtil.copyStream(is, os, 0); is.close(); os.close(); image = Image.getInstance(tmp.toURL()); ThreadHelper.postponeDelete(tmp); } String img = (String) row.get("image"); if (img != null) { byte[] bytes = LightStr.getHexContent(img); image = Image.getInstance(bytes); } if (image != null) { MapUtil.setIfFloat(row, "spacingAfter", image); MapUtil.setIfFloat(row, "spacingBefore", image); MapUtil.setIfFloat(row, "scalePercent", image); MapUtil.setIfFloat(row, "scaleDegree", image); Float fitPer = MapUtil.getFloat(row, "fitPercent", null); if (fitPer != null) { Document doc = this.getDoc(); image.scaleToFit( (doc.getPageSize().getWidth() - doc.leftMargin() - doc.rightMargin()) * fitPer / 100f, (doc.getPageSize().getHeight() - doc.topMargin() - doc.bottomMargin()) * fitPer / 100f); } Float fitWidth = MapUtil.getFloat(row, "fitWidth", null); if (fitWidth != null) { Float fitHeight = MapUtil.getFloat(row, "fitHeight", fitWidth); image.scaleToFit(fitWidth, fitHeight); } image.setAlignment(getAlignment(row, "alignment")); } return image; } private com.itextpdf.text.List getList(Map row) throws Exception { String type = (String) row.get("type"); com.itextpdf.text.List list = null; if ("greek".equals(type)) list = new GreekList(); else if ("roman".equals(type)) list = new RomanList(); else if ("zd".equals(type)) list = new ZapfDingbatsList(1); else if ("zd#".equals(type)) list = new ZapfDingbatsNumberList(1); else list = new com.itextpdf.text.List(); Integer charNumber = (Integer) row.get("charNumber"); if (charNumber != null) { if (list instanceof ZapfDingbatsList) { ((ZapfDingbatsList) list).setCharNumber(charNumber); } if (list instanceof ZapfDingbatsNumberList) { ((ZapfDingbatsNumberList) list).setType(charNumber); } } Boolean lowercase = (Boolean) row.get("lowercase"); if (lowercase != null) list.setLowercase(lowercase); Boolean numbered = (Boolean) row.get("numbered"); if (numbered != null) list.setNumbered(numbered); Boolean lettered = (Boolean) row.get("lettered"); if (lettered != null) list.setLettered(lettered); Integer first = (Integer) row.get("first"); if (first != null) list.setFirst(first); String symbol = (String) row.get("symbol"); if (symbol != null) { Paragraph p = new Paragraph(); getChunks(p, row, symbol); if (p.size() > 0) { Object sym = p.get(0); if (sym instanceof Chunk) { list.setListSymbol((Chunk) sym); } } } Float ident = (Float) row.get("ident"); if (ident == null) ident = 10f; list.setSymbolIndent(ident); List<Map> lst = (List) row.get("items"); if (lst != null) { for (Map item : lst) { Element el = getElement(item); if (el != null) { ListItem li = new ListItem(); li.add(el); list.add(li); } } } if (list.size() > 0) { Object o = list.getItems().get(0); if (o instanceof Paragraph) { Paragraph p = (Paragraph) o; Float spaceBefore = (Float) row.get("spaceBefore"); if (spaceBefore != null) p.setSpacingBefore(spaceBefore); } o = list.getItems().get(list.getItems().size() - 1); if (o instanceof Paragraph) { Paragraph p = (Paragraph) o; Float spaceAfter = (Float) row.get("spaceAfter"); if (spaceAfter != null) p.setSpacingAfter(spaceAfter); } } return list; } private BaseFont getBaseFont(String id, String name, String encoding) { BaseFont font = getFonts().get(id); if (font == null) { try { font = BaseFont.createFont(name, encoding, BaseFont.NOT_EMBEDDED); } catch (Exception e) { e.printStackTrace(); font = getDefaultFont(); } getFonts().put(id, font); } return font; } private BaseFont getZhFont() { return getBaseFont("zh", "STSong-Light", "UniGB-UCS2-H"); } private BaseFont getJpFont() { return getBaseFont("jp", "KozMinPro-Regular", "UniJIS-UCS2-H"); } private BaseFont getKsFont() { return getBaseFont("ks", "HYGoThic-Medium", "UniKS-UCS2-H"); } private boolean compatible(List<BaseFont> base, char c) { BaseFont font; if (c >= 0x4e00 && c <= 0x9fa5) { if (base.size() == 0) { base.add(getZhFont()); base.add(getJpFont()); base.add(getKsFont()); return true; } else { if (base.indexOf(getZhFont()) >= 0) return true; if (base.indexOf(getJpFont()) >= 0) return true; if (base.indexOf(getKsFont()) >= 0) return true; return false; } } if (c >= 0x30a0 && c <= 0x30ff) { if (base.size() == 0) { base.add(getJpFont()); return true; } if (base.indexOf(getJpFont()) >= 0) { if (base.size() == 1) return true; base.clear(); base.add(getJpFont()); return true; } return false; } if (c >= 0xac00 && c <= 0xd7af) { if (base.size() == 0) { base.add(getKsFont()); return true; } if (base.indexOf(getKsFont()) >= 0) { if (base.size() == 1) return true; base.clear(); base.add(getKsFont()); return true; } return false; } if (base.size() == 0) { base.add(getDefaultFont()); } return true; } public Image getBarcode(Map it) throws Exception { String type = MapUtil.getStr(it, "type"); String code = MapUtil.getStr(it, "code"); switch (type) { case "pf417": BarcodePDF417 bar = new BarcodePDF417(); bar.setText(code); return bar.getImage(); case "QRCode": BarcodeQRCode qr = new BarcodeQRCode(code, MapUtil.getInt(it, "qrWidth", 1), MapUtil.getInt(it, "qrHeight", 1), null); return qr.getImage(); default: Barcode barcode; PdfContentByte cb = getWriter().getDirectContent(); switch (type) { case "code128": case "code128_raw": barcode = new Barcode128(); barcode.setCodeType(Barcode.CODE128_RAW); break; case "code128_ucc": barcode = new Barcode128(); barcode.setCodeType(Barcode.CODE128_UCC); break; case "inter25": barcode = new BarcodeInter25(); break; case "postnet": barcode = new BarcodePostnet(); break; case "planet": barcode = new BarcodePostnet(); barcode.setCodeType(Barcode.PLANET); break; case "code39": barcode = new Barcode39(); break; case "codabar": barcode = new BarcodeCodabar(); break; default: barcode = new BarcodeEAN(); MapUtil.setIfBool(it, "guardBars", barcode, "setGuardBars"); MapUtil.setIfFloat(it, "baseLine", barcode, "setBaseLine"); if ("upca".equals(type)) barcode.setCodeType(Barcode.UPCA); if ("ean8".equals(type)) barcode.setCodeType(Barcode.EAN8); if ("upce".equals(type)) barcode.setCodeType(Barcode.UPCE); if ("ean13".equals(type)) barcode.setCodeType(Barcode.EAN13); } barcode.setCode(code); MapUtil.setIfFloat(it, "barHeight", barcode, "setBarHeight"); MapUtil.setIfFloat(it, "x", barcode, "setX"); MapUtil.setIfFloat(it, "n", barcode, "setN"); MapUtil.setIfFloat(it, "size", barcode, "setSize"); barcode.setTextAlignment(getAlignment(it, "alignment")); MapUtil.setIfBool(it, "checksumText", barcode, "setChecksumText"); MapUtil.setIfBool(it, "startStopText", barcode, "setStartStopText"); MapUtil.setIfBool(it, "extended", barcode, "setExtended"); String suppCode = MapUtil.getStr(it, "suppCode"); if (!LightStr.isEmpty(suppCode)) { BarcodeEAN codeSUPP = new BarcodeEAN(); codeSUPP.setCodeType(Barcode.SUPP5); codeSUPP.setCode(suppCode); codeSUPP.setBaseline(-2); BarcodeEANSUPP eanSupp = new BarcodeEANSUPP(barcode, codeSUPP); return eanSupp.createImageWithBarcode(cb, getColor(it, "barColor"), getColor(it, "textColor")); } else { return barcode.createImageWithBarcode(cb, getColor(it, "barColor"), getColor(it, "textColor")); } } } private PdfPTable getTable(Map it) throws Exception { String ss = MapUtil.getStr(it, "widthList", "100"); List<String> st = MapUtil.getSelectList(ss); float[] widths = new float[st.size()]; for (int i = 0; i < st.size(); i++) widths[i] = LightUtil.decodeFloat(st.get(i)); PdfPTable table = new PdfPTable(widths); table.setExtendLastRow(false, false); MapUtil.setIfFloat(it, "spacingAfter", table); MapUtil.setIfFloat(it, "spacingBefore", table); MapUtil.setIfInt(it, "headerRows", table); MapUtil.setIfInt(it, "footerRows", table); MapUtil.setIfBool(it, "skipFirstHeader", table); MapUtil.setIfBool(it, "skipLastFooter", table); MapUtil.setIfFloat(it, "widthPercentage", table); MapUtil.setIfBool(it, "splitRows", table); MapUtil.setIfBool(it, "splitLate", table); MapUtil.setIfBool(it, "extendLastRow", table); MapUtil.setIfBool(it, "keepTogether", table); List<Map> items = (List) it.get("items"); if (items != null) { for (Map cell : items) { Element el = getElement(cell); PdfPCell pc = new PdfPCell(); if (el != null) if (el instanceof Phrase) pc = new PdfPCell((Phrase) el); else if (el instanceof Image) pc = new PdfPCell((Image) el); else { pc.addElement(el); } pc.setHorizontalAlignment(getAlignment(cell, "align")); pc.setVerticalAlignment(getVAlign(cell, "valign")); pc.setUseBorderPadding(true); MapUtil.setIfInt(cell, "rowspan", pc); MapUtil.setIfInt(cell, "colspan", pc); MapUtil.setIfInt(cell, "border", pc); MapUtil.setIfInt(cell, "rotation", pc); MapUtil.setIfFloat(cell, "borderWidth", pc); BaseColor color = getColor(cell, "bgColor"); if (color != null) pc.setBackgroundColor(color); table.addCell(pc); } } return table; } private void getDirectContent(PdfContentByte cb, Rectangle ps, Map it) throws Exception { BaseColor color = getColor(it, "fillColor"); if (color != null) cb.setColorFill(color); float x = MapUtil.getFloat(it, "x", 0f); float y = MapUtil.getFloat(it, "y", 0f); float w = MapUtil.getFloat(it, "w", 0f); float h = MapUtil.getFloat(it, "h", 0f); float xPer = MapUtil.getFloat(it, "xPer", 0f); float yPer = MapUtil.getFloat(it, "yPer", 0f); float wPer = MapUtil.getFloat(it, "wPer", 0f); float hPer = MapUtil.getFloat(it, "hPer", 0f); String pos = MapUtil.getStr(it, "position", "bottom"); switch (pos) { case "top": y += ps.getHeight(); break; case "right": x += ps.getWidth(); break; } float xx = x + ps.getWidth() * xPer / 100f; float yy = y + ps.getWidth() * yPer / 100f; float ww = ps.getWidth() * wPer / 100f + w; float hh = ps.getHeight() * hPer / 100f + h; int font = MapUtil.getInt(it, "fontSize", 8); cb.setFontAndSize(getDefaultFont(), font); cb.beginText(); String cls = MapUtil.getStr(it, "cls", ""); if (cls.equals("image")) { Image img = getImage(it); cb.addImage(img, img.getWidth(), 0, 0, img.getHeight(), xx, yy); } else { String text = LightUtil.macro(MapUtil.getStr(it, "text", ""), '$').toString(); float degree = MapUtil.getFloat(it, "rotateDegree", 0f); boolean kerned = MapUtil.getBool(it, "kerned", false); int align = getAlignment(it, "alignment"); x = xx; y = yy; switch (align) { case Element.ALIGN_CENTER: x = xx + ww / 2; break; case Element.ALIGN_RIGHT: x = xx + ww; break; default: align = Element.ALIGN_LEFT; break; } if (kerned) cb.showTextAlignedKerned(align, text, x, y, degree); else cb.showTextAligned(align, text, x, y, degree); } cb.endText(); } } /* * $Id: HTMLWorker.java 5075 2012-02-27 16:36:18Z blowagie $ * * This file is part of the iText (R) project. * Copyright (c) 1998-2012 1T3XT BVBA * Authors: Bruno Lowagie, Paulo Soares, et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA, 02110-1301 USA, or download the license from the following URL: * http://itextpdf.com/terms-of-use/ * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * In accordance with Section 7(b) of the GNU Affero General Public License, * a covered work must retain the producer line in every PDF that is created * or manipulated using iText. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the iText software without * disclosing the source code of your own applications. * These activities include: offering paid services to customers as an ASP, * serving PDFs on the fly in a web application, shipping iText with a closed * source product. * * For more information, please contact iText Software Corp. at this * address: sales@itextpdf.com */ class HTMLCoWorker extends HTMLWorker { private static Logger LOGGER = LoggerFactory.getLogger(HTMLWorker.class); /** The object defining all the styles. */ private StyleSheet style = new StyleSheet(); /** * Creates a new instance of HTMLWorker * @param document A class that implements <CODE>DocListener</CODE> */ public HTMLCoWorker(final DocListener document) { this(document, null, null); } /** * Creates a new instance of HTMLWorker * @param document A class that implements <CODE>DocListener</CODE> * @param tags A map containing the supported tags * @param style A StyleSheet * @since 5.0.6 */ public HTMLCoWorker(final DocListener document, final Map<String, HTMLTagProcessor> tags, final StyleSheet style) { super(document, tags, style); setStyleSheet(style); } /** * Setter for the StyleSheet * @param style the StyleSheet */ public void setStyleSheet(StyleSheet style) { if (style == null) style = new StyleSheet(); this.style = style; } /** * The current hierarchy chain of tags. * @since 5.0.6 */ private final ChainedProperties chain = new ChainedProperties(); /** * @see com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler#startDocument() */ public void startDocument() { HashMap<String, String> attrs = new HashMap<String, String>(); style.applyStyle(HtmlTags.BODY, attrs); chain.addToChain(HtmlTags.BODY, attrs); } /** * @see com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler#startElement(java.lang.String, java.util.Map) */ public void startElement(final String tag, final Map<String, String> attrs) { HTMLTagProcessor htmlTag = tags.get(tag); if (htmlTag == null) { return; } // apply the styles to attrs style.applyStyle(tag, attrs); // deal with the style attribute StyleSheet.resolveStyleAttribute(attrs, chain); // process the tag try { htmlTag.startElement(this, tag, attrs); } catch (DocumentException e) { throw new ExceptionConverter(e); } catch (IOException e) { throw new ExceptionConverter(e); } } /** * @see com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler#text(java.lang.String) */ public void text(String content) { if (skipText) return; if (currentParagraph == null) { currentParagraph = createParagraph(); } if (!insidePRE) { // newlines and carriage returns are ignored if (content.trim().length() == 0 && content.indexOf(' ') < 0) { return; } content = HtmlUtilities.eliminateWhiteSpace(content); } Chunk chunk = createChunk(content); currentParagraph.add(chunk); } /** * @see com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler#endElement(java.lang.String) */ public void endElement(final String tag) { HTMLTagProcessor htmlTag = tags.get(tag); if (htmlTag == null) { return; } // process the tag try { htmlTag.endElement(this, tag); } catch (DocumentException e) { throw new ExceptionConverter(e); } } /** * @see com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler#endDocument() */ public void endDocument() { try { // flush the stack for (int k = 0; k < stack.size(); ++k) document.add(stack.elementAt(k)); // add current paragraph if (currentParagraph != null) document.add(currentParagraph); currentParagraph = null; } catch (Exception e) { throw new ExceptionConverter(e); } } // stack and current paragraph operations /** * Adds a new line to the currentParagraph. * @since 5.0.6 */ public void newLine() { if (currentParagraph == null) { currentParagraph = new Paragraph(); } currentParagraph.add(createChunk("\n")); } /** * Flushes the current paragraph, indicating that we're starting * a new block. * If the stack is empty, the paragraph is added to the document. * Otherwise the Paragraph is added to the stack. * @since 5.0.6 */ public void carriageReturn() throws DocumentException { if (currentParagraph == null) return; if (stack.empty()) document.add(currentParagraph); else { Element obj = stack.pop(); if (obj instanceof TextElementArray) { TextElementArray current = (TextElementArray) obj; current.add(currentParagraph); } stack.push(obj); } currentParagraph = null; } /** * Stacks the current paragraph, indicating that we're starting * a new span. * @since 5.0.6 */ public void flushContent() { pushToStack(currentParagraph); currentParagraph = new Paragraph(); } /** * Pushes an element to the Stack. * @param element * @since 5.0.6 */ public void pushToStack(final Element element) { if (element != null) stack.push(element); } /** * Updates the chain with a new tag and new attributes. * @param tag the new tag * @param attrs the corresponding attributes * @since 5.0.6 */ public void updateChain(final String tag, final Map<String, String> attrs) { chain.addToChain(tag, attrs); } /** * Updates the chain by removing a tag. * @param tag the new tag * @since 5.0.6 */ public void updateChain(final String tag) { chain.removeChain(tag); } /** * Map containing providers such as a FontProvider or ImageProvider. * @since 5.0.6 (renamed from interfaceProps) */ private Map<String, Object> providers = new HashMap<String, Object>(); /** * Setter for the providers. * If a FontProvider is added, the ElementFactory is updated. * @param providers a Map with different providers * @since 5.0.6 */ public void setProviders(final Map<String, Object> providers) { if (providers == null) return; this.providers = providers; FontProvider ff = null; if (providers != null) ff = (FontProvider) providers.get(FONT_PROVIDER); if (ff != null) factory.setFontProvider(ff); } // factory that helps create objects /** * Factory that is able to create iText Element objects. * @since 5.0.6 */ private ElementFactory factory = new ElementFactory(); /** * Creates a Chunk using the factory. * @param content the content of the chunk * @return a Chunk with content * @since 5.0.6 */ public Chunk createChunk(final String content) { return factory.createChunk(content, chain); } /** * Creates a Paragraph using the factory. * @return a Paragraph without any content * @since 5.0.6 */ public Paragraph createParagraph() { return factory.createParagraph(chain); } /** * Creates a List object. * @param tag should be "ol" or "ul" * @return a List object * @since 5.0.6 */ public com.itextpdf.text.List createList(final String tag) { return factory.createList(tag, chain); } /** * Creates a ListItem object. * @return a ListItem object * @since 5.0.6 */ public ListItem createListItem() { return factory.createListItem(chain); } /** * Creates a LineSeparator object. * @param attrs properties of the LineSeparator * @return a LineSeparator object * @since 5.0.6 */ public LineSeparator createLineSeparator(final Map<String, String> attrs) { return factory.createLineSeparator(attrs, currentParagraph.getLeading() / 2); } /** * Creates an Image object. * @param attrs properties of the Image * @return an Image object (or null if the Image couldn't be found) * @throws DocumentException * @throws IOException * @since 5.0.6 */ public Image createImage(final Map<String, String> attrs) throws DocumentException, IOException { String src = attrs.get(HtmlTags.SRC); if (src == null) return null; Image img = factory.createImage(src, attrs, chain, document, (ImageProvider) providers.get(IMG_PROVIDER), (ImageStore) providers.get(IMG_STORE), (String) providers.get(IMG_BASEURL)); return img; } /** * Creates a Cell. * @param tag the tag * @return a CellWrapper object * @since 5.0.6 */ public CellWrapper createCell(final String tag) { return new CellWrapper(tag, chain); } // processing objects /** * Adds a link to the current paragraph. * @since 5.0.6 */ public void processLink() { if (currentParagraph == null) { currentParagraph = new Paragraph(); } // The link provider allows you to do additional processing LinkProcessor i = (LinkProcessor) providers.get(HTMLWorker.LINK_PROVIDER); if (i == null || !i.process(currentParagraph, chain)) { // sets an Anchor for all the Chunks in the current paragraph String href = chain.getProperty(HtmlTags.HREF); if (href != null) { for (Chunk ck : currentParagraph.getChunks()) { ck.setAnchor(href); } } } // a link should be added to the current paragraph as a phrase if (stack.isEmpty()) { // no paragraph to add too, 'a' tag is first element Paragraph tmp = new Paragraph(new Phrase(currentParagraph)); currentParagraph = tmp; } else { Paragraph tmp = (Paragraph) stack.pop(); tmp.add(new Phrase(currentParagraph)); currentParagraph = tmp; } } /** * Fetches the List from the Stack and adds it to * the TextElementArray on top of the Stack, * or to the Document if the Stack is empty. * @throws DocumentException * @since 5.0.6 */ public void processList() throws DocumentException { if (stack.empty()) return; Element obj = stack.pop(); if (!(obj instanceof com.itextpdf.text.List)) { stack.push(obj); return; } if (stack.empty()) document.add(obj); else ((TextElementArray) stack.peek()).add(obj); } /** * Looks for the List object on the Stack, * and adds the ListItem to the List. * @throws DocumentException * @since 5.0.6 */ public void processListItem() throws DocumentException { if (stack.empty()) return; Element obj = stack.pop(); if (!(obj instanceof ListItem)) { stack.push(obj); return; } if (stack.empty()) { document.add(obj); return; } ListItem item = (ListItem) obj; Element list = stack.pop(); if (!(list instanceof com.itextpdf.text.List)) { stack.push(list); return; } ((com.itextpdf.text.List) list).add(item); item.adjustListSymbolFont(); stack.push(list); } /** * Processes an Image. * @param img * @param attrs * @throws DocumentException * @since 5.0.6 */ public void processImage(final Image img, final Map<String, String> attrs) throws DocumentException { ImageProcessor processor = (ImageProcessor) providers.get(HTMLWorker.IMG_PROCESSOR); if (processor == null || !processor.process(img, attrs, chain, document)) { String align = attrs.get(HtmlTags.ALIGN); if (align != null) { carriageReturn(); } if (currentParagraph == null) { currentParagraph = createParagraph(); } currentParagraph.add(new Chunk(img, 0, 0, true)); currentParagraph.setAlignment(HtmlUtilities.alignmentValue(align)); if (align != null) { carriageReturn(); } } } /** * Processes the Table. * @throws DocumentException * @since 5.0.6 */ public void processTable() throws DocumentException { TableWrapper table = (TableWrapper) stack.pop(); PdfPTable tb = table.createTable(); tb.setSplitRows(true); if (stack.empty()) document.add(tb); else ((TextElementArray) stack.peek()).add(tb); } /** * Gets the TableWrapper from the Stack and adds a new row. * @since 5.0.6 */ public void processRow() { ArrayList<PdfPCell> row = new ArrayList<PdfPCell>(); ArrayList<Float> cellWidths = new ArrayList<Float>(); boolean percentage = false; float width; float totalWidth = 0; int zeroWidth = 0; TableWrapper table = null; while (true) { Element obj = stack.pop(); if (obj instanceof CellWrapper) { CellWrapper cell = (CellWrapper) obj; width = cell.getWidth(); cellWidths.add(new Float(width)); percentage |= cell.isPercentage(); if (width == 0) { zeroWidth++; } else { totalWidth += width; } row.add(cell.getCell()); } if (obj instanceof TableWrapper) { table = (TableWrapper) obj; break; } } table.addRow(row); if (cellWidths.size() > 0) { // cells come off the stack in reverse, naturally totalWidth = 100 - totalWidth; Collections.reverse(cellWidths); float[] widths = new float[cellWidths.size()]; boolean hasZero = false; for (int i = 0; i < widths.length; i++) { widths[i] = cellWidths.get(i).floatValue(); if (widths[i] == 0 && percentage && zeroWidth > 0) { widths[i] = totalWidth / zeroWidth; } if (widths[i] == 0) { hasZero = true; break; } } if (!hasZero) table.setColWidths(widths); } stack.push(table); } // state variables and methods /** Stack to keep track of table tags. */ private final Stack<boolean[]> tableState = new Stack<boolean[]>(); /** Boolean to keep track of TR tags. */ private boolean pendingTR = false; /** Boolean to keep track of TD and TH tags */ private boolean pendingTD = false; /** Boolean to keep track of LI tags */ private boolean pendingLI = false; /** * Boolean to keep track of PRE tags * @since 5.0.6 renamed from isPRE */ private boolean insidePRE = false; /** * Pushes the values of pendingTR and pendingTD * to a state stack. * @since 5.0.6 */ public void pushTableState() { tableState.push(new boolean[] { pendingTR, pendingTD }); } /** * Pops the values of pendingTR and pendingTD * from a state stack. * @since 5.0.6 */ public void popTableState() { boolean[] state = tableState.pop(); pendingTR = state[0]; pendingTD = state[1]; } /** * @return the pendingTR * @since 5.0.6 */ public boolean isPendingTR() { return pendingTR; } /** * @param pendingTR the pendingTR to set * @since 5.0.6 */ public void setPendingTR(final boolean pendingTR) { this.pendingTR = pendingTR; } /** * @return the pendingTD * @since 5.0.6 */ public boolean isPendingTD() { return pendingTD; } /** * @param pendingTD the pendingTD to set * @since 5.0.6 */ public void setPendingTD(final boolean pendingTD) { this.pendingTD = pendingTD; } /** * @return the pendingLI * @since 5.0.6 */ public boolean isPendingLI() { return pendingLI; } /** * @param pendingLI the pendingLI to set * @since 5.0.6 */ public void setPendingLI(final boolean pendingLI) { this.pendingLI = pendingLI; } /** * @return the insidePRE * @since 5.0.6 */ public boolean isInsidePRE() { return insidePRE; } /** * @param insidePRE the insidePRE to set * @since 5.0.6 */ public void setInsidePRE(final boolean insidePRE) { this.insidePRE = insidePRE; } /** * @return the skipText * @since 5.0.6 */ public boolean isSkipText() { return skipText; } /** * @param skipText the skipText to set * @since 5.0.6 */ public void setSkipText(final boolean skipText) { this.skipText = skipText; } // DocListener interface /** * @see com.itextpdf.text.ElementListener#add(com.itextpdf.text.Element) */ public boolean add(final Element element) throws DocumentException { objectList.add(element); return true; } /** * @see com.itextpdf.text.DocListener#close() */ public void close() { } /** * @see com.itextpdf.text.DocListener#newPage() */ public boolean newPage() { return true; } /** * @see com.itextpdf.text.DocListener#open() */ public void open() { } /** * @see com.itextpdf.text.DocListener#resetPageCount() */ public void resetPageCount() { } /** * @see com.itextpdf.text.DocListener#setMarginMirroring(boolean) */ public boolean setMarginMirroring(final boolean marginMirroring) { return false; } /** * @see com.itextpdf.text.DocListener#setMarginMirroring(boolean) * @since 2.1.6 */ public boolean setMarginMirroringTopBottom(final boolean marginMirroring) { return false; } /** * @see com.itextpdf.text.DocListener#setMargins(float, float, float, float) */ public boolean setMargins(final float marginLeft, final float marginRight, final float marginTop, final float marginBottom) { return true; } /** * @see com.itextpdf.text.DocListener#setPageCount(int) */ public void setPageCount(final int pageN) { } /** * @see com.itextpdf.text.DocListener#setPageSize(com.itextpdf.text.Rectangle) */ public boolean setPageSize(final Rectangle pageSize) { return true; } // deprecated methods /** * Sets the providers. * @deprecated use setProviders() instead */ @Deprecated public void setInterfaceProps(final HashMap<String, Object> providers) { setProviders(providers); } /** * Gets the providers * @deprecated use getProviders() instead */ @Deprecated public Map<String, Object> getInterfaceProps() { return providers; } public ElementFactory getFactory() { return factory; } public void setFactory(ElementFactory factory) { this.factory = factory; } } class DirectHtmlWorker extends HTMLCoWorker { public DirectHtmlWorker(DocListener document) { super(document); setFactory(new DirectElementFactory()); } public DirectHtmlWorker(DocListener document, Map<String, HTMLTagProcessor> tags, StyleSheet style) { super(document, tags, style); setFactory(new DirectElementFactory()); } public static List<Element> parse2List(final Reader reader, final StyleSheet style, final Map<String, HTMLTagProcessor> tags, final HashMap<String, Object> providers) throws IOException { DirectHtmlWorker worker = new DirectHtmlWorker(null, tags, style); worker.setFactory(new DirectElementFactory()); worker.document = worker; worker.setProviders(providers); worker.objectList = new ArrayList<Element>(); worker.parse(reader); return worker.objectList; } } class DirectElementFactory extends ElementFactory { @Override public Image createImage(String src, final Map<String, String> attrs, final ChainedProperties chain, final DocListener document, final ImageProvider img_provider, final HashMap<String, Image> img_store, final String img_baseurl) throws DocumentException, IOException { Image img = null; // getting the image using an image provider if (img_provider != null) img = img_provider.getImage(src, attrs, chain, document); // getting the image from an image store if (img == null && img_store != null) { Image tim = img_store.get(src); if (tim != null) img = Image.getInstance(tim); } if (img != null) return img; ////if src start with data: it's dataUri and parse it imme. if (src.startsWith("remote?")) { BeanFactory bf = BeanFactory.getBeanFactory(); String pp = src.substring(7); String[] ss = pp.split("\\&"); try { String id = "~", fsId = LightUtil.getRepository().getFsId(); for (String s : ss) { String[] sss = s.split("="); if (sss[0].equals("id")) id = sss[1]; if (sss[0].equals("fsId")) fsId = sss[1]; } IRepository fs = bf.getRepository(fsId); InputStream is = fs.getResource(id); ByteArrayOutputStream os = new ByteArrayOutputStream(); StreamUtil.copyStream(is, os, 0); is.close(); os.close(); img = Image.getInstance(os.toByteArray()); } catch (Exception e) { e.printStackTrace(); } } else if (src.startsWith("data:")) { int i = src.indexOf(","); byte[] bits = Base64.decode(src.substring(i + 1)); img = Image.getInstance(bits); } else { //// // introducing a base url // relative src references only if (!src.startsWith("http") && img_baseurl != null) { src = img_baseurl + src; } else if (img == null && !src.startsWith("http")) { String path = chain.getProperty(HtmlTags.IMAGEPATH); if (path == null) path = ""; src = new File(path, src).getPath(); } img = Image.getInstance(src); } if (img == null) return null; float actualFontSize = HtmlUtilities.parseLength(chain.getProperty(HtmlTags.SIZE), HtmlUtilities.DEFAULT_FONT_SIZE); if (actualFontSize <= 0f) actualFontSize = HtmlUtilities.DEFAULT_FONT_SIZE; String width = attrs.get(HtmlTags.WIDTH); float widthInPoints = HtmlUtilities.parseLength(width, actualFontSize); String height = attrs.get(HtmlTags.HEIGHT); float heightInPoints = HtmlUtilities.parseLength(height, actualFontSize); if (widthInPoints == 0 && heightInPoints == 0) { Document doc = (Document) document; widthInPoints = doc.getPageSize().getWidth(); } if (widthInPoints > 0 && heightInPoints > 0) { img.scaleAbsolute(widthInPoints, heightInPoints); } else if (widthInPoints > 0) { heightInPoints = img.getHeight() * widthInPoints / img.getWidth(); img.scaleAbsolute(widthInPoints, heightInPoints); } else if (heightInPoints > 0) { widthInPoints = img.getWidth() * heightInPoints / img.getHeight(); img.scaleAbsolute(widthInPoints, heightInPoints); } String before = chain.getProperty(HtmlTags.BEFORE); if (before != null) img.setSpacingBefore(Float.parseFloat(before)); String after = chain.getProperty(HtmlTags.AFTER); if (after != null) img.setSpacingAfter(Float.parseFloat(after)); img.setWidthPercentage(0); return img; } }