package com.cm.common.utils;

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.*;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: PDFUtil
 * @Description: pdf
 * @Author: WangGeng
 * @Date: 2020/12/26 21:39
 * @Version: 1.0
 **/
public class PDFUtil {

    private static final Logger LOG = LoggerFactory.getLogger(PDFUtil.class);

    /**
     * PDF转图片
     *
     * @param pdfBytes pdf字节数组
     * @return
     * @throws IOException
     */
    public static List<byte[]> pdfToImage(byte[] pdfBytes) throws IOException {
        List<byte[]> result = new ArrayList<>();
        try (PDDocument document = PDDocument.load(pdfBytes)) {
            PDFRenderer renderer = new PDFRenderer(document);
            for (int i = 0; i < document.getNumberOfPages(); ++i) {
                // dpi越大转换后越清晰，相对转换速度越慢
                BufferedImage bufferedImage = renderer.renderImageWithDPI(i, 200);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ImageIO.write(bufferedImage, "png", out);
                result.add(out.toByteArray());
            }
        }
        return result;
    }

    /**
     * PDF转图片
     *
     * @param pdfPath   PDF文件全路径
     * @param outFolder 输出文件夹，斜杠结尾
     * @throws IOException
     */
    public static void pdfToImage(String pdfPath, String outFolder) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(pdfPath);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        for (int readLength = 0; (readLength = fileInputStream.read(buf)) > -1; ) {
            byteArrayOutputStream.write(buf, 0, readLength);
        }
        List<byte[]> imageBytesList = pdfToImage(byteArrayOutputStream.toByteArray());
        int imageIndex = 0;
        for (byte[] bytes : imageBytesList) {
            FileOutputStream fileOutputStream = new FileOutputStream(outFolder + imageIndex + ".png");
            fileOutputStream.write(bytes);
            fileOutputStream.flush();
            fileOutputStream.close();
            imageIndex++;
        }
    }

    /**
     * 生成PDF文件
     *
     * @param title       标题
     * @param titleSize   标题大小
     * @param content     正文
     * @param contentSize 正文大小
     * @param ourFile     输出文件
     * @return true：生成成功，false：生成失败
     */
    public static boolean createPdf(String title, int titleSize, String content, int contentSize, String ourFile) {
        // 中文字体
        Document document = new Document();
        try {
            BaseFont baseFontChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            Font titleFont = new Font(baseFontChinese, titleSize, Font.NORMAL);
            titleFont.setColor(BaseColor.RED);

            Font contentFont = new Font(baseFontChinese, contentSize, Font.NORMAL);

            PdfWriter.getInstance(document, new FileOutputStream(ourFile));

            document.open();
            Paragraph paragraph = new Paragraph(title, titleFont);
            paragraph.setAlignment(Element.ALIGN_CENTER);
            document.add(paragraph);
            document.add(new Paragraph("\n"));
            document.add(new Paragraph(content, contentFont));

        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return false;
        } finally {
            document.close();
        }
        return true;
    }

    /**
     * 添加图片到PDF中，并生成新文件
     *
     * @param sourceFile 源文件
     * @param keyword    关键字
     * @param outFile    输出文件
     * @param imageFile  图片文件
     * @return true：成功，false，生成失败
     */
    public static boolean addImageAfterKeyword(String sourceFile, String keyword, String outFile, String imageFile) {
        PdfReader pdfReader = null;
        PdfStamper pdfStamper = null;
        try {
            pdfReader = new PdfReader(sourceFile);
            pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(outFile));
            CharInfo lastCharInfo = readPdfAndGetLastKeywordCharInfo(pdfReader, keyword);
            if (lastCharInfo == null) {
                LOG.debug("未在PDF正文中匹配到关键字");
                return false;
            }
            // 合成PDF
            Image image = Image.getInstance(imageFile);
            image.scalePercent(50F);
            float scaledHeight = image.getScaledHeight();
            float centerHeight = ((float) lastCharInfo.getCharWidth() + scaledHeight) / 2;
            image.setAbsolutePosition((float) (lastCharInfo.getX() + lastCharInfo.getCharWidth()), (float) (lastCharInfo.getY() - centerHeight));
            PdfContentByte underContent = pdfStamper.getUnderContent(lastCharInfo.getPageNum());
            underContent.addImage(image);
            pdfStamper.close();
            pdfReader.close();
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return false;
        } finally {
            if (pdfStamper != null) {
                try {
                    pdfStamper.close();
                } catch (DocumentException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (pdfReader != null) {
                pdfReader.close();
            }
        }
        return true;
    }

    /**
     * 图片替换关键字
     *
     * @param sourceFile
     * @param keyword
     * @param outFile
     * @param imageFile
     * @return
     */
    public static boolean addImageReplaceKeyword(String sourceFile, String keyword, String outFile, String imageFile) {
        PdfReader pdfReader = null;
        PdfStamper pdfStamper = null;
        try {
            pdfReader = new PdfReader(sourceFile);
            pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(outFile));
            CharInfo lastCharInfo = readPdfAndGetFirstKeywordCharInfo(pdfReader, keyword);
            if (lastCharInfo == null) {
                LOG.debug("未在PDF正文中匹配到关键字");
                return false;
            }
            // 合成PDF
            Image image = Image.getInstance(imageFile);
            image.scalePercent(50F);
            // image.scaleAbsolute(200);

            image.setAbsolutePosition((float) (lastCharInfo.getX()), (float) (lastCharInfo.getY()));
            PdfContentByte underContent = pdfStamper.getUnderContent(lastCharInfo.getPageNum());
            underContent.addImage(image);
            pdfStamper.close();
            pdfReader.close();
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return false;
        } finally {
            if (pdfStamper != null) {
                try {
                    pdfStamper.close();
                } catch (DocumentException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (pdfReader != null) {
                pdfReader.close();
            }
        }
        return true;
    }

    /**
     * 读取pdf并获取关键字最后一个字符信息
     *
     * @return
     * @throws Exception
     */
    public static CharInfo readPdfAndGetLastKeywordCharInfo(PdfReader pdfReader, String keyword) throws Exception {
        List<PageInfo> pageInfos = listPageInfo(pdfReader);
        // 关键字最后一个字符
        CharInfo lastCharInfo = null;
        for (PageInfo pageInfo : pageInfos) {
            int index = pageInfo.getContent().indexOf(keyword);
            if (index < 0) {
                continue;
            }
            // keyword.length() - 1
            lastCharInfo = pageInfo.getCharInfos().get(index + keyword.length() - 1);
            lastCharInfo.setPageNum(pageInfo.getPageNum());
            lastCharInfo.setPageWidth(pageInfo.getPageWidth());
            lastCharInfo.setPageHeight(pageInfo.getPageHeight());
        }
        return lastCharInfo;
    }

    /**
     * 读取pdf并获取关键字第一个
     *
     * @param pdfReader
     * @param keyword
     * @return
     * @throws Exception
     */
    public static CharInfo readPdfAndGetFirstKeywordCharInfo(PdfReader pdfReader, String keyword) throws Exception {
        List<PageInfo> pageInfos = listPageInfo(pdfReader);
        // 关键字第一个字符
        CharInfo firstCharInfo = null;
        for (PageInfo pageInfo : pageInfos) {
            int index = pageInfo.getContent().indexOf(keyword);
            if (index < 0) {
                continue;
            }
            firstCharInfo = pageInfo.getCharInfos().get(index);
            firstCharInfo.setPageNum(pageInfo.getPageNum());
            firstCharInfo.setPageWidth(pageInfo.getPageWidth());
            firstCharInfo.setPageHeight(pageInfo.getPageHeight());
        }
        return firstCharInfo;
    }

    /**
     * pdf信息列表
     *
     * @param pdfReader
     * @return
     * @throws IOException
     */
    private static List<PageInfo> listPageInfo(PdfReader pdfReader) throws IOException {
        List<PageInfo> pageInfos = new ArrayList<>();
        // 获取PDF有多少页
        int pageCount = pdfReader.getNumberOfPages();
        for (int i = 0; i < pageCount; i++) {
            int pageNum = i + 1;
            // 当前页大小
            PdfDictionary pageDictionary = pdfReader.getPageN(pageNum);
            Rectangle pageSize = pdfReader.getPageSize(pageDictionary);
            float pageWidth = pageSize.getWidth();
            float pageHeight = pageSize.getHeight();
            // 解析页面内容
            PdfDictionary resourcesPdfDictionary = pageDictionary.getAsDict(PdfName.RESOURCES);
            // 页面渲染监听器
            PdfRenderListener pdfRenderListener = new PdfRenderListener();
            PdfContentStreamProcessor pdfContentStreamProcessor = new PdfContentStreamProcessor(pdfRenderListener);
            // 解析PDF流
            pdfContentStreamProcessor.processContent(ContentByteUtils.getContentBytesForPage(pdfReader, pageNum), resourcesPdfDictionary);
            pageInfos.add(new PageInfo(pageNum, pageWidth, pageHeight, pdfRenderListener.getContent(), pdfRenderListener.getCharInfos()));
        }
        return pageInfos;
    }

    public static class PdfRenderListener implements RenderListener {

        private List<CharInfo> charInfos = new ArrayList<>();
        private StringBuffer content = new StringBuffer();

        @Override
        public void beginTextBlock() {

        }

        // 逐行解析文本
        @Override
        public void renderText(TextRenderInfo textRenderInfo) {
            // 行内容
            List<TextRenderInfo> characterRenderInfos = textRenderInfo.getCharacterRenderInfos();
            // PDF坐标在左下角？
            for (TextRenderInfo characterRenderInfo : characterRenderInfos) {
                String word = characterRenderInfo.getText();
                Rectangle2D.Float boundingRectange = characterRenderInfo.getAscentLine().getBoundingRectange();
                double minX = boundingRectange.getX();
                double minY = boundingRectange.getY();
                CharInfo charInfo = new CharInfo(minX, minY, boundingRectange.getWidth(), boundingRectange.getHeight(), word);
                charInfos.add(charInfo);
                content.append(word);
            }
        }

        @Override
        public void endTextBlock() {

        }

        // 解析图片
        @Override
        public void renderImage(ImageRenderInfo imageRenderInfo) {

        }

        public String getContent() {
            return content.toString();
        }

        public List<CharInfo> getCharInfos() {
            return charInfos;
        }
    }

    /**
     * 页面信息
     */
    public static class PageInfo {
        private Integer pageNum;
        private Float pageWidth;
        private Float pageHeight;
        private String content;
        private List<CharInfo> charInfos;

        public PageInfo(Integer pageNum, Float pageWidth, Float pageHeight, String content, List<CharInfo> charInfos) {
            this.pageNum = pageNum;
            this.pageWidth = pageWidth;
            this.pageHeight = pageHeight;
            this.content = content;
            this.charInfos = charInfos;
        }

        public Integer getPageNum() {
            return pageNum == null ? 0 : pageNum;
        }

        public void setPageNum(Integer pageNum) {
            this.pageNum = pageNum;
        }

        public Float getPageWidth() {
            return pageWidth == null ? 0 : pageWidth;
        }

        public void setPageWidth(Float pageWidth) {
            this.pageWidth = pageWidth;
        }

        public Float getPageHeight() {
            return pageHeight == null ? 0 : pageHeight;
        }

        public void setPageHeight(Float pageHeight) {
            this.pageHeight = pageHeight;
        }

        public String getContent() {
            return content == null ? "" : content;
        }

        public void setContent(String content) {
            this.content = content;
        }

        public List<CharInfo> getCharInfos() {
            if (charInfos == null) {
                return new ArrayList<>();
            }
            return charInfos;
        }

        public void setCharInfos(List<CharInfo> charInfos) {
            this.charInfos = charInfos;
        }
    }

    /**
     * 字体信息
     */
    public static class CharInfo {
        private int pageNum;
        private float pageWidth;
        private float pageHeight;
        private double x;
        private double y;
        private double charWidth;
        private double charHeight;
        private String text;

        public CharInfo(double x, double y, double charWidth, double charHeight, String text) {
            this.x = x;
            this.y = y;
            this.charWidth = charWidth;
            this.charHeight = charHeight;
            this.text = text;
        }

        public int getPageNum() {
            return pageNum;
        }

        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }

        public float getPageWidth() {
            return pageWidth;
        }

        public void setPageWidth(float pageWidth) {
            this.pageWidth = pageWidth;
        }

        public float getPageHeight() {
            return pageHeight;
        }

        public void setPageHeight(float pageHeight) {
            this.pageHeight = pageHeight;
        }

        public double getX() {
            return x;
        }

        public void setX(double x) {
            this.x = x;
        }

        public double getY() {
            return y;
        }

        public void setY(double y) {
            this.y = y;
        }

        public double getCharWidth() {
            return charWidth;
        }

        public void setCharWidth(double charWidth) {
            this.charWidth = charWidth;
        }

        public double getCharHeight() {
            return charHeight;
        }

        public void setCharHeight(double charHeight) {
            this.charHeight = charHeight;
        }

        public String getText() {
            return text == null ? "" : text;
        }

        public void setText(String text) {
            this.text = text;
        }
    }

}
