浮光 工具类

工具类

图片相关

package com.wantong.question.util;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.wantong.common.utils.encrypt.UUIDGenerator;
import com.wantong.question.domain.po.brs.PositionPO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.scilab.forge.jlatexmath.TeXConstants;
import org.scilab.forge.jlatexmath.TeXFormula;
import org.scilab.forge.jlatexmath.TeXIcon;
import sun.awt.image.BufferedImageGraphicsConfig;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xyl
 * @version 1.0
 * @date 2023-09-25
 * @Description 图片工具类
 */
@Slf4j
public final class ImageUtil {
    public static final String DEFAULT_NAME = "image";
    public static final String PNG = "png";
    public static final String JPG = "jpg";


    public static byte[] merger(String first, String second) throws IOException {
        return getImageBytes(mergeImage(first, second), PNG);
    }

    public static byte[] getImageBytes(BufferedImage newImg, String imageType) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(newImg, imageType, os);

        return os.toByteArray();
    }

    /**
     * 将两个图片合并成一个图片【垂直合并】
     *
     * @param first  图片1文件路径
     * @param second 图片2文件路径
     * @return 返回合并之后的图片
     */
    public static BufferedImage mergeImage(String first, String second) throws IOException {
        log.info("开始合并图片,img1:{} img2:{}", first, second);
        FileInputStream img1 = new FileInputStream(first);
        FileInputStream img2 = new FileInputStream(second);
        BufferedImage image01 = ImageIO.read(img1);
        BufferedImage image02 = ImageIO.read(img2);
        return mergeImage(image01, image02, false, false, true, 0, null);
    }

    /**
     * 合并两个图片
     *
     * @param first       图片1
     * @param second      图片2
     * @param horizontal  等于true,则两个图片水平合并显示, 否则两个图片垂直合并显示
     * @param center      图片是否水平居中, horizontal 等于 false 垂直合并才有效
     * @param transparent 合并后的图片背景是否透明色
     * @param gap         图片之间的间距
     * @param color       图片的背景颜色
     * @return 返回合并之后的图片
     */
    public static BufferedImage mergeImage(BufferedImage first, BufferedImage second, boolean horizontal, boolean center, boolean transparent, int gap, Color color) {
        log.info("合并图片参数,水平合并:{}  水平居中:{}  背景透明:{}  图片间距:{}  图片背景:{}",
                horizontal, center, transparent, gap, color);
        if (first == null) {
            return second;
        } else if (second == null) {
            return first;
        }
        // 获取原始图片宽度
        int firstWidth = first.getWidth();
        int firstHeight = first.getHeight();
        int secondWidth = second.getWidth();
        int secondHeight = second.getHeight();
        int picGap = gap * 2;
        // 合并后的图片宽高
        int mergeWidth = Math.max(firstWidth, secondWidth) + picGap;
        int mergeHeight = firstHeight + secondHeight + picGap;
        if (horizontal) {
            mergeWidth = firstWidth + secondWidth + picGap;
            mergeHeight = Math.max(firstHeight, secondHeight) + picGap;
        }

        // 创建目标图片对象
        BufferedImage target = new BufferedImage(mergeWidth, mergeHeight, BufferedImage.TYPE_INT_RGB);
        if (transparent) {
            // 设置图片背景为透明的
            BufferedImageGraphicsConfig config = BufferedImageGraphicsConfig.getConfig(target);
            target = config.createCompatibleImage(mergeWidth, mergeHeight, Transparency.TRANSLUCENT);
        }

        // 创建绘制目标图片对象
        Graphics2D graphics = target.createGraphics();
        int x1, y1;
        int x2, y2;
        if (horizontal) {
            // 水平合并
            x1 = gap;
            y1 = gap;
            x2 = firstWidth + gap;
            y2 = gap;
        } else {
            // 垂直合并
            if (center) {
                // 计算居中位置
                x1 = (mergeWidth - firstWidth) / 2;
                x2 = (mergeWidth - secondWidth) / 2;
            } else {
                x1 = gap;
                x2 = gap;
            }
            y1 = gap;
            y2 = firstHeight + gap;
        }

        // 图片的背景颜色
        if (color != null) {
            graphics.setColor(color);
            graphics.fillRect(0, 0, mergeWidth, mergeHeight);
        }

        // 按照顺序绘制图片
        graphics.drawImage(first, x1, y1, firstWidth, firstHeight, null);
        graphics.drawImage(second, x2, y2, secondWidth, secondHeight, null);
        graphics.dispose();

        log.info("合并图片完成");
        // 返回合并后的图片对象
        return target;
    }

    /**
     * Latex公式转图片
     *
     * @param latexFormula Latex公式
     * @throws IOException
     */
    public static byte[] getLatexBytes(String latexFormula) throws IOException {
        return getImageBytes(latex2Image(latexFormula), PNG);
    }

    private static BufferedImage latex2Image(String latexFormula) {
        log.info("Latex公式转图片,公式:{}", latexFormula);
        if (StringUtils.isBlank(latexFormula)) {
            return null;
        }
        latexFormula = latexFormula.replaceAll("<br>", "")
                .replaceAll("&nbsp;", "\\ ")
                .replaceAll("&amp;", "\\&")
                .replaceAll("&lt;", "<")
                .replaceAll("&gt;", ">")
                .replaceAll("_{6}", "\\\\_") //公式里面的下划线转义 \_
                .replaceAll("&quot;", "''")
                .replaceAll("&copy;", "\\copyright")
                .replaceAll("&reg;", "\\textregistered");
        // 创建 TeXFormula 对象
        TeXFormula formula = new TeXFormula(latexFormula);
        // 创建 TeXIcon 对象,用于渲染公式
        TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_DISPLAY, 20);
        // 获取渲染后的图像
        BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
        // 获取图像的图形上下文
        Graphics2D g2 = image.createGraphics();
        g2.setPaint(new Color(0, 0, 0, 0)); //背景颜色为透明色
        g2.fillRect(0, 0, image.getWidth(), image.getHeight());
        g2.setColor(Color.BLACK); // 设置文本颜色为黑色
        // 渲染公式
        icon.paintIcon(new JLabel(), g2, 0, 0);
        g2.dispose(); // 释放图形上下文资源

        return image;
    }

    /**
     * 保存合并后的图片到文件
     *
     * @param fileName 文件路径名称
     * @param image    图片
     */
    public static void writeToFile(String fileName, BufferedImage image) throws IOException {
        if (fileName == null) {
            fileName = DEFAULT_NAME;
        }
        File file = new File(fileName);
        FileUtil.mkParentDirs(file);
        // 保存图片
        ImageIO.write(image, PNG, file);
    }

    /**
     * 坐标裁图,支持多边形
     *
     * @param src 原图片路径
     * @param target 裁图后路径
     * @param shapes 坐标
     * @throws IOException
     */
    public static void cutImage(String src, String target, List<PositionPO.ShapeVO> shapes) throws IOException {
        // 读取原始图像
        BufferedImage srcImage = ImageIO.read(new File(src));
        // 创建一个多边形
        Polygon polygon = new Polygon(
                shapes.stream().mapToInt(shape -> shape.getX().intValue()).toArray(),
                shapes.stream().mapToInt(shape -> shape.getY().intValue()).toArray(),
                shapes.size()
        );
        // 计算剪切后的多边形边界框
        Rectangle bounds = polygon.getBounds();

        // 创建 BufferedImage,用于裁剪后的图像
        BufferedImage targetImage = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
        // 创建 Graphics2D 对象
        Graphics2D g2d = targetImage.createGraphics();
        // 平移到多边形范围边界框的左上角,以便正确地绘制多边形区域
        g2d.translate(-bounds.x, -bounds.y);
        // 设置剪切区域为多边形
        g2d.setClip(polygon);
        // 绘制原始图像到剪切后的画布上
        g2d.drawImage(srcImage, 0, 0, null);
        // 释放资源
        g2d.dispose();

        writeToFile(target, targetImage);
    }


    public static void main(String[] args) throws Exception {
        String s = "[{\"x\":859.0,\"y\":887.0},{\"x\":1167.0,\"y\":1030.0},{\"x\":1088.0,\"y\":1377.0},{\"x\":1266.0,\"y\":1708.0},{\"x\":1027.0,\"y\":1981.0},{\"x\":754.0,\"y\":1949.0},{\"x\":766.0,\"y\":1514.0},{\"x\":693.0,\"y\":1138.0}]";
        List<PositionPO.ShapeVO> shapes = JSON.parseArray(s, PositionPO.ShapeVO.class);
        cutImage("E:\\cutSrc.png", "D:\\cutTarget.png", shapes);



        Pattern LATEX_PATTERN = Pattern.compile("\\$\\(([^$]*)\\)\\$");
        String text = "<p>哈哈哈哈$VT-G[1]$ $(\\left(x-1\\right)\\left(x+______\\right) )$</p>";
        // 用于存储替换后的富文本
        StringBuilder newText = new StringBuilder();
        int index = 0; //起始索引
        Matcher matcher = LATEX_PATTERN.matcher(text);
        while (matcher.find()) {
            String fileName = UUIDGenerator.getUUID32() + "." + PNG;
            writeToFile("D:\\latex\\" + fileName, latex2Image(matcher.group(1)));
            // 替换原始文本中的 LaTeX 公式
            newText.append(text, index, matcher.start()); //添加 LaTeX 前的文本
            newText.append("<img src=\"").append(fileName).append("\" alt=\"LaTeX Formula\"/>"); // 添加图片标签
            index = matcher.end(); // 更新起始索引
        }

        // 添加最后一个 LaTeX 后的文本
        newText.append(text.substring(index));
        System.out.println(newText.toString());


        FileInputStream img1 = new FileInputStream("E:\\1.png");
        FileInputStream img2 = new FileInputStream("E:\\2.jpg");
        BufferedImage image01 = ImageIO.read(img1);
        BufferedImage image02 = ImageIO.read(img2);

        // 合并两个图片,背景不透明
        BufferedImage target = mergeImage(image01, image02, false, true, false, 0, Color.white);
        writeToFile("D:\\003.jpg", target);
        System.out.println("合并成功......");

        // 合并两个图片,背景透明
        target = mergeImage(image01, image02, true, true, true, 20, null);
        writeToFile("D:\\004.jpg", target);
        System.out.println("合并成功......");
    }
}

http调用

package com.wantong.common.utils.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.FormBodyPartBuilder;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.apache.poi.ss.formula.functions.T;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * HttpClientPool
 * extends AbsHttpClientPool
 * init with method `{@link AbsHttpClientPool#initHttpClient()}`
 **/
@Slf4j
public abstract class AbsHttpClientPool {
    protected CloseableHttpClient httpClient;

    protected CookieStore cookieStore;

    protected PoolingHttpClientConnectionManager manager;

    protected ThreadLocal<CloseableHttpResponse> respThreadLocal = new InheritableThreadLocal<>();
    protected ThreadLocal<byte[]> respEntityResp = new InheritableThreadLocal<>();

    /**
     * 定义超时时间
     */
    public int getTimeout() {
        return 5000;
    }

    /**
     * 定义连接数
     */
    public int getConnectionAmount() {
        return 10;
    }

    @SneakyThrows
    public void initHttpClient() {
        //HTTPS
        SSLContext ctx = SSLContext.getInstance("TLS");
        //debug 信任所有
        //SSLContext ctx = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true).build();

        X509TrustManager tm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        //SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
//        DEBUG
//        SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
        SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(ctx,
                SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", ssf)
                .register("http", PlainConnectionSocketFactory.INSTANCE).build();

        HttpClientBuilder httpClientBuilder = HttpClients.custom();
//                .setProxy(new HttpHost("127.0.0.1", 8888, "http"));//                 debug

        manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        //连接池的相关配置
        SocketConfig config = SocketConfig.custom().setSoTimeout(getTimeout()).
                setSoKeepAlive(true).setTcpNoDelay(true).build();
        manager.setDefaultSocketConfig(config);
        //控制最大连接数
        manager.setDefaultMaxPerRoute(getConnectionAmount());
        manager.setMaxTotal(getConnectionAmount());
        //为连接池配置管链接理器
        httpClientBuilder.setConnectionManager(manager);
        httpClientBuilder.setDefaultSocketConfig(config);
        //存储cookie
        cookieStore = new BasicCookieStore();
        httpClientBuilder.setDefaultCookieStore(cookieStore);
        //redirect 301
//        httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
        beforeClientBuild(httpClientBuilder, config, manager);
        //生成我们配置好的httpclient类
        httpClient = httpClientBuilder.build();
    }

    /**
     * 扩展http-client配置
     * 例如: 配置抓包代理
     * HttpHost proxy = new HttpHost("127.0.0.1", 8888);
     * RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();
     * httpClientBuilder.setDefaultRequestConfig(requestConfig);
     * @param httpClientBuilder
     * @param socketConfig
     * @param httpClientConnectionManager
     */
    protected void beforeClientBuild(HttpClientBuilder httpClientBuilder, SocketConfig socketConfig, PoolingHttpClientConnectionManager httpClientConnectionManager) {
    }

    @Data
    @Accessors(chain = true)
    public static class ParamEntity {
        Object body;
        String fileName;
    }

    /**
     * 参数转换
     *
     * @param params
     */
    public static List<NameValuePair> convertParams(Map<String, Object> params) {
        List<NameValuePair> p = new LinkedList<>();
        params.forEach((key, value) -> p.add(new BasicNameValuePair(key, String.valueOf(value))));

        return p;
    }

    /**
     * get
     * map params serial in url
     *
     * @param url
     * @param params
     * @param reference
     * @param <T>
     * @return
     */
    protected <T> T executeGet(String url, Map<String, Object> params, TypeReference<T> reference) {
        CloseableHttpResponse httpResponse = null;
        try {
            respThreadLocal.set(httpResponse);
            String param = params == null ? "" : params.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
            HttpGet request = new HttpGet(url + "?" + param);
            httpResponse = httpClient.execute(request);
            respThreadLocal.set(httpResponse);
            int code = httpResponse.getStatusLine().getStatusCode();
            if (code == HttpStatus.SC_OK) {
                String content = EntityUtils.toString(httpResponse.getEntity());
                return JSON.parseObject(content, reference);
            } else {
                String content = EntityUtils.toString(httpResponse.getEntity(), HTTP.UTF_8);
                log.warn("远端服务返回错误[{}]: {}", code, content);

                return null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 执行器
     * map params serial in form
     *
     * @param params
     */
    protected <T> T executePost(String url, Map<String, Object> params, TypeReference<T> reference) {
        return executePost(url, null, params, reference);
    }

    /**
     * form
     * map params serial in form with custom headers
     *
     * @param url
     * @param headers
     * @param params
     * @param reference
     * @param <T>
     * @return
     */
    protected <T> T executePost(String url, Map<String, String> headers, Map<String, Object> params, TypeReference<T> reference) {
        UrlEncodedFormEntity entity = null;
        try {
            entity = new UrlEncodedFormEntity(convertParams(params), "utf-8");
        } catch (UnsupportedEncodingException e) {
            log.error("UnsupportedEncodingException", e);
            throw new RuntimeException(e);
        }

        return executePost(url, headers, entity, reference);
    }

    /**
     * json
     * params serial in json
     *
     * @param url
     * @param headers
     * @param params
     * @param reference
     * @param <T>
     * @return
     */
    protected <T> T executePostMulti(String url, Map<String, String> headers, Map<String, ParamEntity> params, TypeReference<T> reference) {
        MultipartEntityBuilder builder = null;
        builder = MultipartEntityBuilder.create();
        if (params != null && params.size() > 0) {
            AtomicInteger i = new AtomicInteger();
            MultipartEntityBuilder finalBuilder = builder;
            params.forEach((key, value) -> {
                if (value.getBody() instanceof byte[]) {
                    finalBuilder.addBinaryBody(key, (byte[]) value.getBody(), ContentType.APPLICATION_OCTET_STREAM, value.getFileName());
                } else {
                    finalBuilder.addPart(FormBodyPartBuilder.create().setName(key).setBody(new StringBody((String) value.getBody(), ContentType.TEXT_PLAIN)).build());
                }
            });
        }

        return executePost(url, headers, builder.build(), reference);
    }

    protected <T> T executePost(String url, Map<String, String> headers, Object params, TypeReference<T> reference) {
        StringEntity se = null;
        String jsonStr = null;
        if (params instanceof CharSequence) {
            jsonStr = (String) params;
        } else {
            jsonStr = JSONObject.toJSONString(params);
        }

        if (!StringUtils.isEmpty(jsonStr)) {
            se = new StringEntity(jsonStr, "utf-8");
            se.setContentType("application/json;charset=utf-8");
        }

        if (headers == null) {
            headers = new HashMap<>();
        }

        return executePost(url, headers, se, reference);
    }

    /**
     * post with custom entity
     *
     * @param url
     * @param headers
     * @param entity
     * @param reference
     * @param <T>
     * @return
     */
    protected <T> T executePost(String url, Map<String, String> headers, HttpEntity entity, TypeReference<T> reference) {
        CloseableHttpResponse httpResponse = null;
        String content = null;
        try {
            respThreadLocal.set(httpResponse);
            respEntityResp.set(null);
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(entity);
            Optional.ofNullable(headers).ifPresent(e -> e.forEach(httpPost::addHeader));
            httpResponse = httpClient.execute(httpPost);
            respThreadLocal.set(httpResponse);
            int code = httpResponse.getStatusLine().getStatusCode();
            byte[] bytes = EntityUtils.toByteArray(httpResponse.getEntity());
            respEntityResp.set(bytes);
            if (code == HttpStatus.SC_OK) {
                if (reference == null) {
                    return null;
                }

                content = new String(bytes, StandardCharsets.UTF_8);
                return JSON.parseObject(content, reference);
            }

            content = new String(bytes, StandardCharsets.UTF_8);
            log.warn("远端服务返回错误[{}]: {}", code, content);
            if (reference == null) {
                throw new HttpReqException(content);
            }

            return null;
        } catch (UnsupportedEncodingException e) {
            log.error("参数转换异常", e);
        } catch (ClientProtocolException e) {
            log.error("请求协议异常", e);
        } catch (IOException e) {
            log.error("请求IO异常", e);
        } catch (Exception e) {
            log.error("响应JSON字符串格式化异常,content: {}", content, e);
        }

        return null;
    }

    public CloseableHttpResponse getResp() {
        return respThreadLocal.get();
    }

    public byte[] getRespEntity() {
        return respEntityResp.get();
    }

    public CookieStore getCookieStore() {
        return this.cookieStore;
    }

    public String getCookie(String name) {
        if (name == null) {
            return null;
        }

        return getCookieStore().getCookies().parallelStream()
                .filter(e -> name.equals(e.getName()))
                .findFirst().map(Cookie::getValue).orElse(null);
    }

    public static <T> void doNotNull(T val, Consumer<T> c) {
        if (val == null) {
            return;
        }

        c.accept(val);
    }

    public static <T extends CharSequence> void doNotEmpty(T val, Consumer<T> c) {
        if (StringUtils.isEmpty(val)) {
            return;
        }

        c.accept(val);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值