diff --git a/src/main/java/com/budwk/app/base/utils/OfficePlusUtil.java b/src/main/java/com/budwk/app/base/utils/OfficePlusUtil.java index 30a93a2..7fa37f7 100644 --- a/src/main/java/com/budwk/app/base/utils/OfficePlusUtil.java +++ b/src/main/java/com/budwk/app/base/utils/OfficePlusUtil.java @@ -1,10 +1,16 @@ package com.budwk.app.base.utils; import cn.hutool.core.io.FileUtil; +import com.aspose.cells.FontConfigs; import com.aspose.cells.PdfSaveOptions; import com.aspose.cells.Workbook; +import com.aspose.slides.FontsLoader; +import com.aspose.slides.LoadOptions; import com.aspose.slides.Presentation; import com.aspose.words.Document; +import com.aspose.words.FontSettings; +import com.aspose.words.FontSourceBase; +import com.aspose.words.MemoryFontSource; import com.aspose.words.SaveFormat; import com.budwk.app.base.exception.BaseException; import lombok.extern.slf4j.Slf4j; @@ -12,6 +18,8 @@ import lombok.extern.slf4j.Slf4j; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; /** * 文档转换 @@ -19,6 +27,81 @@ import java.io.IOException; @Slf4j public class OfficePlusUtil { + private static final String DEFAULT_FONT_NAME = "Noto Sans SC"; + private static final String DEFAULT_FONT_RESOURCE = "/fonts/NotoSansSC-VF.ttf"; + private static final Object FONT_CONFIG_LOCK = new Object(); + private static volatile boolean fontConfigured; + + /** + * 注册系统内置字体,并将其作为 Office 文档缺失字体或缺失字形时的默认回退字体。 + * 原文档字体在服务器可用时仍优先使用,只有找不到原字体时才使用内置字体。 + */ + private static void configureDefaultFont() { + if (fontConfigured) { + return; + } + synchronized (FONT_CONFIG_LOCK) { + if (fontConfigured) { + return; + } + byte[] fontData = loadDefaultFontData(); + configureWordFont(fontData); + configureExcelFont(fontData); + FontsLoader.loadExternalFont(fontData); + fontConfigured = true; + } + } + + /** + * 从 classpath 读取随应用打包的字体,避免依赖线上服务器安装字体。 + * + * @return 默认字体的二进制数据 + */ + private static byte[] loadDefaultFontData() { + try (InputStream inputStream = OfficePlusUtil.class.getResourceAsStream(DEFAULT_FONT_RESOURCE)) { + if (inputStream == null) { + throw new BaseException("系统内置默认字体不存在"); + } + return inputStream.readAllBytes(); + } catch (IOException e) { + log.error("读取系统内置默认字体失败", e); + throw new BaseException("读取系统内置默认字体失败"); + } + } + + /** + * 为 Word 转换保留已有字体源并追加内置字体,同时配置缺失字体和缺失字形回退。 + * + * @param fontData 默认字体的二进制数据 + */ + private static void configureWordFont(byte[] fontData) { + FontSettings fontSettings = FontSettings.getDefaultInstance(); + FontSourceBase[] currentSources = fontSettings.getFontsSources(); + FontSourceBase[] fontSources = Arrays.copyOf(currentSources, currentSources.length + 1); + fontSources[currentSources.length] = new MemoryFontSource(fontData); + fontSettings.setFontsSources(fontSources); + fontSettings.getSubstitutionSettings().getDefaultFontSubstitution().setDefaultFontName(DEFAULT_FONT_NAME); + try { + fontSettings.getFallbackSettings().loadNotoFallbackSettings(); + } catch (Exception e) { + log.error("配置 Word 默认字体回退规则失败", e); + throw new BaseException("配置 Word 默认字体失败"); + } + } + + /** + * 为 Excel 转换追加内置字体,并将其设置为找不到原字体时的默认字体。 + * + * @param fontData 默认字体的二进制数据 + */ + private static void configureExcelFont(byte[] fontData) { + com.aspose.cells.FontSourceBase[] currentSources = FontConfigs.getFontSources(); + com.aspose.cells.FontSourceBase[] fontSources = Arrays.copyOf(currentSources, currentSources.length + 1); + fontSources[currentSources.length] = new com.aspose.cells.MemoryFontSource(fontData); + FontConfigs.setFontSources(fontSources); + FontConfigs.setDefaultFontName(DEFAULT_FONT_NAME); + } + /** * 转换 * @@ -47,7 +130,11 @@ public class OfficePlusUtil { public static void presentationConvertPdf(String sourcePath, String targetPath) { Presentation presentation = null; try { - presentation = new Presentation(sourcePath); + configureDefaultFont(); + LoadOptions loadOptions = new LoadOptions(); + loadOptions.setDefaultRegularFont(DEFAULT_FONT_NAME); + loadOptions.setDefaultAsianFont(DEFAULT_FONT_NAME); + presentation = new Presentation(sourcePath, loadOptions); presentation.save(targetPath, com.aspose.slides.SaveFormat.Pdf); } catch (Exception e) { log.info("转换pdf报错", e); @@ -68,10 +155,12 @@ public class OfficePlusUtil { public static void excelConvertPdf(String sourcePath, String targetPath) { FileOutputStream fileOS = null; try { + configureDefaultFont(); Workbook wb = new Workbook(sourcePath); fileOS = new FileOutputStream(targetPath); PdfSaveOptions pdfSaveOptions = new PdfSaveOptions(); pdfSaveOptions.setOnePagePerSheet(true); + pdfSaveOptions.setDefaultFont(DEFAULT_FONT_NAME); // for (int i = 0; i < 3; i++) { // wb.getWorksheets().get(i).getHorizontalPageBreaks().clear(); // wb.getWorksheets().get(i).getVerticalPageBreaks().clear(); @@ -105,6 +194,7 @@ public class OfficePlusUtil { public static void wordConvertPdf(String sourcePath, String targetPath) { FileOutputStream os = null; try { + configureDefaultFont(); File file = new File(targetPath); os = new FileOutputStream(file); Document doc = new Document(sourcePath); diff --git a/src/main/resources/fonts/LICENSE_NotoSansSC.txt b/src/main/resources/fonts/LICENSE_NotoSansSC.txt new file mode 100644 index 0000000..d952d62 --- /dev/null +++ b/src/main/resources/fonts/LICENSE_NotoSansSC.txt @@ -0,0 +1,92 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/main/resources/fonts/NotoSansSC-VF.ttf b/src/main/resources/fonts/NotoSansSC-VF.ttf new file mode 100644 index 0000000..cc79aef Binary files /dev/null and b/src/main/resources/fonts/NotoSansSC-VF.ttf differ