生成PDF并转图片丢失中文显示的问题
问题背景
在系统中需要对PDF模板填充数据内容并生成临时PDF,再将临时PDF文档转换为图片格式并保存(此过程无论成功还是失败都会把临时生成的PDF删除,减少不必要的存储空间)。PDF模板部分内容截图如下:
但是最终生成的图片中,却缺失了填充内容的中文信息,如下图所示:
查找问题
一开始一直怀疑是使用因为使用itextpdf的jar包在填充PDF模板生成PDF的时候出问题的,因为当时搜了一下网上确实有很多因为使用了这种jar包而导致中文缺失或是乱码的问题。因此花了一段时间一直在这个方面查找和解决问题,包括引入itextpdf的字体包itext-asian来解决此问题,但最终生成的图片还是没有中文。最终,个人把代码中删除临时PDF该操作放开(个人愚钝当时未想到把此步骤拆开来查找问题),来将生成好的临时PDF和最后图片进行比对。最后终于发现是PDF转图片的过程中丢失了填充内容的中文。
解决问题
1.在将PDF转为图片时,个人使用的是apache开源pdfbox的jar包(问题出在此)。
2.查找各相关文档,发现大部分解决方法都是复写源码来指定字体库,当然还有其他方案,就是本地系统安装相关字体库,亦能解决问题。
代码实现
现贴出解决该需求(根据PDF模板生成PDF,再将PDF转为图片)以及解决相关问题(PDF转图片丢失中文)的相关代码。
1.maven配置:
说明:1.itextpdf的两个包是用于PDF模板生成PDF用的,itext-asian可使能兼容中文内容。
2.pdfbox是用于PDF转图片的(亦是我们要解决不能显示中文内容的问题)。
2.根据PDF模板生成PDF工具类
public class PdfUtils {private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);public static ByteArrayOutputStream generatePdf(String templatePath, Map<String, String> dataMap, Map<String, String> imgMap) {try {FileInputStream inputStream = new FileInputStream(templatePath);Throwable var4 = null;ByteArrayOutputStream var5;try {var5 = generatePdf((InputStream)inputStream, dataMap, imgMap);} catch (Throwable var15) {var4 = var15;throw var15;} finally {if (inputStream != null) {if (var4 != null) {try {inputStream.close();} catch (Throwable var14) {var4.addSuppressed(var14);}} else {inputStream.close();}}}return var5;} catch (IOException var17) {logger.error("生成PDF异常, e:", var17);throw new PDFException("生成PDF异常");}}public static ByteArrayOutputStream generatePdf(InputStream templateInputStream, Map<String, String> dataMap, Map<String, String> imgMap) {try {ByteArrayOutputStream bos = new ByteArrayOutputStream();Throwable var6 = null;ByteArrayOutputStream var29;try {PdfReader reader = new PdfReader(templateInputStream);PdfStamper stamper = new PdfStamper(reader, bos);AcroFields form = stamper.getAcroFields();BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);form.addSubstitutionFont(bfChinese);Iterator var9;String key;String value;if (dataMap != null) {var9 = dataMap.keySet().iterator();while(var9.hasNext()) {key = (String)var9.next();value = (String)dataMap.get(key);form.setField(key, value);}}if (imgMap != null) {var9 = imgMap.keySet().iterator();while(var9.hasNext()) {key = (String)var9.next();value = (String)imgMap.get(key);int pageNo = ((AcroFields.FieldPosition)form.getFieldPositions(key).get(0)).page;Rectangle signRect = ((AcroFields.FieldPosition)form.getFieldPositions(key).get(0)).position;float x = signRect.getLeft();float y = signRect.getBottom();Image image = Image.getInstance(value);PdfContentByte under = stamper.getOverContent(pageNo);image.scaleToFit(signRect.getWidth(), signRect.getHeight());image.setAbsolutePosition(x, y);under.addImage(image);}}stamper.setFormFlattening(true);stamper.close();var29 = bos;} catch (Throwable var26) {var6 = var26;throw var26;} finally {if (bos != null) {if (var6 != null) {try {bos.close();} catch (Throwable var25) {var6.addSuppressed(var25);}} else {bos.close();}}}return var29;} catch (Exception var28) {logger.error("生成PDF异常, e:", var28);throw new PDFException("生成PDF异常");}}public static void generatePdfFile(String templatePath, Map<String, String> dataMap, Map<String, String> imgMap, String newPdfPath) {try {FileOutputStream out = new FileOutputStream(newPdfPath);Throwable var5 = null;try {ByteArrayOutputStream bos = generatePdf(templatePath, dataMap, imgMap);Throwable var7 = null;try {Document doc = new Document();PdfCopy copy = new PdfCopy(doc, out);doc.open();PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);copy.addPage(importPage);doc.close();} catch (Throwable var34) {var7 = var34;throw var34;} finally {if (bos != null) {if (var7 != null) {try {bos.close();} catch (Throwable var33) {var7.addSuppressed(var33);}} else {bos.close();}}}} catch (Throwable var36) {var5 = var36;throw var36;} finally {if (out != null) {if (var5 != null) {try {out.close();} catch (Throwable var32) {var5.addSuppressed(var32);}} else {out.close();}}}} catch (Exception var38) {logger.error("生成PDF异常, e:", var38);throw new PDFException("生成PDF异常");}}public static InputStream generatePdfIntoInputStream(InputStream templateInputStream, Map<String, String> dataMap, Map<String, String> imgMap) {byte[] fileBytes = generatePdfIntoByte(templateInputStream, dataMap, imgMap);return new ByteArrayInputStream(fileBytes);}public static byte[] generatePdfIntoByte(InputStream templateInputStream, Map<String, String> dataMap, Map<String, String> imgMap) {ByteArrayOutputStream outputStream = generatePdf(templateInputStream, dataMap, imgMap);return outputStream.toByteArray();}}
3.根据PDF转换为图片工具类
public class PdfToImagesUtil{public static final String PNG = "png";private static final Logger logger = LoggerFactory.getLogger(PdfToImagesUtil.class);/*** 转换全部的pdf* @param fileAddress 文件地址* @param filename PDF文件名* @param type 图片类型*/public static boolean pdf2png(String fileAddress,String filename,String type) {boolean isSuccess = false;// 将pdf装图片 并且自定义图片得格式大小File file = new File(fileAddress + "/" + filename + ".pdf");try {PDDocument pDoc = PDDocument.load(file);PDFRenderer renderer = new PDFRenderer(pDoc);BufferedImage image = renderer.renderImageWithDPI(0, 144); // Windows native DPI// BufferedImage srcImage = resize(image, 240, 240);//产生缩略图isSuccess = ImageIO.write(image, type, new File(fileAddress + "/" + filename + "." + type));pDoc.close();return isSuccess;} catch (IOException e) {logger.error("pdf转换为图片失败:", e);return isSuccess;}}/***自由确定起始页和终止页* @param fileAddress 文件地址* @param filename pdf文件名* @param indexOfStart 开始页 开始转换的页码,从0开始* @param indexOfEnd 结束页 停止转换的页码,-1为全部* @param type 图片类型*/public static void pdf2png(String fileAddress,String filename,int indexOfStart,int indexOfEnd,String type) {// 将pdf装图片 并且自定义图片得格式大小File file = new File(fileAddress+"\\"+filename+".pdf");try {PDDocument doc = PDDocument.load(file);PDFRenderer renderer = new PDFRenderer(doc);int pageCount = doc.getNumberOfPages();for (int i = indexOfStart; i < indexOfEnd; i++) {BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPI// BufferedImage srcImage = resize(image, 240, 240);//产生缩略图ImageIO.write(image, type, new File(fileAddress+"\\"+filename+"_"+(i+1)+"."+type));}} catch (IOException e) {logger.error("pdf转换为图片失败:", e);}}}
4.开始解决PDF转图片不能显示中文的问题。
1.在项目里建一个与源码包名一样的路径及文件FontMapperImpl.java
说明:个人是把字体库和pdf模板文件放在resources下,放在此文件夹下,maven还需再加一个配置,来防止maven打包时将字体库和pdf模板文件编码打包,导致字体包和pdf模板被编译转码破坏不可用。
2.复写源码的FontMapperImpl.java文件
代码如下:
package org.apache.pdfbox.pdmodel.font;import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;import org.apache.fontbox.FontBoxFont;
import org.apache.fontbox.ttf.OpenTypeFont;
import org.apache.fontbox.ttf.TTFParser;
import org.apache.fontbox.ttf.TrueTypeFont;
import org.apache.fontbox.type1.Type1Font;final class FontMapperImpl implements FontMapper {private static final FontCache fontCache = new FontCache();private FontProvider fontProvider;private Map<String, FontInfo> fontInfoByName;private final TrueTypeFont lastResortFont;private final Map<String, List<String>> substitutes = new HashMap();FontMapperImpl() {this.substitutes.put("Courier", Arrays.asList("CourierNew", "CourierNewPSMT", "LiberationMono", "NimbusMonL-Regu"));this.substitutes.put("Courier-Bold", Arrays.asList("CourierNewPS-BoldMT", "CourierNew-Bold", "LiberationMono-Bold", "NimbusMonL-Bold"));this.substitutes.put("Courier-Oblique", Arrays.asList("CourierNewPS-ItalicMT", "CourierNew-Italic", "LiberationMono-Italic", "NimbusMonL-ReguObli"));this.substitutes.put("Courier-BoldOblique", Arrays.asList("CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", "LiberationMono-BoldItalic", "NimbusMonL-BoldObli"));this.substitutes.put("Helvetica", Arrays.asList("ArialMT", "Arial", "LiberationSans", "NimbusSanL-Regu"));this.substitutes.put("Helvetica-Bold", Arrays.asList("Arial-BoldMT", "Arial-Bold", "LiberationSans-Bold", "NimbusSanL-Bold"));this.substitutes.put("Helvetica-Oblique", Arrays.asList("Arial-ItalicMT", "Arial-Italic", "Helvetica-Italic", "LiberationSans-Italic", "NimbusSanL-ReguItal"));this.substitutes.put("Helvetica-BoldOblique", Arrays.asList("Arial-BoldItalicMT", "Helvetica-BoldItalic", "LiberationSans-BoldItalic", "NimbusSanL-BoldItal"));this.substitutes.put("Times-Roman", Arrays.asList("TimesNewRomanPSMT", "TimesNewRoman", "TimesNewRomanPS", "LiberationSerif", "NimbusRomNo9L-Regu"));this.substitutes.put("Times-Bold", Arrays.asList("TimesNewRomanPS-BoldMT", "TimesNewRomanPS-Bold", "TimesNewRoman-Bold", "LiberationSerif-Bold", "NimbusRomNo9L-Medi"));this.substitutes.put("Times-Italic", Arrays.asList("TimesNewRomanPS-ItalicMT", "TimesNewRomanPS-Italic", "TimesNewRoman-Italic", "LiberationSerif-Italic", "NimbusRomNo9L-ReguItal"));this.substitutes.put("Times-BoldItalic", Arrays.asList("TimesNewRomanPS-BoldItalicMT", "TimesNewRomanPS-BoldItalic", "TimesNewRoman-BoldItalic", "LiberationSerif-BoldItalic", "NimbusRomNo9L-MediItal"));this.substitutes.put("Symbol", Arrays.asList("Symbol", "SymbolMT", "StandardSymL"));this.substitutes.put("ZapfDingbats", Arrays.asList("ZapfDingbatsITC", "Dingbats", "MS-Gothic"));this.substitutes.put("STSong-Light", Arrays.asList("STSONG-light","DengXian"));//自己加的Iterator var1 = Standard14Fonts.getNames().iterator();while(var1.hasNext()) {String baseName = (String)var1.next();if (!this.substitutes.containsKey(baseName)) {String mappedName = Standard14Fonts.getMappedFontName(baseName);this.substitutes.put(baseName, this.copySubstitutes(mappedName));}}try {String ttfName = "/pdftemplates/font/STSONG-light.ttf";InputStream ttfStream = FontMapperImpl.class.getResourceAsStream(ttfName);TTFParser ttfParser = new TTFParser();System.out.println("----->>读取自定义字体:" + ttfName);this.lastResortFont = ttfParser.parse(ttfStream);} catch (IOException var5) {throw new RuntimeException(var5);}}public synchronized void setProvider(FontProvider fontProvider) {this.fontInfoByName = this.createFontInfoByName(fontProvider.getFontInfo());this.fontProvider = fontProvider;}public synchronized FontProvider getProvider() {if (this.fontProvider == null) {this.setProvider(FontMapperImpl.DefaultFontProvider.INSTANCE);}return this.fontProvider;}public FontCache getFontCache() {return fontCache;}private Map<String, FontInfo> createFontInfoByName(List<? extends FontInfo> fontInfoList) {Map<String, FontInfo> map = new LinkedHashMap();Iterator var3 = fontInfoList.iterator();while(var3.hasNext()) {FontInfo info = (FontInfo)var3.next();Iterator var5 = this.getPostScriptNames(info.getPostScriptName()).iterator();while(var5.hasNext()) {String name = (String)var5.next();map.put(name, info);}}return map;}private Set<String> getPostScriptNames(String postScriptName) {Set<String> names = new HashSet();names.add(postScriptName);names.add(postScriptName.replaceAll("-", ""));return names;}private List<String> copySubstitutes(String postScriptName) {return new ArrayList((Collection)this.substitutes.get(postScriptName));}public void addSubstitute(String match, String replace) {if (!this.substitutes.containsKey(match)) {this.substitutes.put(match, new ArrayList());}((List)this.substitutes.get(match)).add(replace);}private List<String> getSubstitutes(String postScriptName) {List<String> subs = (List)this.substitutes.get(postScriptName.replaceAll(" ", ""));return subs != null ? subs : Collections.emptyList();}private String getFallbackFontName(PDFontDescriptor fontDescriptor) {String fontName;if (fontDescriptor != null) {boolean isBold = false;String name = fontDescriptor.getFontName();if (name != null) {String lower = fontDescriptor.getFontName().toLowerCase();isBold = lower.contains("bold") || lower.contains("black") || lower.contains("heavy");}if (fontDescriptor.isFixedPitch()) {fontName = "Courier";if (isBold && fontDescriptor.isItalic()) {fontName = fontName + "-BoldOblique";} else if (isBold) {fontName = fontName + "-Bold";} else if (fontDescriptor.isItalic()) {fontName = fontName + "-Oblique";}} else if (fontDescriptor.isSerif()) {fontName = "Times";if (isBold && fontDescriptor.isItalic()) {fontName = fontName + "-BoldItalic";} else if (isBold) {fontName = fontName + "-Bold";} else if (fontDescriptor.isItalic()) {fontName = fontName + "-Italic";} else {fontName = fontName + "-Roman";}} else {fontName = "Helvetica";if (isBold && fontDescriptor.isItalic()) {fontName = fontName + "-BoldOblique";} else if (isBold) {fontName = fontName + "-Bold";} else if (fontDescriptor.isItalic()) {fontName = fontName + "-Oblique";}}} else {fontName = "Times-Roman";}return fontName;}public FontMapping<TrueTypeFont> getTrueTypeFont(String baseFont, PDFontDescriptor fontDescriptor) {TrueTypeFont ttf = (TrueTypeFont)this.findFont(FontFormat.TTF, baseFont);if (ttf != null) {return new FontMapping(ttf, false);} else {String fontName = this.getFallbackFontName(fontDescriptor);ttf = (TrueTypeFont)this.findFont(FontFormat.TTF, fontName);if (ttf == null) {ttf = this.lastResortFont;}return new FontMapping(ttf, true);}}public FontMapping<FontBoxFont> getFontBoxFont(String baseFont, PDFontDescriptor fontDescriptor) {FontBoxFont font = this.findFontBoxFont(baseFont);if (font != null) {return new FontMapping(font, false);} else {String fallbackName = this.getFallbackFontName(fontDescriptor);FontBoxFont font2 = this.findFontBoxFont(fallbackName);if (font2 == null) {font = this.lastResortFont;}else {font= font2;}return new FontMapping((FontBoxFont)font, true);}}private FontBoxFont findFontBoxFont(String postScriptName) {Type1Font t1 = (Type1Font)this.findFont(FontFormat.PFB, postScriptName);if (t1 != null) {return t1;} else {TrueTypeFont ttf = (TrueTypeFont)this.findFont(FontFormat.TTF, postScriptName);if (ttf != null) {return ttf;} else {OpenTypeFont otf = (OpenTypeFont)this.findFont(FontFormat.OTF, postScriptName);return otf != null ? otf : null;}}}private FontBoxFont findFont(FontFormat format, String postScriptName) {if (postScriptName == null) {return null;} else {if (this.fontProvider == null) {this.getProvider();}FontInfo info = this.getFont(format, postScriptName);if (info != null) {return info.getFont();} else {info = this.getFont(format, postScriptName.replaceAll("-", ""));if (info != null) {return info.getFont();} else {Iterator var4 = this.getSubstitutes(postScriptName).iterator();do {if (!var4.hasNext()) {info = this.getFont(format, postScriptName.replaceAll(",", "-"));if (info != null) {return info.getFont();}info = this.getFont(format, postScriptName + "-Regular");if (info != null) {return info.getFont();}return null;}String substituteName = (String)var4.next();info = this.getFont(format, substituteName);} while(info == null);return info.getFont();}}}}private FontInfo getFont(FontFormat format, String postScriptName) {if (postScriptName.contains("+")) {postScriptName = postScriptName.substring(postScriptName.indexOf(43) + 1);}FontInfo info = (FontInfo)this.fontInfoByName.get(postScriptName);return info != null && info.getFormat() == format ? info : null;}public CIDFontMapping getCIDFont(String baseFont, PDFontDescriptor fontDescriptor, PDCIDSystemInfo cidSystemInfo) {OpenTypeFont otf1 = (OpenTypeFont)this.findFont(FontFormat.OTF, baseFont);if (otf1 != null) {return new CIDFontMapping(otf1, (FontBoxFont)null, false);} else {TrueTypeFont ttf = (TrueTypeFont)this.findFont(FontFormat.TTF, baseFont);if (ttf != null) {return new CIDFontMapping((OpenTypeFont)null, ttf, false);} else {/*if (cidSystemInfo != null) {String collection = cidSystemInfo.getRegistry() + "-" + cidSystemInfo.getOrdering();if (collection.equals("Adobe-GB1") || collection.equals("Adobe-CNS1") || collection.equals("Adobe-Japan1") || collection.equals("Adobe-Korea1")) {PriorityQueue<FontMapperImpl.FontMatch> queue = this.getFontMatches(fontDescriptor, cidSystemInfo);FontMapperImpl.FontMatch bestMatch = (FontMapperImpl.FontMatch)queue.poll();if (bestMatch != null) {FontBoxFont font = bestMatch.info.getFont();if (font instanceof OpenTypeFont) {return new CIDFontMapping((OpenTypeFont)font, (FontBoxFont)null, true);}if (font != null) {return new CIDFontMapping((OpenTypeFont)null, font, true);}}}}*/return new CIDFontMapping((OpenTypeFont)null, this.lastResortFont, true);}}}private PriorityQueue<FontMapperImpl.FontMatch> getFontMatches(PDFontDescriptor fontDescriptor, PDCIDSystemInfo cidSystemInfo) {PriorityQueue<FontMapperImpl.FontMatch> queue = new PriorityQueue(20);Iterator var4 = this.fontInfoByName.values().iterator();while(true) {FontMapperImpl.FontMatch match;while(true) {FontInfo info;do {if (!var4.hasNext()) {return queue;}info = (FontInfo)var4.next();} while(cidSystemInfo != null && !this.isCharSetMatch(cidSystemInfo, info));match = new FontMapperImpl.FontMatch(info);if (fontDescriptor.getPanose() != null && info.getPanose() != null) {PDPanoseClassification panose = fontDescriptor.getPanose().getPanose();if (panose.getFamilyKind() != info.getPanose().getFamilyKind()) {break;}if (panose.getFamilyKind() == 0 && (info.getPostScriptName().toLowerCase().contains("barcode") || info.getPostScriptName().startsWith("Code")) && !this.probablyBarcodeFont(fontDescriptor)) {continue;}if (panose.getSerifStyle() == info.getPanose().getSerifStyle()) {match.score += 2.0D;} else if (panose.getSerifStyle() >= 2 && panose.getSerifStyle() <= 5 && info.getPanose().getSerifStyle() >= 2 && info.getPanose().getSerifStyle() <= 5) {++match.score;} else if (panose.getSerifStyle() >= 11 && panose.getSerifStyle() <= 13 && info.getPanose().getSerifStyle() >= 11 && info.getPanose().getSerifStyle() <= 13) {++match.score;} else if (panose.getSerifStyle() != 0 && info.getPanose().getSerifStyle() != 0) {--match.score;}int weight = info.getPanose().getWeight();int weightClass = info.getWeightClassAsPanose();if (Math.abs(weight - weightClass) > 2) {weight = weightClass;}if (panose.getWeight() == weight) {match.score += 2.0D;} else if (panose.getWeight() > 1 && weight > 1) {float dist = (float)Math.abs(panose.getWeight() - weight);match.score += 1.0D - (double)dist * 0.5D;}break;}if (fontDescriptor.getFontWeight() > 0.0F && info.getWeightClass() > 0) {float dist = Math.abs(fontDescriptor.getFontWeight() - (float)info.getWeightClass());match.score += 1.0D - (double)(dist / 100.0F) * 0.5D;}break;}queue.add(match);}}private boolean probablyBarcodeFont(PDFontDescriptor fontDescriptor) {String ff = fontDescriptor.getFontFamily();if (ff == null) {ff = "";}String fn = fontDescriptor.getFontName();if (fn == null) {fn = "";}return ff.startsWith("Code") || ff.toLowerCase().contains("barcode") || fn.startsWith("Code") || fn.toLowerCase().contains("barcode");}private boolean isCharSetMatch(PDCIDSystemInfo cidSystemInfo, FontInfo info) {if (info.getCIDSystemInfo() != null) {return info.getCIDSystemInfo().getRegistry().equals(cidSystemInfo.getRegistry()) && info.getCIDSystemInfo().getOrdering().equals(cidSystemInfo.getOrdering());} else {long codePageRange = info.getCodePageRange();long JIS_JAPAN = 131072L;long CHINESE_SIMPLIFIED = 262144L;long KOREAN_WANSUNG = 524288L;long CHINESE_TRADITIONAL = 1048576L;long KOREAN_JOHAB = 2097152L;if (cidSystemInfo.getOrdering().equals("GB1") && (codePageRange & CHINESE_SIMPLIFIED) == CHINESE_SIMPLIFIED) {return true;} else if (cidSystemInfo.getOrdering().equals("CNS1") && (codePageRange & CHINESE_TRADITIONAL) == CHINESE_TRADITIONAL) {return true;} else if (cidSystemInfo.getOrdering().equals("Japan1") && (codePageRange & JIS_JAPAN) == JIS_JAPAN) {return true;} else {return cidSystemInfo.getOrdering().equals("Korea1") && (codePageRange & KOREAN_WANSUNG) == KOREAN_WANSUNG || (codePageRange & KOREAN_JOHAB) == KOREAN_JOHAB;}}}private FontMapperImpl.FontMatch printMatches(PriorityQueue<FontMapperImpl.FontMatch> queue) {FontMapperImpl.FontMatch bestMatch = (FontMapperImpl.FontMatch)queue.peek();System.out.println("-------");while(!queue.isEmpty()) {FontMapperImpl.FontMatch match = (FontMapperImpl.FontMatch)queue.poll();FontInfo info = match.info;System.out.println(match.score + " | " + info.getMacStyle() + " " + info.getFamilyClass() + " " + info.getPanose() + " " + info.getCIDSystemInfo() + " " + info.getPostScriptName() + " " + info.getFormat());}System.out.println("-------");return bestMatch;}private static class FontMatch implements Comparable<FontMapperImpl.FontMatch> {double score;final FontInfo info;FontMatch(FontInfo info) {this.info = info;}public int compareTo(FontMapperImpl.FontMatch match) {return Double.compare(match.score, this.score);}}private static class DefaultFontProvider {private static final FontProvider INSTANCE;private DefaultFontProvider() {}static {INSTANCE = new FileSystemFontProvider(FontMapperImpl.fontCache);}}
}
说明:中间还注释了一段代码,即没有字体库会使用系统默认字体库的代码。各位看情况决定在各自系统中是否放开。
5.最终效果如下:
总结说明
1.个人非常不建议使用这种方式,因为此方式,一是乱了自己项目的结构,项目结构很难看,要是再遇到其他问题也是这么做,加各种开源项目的包名路径,大家可以想象最终项目结构了;二是需要在项目里使用字体包,字体包也很大,一般都是10~15m左右,若是使用多个字体包,那最后打的包也很大。好处是不是所有服务器都有对应的字体包,那运行在各环境也能正常显示中文字体,可以不需要麻烦运维在生产上去安装各种字体包(个人也是因为这原因才使用这种方式)。
2.个人推荐的方式,一是重写需要修改的源码部分并重新打包,使用特殊的版本号上传至maven私服,仅供自己项目使用;二是在对应系统环境安装所需字体包,这种亦是最方便的。
生成PDF并转图片丢失中文显示的问题相关推荐
- 使用itextpdf生成pdf,设置图片和中文
依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artif ...
- java中生成pdf,插入图片,页眉、页脚、表格
全栈工程师开发手册 (作者:栾鹏) java教程全解 java中生成pdf,插入图片,页眉.页脚.表格 import com.lowagie.text.*; import com.lowagie.te ...
- JAVA 使用Itext模板生成pdf,解决图片插入,文本域超出字体缩放,半自动换行
1.前言 前一段时间遇到一个制作Pdf的业务,自己下来摸索了一下,基本上解决.将其中遇到的几个问题及解决方法做以记录,仅供大家参考. 首先在这里对于刚接触该类型业务的同学说明下,ItexPdf支持使用 ...
- itext生成pdf文档加载中文字体解决方法
最近接手一个任务,在网页中通过用户的输入的信息生成一个pdf文档并且下载到用户本地.iText是用于生成PDF文档的一个java类库.通过iText不仅可以生成PDF或rtf的文档,而且可以将XML. ...
- python 生成pdf 文字和图片_Python系列—PDF文本与图片抽取
PDF是人们日常使用最多的跨平台文档.其是一种用独立于应用程序.硬件.操作系统的方式呈现文档的文件格式.每个PDF文件包含固定布局的平面文档的完整描述,包括文本.字形.图形及其他需要显示的信息.具有良 ...
- FreeMarker 模板生成 PDF电子凭证/图片
一.场景 在某些业务场景中,需要提供相关的电子凭证,比如网银/支付宝中转账的电子回单,签约的电子合同等.方便用户查看,下载,打印.目前常用的解决方案是,把相关数据信息,生成对应的pdf文件返回给用户. ...
- 图片如何生成pdf格式?图片怎么生成pdf格式文件?
图片如何生成pdf格式?平时的工作中我们在处理pdf文件的时候,难免会遇到各种各样的问题.有时候我们需要把图片生成pdf.可能还有些小伙伴不知道该怎么做,图片生成pdf其实很简单,下面给大家分享一个图 ...
- 使用Tcpdf生成pdf时没有图片的问题
在生成pdf插入图片时使用了 $pdf->image方法进行渲染,发现图片渲染不了,解决方法是在用image方法之前使用 $pdf->AddPage(); 但是这样会使图片和文字重叠,所以 ...
- qt生成pdf(用图片qgrabwidget抓取图片生成PDF;用文字生成pdf)
1.用图片生成PDF: QPrinter printerPixmap(QPrinter::HighResolution);printerPixmap.setPageSize(QPrinter::A4) ...
最新文章
- 深入分析Java单例模式的各种方案
- 2020牛客多校第5场B-Graph完全图异或最小生成树
- 块级元素 Vs 内联元素
- 4E4 models
- [BZOJ 1047] [HAOI2007] 理想的正方形 【单调队列】
- Android—内存泄漏、GC及LeakCanary源码解析
- linux命令编写,编写简单的linux命令
- ubuntu 10.4非法关机后上不了网
- python 保留顺序去重_Python入门很简单,只要掌握3456点
- 【语音去噪】基于matlab GUI傅立叶变换语音降噪混频【含Matlab源码 297期】
- html embed自动播放,embed嵌入多个优酷视频并自动播放
- WEBSHELL 提权方法总结
- 第一章 核磁共振的物理学基础
- Mr. Panda and Fantastic Beasts(EC2016 后缀数组)
- JMF(java media framework)综述
- 生育指南(写给临产准妈妈)
- 如何用TensorFlow图像处理函数裁剪图像?
- Java基础361问第5问——equals和==的区别
- 终于明白了异地恋为什么那么难
- 这应该是关于GPS定位写得最详实清晰的文章之一