该工具使用了fontbox。
1、解决提示:pdfbox font STSong-Light转换图片缺少字体导致乱码问题
2、不需要设置系统的字体,如可以安装系统字体就不需要使用该方法设置字体的
引入依赖
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>fontbox</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
自定义继承实现了pdfbox字体信息类,用于实现设置字体信息
package cn.xx.common.pdf;
import cn.xx.RuntimeException;
import org.apache.fontbox.FontBoxFont;
import org.apache.fontbox.ttf.OTFParser;
import org.apache.fontbox.ttf.TTFParser;
import org.apache.fontbox.ttf.TrueTypeCollection;
import org.apache.fontbox.ttf.TrueTypeFont;
import org.apache.fontbox.type1.Type1Font;
import org.apache.pdfbox.pdmodel.font.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 自定义pdf字体信息
* @author xwt
* @date 2022/10/19 17:23
*/
public class MyFontInfo extends FontInfo{
private final String postScriptName;
private final FontFormat format;
private final CIDSystemInfo cidSystemInfo;
private final int usWeightClass;
private final int sFamilyClass;
private final int ulCodePageRange1;
private final int ulCodePageRange2;
private final int macStyle;
private final File file;
private final FontBoxFont boxFont;
public MyFontInfo(File file, FontFormat format, String postScriptName,
CIDSystemInfo cidSystemInfo, int usWeightClass, int sFamilyClass,
int ulCodePageRange1, int ulCodePageRange2, int macStyle)
{
this.file = file;
this.format = format;
this.postScriptName = postScriptName;
this.cidSystemInfo = cidSystemInfo;
this.usWeightClass = usWeightClass;
this.sFamilyClass = sFamilyClass;
this.ulCodePageRange1 = ulCodePageRange1;
this.ulCodePageRange2 = ulCodePageRange2;
this.macStyle = macStyle;
try {
switch (format) {
case PFB: this.boxFont = Type1Font.createWithPFB(new FileInputStream(file)); break;
case OTF: this.boxFont = new OTFParser(false, true).parse(file); break;
case TTF: this.boxFont = new TTFParser(false, true).parse(file); break;
default: throw new CustomMsgException("can't happen");
}
}catch (IOException e){
e.printStackTrace();
throw new RuntimeException("pdfbox自定义字体失败!");
}
}
public MyFontInfo(File file, FontFormat format, String postScriptName) {
this(file, format, postScriptName, null, 0, 0, 0, 0, 0);
}
@Override
public String getPostScriptName()
{
return postScriptName;
}
@Override
public FontFormat getFormat()
{
return format;
}
@Override
public CIDSystemInfo getCIDSystemInfo()
{
return cidSystemInfo;
}
/**
* {@inheritDoc}
* <p>
* The method returns null if there is there was an error opening the font.
*
*/
@Override
public FontBoxFont getFont(){
return this.boxFont;
}
@Override
public int getFamilyClass()
{
return sFamilyClass;
}
@Override
public int getWeightClass()
{
return usWeightClass;
}
@Override
public int getCodePageRange1()
{
return ulCodePageRange1;
}
@Override
public int getCodePageRange2()
{
return ulCodePageRange2;
}
@Override
public int getMacStyle()
{
return macStyle;
}
@Override
public PDPanoseClassification getPanose()
{
return null;
}
@Override
public String toString()
{
return super.toString() + " " + file;
}
}
pdf工具类
package cn.xxx.utils;
import cn.xx.RuntimeException;
import cn.xx.FileExtEnum;
import cn.xxx.MyFontInfo; // 自定义字体信息类
import cn.hutool.core.text.CharSequenceUtil;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.font.*;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
/**
* pdf工具类
*
* @author xwt
* @date 2022/12/7 11:00
*/
public class PdfUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(PdfUtils.class);
public static final Base64.Decoder DECODER = Base64.getDecoder();
private static Map<String, FontInfo> fontInfoMap = null;
private static final String TTC_TYPE = "TTC";
private static final String OTC_TYPE = "OTC";
/***
* pdfBase64转图片,返回base64编码集合
*
* @param pdfBase64 pdf类型base64
* @return 图片base64
*/
public static List<String> pdfToImageBase64(String pdfBase64){
List<String> list = new ArrayList<>();
if (CharSequenceUtil.isEmpty(pdfBase64)){
return list;
}
return ImageUtils.imageToBase64(PdfUtils.pdfToImage(pdfBase64));
}
/***
* pdfBase64转图片,返回图片对象
*
* @param pdfBase64 pdf类型base64
* @return BufferedImage
*/
public static List<BufferedImage> pdfToImage(String pdfBase64){
List<BufferedImage> list = new ArrayList<>();
if (CharSequenceUtil.isEmpty(pdfBase64)){
return list;
}
byte[] decode = DECODER.decode(pdfBase64.getBytes(StandardCharsets.UTF_8));
try(
ByteArrayInputStream stream = new ByteArrayInputStream(decode);
// 加载解析PDF文件
PDDocument doc = PDDocument.load(stream);
) {
// 业务处理
PDFRenderer pdfRenderer = new PDFRenderer(doc);
PDPageTree pages = doc.getPages();
int pageCount = pages.getCount();
for (int i = 0; i < pageCount; i++) {
list.add(pdfRenderer.renderImageWithDPI(i, 200));
}
} catch (Exception e) {
LOGGER.error("pdfBase64转图片异常", e);
}
return list;
}
/***
* 设置字体
* @param fontFormat 字体文件类型
* @param fontName 字体名称
* @param file 字体文件
*/
public static void setFonts(FontFormat fontFormat, String fontName, File file){
FontInfo fontInfo = getFontInfoMap().get(fontName);
if(fontInfo != null){
LOGGER.warn("pdfFont添加字体已经存在");
return;
}
// 后缀
LOGGER.info("pdfFont 添加字体{}", file.getName());
String suffix = FileUtil.getSuffix(file);
if(TTC_TYPE.equalsIgnoreCase(suffix) || OTC_TYPE.equalsIgnoreCase(suffix)){
try(TrueTypeCollection trueTypeCollection = new TrueTypeCollection(file);) {
trueTypeCollection.processAllFonts(trueTypeFont -> addTrueTypeFontImpl(trueTypeFont, file));
} catch (IOException e) {
LOGGER.warn("无法加载字体:{}", file.getAbsolutePath());
}
return;
}
getFontInfoMap().put(fontName, new MyFontInfo(file, fontFormat, fontName));
}
/***
* 设置字体,扫描目录下所有字体
* @param dir 目录
*/
public static void setFonts(File dir){
if (dir == null || !dir.exists() || !dir.isDirectory()) {
LOGGER.warn("pdfFont添加字体为空");
return;
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
setFonts(file);
} else {
String fileName = file.getName();
// 后缀
String suffix = FileUtil.getSuffix(fileName);
// 文件名称,不带后缀
String prefix = FileUtil.getPrefix(fileName);
if(FontFormat.OTF.name().equalsIgnoreCase(suffix)){
setFonts(FontFormat.OTF, prefix, file);
}else if(FontFormat.TTF.name().equalsIgnoreCase(suffix) || TTC_TYPE.equalsIgnoreCase(suffix) || OTC_TYPE.equalsIgnoreCase(suffix)) {
setFonts(FontFormat.TTF, prefix, file);
}else if(FontFormat.PFB.name().equalsIgnoreCase(suffix)) {
setFonts(FontFormat.PFB, prefix, file);
}else{
LOGGER.warn("无法识别字体:{}", file.getAbsolutePath());
}
}
}
}
/***
* 获取系统字体缓存
* @return 字体缓存
*/
private static Map<String, FontInfo> getFontInfoMap(){
if(fontInfoMap != null){
return fontInfoMap;
}
FontMapper instance = FontMappers.instance();
// 初始化加载系统字体
instance.getCIDFont("STSong-Light", null, null);
Class<? extends FontMapper> aClass = instance.getClass();
try {
Field field = aClass.getDeclaredField("fontInfoByName");
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
// 获取系统字体缓存
fontInfoMap = (Map<String, FontInfo>) field.get(instance);
}catch (Exception e){
LOGGER.error("PdfUtils初始化异常", e);
throw new CustomMsgException("PdfUtils初始化异常");
}
return fontInfoMap;
}
/***
* 将OTF或TTF字体添加到文件缓存
* @param ttf 字体
* @param file 字体文件
* @throws IOException 异常
*/
private static void addTrueTypeFontImpl(TrueTypeFont ttf, File file) throws IOException {
if(ttf.getName() != null){
if (ttf.getHeader() == null) {
getFontInfoMap().put(ttf.getName(), new MyFontInfo(file, FontFormat.TTF, ttf.getName()));
return;
}
int macStyle = ttf.getHeader().getMacStyle();
int sFamilyClass = -1;
int usWeightClass = -1;
int ulCodePageRange1 = 0;
int ulCodePageRange2 = 0;
// Apple's AAT fonts don't have an OS/2 table
if (ttf.getOS2Windows() != null)
{
sFamilyClass = ttf.getOS2Windows().getFamilyClass();
usWeightClass = ttf.getOS2Windows().getWeightClass();
ulCodePageRange1 = (int)ttf.getOS2Windows().getCodePageRange1();
ulCodePageRange2 = (int)ttf.getOS2Windows().getCodePageRange2();
}
CIDSystemInfo ros = null;
String registry = null;
String ordering = null;
int supplement = 0;
FontFormat fontFormat;
if (ttf instanceof OpenTypeFont && ((OpenTypeFont)ttf).isPostScript()) {
CFFFont cff = ((OpenTypeFont)ttf).getCFF().getFont();
fontFormat = FontFormat.OTF;
if (cff instanceof CFFCIDFont) {
CFFCIDFont cidFont = (CFFCIDFont)cff;
registry = cidFont.getRegistry();
ordering = cidFont.getOrdering();
supplement = cidFont.getSupplement();
}
} else {
fontFormat = FontFormat.TTF;
if (ttf.getTableMap().containsKey("gcid")) {
// Apple's AAT fonts have a "gcid" table with CID info
byte[] bytes = ttf.getTableBytes(ttf.getTableMap().get("gcid"));
String reg = new String(bytes, 10, 64, Charsets.US_ASCII);
registry = reg.substring(0, reg.indexOf('\0'));
String ord = new String(bytes, 76, 64, Charsets.US_ASCII);
ordering = ord.substring(0, ord.indexOf('\0'));
supplement = bytes[140] << 8 & bytes[141];
}
}
try {
Constructor<CIDSystemInfo> constructor = CIDSystemInfo.class.getDeclaredConstructor(String.class, String.class, int.class);
if ((!Modifier.isPublic(constructor.getModifiers()) || !Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) || Modifier.isFinal(constructor.getModifiers())) && !constructor.isAccessible()) {
constructor.setAccessible(true);
}
ros = constructor.newInstance(registry, ordering, supplement);
}catch (Exception e){
e.printStackTrace();
}
getFontInfoMap().put(ttf.getName(), new MyFontInfo(file, fontFormat, ttf.getName(), ros, usWeightClass, sFamilyClass, ulCodePageRange1, ulCodePageRange2,
macStyle));
}
}
}
ImageUtils发布专栏的开发工具里面:https://mp.csdn.net/mp_blog/creation/editor/129165385
使用示例1,单文件,建议设置为单例
//设置字体 1、字体类型ttf\OTF\PFB,2、字体名称, 3、字体文件
// 尽量只设置一次字体,设置一次后会放到缓存中
PdfUtils.setFonts(FontFormat.TTF, "STSong-Light", new File("D:\\ttf\\STSong-Light.ttf"));
// 之后就可以使用转换为图片操作,不用再次重新设置
PdfUtils.pdfToImageBase64("");
使用示例2,目录下所有字体文件
// 1、目录下多文件字体使用
File file = new File("src/main/resources/fonts");
PdfUtils.setFonts(file);
// 注意spring boot,字体文件获取问题,下面有自用的解决办法
注意spring boot,jar内字体目录文件获取问题,下面是一种方法
http://t.csdn.cn/rZ5YD
然后使用获取目录下文件,传入设置字体
Application.class 文件所在jar包内class文件即可
File file = JarFileLoaderUtils.getResourcesFile("fonts", Application.class);
//设置字体 1、字体类型ttf\OTF\PFB,2、字体名称, 3、字体文件
// 尽量只设置一次字体,设置一次后会放到缓存中
PdfUtils.setFonts(FontFormat.TTF, "STSong-Light", file);
// 之后就可以使用转换为图片操作,不用再次重新设置
PdfUtils.pdfToImageBase64("");