基于DeepSeek实现PDF嵌入SVG图片无损放大

1. PDF中效果图

2. 询问Deepseek进行代码书写,不断优化后结果

java 复制代码
/**
 * SVG工具类,用于生成价格趋势的SVG图表
 */
public class SvgUtils {

    // SVG画布尺寸
    private static final int WIDTH = 800;
    private static final int HEIGHT = 500;
    private static final int PADDING = 60;

    // 生成SVG折线图
    public static String generatePriceTrendSvg(List<PriceData> data, String title, String subtitle) {
        if (data.isEmpty()) {
            return "<svg xmlns='http://www.w3.org/2000/svg' width='" + WIDTH + "' height='" + HEIGHT + "'></svg>";
        }

        // 计算价格和时间的范围
        double minPrice = data.stream().mapToDouble(PriceData::getPrice).min().orElse(0) * 0.8;
        double maxPrice = data.stream().mapToDouble(PriceData::getPrice).max().orElse(1) * 1.2;

        // 解析最小和最大日期
        String minYearMonth = data.stream().map(PriceData::getYearmonth).min(String::compareTo).orElse("2024-06");
        String maxYearMonth = data.stream().map(PriceData::getYearmonth).max(String::compareTo).orElse("2025-01");

        // 构建SVG内容
        StringBuilder svg = new StringBuilder();
        svg.append(String.format("<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>", WIDTH, HEIGHT));

        // 画背景
        svg.append(String.format("<rect width='%d' height='%d' fill='#ffffff'/>", WIDTH, HEIGHT));

        // 添加标题和副标题(居中显示)
        svg.append(String.format("<text x='%d' y='%d' font-size='20' font-family='Arial' fill='#333333' font-weight='bold' text-anchor='middle'>%s</text>",
                WIDTH / 2, 30, title));
        svg.append(String.format("<text x='%d' y='%d' font-size='14' font-family='Arial' fill='#666666' text-anchor='middle'>%s</text>",
                WIDTH / 2, 50, subtitle));

        // 画虚线网格线
        svg.append(drawGrid(minYearMonth, maxYearMonth, minPrice, maxPrice));

        // 画深色坐标轴
        svg.append(drawAxis(minYearMonth, maxYearMonth, minPrice, maxPrice));

        // 画浅色平滑折线
        svg.append(drawSmoothLine(data, minYearMonth, maxYearMonth, minPrice, maxPrice));

        svg.append("</svg>");
        return svg.toString();
    }

    // 画虚线网格线
    private static String drawGrid(String minYearMonth, String maxYearMonth, double minPrice, double maxPrice) {
        StringBuilder grid = new StringBuilder();

        // 水平网格线(价格)
        int numHorizontalLines = 5;
        for (int i = 0; i <= numHorizontalLines; i++) {
            double price = minPrice + (maxPrice - minPrice) * i / numHorizontalLines;
            int y = HEIGHT - PADDING - (int) ((price - minPrice) * (HEIGHT - 2 * PADDING) / (maxPrice - minPrice));
            grid.append(String.format("<line x1='%d' y1='%d' x2='%d' y2='%d' stroke='#e0e0e0' stroke-width='1' stroke-dasharray='5,5'/>",
                    PADDING, y, WIDTH - PADDING, y));
        }

        // 垂直网格线(时间)
        List<String> yearMonths = generateYearMonths(minYearMonth, maxYearMonth);
        for (String yearMonth : yearMonths) {
            int x = PADDING + (int) ((getMonthIndex(yearMonth) - getMonthIndex(minYearMonth)) * (WIDTH - 2 * PADDING) / (yearMonths.size() - 1));
            grid.append(String.format("<line x1='%d' y1='%d' x2='%d' y2='%d' stroke='#e0e0e0' stroke-width='1' stroke-dasharray='5,5'/>",
                    x, PADDING, x, HEIGHT - PADDING));
        }

        return grid.toString();
    }

    // 画深色坐标轴
    private static String drawAxis(String minYearMonth, String maxYearMonth, double minPrice, double maxPrice) {
        StringBuilder axis = new StringBuilder();

        // X轴(时间)
        axis.append(String.format("<line x1='%d' y1='%d' x2='%d' y2='%d' stroke='#333333' stroke-width='2'/>",
                PADDING, HEIGHT - PADDING, WIDTH - PADDING, HEIGHT - PADDING));

        // Y轴(价格)
        axis.append(String.format("<line x1='%d' y1='%d' x2='%d' y2='%d' stroke='#333333' stroke-width='2'/>",
                PADDING, PADDING, PADDING, HEIGHT - PADDING));

        // X轴标签
        List<String> yearMonths = generateYearMonths(minYearMonth, maxYearMonth);
        for (String yearMonth : yearMonths) {
            int x = PADDING + (int) ((getMonthIndex(yearMonth) - getMonthIndex(minYearMonth)) * (WIDTH - 2 * PADDING) / (yearMonths.size() - 1));
            axis.append(String.format("<text x='%d' y='%d' font-size='12' font-family='Arial' fill='#000000' text-anchor='middle'>%s</text>",
                    x, HEIGHT - PADDING + 20, yearMonth));
        }

        // Y轴标签
        int numYLabels = 5;
        for (int i = 0; i <= numYLabels; i++) {
            double price = minPrice + (maxPrice - minPrice) * i / numYLabels;
            int y = HEIGHT - PADDING - (int) ((price - minPrice) * (HEIGHT - 2 * PADDING) / (maxPrice - minPrice));
            axis.append(String.format("<text x='%d' y='%d' font-size='12' font-family='Arial' fill='#000000' text-anchor='end'>%.2f</text>",
                    PADDING - 10, y + 5, price));
        }

        return axis.toString();
    }

    // 画浅色平滑折线
    private static String drawSmoothLine(List<PriceData> data, String minYearMonth, String maxYearMonth, double minPrice, double maxPrice) {
        if (data.size() < 2) {
            return "";
        }

        StringBuilder path = new StringBuilder("<path d='M");

        for (int i = 0; i < data.size(); i++) {
            PriceData point = data.get(i);
            int x = PADDING + (int) ((getMonthIndex(point.getYearmonth()) - getMonthIndex(minYearMonth)) * (WIDTH - 2 * PADDING) / (getMonthIndex(maxYearMonth) - getMonthIndex(minYearMonth)));
            int y = HEIGHT - PADDING - (int) ((point.getPrice() - minPrice) * (HEIGHT - 2 * PADDING) / (maxPrice - minPrice));

            if (i == 0) {
                path.append(x).append(",").append(y);
            } else {
                // 计算控制点以实现平滑效果
                PriceData prevPoint = data.get(i - 1);
                int prevX = PADDING + (int) ((getMonthIndex(prevPoint.getYearmonth()) - getMonthIndex(minYearMonth)) * (WIDTH - 2 * PADDING) / (getMonthIndex(maxYearMonth) - getMonthIndex(minYearMonth)));
                int prevY = HEIGHT - PADDING - (int) ((prevPoint.getPrice() - minPrice) * (HEIGHT - 2 * PADDING) / (maxPrice - minPrice));
                int ctrlX1 = (prevX + x) / 2;
                int ctrlY1 = prevY;
                int ctrlX2 = (prevX + x) / 2;
                int ctrlY2 = y;

                path.append(" C").append(ctrlX1).append(",").append(ctrlY1)
                        .append(" ").append(ctrlX2).append(",").append(ctrlY2)
                        .append(" ").append(x).append(",").append(y);
            }
        }

        path.append("' fill='none' stroke='#66B3FF' stroke-width='2'/>");
        return path.toString();
    }

    // 生成从 minYearMonth 到 maxYearMonth 的连续月份列表
    private static List<String> generateYearMonths(String minYearMonth, String maxYearMonth) {
        List<String> yearMonths = new ArrayList<>();
        int year = Integer.parseInt(minYearMonth.substring(0, 4));
        int month = Integer.parseInt(minYearMonth.substring(5));

        while (true) {
            yearMonths.add(String.format("%04d-%02d", year, month));
            if (yearMonths.get(yearMonths.size() - 1).equals(maxYearMonth)) {
                break;
            }
            month++;
            if (month > 12) {
                month = 1;
                year++;
            }
        }

        return yearMonths;
    }

    // 将 yearmonth 转换为索引(从 0 开始)
    private static int getMonthIndex(String yearMonth) {
        int year = Integer.parseInt(yearMonth.substring(0, 4));
        int month = Integer.parseInt(yearMonth.substring(5));
        return (year - 2024) * 12 + (month - 1); // 假设最小年份是 2024
    }

    public static void main(String[] args) {
        List<PriceData> data = PriceData.getSampleData();
        String title = "价格走势图";
        String subtitle = "2024-06 至 2025-01";
        String svg = SvgUtils.generatePriceTrendSvg(data, title, subtitle);
        System.out.println(svg);
    }
}
java 复制代码
@Data
public class PriceData {
    private String yearmonth;
    private Double price;

    public PriceData() {}

    public PriceData(String yearmonth, Double price) {
        this.yearmonth = yearmonth;
        this.price = price;
    }

    public static List<PriceData> getSampleData() {
        List<PriceData> data = new ArrayList<>();
        data.add(new PriceData("2025-01", 100.0)); // 2023-01-01
        data.add(new PriceData("2025-02", 105.0)); // 2023-01-02
        data.add(new PriceData("2025-03", 102.0)); // 2023-01-03
        data.add(new PriceData("2025-04", 110.0)); // 2023-01-04
        data.add(new PriceData("2025-05", 108.0)); // 2023-01-05
        return data;
    }
}

OK, 生成的SVG嵌入到html网页中进行渲染即可

相关推荐
JobsandCzj8 小时前
PDF 分割工具
javascript·小程序·pdf
d3soft11 小时前
【清华大学】实用DeepSeek赋能家庭教育 56页PDF文档完整版
ai·pdf·deepseek
开开心心就好12 小时前
电脑睡眠智能管控:定时、依状态灵活调整,多模式随心选
javascript·windows·python·安全·django·pdf·电脑
3D小将14 小时前
SolidWorks 转 PDF3D 技术详解
3d·pdf·3d格式转换·模型格式转换
伟贤AI之路16 小时前
夏门大学DeepSeek 手册:从社会大众到高校及企业的全面应用实践研究(附 PDF 下载)
pdf
勘察加熊人1 天前
python将pdf转换成word
python·pdf·word
IDRSolutions_CN2 天前
PDF 文件中的文本链接是如何定义的?
java·经验分享·pdf·软件工程·团队开发
ComPDFKit2 天前
无需再单独保存 PDF 的 Word 副本!
pdf·word
文慧的科技江湖2 天前
OCPP扩展机制与自定义功能开发:协议灵活性设计与实践 - 慧知开源充电桩平台
网络·pdf·开源·开源协议·云快充协议·ocpp协议