@@ -49,6 +49,7 @@ This file is part of the iText (R) project.
49
49
import com .itextpdf .io .font .TrueTypeFont ;
50
50
import com .itextpdf .io .font .otf .Glyph ;
51
51
import com .itextpdf .io .font .otf .GlyphLine ;
52
+ import com .itextpdf .io .util .EnumUtil ;
52
53
import com .itextpdf .io .util .MessageFormatUtil ;
53
54
import com .itextpdf .io .util .TextUtil ;
54
55
import com .itextpdf .kernel .colors .Color ;
@@ -559,52 +560,68 @@ public void applyOtf() {
559
560
if (!otfFeaturesApplied && TypographyUtils .isTypographyModuleInitialized () && text .start < text .end ) {
560
561
if (hasOtfFont ()) {
561
562
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 );
573
577
for (int i = text .start ; i < text .end ; i ++) {
574
578
int unicode = text .get (i ).getUnicode ();
575
579
if (unicode > -1 ) {
576
580
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
+ }
581
593
}
582
594
}
583
595
}
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 ;
593
605
}
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 );
602
617
}
603
- }
618
+ TypographyUtils . applyOtfScript ( font . getFontProgram (), text , scriptsRange . script , typographyConfig );
604
619
605
- if ( script != null ) {
606
- TypographyUtils . applyOtfScript ( font . getFontProgram (), text , script , typographyConfig ) ;
620
+ delta += text . end - scriptsRange . rangeEnd ;
621
+ scriptsRange . rangeEnd = shapingRangeStart = text . end ;
607
622
}
623
+ text .start = origTextStart ;
624
+ text .end = origTextEnd + delta ;
608
625
}
609
626
610
627
FontKerning fontKerning = (FontKerning ) this .<FontKerning >getProperty (Property .FONT_KERNING , FontKerning .NO );
@@ -1456,4 +1473,14 @@ public void remove() {
1456
1473
}
1457
1474
1458
1475
}
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
+ }
1459
1486
}
0 commit comments