前端打印功能(vue +springboot)

后端

后端

依赖

依赖 一个是用模版生成对应的pdf,一个是用来将pdf转成图片需要的

      <!--打印的-->
        <dependency>
            <groupId>net.sf.jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>3.0.0</version>
        </dependency>

所需的文件资源 jasper是模版,然后font是pdf转图片的时候需要的字体

由于有资源 所以pom配置俩包下面不允许压缩

    <build>
        <resources>
            <resource>
                <targetPath>${project.build.directory}/classes</targetPath>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.jasper</exclude>
                    <exclude>**/*.jrxml</exclude>
                    <exclude>**/*.TTF</exclude>
                </excludes>
            </resource>
            <resource>
                <targetPath>${project.build.directory}/classes</targetPath>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.jasper</include>
                    <include>**/*.jrxml</include>
                    <include>**/*.TTF</include>
                </includes>
            </resource>
        </resources>
    </build>

生成pdf的方法

用到的实体类

package com.xueyi.common.core.utils.print.dto;

import lombok.Data;

@Data
public class PdfInfoDto {
    private Integer height;
    private Integer width;
    private byte[] pdfBytes;
}

生成pdf的方法

package com.xueyi.common.core.utils.print;

import com.xueyi.common.core.utils.print.dto.PdfInfoDto;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimplePdfExporterConfiguration;
import org.springframework.core.io.ClassPathResource;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

public class PrintUtil {

    public static PdfInfoDto printPDF(String modelName, HashMap hashMap) throws IOException, JRException {
        // 获取模板路径
        ClassPathResource classPathResource = new ClassPathResource("/jasper/"+modelName+".jasper");


        // 获取模板输入流
        InputStream inputStream = classPathResource.getInputStream();
        // 读取模板
        JasperReport report = (JasperReport) JRLoader.loadObject(inputStream);
        //数据库数据填充报表
        JasperPrint jprint = JasperFillManager.fillReport(report, hashMap,new JREmptyDataSource());

       DefaultJasperReportsContext defaultJasperReportsContext=DefaultJasperReportsContext.getInstance();
//        defaultJasperReportsContext.setProperty("net.sf.jasperreports.fonts.STSong-Light", "path/to/STSong-Light.ttf");

        //创建导出对象
        JRPdfExporter exporter = new JRPdfExporter(defaultJasperReportsContext);

        //设置要导出的流
        exporter.setExporterInput(new SimpleExporterInput(jprint));
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));
        SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
        configuration.setCreatingBatchModeBookmarks(true);
        exporter.setConfiguration(configuration);
        // 导出pdf
        exporter.exportReport();
        byte[] bytes=byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();

        PdfInfoDto pdfInfo=new PdfInfoDto();
        pdfInfo.setPdfBytes(bytes);
        pdfInfo.setHeight(jprint.getPageHeight());
        pdfInfo.setWidth(jprint.getPageWidth());
        return pdfInfo;
    }

}

pdf转图片

用到的实体类

package com.xueyi.common.core.utils.print.dto;

import lombok.Data;

@Data
public class ImageInfoDto {
    private Integer height;
    private Integer width;
    private byte[] imageBytes;
}



package com.xueyi.common.core.utils.print;


import com.xueyi.common.core.utils.print.dto.ImageInfoDto;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Pdf2PngUtil {

    public static List<ImageInfoDto> pdf2png(byte[] bytes) throws IOException {
          ArrayList<ImageInfoDto> list=new ArrayList();
            PDDocument doc=Loader.loadPDF(bytes);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for (int i = 0; i < pageCount; i++) {
                // dpi为144,越高越清晰,转换越慢
                BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPI
                ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
                ImageIO.createImageOutputStream(byteArrayOutputStream);
                ImageIO.write(image, "png", byteArrayOutputStream);

                ImageInfoDto imageInfo=new ImageInfoDto();
                imageInfo.setHeight(image.getHeight());
                imageInfo.setWidth(image.getWidth());
                imageInfo.setImageBytes(byteArrayOutputStream.toByteArray());
                list.add(imageInfo);
                byteArrayOutputStream.close();
            }
            doc.close();
            return  list;
    }
}

使用(用的打印模版是带参数的 ,参数是aaa)

返回类

@Data
public class PrintDto {
    @Serial
    private static final long serialVersionUID = 1L;
    private String jasperName;
    private HashMap hashMap;
    private List<ImageInfoDto> imageInfoDtoList;
    private PdfInfoDto pdfInfoDto;
}

使用

    @PostMapping("/print")
    public AjaxResult print(@RequestBody TransSchedulerQuery transSchedulerQuery) throws IOException, JRException {
        PrintDto printDto=new PrintDto();
        printDto.setJasperName(transSchedulerQuery.getJasperName());
        HashMap hashMap=new HashMap();
        hashMap.put("aaa","你看得到吗?");
        printDto.setPdfInfoDto(PrintUtil.printPDF(transSchedulerQuery.getJasperName(),hashMap));
        printDto.setImageInfoDtoList(Pdf2PngUtil.pdf2png(printDto.getPdfInfoDto().getPdfBytes()));
        return  AjaxResult.success(printDto);
    }

总结

后端的思路其实很简单,就是用带参数的打印模版,然后把对应参数送进去生成pdf,由于前端需要图片和pdf两种,所以又把生成的pdf转图片生成了一下图片的list.然后不管事pdf和图片,都是直接把文件本身传递回去了,没有存到本地,用url的方法.直接把文件的byte数组传递到前端了(一般序列化的方式就是base64,所以json序列化的时候自动转好了的前端接到的是字符串形式的)

前端

前端其实很简单,就是把得到的文件信息拿到,转成blob形式,然后预览一下.唯一的问题就是拿到的字符串需要进行转码,从base64变回byte数组.因为要有预览的效果,所以一直尝试前端pdf转换然后要自定义打印啥的(Lodop),用了很多无用的代码,后续定下来打印就调用浏览器的打印啥的,那个打印按钮有打印前和打印后的回调函数的,已经满足需求了.

export function base64ToByteArray(base64String: string): Uint8Array {
  const binaryString = atob(base64String);
  const length = binaryString.length;
  const bytes = new Uint8Array(length);

  for (let i = 0; i < length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }

  return bytes;
}

页面

<template>
  <BasicModal  v-bind="$attrs" :width="ModalWidthEnum.COMMON" @register="registerModal" :canFullscreen="false" :title="variable.title" :height="400"   :okText="t('public.button.print')"   @ok="handleSubmit" >


    <div style="margin-top: 5px;text-align: center;">
      <span> {{t("public.describe.all")}}{{variable.imagesUrlList.length}}{{t("public.describe.page")}}{{t("public.describe.clickToTurnOver")}}</span>
    </div>

    <iframe ref="pdfFrameRef" :src="variable.pdfUrl" style="display: none"></iframe>

    <Row ref="rowRef">
<!--      <Col :span="1" >-->
<!--        <div style="height: 100%;">-->
<!--          <CaretLeftOutlined style="position: absolute;top: 45%"/>-->
<!--        </div>-->
<!--      </Col>-->
      <Col :span="24">
        <div style="border: black 1px solid;" >
          <Image :src="variable.imagesUrlList[0]" width="100%" :preview="variable.preview" @click="variable.preview = true"/>
          <div style="display: none">
            <Image.PreviewGroup :preview="{ visible:variable.preview, onVisibleChange: vis => (variable.preview = vis) }">
              <Image v-for="(item) in variable.imagesUrlList"  :src="item" width="100%" />
            </Image.PreviewGroup>
          </div>

        </div>

      </Col>
<!--      <Col :span="1" >-->
<!--        <div style="height: 100%;">-->
<!--          <CaretRightOutlined style="position: absolute;top: 45%"/>-->
<!--        </div>-->

<!--      </Col>-->

    </Row>










  </BasicModal>
</template>

<script setup lang="ts">
import {reactive, ref} from 'vue';
import {useMessage} from '@/hooks/web/useMessage';
import {BasicModal, useModalInner} from '@/components/Modal';
import {useI18n} from "@/hooks/web/useI18n";
import {ModalWidthEnum} from "@/enums";
import {Image,Row,Col,} from 'ant-design-vue';
// 定义国际化
const { t } = useI18n();
const emit = defineEmits(['success']);
const {createMessage} = useMessage();
const isUpdate = ref(true);
import {base64ToByteArray} from "@/utils/commonFunctions/commonFunctions";
/** 标题初始化 */
const variable = reactive<any>({
  ids: [],
  fatherParam:{},
  title:"",
  pdfUrl:null,
  pdfBlob:null,
  imagesUrlList:[],
  currentIndex:0,
  currentHeight:0,
  preview:false,
  afterPrintFunction:null
});

const pdfFrameRef=ref()
const rowRef=ref()


const [registerModal, {setModalProps, closeModal,changeOkLoading,changeLoading}] = useModalInner(async (data) => {

  variable.fatherParam=JSON.parse(JSON.stringify(data.fatherParam))
  variable.afterPrintFunction=data.afterPrintFunction
  variable.title=data.fatherParam.title
  variable.imagesUrlList=[]
  variable.currentIndex=0
  variable.preview=false
  //有图片的情况
  if (data.fatherParam.data.imageInfoDtoList?.length>0){
    variable.currentHeight=variable.fatherParam.data.imageInfoDtoList[0].height
    variable.pdfBlob = new Blob([base64ToByteArray(variable.fatherParam.data?.pdfInfoDto?.pdfBytes)], { type: 'application/pdf'});
    variable.pdfUrl= window.URL.createObjectURL(variable.pdfBlob)
    variable.fatherParam.data.imageInfoDtoList.forEach(item=>{
      const blob1 = new Blob([base64ToByteArray(item.imageBytes)], { type: 'image/png'});
      variable.imagesUrlList.push(window.URL.createObjectURL(blob1))
    })
  }


});


// const getPrintDevice = () => {
//   var loop = getLodop(); // 创建一个LODOP对象
//   let counter = loop.GET_PRINTER_COUNT(); // 获取打印机个数
//   //初始化下printNameList打印数组
//   variable.printDeviceList = [];
//   for (let i = 0; i < counter; i++) {
//     //将打印机存入printList数组中
//     variable.printDeviceList.push({name:loop.GET_PRINTER_NAME(i),value:i});
//   }
//
//   //获取默认打印机并设置
//   var defaultName =loop.GET_PRINTER_NAME(-1);
//   variable.printDeviceList.forEach(item=>{
//     if(item.name==defaultName){
//       variable.printDevice=item.value
//     }
//   })
// }

// const doPrint = () => {
//   var LODOP = getLodop(); // 创建一个LODOP对象
//   // LODOP.ADD_PRINT_IMAGE(0, 0, "<img>", "EMF", variable.pdfUrl);
//   LODOP.PRINT_INIT("打印控件功能演示_Lodop功能_按网址打印");
//   let base64="data:image/png;base64,"+variable.fatherParam.data.imageListByte[0]
//   LODOP.ADD_PRINT_IMAGE(0,0,"100%","100%",base64);
//   console.log(base64)
//   LODOP.PRINT();
// }


// // 创建 LODOP 实例
// function getLodop() {
//   let LODOP;
//   if (window.LODOP) {
//     LODOP = window.LODOP;
//   } else if (window.parent.LODOP) {
//     LODOP = window.parent.LODOP;
//   } else {
//     LODOP = (function () {
//       var LODOP = null;
//       var s = document.createElement('script');
//       s.src = '/LODOPfuncs.js';
//       s.id = 'LODOPfuncs';
//       s.type = 'text/javascript';
//       document.getElementsByTagName('head')[0].appendChild(s);
//       document.onpropertychange = function (e) {
//         if (e.propertyName === 'LODOP') {
//           LODOP = e.srcElement.LODOP;
//         }
//       };
//       return LODOP;
//     })();
//   }
//   return LODOP;
// }

/** 提交按钮 */
async function handleSubmit() {
  pdfFrameRef.value.contentWindow.print();
  window.onafterprint = afterPrint;
}

/**
 * 浏览器打印的回调
 */
const afterPrint = async () =>  {
  variable.afterPrintFunction()
}

</script>

效果

在pdf转图片的时候可能会出现乱码

1.模版那边选的字体就是有问题的

2.报错什么jvm没有字体啥的,说明机子上没有装对应的字体,装上就好了

3.虽然pdf有字体,但是pdf转图片的时候,没有找到对应的字体,出现了部分的口口之类的,因为模版那边虽然写着STSong-Light(这个是做模版的时候如果用中文,必须选这个),但是控件上可以选楷体啥的,于是两个对应不上,就让做模版的固定用STSong,然后我只要发现STSong-Light就是去找STSong

this.addSubstitutes("STSong-Light", new ArrayList(Arrays.asList("STSong")));

并且机子上安好STSong,就可以不乱码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.pdfbox.pdmodel.font;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
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.Locale;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
import org.apache.fontbox.util.autodetect.FontFileFinder;
import org.apache.pdfbox.io.RandomAccessReadBuffer;

/**
 * 告诉PDFBox如何找到字体,以及如何从字体中获取 glyphs。
 */

final class FontMapperImpl implements FontMapper {
    private static final Log LOG = LogFactory.getLog(FontMapperImpl.class);
    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.addSubstitutes("Courier", new ArrayList(Arrays.asList("CourierNew", "CourierNewPSMT", "LiberationMono", "NimbusMonL-Regu")));
        this.addSubstitutes("Courier-Bold", new ArrayList(Arrays.asList("CourierNewPS-BoldMT", "CourierNew-Bold", "LiberationMono-Bold", "NimbusMonL-Bold")));
        this.addSubstitutes("Courier-Oblique", new ArrayList(Arrays.asList("CourierNewPS-ItalicMT", "CourierNew-Italic", "LiberationMono-Italic", "NimbusMonL-ReguObli")));
        this.addSubstitutes("Courier-BoldOblique", new ArrayList(Arrays.asList("CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", "LiberationMono-BoldItalic", "NimbusMonL-BoldObli")));
        this.addSubstitutes("Helvetica", new ArrayList(Arrays.asList("ArialMT", "Arial", "LiberationSans", "NimbusSanL-Regu")));
        this.addSubstitutes("Helvetica-Bold", new ArrayList(Arrays.asList("Arial-BoldMT", "Arial-Bold", "LiberationSans-Bold", "NimbusSanL-Bold")));
        this.addSubstitutes("Helvetica-Oblique", new ArrayList(Arrays.asList("Arial-ItalicMT", "Arial-Italic", "Helvetica-Italic", "LiberationSans-Italic", "NimbusSanL-ReguItal")));
        this.addSubstitutes("Helvetica-BoldOblique", new ArrayList(Arrays.asList("Arial-BoldItalicMT", "Helvetica-BoldItalic", "LiberationSans-BoldItalic", "NimbusSanL-BoldItal")));
        this.addSubstitutes("Times-Roman", new ArrayList(Arrays.asList("TimesNewRomanPSMT", "TimesNewRoman", "TimesNewRomanPS", "LiberationSerif", "NimbusRomNo9L-Regu")));
        this.addSubstitutes("Times-Bold", new ArrayList(Arrays.asList("TimesNewRomanPS-BoldMT", "TimesNewRomanPS-Bold", "TimesNewRoman-Bold", "LiberationSerif-Bold", "NimbusRomNo9L-Medi")));
        this.addSubstitutes("Times-Italic", new ArrayList(Arrays.asList("TimesNewRomanPS-ItalicMT", "TimesNewRomanPS-Italic", "TimesNewRoman-Italic", "LiberationSerif-Italic", "NimbusRomNo9L-ReguItal")));
        this.addSubstitutes("Times-BoldItalic", new ArrayList(Arrays.asList("TimesNewRomanPS-BoldItalicMT", "TimesNewRomanPS-BoldItalic", "TimesNewRoman-BoldItalic", "LiberationSerif-BoldItalic", "NimbusRomNo9L-MediItal")));
        this.addSubstitutes("Symbol", new ArrayList(Arrays.asList("Symbol", "SymbolMT", "StandardSymL")));
        this.addSubstitutes("ZapfDingbats", new ArrayList(Arrays.asList("ZapfDingbatsITCbyBT-Regular", "ZapfDingbatsITC", "Dingbats", "MS-Gothic")));

        this.addSubstitutes("STSong-Light", new ArrayList(Arrays.asList("STSong")));

        //这是拿标准的14个字体让放进去
        Iterator var1 = Standard14Fonts.getNames().iterator();

        while(var1.hasNext()) {
            String baseName = (String)var1.next();
            if (this.getSubstitutes(baseName).isEmpty()) {
                Standard14Fonts.FontName mappedName = Standard14Fonts.getMappedFontName(baseName);
                this.addSubstitutes(baseName, new ArrayList(this.getSubstitutes(mappedName.getName())));
            }
        }




        try {
            String resourceName = "/org/apache/pdfbox/resources/ttf/LiberationSans-Regular.ttf";
            InputStream resourceAsStream = FontMapper.class.getResourceAsStream(resourceName);
            if (resourceAsStream == null) {
                throw new IOException("resource '" + resourceName + "' not found");
            } else {
                RandomAccessReadBuffer randomAccessReadBuffer = new RandomAccessReadBuffer(resourceAsStream);
                TTFParser ttfParser = new TTFParser();
                this.lastResortFont = ttfParser.parse(randomAccessReadBuffer);
            }
        } catch (IOException var5) {
            IOException e = var5;
            throw new RuntimeException(e);
        }
    }

    public synchronized void setProvider(FontProvider fontProvider) {
        this.fontInfoByName = this.createFontInfoByName(fontProvider.getFontInfo());
        this.fontProvider = fontProvider;
//        //获取本地的
//        fontProvider.getFontInfo().forEach((item)->{
//            System.out.println("baseName-------------------"+item);
//        });
    }

    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.toLowerCase(Locale.ENGLISH), info);
            }
        }

        return map;
    }

    private Set<String> getPostScriptNames(String postScriptName) {
        Set<String> names = new HashSet(2);
        names.add(postScriptName);
        names.add(postScriptName.replace("-", ""));
        return names;
    }

    public void addSubstitute(String match, String replace) {
        String lowerCaseMatch = match.toLowerCase(Locale.ENGLISH);
        ((List)this.substitutes.computeIfAbsent(lowerCaseMatch, (key) -> {
            return new ArrayList();
        })).add(replace);
    }

    private void addSubstitutes(String match, List<String> replacements) {
        this.substitutes.put(match.toLowerCase(Locale.ENGLISH), replacements);
    }

    private List<String> getSubstitutes(String postScriptName) {
        List<String> subs = (List)this.substitutes.get(postScriptName.replace(" ", "").toLowerCase(Locale.ENGLISH));
        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);
            font = this.findFontBoxFont(fallbackName);
            if (font == null) {
                font = this.lastResortFont;
            }

            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.replace("-", ""));
                if (info != null) {
                    return info.getFont();
                } else {
                    Iterator var4 = this.getSubstitutes(postScriptName).iterator();

                    do {
                        if (!var4.hasNext()) {
                            info = this.getFont(format, postScriptName.replace(",", "-"));
                            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.toLowerCase(Locale.ENGLISH));
        if (info != null && info.getFormat() == format) {
            if (LOG.isDebugEnabled()) {
                LOG.debug(String.format("getFont('%s','%s') returns %s", format, postScriptName, info));
            }

            return info;
        } else {
            return 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<FontMatch> queue = this.getFontMatches(fontDescriptor, cidSystemInfo);
                        FontMatch bestMatch = (FontMatch)queue.poll();
                        if (bestMatch != null) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("Best match for '" + baseFont + "': " + bestMatch.info);
                            }

                            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<FontMatch> getFontMatches(PDFontDescriptor fontDescriptor, PDCIDSystemInfo cidSystemInfo) {
        PriorityQueue<FontMatch> queue = new PriorityQueue(20);
        Iterator var4 = this.fontInfoByName.values().iterator();

        while(true) {
            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 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.0;
                    } 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.0;
                    } else if (panose.getWeight() > 1 && weight > 1) {
                        float dist = (float)Math.abs(panose.getWeight() - weight);
                        match.score += 1.0 - (double)dist * 0.5;
                    }
                    break;
                }

                if (fontDescriptor.getFontWeight() > 0.0F && info.getWeightClass() > 0) {
                    float dist = Math.abs(fontDescriptor.getFontWeight() - (float)info.getWeightClass());
                    match.score += 1.0 - (double)(dist / 100.0F) * 0.5;
                }
                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 ("MalgunGothic-Semilight".equals(info.getPostScriptName())) {
                codePageRange &= ~(JIS_JAPAN | CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL);
            }

            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 FontMatch printMatches(PriorityQueue<FontMatch> queue) {
        FontMatch bestMatch = (FontMatch)queue.peek();
        System.out.println("-------");

        while(!queue.isEmpty()) {
            FontMatch match = (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<FontMatch> {
        double score;
        final FontInfo info;

        FontMatch(FontInfo info) {
            this.info = info;
        }

        public int compareTo(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);
        }
    }
}
相关推荐
码字哥6 分钟前
EasyExcel设置表头上面的那种大标题(前端传递来的大标题)
java·服务器·前端
GIS好难学2 小时前
《Vue进阶教程》第六课:computed()函数详解(上)
前端·javascript·vue.js
nyf_unknown2 小时前
(css)element中el-select下拉框整体样式修改
前端·css
执键行天涯2 小时前
element-plus中的resetFields()方法
前端·javascript·vue.js
一个努力学习的小男孩2 小时前
【自学】Vues基础
vue.js
Days20502 小时前
uniapp小程序增加加载功能
开发语言·前端·javascript
喵喵酱仔__2 小时前
vue 给div增加title属性
前端·javascript·vue.js
dazhong20122 小时前
HTML前端开发-- Iconfont 矢量图库使用简介
前端·html·svg·矢量图·iconfont
m0_748248773 小时前
前端vue使用onlyoffice控件实现word在线编辑、预览(仅列出前端部分需要做的工作,不包含后端部分)
前端·vue.js·word