diff --git a/components/api/api-pdf/src/main/java/org/eclipse/dirigible/components/api/pdf/PDFFacade.java b/components/api/api-pdf/src/main/java/org/eclipse/dirigible/components/api/pdf/PDFFacade.java index c1e870bb520..c49c081fe5b 100644 --- a/components/api/api-pdf/src/main/java/org/eclipse/dirigible/components/api/pdf/PDFFacade.java +++ b/components/api/api-pdf/src/main/java/org/eclipse/dirigible/components/api/pdf/PDFFacade.java @@ -9,9 +9,16 @@ */ package org.eclipse.dirigible.components.api.pdf; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import javax.xml.transform.Result; import javax.xml.transform.Transformer; @@ -25,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import org.xml.sax.SAXException; /** * The Class PDFFacade. @@ -35,6 +43,44 @@ public class PDFFacade { /** The Constant logger. */ private static final Logger logger = LoggerFactory.getLogger(PDFFacade.class); + /** + * The classpath folder of the bundled Unicode fonts. FOP's built-in base-14 fonts (Helvetica, + * Times, Courier) are metric-only and Latin-1 encoded, so any non-Latin glyph (e.g. Cyrillic) + * renders as '#'. DejaVu Sans is embedded as a subset to cover Latin, Cyrillic and Greek; + * stylesheets opt in via font-family="DejaVu Sans, Helvetica". + */ + private static final String FONTS_RESOURCE_FOLDER = "/META-INF/fonts/dejavu/"; + + /** The bundled font files. */ + private static final String[] FONT_FILES = + {"DejaVuSans.ttf", "DejaVuSans-Bold.ttf", "DejaVuSans-Oblique.ttf", "DejaVuSans-BoldOblique.ttf"}; + + /** The FOP configuration registering the bundled fonts for the PDF renderer. */ + private static final String FOP_CONFIG = """ + + + + + + + + + + + + + + + + + + + + """; + + /** The shared factory (the font configuration is parsed once). */ + private static volatile FopFactory fopFactory; + /** * Generate. * @@ -51,7 +97,7 @@ public static byte[] generate(String template, String data) { StreamSource templateSource = new StreamSource(IOUtils.toInputStream(template, Charset.defaultCharset())); StreamSource dataSource = new StreamSource(IOUtils.toInputStream(data, Charset.defaultCharset())); - FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); + FopFactory fopFactory = getFopFactory(); FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -71,4 +117,72 @@ public static byte[] generate(String template, String data) { throw new PDFException(e.getMessage(), e); } } + + /** + * Gets the shared FOP factory with the bundled Unicode fonts registered. Falls back to an + * unconfigured factory (base-14 fonts only) if the fonts cannot be registered. + * + * @return the FOP factory + */ + private static FopFactory getFopFactory() { + FopFactory factory = fopFactory; + if (factory == null) { + synchronized (PDFFacade.class) { + factory = fopFactory; + if (factory == null) { + factory = createFopFactory(); + fopFactory = factory; + } + } + } + return factory; + } + + /** + * Creates the FOP factory. The bundled TTF files are extracted from the classpath to a temporary + * directory, which serves as the base URI the configuration's embed-url entries resolve against + * (FOP cannot load fonts from inside a jar directly). + * + * @return the FOP factory + */ + private static FopFactory createFopFactory() { + try { + Path fontsDir = extractFonts(); + try (InputStream config = new ByteArrayInputStream(FOP_CONFIG.getBytes(StandardCharsets.UTF_8))) { + FopConfParser parser = new FopConfParser(config, fontsDir.toUri()); + return parser.getFopFactoryBuilder() + .build(); + } + } catch (IOException | SAXException e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to register the bundled Unicode fonts, falling back to the FOP base-14 fonts" + + " (non-Latin characters will render as '#'): {}", e.getMessage(), e); + } + return FopFactory.newInstance(new File(".").toURI()); + } + } + + /** + * Extract the bundled fonts to a temporary directory. + * + * @return the directory the fonts were extracted to + * @throws IOException Signals that an I/O exception has occurred. + */ + private static Path extractFonts() throws IOException { + Path fontsDir = Files.createTempDirectory("dirigible-fop-fonts"); + fontsDir.toFile() + .deleteOnExit(); + for (String fontFile : FONT_FILES) { + try (InputStream font = PDFFacade.class.getResourceAsStream(FONTS_RESOURCE_FOLDER + fontFile)) { + if (font == null) { + throw new IOException("Bundled font not found on the classpath: " + FONTS_RESOURCE_FOLDER + fontFile); + } + Path target = fontsDir.resolve(fontFile); + Files.copy(font, target, StandardCopyOption.REPLACE_EXISTING); + target.toFile() + .deleteOnExit(); + } + } + return fontsDir; + } } diff --git a/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-Bold.ttf b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-Bold.ttf new file mode 100644 index 00000000000..6d65fa7dc41 Binary files /dev/null and b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-Bold.ttf differ diff --git a/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-BoldOblique.ttf b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-BoldOblique.ttf new file mode 100644 index 00000000000..753f2d80b1f Binary files /dev/null and b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-BoldOblique.ttf differ diff --git a/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-Oblique.ttf b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-Oblique.ttf new file mode 100644 index 00000000000..999bac77141 Binary files /dev/null and b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans-Oblique.ttf differ diff --git a/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans.ttf b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans.ttf new file mode 100644 index 00000000000..e5f7eecce43 Binary files /dev/null and b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/DejaVuSans.ttf differ diff --git a/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/LICENSE b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/LICENSE new file mode 100644 index 00000000000..df52c1709be --- /dev/null +++ b/components/api/api-pdf/src/main/resources/META-INF/fonts/dejavu/LICENSE @@ -0,0 +1,187 @@ +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +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 BITSTREAM OR THE GNOME +FOUNDATION 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. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +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 +TAVMJONG BAH 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. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +TeX Gyre DJV Math +----------------- +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. + +Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski +(on behalf of TeX users groups) are in public domain. + +Letters imported from Euler Fraktur from AMSfonts are (c) American +Mathematical Society (see below). +Bitstream Vera Fonts Copyright +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera +is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license (“Fonts”) and associated +documentation +files (the “Font Software”), to reproduce and distribute the Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, +and/or sell copies of the Font Software, and to permit persons to whom +the Font Software is furnished to do so, subject to the following +conditions: + +The above copyright and trademark notices and this permission notice +shall be +included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional +glyphs or characters may be added to the Fonts, only if the fonts are +renamed +to names not containing either the words “Bitstream” or the word “Vera”. + +This License becomes null and void to the extent applicable to Fonts or +Font Software +that has been modified and is distributed under the “Bitstream Vera” +names. + +The Font Software may be sold as part of a larger software package but +no copy +of one or more of the Font Software typefaces may be sold by itself. + +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 BITSTREAM OR THE GNOME +FOUNDATION +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. +Except as contained in this notice, the names of GNOME, the GNOME +Foundation, +and Bitstream Inc., shall not be used in advertising or otherwise to promote +the sale, use or other dealings in this Font Software without prior written +authorization from the GNOME Foundation or Bitstream Inc., respectively. +For further information, contact: fonts at gnome dot org. + +AMSFonts (v. 2.2) copyright + +The PostScript Type 1 implementation of the AMSFonts produced by and +previously distributed by Blue Sky Research and Y&Y, Inc. are now freely +available for general use. This has been accomplished through the +cooperation +of a consortium of scientific publishers with Blue Sky Research and Y&Y. +Members of this consortium include: + +Elsevier Science IBM Corporation Society for Industrial and Applied +Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) + +In order to assure the authenticity of these fonts, copyright will be +held by +the American Mathematical Society. This is not meant to restrict in any way +the legitimate use of the fonts, such as (but not limited to) electronic +distribution of documents containing these fonts, inclusion of these fonts +into other public domain or commercial font collections or computer +applications, use of the outline data to create derivative fonts and/or +faces, etc. However, the AMS does require that the AMS copyright notice be +removed from any derivative versions of the fonts which have been altered in +any way. In addition, to ensure the fidelity of TeX documents using Computer +Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, +has requested that any alterations which yield different font metrics be +given a different name. + +$Id$ diff --git a/components/api/api-pdf/src/test/java/org/eclipse/dirigible/components/api/pdf/PDFFacadeTest.java b/components/api/api-pdf/src/test/java/org/eclipse/dirigible/components/api/pdf/PDFFacadeTest.java index ffc18b69c76..d6e3ac60136 100644 --- a/components/api/api-pdf/src/test/java/org/eclipse/dirigible/components/api/pdf/PDFFacadeTest.java +++ b/components/api/api-pdf/src/test/java/org/eclipse/dirigible/components/api/pdf/PDFFacadeTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; @@ -53,6 +54,29 @@ public void generatePdfTest() throws IOException { assertTrue(pdf.length > 0); } + /** + * Generate pdf with Cyrillic content test. The base-14 fonts have no Cyrillic glyphs (FOP renders + * them as '#'), so the bundled DejaVu Sans must be registered and embedded (as a subset) instead. + * + * @throws IOException Signals that an I/O exception has occurred. + */ + @Test + public void generateCyrillicPdfTest() throws IOException { + String template = IOUtils.toString(getClass().getClassLoader() + .getResourceAsStream("template-cyrillic.xsl"), + StandardCharsets.UTF_8); + String data = IOUtils.toString(getClass().getClassLoader() + .getResourceAsStream("data.xml"), + Charset.defaultCharset()); + + byte[] pdf = PDFFacade.generate(template, data); + + assertNotNull(pdf); + assertTrue(pdf.length > 0); + String content = new String(pdf, StandardCharsets.ISO_8859_1); + assertTrue(content.contains("DejaVuSans"), "The bundled DejaVu Sans font should be embedded in the PDF"); + } + /** * Generate larger pdf test. * diff --git a/components/api/api-pdf/src/test/resources/template-cyrillic.xsl b/components/api/api-pdf/src/test/resources/template-cyrillic.xsl new file mode 100644 index 00000000000..06a4da29d36 --- /dev/null +++ b/components/api/api-pdf/src/test/resources/template-cyrillic.xsl @@ -0,0 +1,27 @@ + + + + + + + + + + + + + Фактура — Тестова компания: + + + Кирилица: АБВГДЕЖЗИЙ абвгдежзий + + + + + + diff --git a/modules/parsers/document/src/main/java/org/eclipse/dirigible/parsers/document/renderer/XslFoRenderer.java b/modules/parsers/document/src/main/java/org/eclipse/dirigible/parsers/document/renderer/XslFoRenderer.java index 2179ac292d8..61007b77d97 100644 --- a/modules/parsers/document/src/main/java/org/eclipse/dirigible/parsers/document/renderer/XslFoRenderer.java +++ b/modules/parsers/document/src/main/java/org/eclipse/dirigible/parsers/document/renderer/XslFoRenderer.java @@ -71,7 +71,7 @@ public String render(LayoutNode root) { - + """); fo.append("