diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 035a21dea8..db50e4f01d 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -3,6 +3,7 @@
name: OpenPDF maven build
on:
+ workflow_dispatch:
push:
branches: [ '*' ]
pull_request:
diff --git a/openpdf/src/main/java/com/lowagie/text/pdf/BaseFont.java b/openpdf/src/main/java/com/lowagie/text/pdf/BaseFont.java
index 671e4cdaaf..b6b8e59eae 100644
--- a/openpdf/src/main/java/com/lowagie/text/pdf/BaseFont.java
+++ b/openpdf/src/main/java/com/lowagie/text/pdf/BaseFont.java
@@ -724,6 +724,7 @@ public static BaseFont createFont(String name, String encoding,
if (cached) {
fontFound = fontCache.get(key);
if (fontFound != null) {
+ LayoutProcessor.loadFont(fontFound, name);
return fontFound;
}
}
diff --git a/openpdf/src/main/java/com/lowagie/text/pdf/FontDetails.java b/openpdf/src/main/java/com/lowagie/text/pdf/FontDetails.java
index 54beb26f99..567210e793 100755
--- a/openpdf/src/main/java/com/lowagie/text/pdf/FontDetails.java
+++ b/openpdf/src/main/java/com/lowagie/text/pdf/FontDetails.java
@@ -54,6 +54,7 @@
import com.lowagie.text.Utilities;
import java.awt.font.GlyphVector;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -155,6 +156,18 @@ void putFillerCmap(Integer key, int[] value) {
fillerCmap.put(key, value);
}
+ void addMissingCmapEntries(String text, GlyphVector glyphVector, BaseFont baseFont) {
+
+ if (baseFont instanceof TrueTypeFontUnicode trueTypeFont && getFillerCmap() != null) {
+ int[][] localCmap = trueTypeFont.getSentenceMissingCmap(text, glyphVector);
+
+ for (int[] ints : localCmap) {
+ putFillerCmap(ints[0], new int[]{ints[0], ints[1]});
+ }
+ }
+ }
+
+
/**
* Gets the indirect reference to this font.
*
@@ -291,6 +304,32 @@ private byte[] getCJKEncodingBytes(int[] glyph, int size) {
return result;
}
+ /**
+ * Convert a glyph code to bytes
+ *
+ * @param glyphCode
+ * @return byte array with one or two bytes as UTF-16BE representation of the glyph code
+ * @see convertToBytes(GlyphVector glyphVector,...)
+ */
+ byte[] convertToBytes(final int glyphCode) {
+ if (fontType != BaseFont.FONT_TYPE_TTUNI) {
+ throw new UnsupportedOperationException("Only supported for True Type Unicode fonts");
+ }
+ if (glyphCode == 0xFFFE || glyphCode == 0xFFFF) {
+ // considered non-glyphs by AWT
+ return new byte[]{};
+ }
+ if (!longTag.containsKey(glyphCode)) {
+ int glyphWidth = ttu.getGlyphWidth(glyphCode);
+ Integer charCode = ttu.getCharacterCode(glyphCode);
+ int[] metrics = charCode != null ? new int[]{glyphCode, glyphWidth, charCode} : new int[]{
+ glyphCode, glyphWidth};
+ longTag.put(glyphCode, metrics);
+ }
+ String s = new String(Character.toChars(glyphCode));
+ return s.getBytes(StandardCharsets.UTF_16BE);
+ }
+
byte[] convertToBytes(GlyphVector glyphVector, int beginIndex, int endIndex) {
if (fontType != BaseFont.FONT_TYPE_TTUNI || symbolic) {
throw new UnsupportedOperationException("Only supported for True Type Unicode fonts");
@@ -305,7 +344,7 @@ byte[] convertToBytes(GlyphVector glyphVector, int beginIndex, int endIndex) {
continue;
}
- glyphs[glyphCount++] = (char) code; // FIXME supplementary plane?
+ glyphCount += Character.toChars(code, glyphs, glyphCount);
Integer codeKey = code;
if (!longTag.containsKey(codeKey)) {
@@ -318,11 +357,7 @@ byte[] convertToBytes(GlyphVector glyphVector, int beginIndex, int endIndex) {
}
String s = new String(glyphs, 0, glyphCount);
- try {
- return s.getBytes(CJKFont.CJK_ENCODING);
- } catch (UnsupportedEncodingException e) {
- throw new ExceptionConverter(e);
- }
+ return s.getBytes(StandardCharsets.UTF_16BE);
}
diff --git a/openpdf/src/main/java/com/lowagie/text/pdf/LayoutProcessor.java b/openpdf/src/main/java/com/lowagie/text/pdf/LayoutProcessor.java
index 58e81d9943..8c66f7c69b 100644
--- a/openpdf/src/main/java/com/lowagie/text/pdf/LayoutProcessor.java
+++ b/openpdf/src/main/java/com/lowagie/text/pdf/LayoutProcessor.java
@@ -1,7 +1,7 @@
/*
* LayoutProcessor.java
*
- * Copyright 2020-2022 Volker Kunert.
+ * Copyright 2020-2024 Volker Kunert.
*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
@@ -65,6 +65,13 @@
*/
public class LayoutProcessor {
+ public enum Version {
+ ONE,
+ TWO
+ }
+
+ private static Version version = Version.TWO;
+
private static final int DEFAULT_FLAGS = -1;
private static final Map awtFontMap = new ConcurrentHashMap<>();
@@ -74,6 +81,8 @@ public class LayoutProcessor {
private static boolean enabled = false;
private static int flags = DEFAULT_FLAGS;
+ private static boolean writeActualText;
+
private LayoutProcessor() {
throw new UnsupportedOperationException("static class");
}
@@ -132,6 +141,17 @@ public static boolean isEnabled() {
return enabled;
}
+ /**
+ * Set version
+ *
+ * @param version to set
+ * @deprecated To be used *only*, if version two produces incorrect PDF - please file an issue if this occurs
+ */
+ @Deprecated
+ public static void setVersion(Version version) {
+ LayoutProcessor.version = version;
+ }
+
/**
* Set kerning
*
@@ -208,8 +228,8 @@ private static void setRunDirection(com.lowagie.text.Font font, Boolean runDirec
*
* @param font The font for which kerning is to be turned on
* @param textAttributes Map of text attributes to be set
- * @see Oracle: The Java™ Tutorials,
- * Using Text Attributes to Style Text
+ * @see
+ * Oracle: The Java™ Tutorials, Using Text Attributes to Style Text
*/
private static void setTextAttributes(com.lowagie.text.Font font, Map textAttributes) {
BaseFont baseFont = font.getBaseFont();
@@ -220,10 +240,26 @@ private static void setTextAttributes(com.lowagie.text.Font font, Map= deltaY) {
+ if (!ga.isEmpty()) {
+ cb.showText(ga);
+ ga.clear();
+ }
+ cb.setTextRise(-py);
+ }
+ if (Math.abs(dx) >= deltaX) {
+ ga.add(-dx * factorX);
+ }
+ ga.add(glyphVector.getGlyphCode(i));
+ if (Math.abs(py) >= deltaY) {
+ cb.showText(ga);
+ ga.clear();
+ cb.setTextRise(0.0f);
+ }
+ lastX = (float) p.getX();
+ }
+ Point2D p = glyphVector.getGlyphPosition(glyphVector.getNumGlyphs());
+ float ax = (glyphVector.getNumGlyphs() == 0) ? 0.0f : glyphVector.getGlyphMetrics(glyphVector.getNumGlyphs() - 1).getAdvanceX();
+ float dx = (float) p.getX() - lastX - ax;
+ if (Math.abs(dx) >= deltaX) {
+ ga.add(-dx * factorX);
+ }
+ cb.showText(ga);
+ ga.clear();
}
public static void disable() {
@@ -407,5 +519,7 @@ public static void disable() {
flags = DEFAULT_FLAGS;
awtFontMap.clear();
globalTextAttributes.clear();
+ writeActualText = false;
+ setVersion(Version.TWO);
}
}
diff --git a/openpdf/src/main/java/com/lowagie/text/pdf/PdfContentByte.java b/openpdf/src/main/java/com/lowagie/text/pdf/PdfContentByte.java
index 40b1fae2c7..fc11e8acbc 100644
--- a/openpdf/src/main/java/com/lowagie/text/pdf/PdfContentByte.java
+++ b/openpdf/src/main/java/com/lowagie/text/pdf/PdfContentByte.java
@@ -261,17 +261,17 @@ public static PdfTextArray getKernArray(String text, BaseFont font) {
*/
static byte[] escapeString(byte[] b) {
ByteBuffer content = new ByteBuffer();
- escapeString(b, content);
+ escapeAndAppendString(b, content);
return content.toByteArray();
}
/**
- * Escapes a byte array according to the PDF conventions.
+ * Escapes a byte array according to the PDF conventions and append it to content
*
* @param b the byte array to escape
- * @param content the content
+ * @param content the content to append the escaped string
*/
- static void escapeString(byte[] b, ByteBuffer content) {
+ static void escapeAndAppendString(byte[] b, ByteBuffer content) {
content.append_i('(');
for (byte c : b) {
switch (c) {
@@ -1688,7 +1688,7 @@ private void showText2(String text) {
MessageLocalization.getComposedMessage("font.and.size.must.be.set.before.writing.any.text"));
}
byte[] b = state.fontDetails.convertToBytes(text, getPdfDocument().getTextRenderingOptions());
- escapeString(b, content);
+ escapeAndAppendString(b, content);
}
/**
@@ -1747,7 +1747,7 @@ public void showText(GlyphVector glyphVector, int beginIndex, int endIndex) {
MessageLocalization.getComposedMessage("font.and.size.must.be.set.before.writing.any.text"));
}
byte[] b = state.fontDetails.convertToBytes(glyphVector, beginIndex, endIndex);
- escapeString(b, content);
+ escapeAndAppendString(b, content);
content.append("Tj").append_i(separator);
}
@@ -2161,8 +2161,8 @@ public PdfPatternPainter createPattern(float width, float height, Color color) {
* Creates a new template.
*
* Creates a new template that is nothing more than a form XObject. This template can be included in this
- * PdfContentByte or in another template. Templates are only written to the output when the document
- * is closed permitting things like showing text in the first page that is only defined in the last page.
+ * PdfContentByte or in another template. Templates are only written to the output when the document is
+ * closed permitting things like showing text in the first page that is only defined in the last page.
*
* @param width the bounding box width
* @param height the bounding box height
@@ -2750,6 +2750,36 @@ protected void checkWriter() {
}
}
+ /**
+ * Show an array of glyphs.
+ *
+ * @param glyphs array of glyphs
+ */
+ public void showText(PdfGlyphArray glyphs) {
+ if (state.fontDetails == null) {
+ throw new NullPointerException(
+ MessageLocalization.getComposedMessage("font.and.size.must.be.set.before.writing.any.text"));
+ }
+ content.append("[");
+ List arrayList = glyphs.getList();
+ boolean lastWasDisplacement = false;
+ for (Object obj : arrayList) {
+ if (obj instanceof Integer) { // glyph code
+ byte[] b = state.fontDetails.convertToBytes((Integer) obj);
+ escapeAndAppendString(b, content); // appends escapedString to content
+ lastWasDisplacement = false;
+ } else { // displacement
+ if (lastWasDisplacement) {
+ content.append(' ');
+ } else {
+ lastWasDisplacement = true;
+ }
+ content.append((Float) obj);
+ }
+ }
+ content.append("]TJ").append_i(separator);
+ }
+
/**
* Show an array of text.
*
diff --git a/openpdf/src/main/java/com/lowagie/text/pdf/PdfGlyphArray.java b/openpdf/src/main/java/com/lowagie/text/pdf/PdfGlyphArray.java
new file mode 100644
index 0000000000..fc491c76f0
--- /dev/null
+++ b/openpdf/src/main/java/com/lowagie/text/pdf/PdfGlyphArray.java
@@ -0,0 +1,70 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * https://github.com/LibrePDF/OpenPDF
+ *
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Defines an array with displacements and glyph codes.
+ */
+public class PdfGlyphArray {
+ private final List