Skip to content

Commit 7fe7931

Browse files
Apply typography logic for every script in single text chunk separately
DEVSIX-1813
1 parent d8b3723 commit 7fe7931

File tree

1 file changed

+62
-35
lines changed

1 file changed

+62
-35
lines changed

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

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ This file is part of the iText (R) project.
4949
import com.itextpdf.io.font.TrueTypeFont;
5050
import com.itextpdf.io.font.otf.Glyph;
5151
import com.itextpdf.io.font.otf.GlyphLine;
52+
import com.itextpdf.io.util.EnumUtil;
5253
import com.itextpdf.io.util.MessageFormatUtil;
5354
import com.itextpdf.io.util.TextUtil;
5455
import com.itextpdf.kernel.colors.Color;
@@ -559,52 +560,68 @@ public void applyOtf() {
559560
if (!otfFeaturesApplied && TypographyUtils.isTypographyModuleInitialized() && text.start < text.end) {
560561
if (hasOtfFont()) {
561562
Object typographyConfig = this.<Object>getProperty(Property.TYPOGRAPHY_CONFIG);
562-
if (script == null) {
563-
Collection<Character.UnicodeScript> supportedScripts = null;
564-
if (typographyConfig != null) {
565-
supportedScripts = TypographyUtils.getSupportedScripts(typographyConfig);
566-
}
567-
if (supportedScripts == null) {
568-
supportedScripts = TypographyUtils.getSupportedScripts();
569-
}
570-
571-
// Try to autodetect complex script.
572-
Map<Character.UnicodeScript, Integer> scriptFrequency = new EnumMap<Character.UnicodeScript, Integer>(Character.UnicodeScript.class);
563+
Collection<Character.UnicodeScript> supportedScripts = null;
564+
if (typographyConfig != null) {
565+
supportedScripts = TypographyUtils.getSupportedScripts(typographyConfig);
566+
}
567+
if (supportedScripts == null) {
568+
supportedScripts = TypographyUtils.getSupportedScripts();
569+
}
570+
List<ScriptRange> scriptsRanges = new ArrayList<>();
571+
if (script != null) {
572+
scriptsRanges.add(new ScriptRange(script, text.end));
573+
} else {
574+
// Try to autodetect script.
575+
ScriptRange currRange = new ScriptRange(null, text.end);
576+
scriptsRanges.add(currRange);
573577
for (int i = text.start; i < text.end; i++) {
574578
int unicode = text.get(i).getUnicode();
575579
if (unicode > -1) {
576580
Character.UnicodeScript glyphScript = Character.UnicodeScript.of(unicode);
577-
if (scriptFrequency.containsKey(glyphScript)) {
578-
scriptFrequency.put(glyphScript, scriptFrequency.get(glyphScript) + 1);
579-
} else {
580-
scriptFrequency.put(glyphScript, 1);
581+
if (Character.UnicodeScript.COMMON.equals(glyphScript) || Character.UnicodeScript.UNKNOWN.equals(glyphScript)
582+
|| Character.UnicodeScript.INHERITED.equals(glyphScript)) {
583+
continue;
584+
}
585+
if (glyphScript != currRange.script) {
586+
if (currRange.script == null) {
587+
currRange.script = glyphScript;
588+
} else {
589+
currRange.rangeEnd = i;
590+
currRange = new ScriptRange(glyphScript, text.end);
591+
scriptsRanges.add(currRange);
592+
}
581593
}
582594
}
583595
}
584-
Integer max = 0;
585-
Map.Entry<Character.UnicodeScript, Integer> selectedEntry = null;
586-
for (Map.Entry<Character.UnicodeScript, Integer> entry : scriptFrequency.entrySet()) {
587-
Character.UnicodeScript entryScript = entry.getKey();
588-
if (entry.getValue() > max && !Character.UnicodeScript.COMMON.equals(entryScript) && !Character.UnicodeScript.UNKNOWN.equals(entryScript)
589-
&& !Character.UnicodeScript.INHERITED.equals(entryScript)) {
590-
max = entry.getValue();
591-
selectedEntry = entry;
592-
}
596+
}
597+
598+
int delta = 0;
599+
int origTextStart = text.start;
600+
int origTextEnd = text.end;
601+
int shapingRangeStart = text.start;
602+
for (ScriptRange scriptsRange : scriptsRanges) {
603+
if (scriptsRange.script == null || !supportedScripts.contains(EnumUtil.throwIfNull(scriptsRange.script))) {
604+
continue;
593605
}
594-
if (selectedEntry != null) {
595-
Character.UnicodeScript selectScript = ((Map.Entry<Character.UnicodeScript, Integer>) selectedEntry).getKey();
596-
if ((selectScript == Character.UnicodeScript.ARABIC || selectScript == Character.UnicodeScript.HEBREW) && parent instanceof LineRenderer) {
597-
setProperty(Property.BASE_DIRECTION, BaseDirection.DEFAULT_BIDI);
598-
}
599-
if (supportedScripts != null && supportedScripts.contains(selectScript)) {
600-
script = selectScript;
601-
}
606+
scriptsRange.rangeEnd += delta;
607+
text.start = shapingRangeStart;
608+
text.end = scriptsRange.rangeEnd;
609+
610+
if ((scriptsRange.script == Character.UnicodeScript.ARABIC || scriptsRange.script == Character.UnicodeScript.HEBREW) && parent instanceof LineRenderer) {
611+
// It's safe to set here BASE_DIRECTION to TextRenderer without additional checks, because
612+
// by convention this property makes sense only if it's applied to LineRenderer or it's
613+
// parents (Paragraph or above).
614+
// Only if it's not found there first, LineRenderer tries to fetch autodetected BaseDirection
615+
// from text renderers (see LineRenderer#applyOtf).
616+
setProperty(Property.BASE_DIRECTION, BaseDirection.DEFAULT_BIDI);
602617
}
603-
}
618+
TypographyUtils.applyOtfScript(font.getFontProgram(), text, scriptsRange.script, typographyConfig);
604619

605-
if (script != null) {
606-
TypographyUtils.applyOtfScript(font.getFontProgram(), text, script, typographyConfig);
620+
delta += text.end - scriptsRange.rangeEnd;
621+
scriptsRange.rangeEnd = shapingRangeStart = text.end;
607622
}
623+
text.start = origTextStart;
624+
text.end = origTextEnd + delta;
608625
}
609626

610627
FontKerning fontKerning = (FontKerning) this.<FontKerning>getProperty(Property.FONT_KERNING, FontKerning.NO);
@@ -1456,4 +1473,14 @@ public void remove() {
14561473
}
14571474

14581475
}
1476+
1477+
private static class ScriptRange {
1478+
Character.UnicodeScript script;
1479+
int rangeEnd;
1480+
1481+
ScriptRange(Character.UnicodeScript script, int rangeEnd) {
1482+
this.script = script;
1483+
this.rangeEnd = rangeEnd;
1484+
}
1485+
}
14591486
}

0 commit comments

Comments
 (0)