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("