Skip to content

Commit 8e46d97

Browse files
committed
Layout: script autodetection
Now iText tries to automatically guess if one of the supported complex scripts is used in Text elements in case typography module is initialized and no script is specified. If a script is manually specified, it has priority over autodetection DEVSIX-474
1 parent 813dfa1 commit 8e46d97

File tree

5 files changed

+61
-28
lines changed

5 files changed

+61
-28
lines changed

forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2278,7 +2278,6 @@ private void setParagraphProperties(Paragraph paragraph, String value) {
22782278
// TODO this is temporary and will be replaced by script autodetection logic on layout level
22792279
if (value != null && value.length() > 0) {
22802280
Character.UnicodeScript script = Character.UnicodeScript.of(value.charAt(0));
2281-
paragraph.setFontScript(script);
22822281
if (script == Character.UnicodeScript.ARABIC || script == Character.UnicodeScript.HEBREW) {
22832282
paragraph.setBaseDirection(Property.BaseDirection.RIGHT_TO_LEFT);
22842283
}

kernel/src/main/java/com/itextpdf/kernel/parser/SimpleTextExtractionStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public void eventOccurred(EventData data, EventType type) {
6565

6666
@Override
6767
public Set<EventType> getSupportedEvents() {
68-
return new LinkedHashSet<>(Collections.singletonList(EventType.RENDER_TEXT));
68+
return Collections.unmodifiableSet(new LinkedHashSet<>(Collections.singletonList(EventType.RENDER_TEXT)));
6969
}
7070

7171
/**

layout/src/main/java/com/itextpdf/layout/renderer/TextRenderer.java

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
import com.itextpdf.io.font.TrueTypeFont;
66
import com.itextpdf.io.font.otf.Glyph;
77
import com.itextpdf.io.font.otf.GlyphLine;
8+
import com.itextpdf.kernel.color.Color;
9+
import com.itextpdf.kernel.font.PdfFont;
10+
import com.itextpdf.kernel.font.PdfType0Font;
811
import com.itextpdf.kernel.geom.Rectangle;
912
import com.itextpdf.kernel.pdf.PdfDocument;
1013
import com.itextpdf.kernel.pdf.PdfName;
1114
import com.itextpdf.kernel.pdf.canvas.CanvasArtifact;
1215
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
13-
import com.itextpdf.kernel.color.Color;
14-
import com.itextpdf.kernel.font.PdfFont;
15-
import com.itextpdf.kernel.font.PdfType0Font;
1616
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants;
1717
import com.itextpdf.kernel.pdf.tagutils.IAccessibleElement;
1818
import com.itextpdf.kernel.pdf.tagutils.PdfTagStructure;
@@ -27,13 +27,11 @@
2727
import com.itextpdf.layout.layout.TextLayoutResult;
2828
import com.itextpdf.layout.splitting.ISplitCharacters;
2929

30-
import java.lang.reflect.Constructor;
31-
import java.lang.reflect.Method;
32-
import java.util.ArrayList;
30+
import java.util.Collection;
31+
import java.util.EnumMap;
32+
import java.util.HashMap;
3333
import java.util.List;
34-
35-
import org.slf4j.Logger;
36-
import org.slf4j.LoggerFactory;
34+
import java.util.Map;
3735

3836
/**
3937
* This class represents the {@link IRenderer renderer} object for a {@link Text}
@@ -323,14 +321,47 @@ public void applyOtf() {
323321
Character.UnicodeScript script = getProperty(Property.FONT_SCRIPT);
324322
Property.FontKerning fontKerning = getProperty(Property.FONT_KERNING);
325323
PdfFont font = getPropertyAsFont(Property.FONT);
326-
if (!otfFeaturesApplied && script != null && isOtfFont(font)) {
327-
TypographyUtils.applyOtfScript(font.getFontProgram(), text, script);
328-
}
324+
if (!otfFeaturesApplied) {
325+
if (script == null && TypographyUtils.isTypographyModuleInitialized()) {
326+
// Try to autodetect complex script.
327+
Collection<Character.UnicodeScript> supportedScripts = TypographyUtils.getSupportedScripts();
328+
if (supportedScripts != null) {
329+
Map<Character.UnicodeScript, Integer> scriptFrequency = new EnumMap<>(Character.UnicodeScript.class);
330+
for (int i = text.start; i < text.end; i++) {
331+
Integer unicode = text.get(i).getUnicode();
332+
Character.UnicodeScript glyphScript = unicode != null ? Character.UnicodeScript.of(unicode) : null;
333+
if (glyphScript != null && supportedScripts.contains(glyphScript)) {
334+
if (scriptFrequency.containsKey(glyphScript)) {
335+
scriptFrequency.put(glyphScript, scriptFrequency.get(glyphScript));
336+
} else {
337+
scriptFrequency.put(glyphScript, 1);
338+
}
339+
}
340+
}
341+
int max = 0;
342+
Character.UnicodeScript selectScript = null;
343+
for (Map.Entry<Character.UnicodeScript, Integer> entry : scriptFrequency.entrySet()) {
344+
if (entry.getValue() > max) {
345+
max = entry.getValue();
346+
selectScript = entry.getKey();
347+
}
348+
}
349+
if (selectScript != null) {
350+
script = selectScript;
351+
}
352+
}
353+
}
354+
355+
if (isOtfFont(font) && script != null) {
356+
TypographyUtils.applyOtfScript(font.getFontProgram(), text, script);
357+
}
329358

330-
if (!otfFeaturesApplied && fontKerning == Property.FontKerning.YES) {
331-
TypographyUtils.applyKerning(font.getFontProgram(), text);
359+
if (fontKerning == Property.FontKerning.YES) {
360+
TypographyUtils.applyKerning(font.getFontProgram(), text);
361+
}
362+
363+
otfFeaturesApplied = true;
332364
}
333-
otfFeaturesApplied = true;
334365
}
335366

336367
@Override
@@ -647,16 +678,6 @@ public TextRenderer getNextRenderer() {
647678
return new TextRenderer((Text) modelElement, null);
648679
}
649680

650-
private static boolean checkTypographyModulePresence() {
651-
boolean moduleFound = false;
652-
try {
653-
Class.forName("com.itextpdf.typography.shaping.Shaper");
654-
moduleFound = true;
655-
} catch (ClassNotFoundException ignored) {
656-
}
657-
return moduleFound;
658-
}
659-
660681
private boolean isNewLine(GlyphLine text, int ind) {
661682
return text.glyphs.get(ind).getUnicode() != null && text.glyphs.get(ind).getUnicode() == '\n';
662683
}

layout/src/main/java/com/itextpdf/layout/renderer/TypographyUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.lang.reflect.Constructor;
1010
import java.lang.reflect.Method;
1111
import java.util.ArrayList;
12+
import java.util.Collection;
1213
import java.util.List;
1314

1415
import org.slf4j.Logger;
@@ -105,6 +106,19 @@ static List<LineRenderer.RendererGlyph> reoderLine(List<LineRenderer.RendererGly
105106
return null;
106107
}
107108

109+
static Collection<Character.UnicodeScript> getSupportedScripts() {
110+
if (!TYPOGRAPHY_MODULE_INITIALIZED) {
111+
logger.warn("Cannot find advanced typography module, which was implicitly required by one of the layout properties");
112+
return null;
113+
} else {
114+
return (Collection<Character.UnicodeScript>)callMethod(TYPOGRAPHY_PACKAGE + "shaping.Shaper", "getSupportedScripts", new Class[] {});
115+
}
116+
}
117+
118+
static boolean isTypographyModuleInitialized() {
119+
return TYPOGRAPHY_MODULE_INITIALIZED;
120+
}
121+
108122
private static boolean checkTypographyModulePresence() {
109123
boolean moduleFound = false;
110124
try {

sign/src/main/java/com/itextpdf/signatures/PdfSignatureAppearance.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,6 @@ private Paragraph setParagraphProperties(Paragraph paragraph, String value) {
823823
// TODO this is temporary and will be replaced by script autodetection logic on layout level
824824
if (value != null && value.length() > 0) {
825825
Character.UnicodeScript script = Character.UnicodeScript.of(value.charAt(0));
826-
paragraph.setFontScript(script);
827826
if (script == Character.UnicodeScript.ARABIC || script == Character.UnicodeScript.HEBREW) {
828827
paragraph.setBaseDirection(Property.BaseDirection.RIGHT_TO_LEFT);
829828
}

0 commit comments

Comments
 (0)