Explorar el Código

解决在服务器上的字体问题

zhangchen hace 2 meses
padre
commit
4ddd64a382

+ 57 - 34
alien-store/src/main/java/shop/alien/store/util/StatisticsComparisonImageUtil.java

@@ -6,9 +6,12 @@ import shop.alien.entity.store.vo.StoreOperationalStatisticsComparisonVo;
 import javax.imageio.ImageIO;
 import java.awt.*;
 import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.InputStream;
 import java.math.BigDecimal;
+import java.nio.file.Files;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.List;
@@ -32,8 +35,13 @@ public class StatisticsComparisonImageUtil {
     private static final Color SECTION_TITLE_COLOR = new Color(33, 33, 33); // #212121
     private static final Color BORDER_COLOR = new Color(224, 224, 224); // #E0E0E0
 
-    // 字体:优先 classpath 内嵌字体(Linux 无微软雅黑时避免中文方框),其次系统支持中文的字体
-    private static final String FONT_CLASSPATH = "fonts/NotoSansSC-Regular.ttf";
+    // 字体:优先 classpath 内 fonts/ 下的字体文件(与 resources/fonts/ 对应),支持 .ttf / .otf
+    private static final String[] FONT_RESOURCE_PATHS = {
+            "fonts/NotoSansSC-Regular.ttf",
+            "fonts/NotoSansCJKsc-Regular.otf",
+            "/fonts/NotoSansSC-Regular.ttf",
+            "/fonts/NotoSansCJKsc-Regular.otf"
+    };
     private static final String[] FONT_FALLBACK_NAMES = {
             "Microsoft YaHei",
             "PingFang SC",
@@ -64,10 +72,28 @@ public class StatisticsComparisonImageUtil {
     private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");
     private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("#,##0.00%");
 
-    /**
-     * 获取支持中文的字体(用于绘制报表,避免 Linux 下无微软雅黑导致中文方框)
-     * 优先:classpath 下 fonts/NotoSansSC-Regular.ttf;其次系统已安装的中文字体名
-     */
+    /** 从 classpath 读取字体资源为字节数组(JAR 内更可靠) */
+    private static byte[] readFontBytes(String path) {
+        ClassLoader cl = StatisticsComparisonImageUtil.class.getClassLoader();
+        for (String p : new String[]{path, path.startsWith("/") ? path.substring(1) : "/" + path}) {
+            InputStream is = cl.getResourceAsStream(p);
+            if (is == null && Thread.currentThread().getContextClassLoader() != null) {
+                is = Thread.currentThread().getContextClassLoader().getResourceAsStream(p);
+            }
+            if (is == null) continue;
+            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+                byte[] buf = new byte[8192];
+                int n;
+                while ((n = is.read(buf)) != -1) out.write(buf, 0, n);
+                return out.toByteArray();
+            } catch (Exception ignored) {
+            } finally {
+                try { is.close(); } catch (Exception ignored) {}
+            }
+        }
+        return null;
+    }
+
     private static Font getChineseFont(int style, int size) {
         if (baseChineseFont != null) {
             return baseChineseFont.deriveFont(style, size);
@@ -76,49 +102,46 @@ public class StatisticsComparisonImageUtil {
             return new Font(chineseFontName, style, size);
         }
         synchronized (StatisticsComparisonImageUtil.class) {
-            if (baseChineseFont != null) {
-                return baseChineseFont.deriveFont(style, size);
-            }
-            if (chineseFontName != null) {
-                return new Font(chineseFontName, style, size);
-            }
-            try {
-                InputStream is = StatisticsComparisonImageUtil.class.getClassLoader().getResourceAsStream(FONT_CLASSPATH);
-                if (is != null) {
-                    baseChineseFont = Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(Font.PLAIN, 14);
-                    is.close();
-                    log.info("使用内嵌中文字体: {}", FONT_CLASSPATH);
+            if (baseChineseFont != null) return baseChineseFont.deriveFont(style, size);
+            if (chineseFontName != null) return new Font(chineseFontName, style, size);
+            for (String path : FONT_RESOURCE_PATHS) {
+                byte[] bytes = readFontBytes(path);
+                if (bytes == null || bytes.length == 0) continue;
+                try {
+                    baseChineseFont = Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(bytes)).deriveFont(Font.PLAIN, 14f);
                     return baseChineseFont.deriveFont(style, size);
+                } catch (Exception e1) {
+                    try {
+                        File tmp = File.createTempFile("noto_", path.endsWith(".otf") ? ".otf" : ".ttf");
+                        Files.write(tmp.toPath(), bytes);
+                        baseChineseFont = Font.createFont(Font.TRUETYPE_FONT, tmp).deriveFont(Font.PLAIN, 14f);
+                        tmp.delete();
+                        return baseChineseFont.deriveFont(style, size);
+                    } catch (Exception e2) {
+                        // skip
+                    }
                 }
-            } catch (Exception e) {
-                log.warn("加载内嵌中文字体失败,将尝试系统字体: {}", e.getMessage());
             }
             GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
             String[] names = ge.getAvailableFontFamilyNames();
             if (names != null) {
                 for (String candidate : FONT_FALLBACK_NAMES) {
                     for (String name : names) {
-                        if (candidate.equals(name)) {
-                            Font f = new Font(name, Font.PLAIN, 14);
-                            if (f.canDisplayUpTo("经营数据") < 0) {
-                                chineseFontName = name;
-                                log.info("使用系统中文字体: {}", name);
-                                return new Font(chineseFontName, style, size);
-                            }
-                        }
+                        if (!candidate.equals(name)) continue;
+                        Font f = new Font(name, Font.PLAIN, 14);
+                        if (f.canDisplayUpTo("经") >= 0) continue;
+                        chineseFontName = name;
+                        return new Font(chineseFontName, style, size);
                     }
                 }
                 for (String name : names) {
                     Font f = new Font(name, Font.PLAIN, 14);
-                    if (f.canDisplayUpTo("经") < 0) {
-                        chineseFontName = name;
-                        log.info("使用系统中文字体(自动): {}", name);
-                        return new Font(chineseFontName, style, size);
-                    }
+                    if (f.canDisplayUpTo("经") >= 0) continue;
+                    chineseFontName = name;
+                    return new Font(chineseFontName, style, size);
                 }
             }
             chineseFontName = Font.SANS_SERIF;
-            log.warn("未找到支持中文的字体,使用 SansSerif,Linux 下中文可能显示为方框,建议在 resources/fonts/ 放入 NotoSansSC-Regular.ttf");
             return new Font(chineseFontName, style, size);
         }
     }