Skip to content

Commit a05a7c5

Browse files
Introduce TYPOGRAPHY_CONFIG property
Also introduce EnumUtil#throwIfNull for unwrapping enums from nullable in .net; introduce methods for surrogate pairs creation in TextUtil. DEVSIX-1813
1 parent 48b480c commit a05a7c5

File tree

6 files changed

+97
-48
lines changed

6 files changed

+97
-48
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ public final class LogMessageConstant {
7171
public static final String COULD_NOT_FIND_GLYPH_WITH_CODE = "Could not find glyph with the following code: {0}";
7272
public static final String CREATED_ROOT_TAG_HAS_MAPPING = "Created root tag has role mapping: \"/Document\" role{0} is mapped{1}. Resulting tag structure might have invalid root tag.";
7373
public static final String DESTINATION_NOT_PERMITTED_WHEN_ACTION_IS_SET = "Destinations are not permitted for link annotations that already have actions. The old action will be removed.";
74-
public static final String EMBEDDED_GO_TO_DESTINATION_NOT_SPECIFIED = "No destination in the target was specified for action. Destination entry is mandatory for embedded go-to actions.";
7574
public static final String DIRECTONLY_OBJECT_CANNOT_BE_INDIRECT = "DirectOnly object cannot be indirect";
7675
public static final String DOCFONT_HAS_ILLEGAL_DIFFERENCES = "Document Font has illegal differences array. Entry {0} references a glyph ID over 255 and will be ignored.";
7776
public static final String DOCUMENT_ALREADY_HAS_FIELD = "The document already has field {0}. Annotations of the fields with this name will be added to the existing one as children. If you want to have separate fields, please, rename them manually before copying.";
7877
public static final String DOCUMENT_SERIALIZATION_EXCEPTION_RAISED = "Unhandled exception while serialization";
7978
public static final String ELEMENT_DOES_NOT_FIT_AREA = "Element does not fit current area. {0}";
8079
public static final String ELEMENT_WAS_FORCE_PLACED_KEEP_WITH_NEXT_WILL_BE_IGNORED = "Element was placed in a forced way. Keep with next property will be ignored";
80+
public static final String EMBEDDED_GO_TO_DESTINATION_NOT_SPECIFIED = "No destination in the target was specified for action. Destination entry is mandatory for embedded go-to actions.";
8181
public static final String ENCOUNTERED_INVALID_MCR = "Corrupted tag structure: encountered invalid marked content reference - it doesn't refer to any page or any mcid. This content reference will be ignored.";
8282
public static final String ENCRYPTED_PAYLOAD_FILE_SPEC_SHALL_HAVE_AFRELATIONSHIP_FILED_EQUAL_TO_ENCRYPTED_PAYLOAD = "Encrypted payload file spec shall have 'AFRelationship' filed equal to 'EncryptedPayload'";
8383
public static final String ENCRYPTION_ENTRIES_P_AND_ENCRYPT_METADATA_NOT_CORRESPOND_PERMS_ENTRY = "Encryption dictionary entries P and EncryptMetadata have value that does not correspond to encrypted values in Perms key.";
@@ -97,12 +97,12 @@ public final class LogMessageConstant {
9797
public static final String GRAPHICS_STATE_WAS_DELETED = "Graphics state is always deleted after event dispatching. If you want to preserve it in renderer info, use preserveGraphicsState method after receiving renderer info.";
9898
public static final String IF_PATH_IS_SET_VERTICES_SHALL_NOT_BE_PRESENT = "If Path key is set, Vertices key shall not be present. Remove Vertices key before setting Path";
9999
public static final String IMAGE_HAS_AMBIGUOUS_SCALE = "The image cannot be auto scaled and scaled by a certain parameter simultaneously";
100-
public static final String IMAGE_HAS_JBIG2DECODE_FILTER = "Image cannot be inline if it has JBIG2Decode filter. It will be added as an ImageXObject";
101-
public static final String IMAGE_HAS_JPXDECODE_FILTER = "Image cannot be inline if it has JPXDecode filter. It will be added as an ImageXObject";
102-
public static final String IMAGE_HAS_ICC_PROFILE_WITH_INCOMPATIBLE_NUMBER_OF_COLOR_COMPONENTS_COMPARED_TO_COLOR_SPACE = "Image has icc profile with incompatible number of color components compared to image color space. The icc profile will be ignored.";
103100
public static final String IMAGE_HAS_ICC_PROFILE_WITH_INCOMPATIBLE_NUMBER_OF_COLOR_COMPONENTS_COMPARED_TO_BASE_COLOR_SPACE_IN_INDEXED_COLOR_SPACE = "Image has icc profile with incompatible number of color components compared to base color space in image indexed color space. The icc profile will be ignored.";
101+
public static final String IMAGE_HAS_ICC_PROFILE_WITH_INCOMPATIBLE_NUMBER_OF_COLOR_COMPONENTS_COMPARED_TO_COLOR_SPACE = "Image has icc profile with incompatible number of color components compared to image color space. The icc profile will be ignored.";
104102
public static final String IMAGE_HAS_INCORRECT_OR_UNSUPPORTED_BASE_COLOR_SPACE_IN_INDEXED_COLOR_SPACE_OVERRIDDEN_BY_ICC_PROFILE = "Image has incorrect or unsupported base color space in indexed color space, it will be overridden by one based on embedded icc profile.";
105103
public static final String IMAGE_HAS_INCORRECT_OR_UNSUPPORTED_COLOR_SPACE_OVERRIDDEN_BY_ICC_PROFILE = "Image has incorrect or unsupported color space, that will be overridden by one based on embedded icc profile.";
104+
public static final String IMAGE_HAS_JBIG2DECODE_FILTER = "Image cannot be inline if it has JBIG2Decode filter. It will be added as an ImageXObject";
105+
public static final String IMAGE_HAS_JPXDECODE_FILTER = "Image cannot be inline if it has JPXDecode filter. It will be added as an ImageXObject";
106106
public static final String IMAGE_HAS_MASK = "Image cannot be inline if it has a Mask";
107107
public static final String IMAGE_SIZE_CANNOT_BE_MORE_4KB = "Inline image size cannot be more than 4KB. It will be added as an ImageXObject";
108108
public static final String INCORRECT_PAGEROTATION = "Encounterd a page rotation that was not a multiple of 90°/ (Pi/2) when generating default appearances for form fields";
@@ -145,15 +145,16 @@ public final class LogMessageConstant {
145145
public static final String SOME_TARGET_FIELDS_ARE_NOT_SET_OR_INCORRECT = "Some fields in target dictionary are not set or incorrect. Null will be returned.";
146146
public static final String SOURCE_DOCUMENT_HAS_ACROFORM_DICTIONARY = "Source document has AcroForm dictionary. The pages you are going to copy may have FormFields, but they will not be copied, because you have not used any IPdfPageExtraCopier";
147147
public static final String START_MARKER_MISSING_IN_PFB_FILE = "Start marker is missing in the pfb file";
148+
public static final String STDSCRIPTCONFIG_DESIGNED_ONLY_FOR_STD_SCRIPTS = "StandardScriptConfig class is designed for only standard scripts, otherwise it might be incompatible.";
148149
public static final String STRUCTURE_ELEMENT_REPLACED_BY_ITS_ID_IN_STRUCTURE_DESTINATION = "Structure destinations shall specify structure element ID in remote go-to actions. Structure element has been replaced with its ID in the structure destination";
149150
public static final String SUM_OF_TABLE_COLUMNS_IS_GREATER_THAN_100 = "Sum of table columns is greater than 100%.";
150151
public static final String TABLE_WIDTH_IS_MORE_THAN_EXPECTED_DUE_TO_MIN_WIDTH = "Table width is more than expected due to min width of cell(s).";
151152
public static final String TAGGING_HINT_NOT_FINISHED_BEFORE_CLOSE = "Tagging hint wasn't finished before closing.";
152153
public static final String TAG_STRUCTURE_CONTEXT_WILL_BE_REINITIALIZED_ON_SERIALIZATION = "Tag structure context is not null and will be reinitialized in the copy of document. The copy may lose some data";
153154
public static final String TAG_STRUCTURE_INIT_FAILED = "Tag structure initialization failed, tag structure is ignored, it might be corrupted.";
155+
public static final String TOUNICODE_CMAP_MORE_THAN_2_BYTES_NOT_SUPPORTED = "ToUnicode CMap more than 2 bytes not supported.";
154156
public static final String TYPE3_FONT_CANNOT_BE_ADDED = "Type 3 font cannot be added to FontSet. Custom FontProvider class may be created for this purpose.";
155157
public static final String TYPE3_FONT_ISSUE_TAGGED_PDF = "Type 3 font issue. Font Descriptor is required for tagged PDF. FontName shall be specified.";
156-
public static final String TOUNICODE_CMAP_MORE_THAN_2_BYTES_NOT_SUPPORTED = "ToUnicode CMap more than 2 bytes not supported.";
157158
public static final String UNABLE_TO_REGISTER_EVENT_DATA_HANDLER_SHUTDOWN_HOOK = "Unable to register event data handler shutdown hook because of security reasons.";
158159
public static final String UNABLE_TO_SEARCH_FOR_EVENT_CONTEXT = "It is impossible to retrieve event context because of the security reasons. Event counting may behave in unexpected way";
159160
public static final String UNEXPECTED_BEHAVIOUR_DURING_TABLE_ROW_COLLAPSING = "Unexpected behaviour during table row collapsing. Calculated rowspan was less then 1.";
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.itextpdf.io.util;
2+
3+
/**
4+
* This file is a helper class for internal usage only.
5+
* Be aware that it's API and functionality may be changed in future.
6+
*/
7+
public final class EnumUtil {
8+
private EnumUtil() {
9+
}
10+
11+
public static <T extends Enum<T>> T throwIfNull(T enumInstance) {
12+
if (enumInstance == null) {
13+
throw new RuntimeException("Expected not null enum instance");
14+
}
15+
return enumInstance;
16+
}
17+
}

io/src/main/java/com/itextpdf/io/util/TextUtil.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ public static boolean isSurrogateLow(char c) {
8181
return c >= '\udc00' && c <= '\udfff';
8282
}
8383

84+
public static char highSurrogate(int codePoint) {
85+
return (char) ((codePoint >>> 10)
86+
+ ('\uD800' - (0x010000 >>> 10)));
87+
}
88+
89+
public static char lowSurrogate(int codePoint) {
90+
return (char) ((codePoint & 0x3ff) + '\uDC00');
91+
}
92+
8493
/**
8594
* Checks if two subsequent characters in a String are
8695
* are the higher and the lower character in a surrogate

layout/src/main/java/com/itextpdf/layout/property/Property.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ private Property() {
191191
public static final int TEXT_RISE = 72;
192192
public static final int TOP = 73;
193193
public static final int TRANSFORM = 53;
194+
public static final int TYPOGRAPHY_CONFIG = 117;
194195
public static final int UNDERLINE = 74;
195196
public static final int VERTICAL_ALIGNMENT = 75;
196197
public static final int VERTICAL_BORDER_SPACING = 116;
@@ -207,7 +208,7 @@ private Property() {
207208
* related to textual operations. Indicates whether or not this type of property is inheritable.
208209
*/
209210
private static final boolean[] INHERITED_PROPERTIES;
210-
private static final int MAX_INHERITED_PROPERTY_ID = 108;
211+
private static final int MAX_INHERITED_PROPERTY_ID = 117;
211212

212213
static {
213214
INHERITED_PROPERTIES = new boolean[MAX_INHERITED_PROPERTY_ID + 1];
@@ -242,6 +243,7 @@ private Property() {
242243
INHERITED_PROPERTIES[Property.UNDERLINE] = true;
243244
INHERITED_PROPERTIES[Property.WORD_SPACING] = true;
244245
INHERITED_PROPERTIES[Property.TAGGING_HELPER] = true;
246+
INHERITED_PROPERTIES[Property.TYPOGRAPHY_CONFIG] = true;
245247
}
246248

247249
/**

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

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -556,45 +556,55 @@ public LayoutResult layout(LayoutContext layoutContext) {
556556
public void applyOtf() {
557557
updateFontAndText();
558558
Character.UnicodeScript script = this.<Character.UnicodeScript>getProperty(Property.FONT_SCRIPT);
559-
if (!otfFeaturesApplied) {
560-
if (script == null && TypographyUtils.isTypographyModuleInitialized()) {
561-
// Try to autodetect complex script.
562-
Collection<Character.UnicodeScript> supportedScripts = TypographyUtils.getSupportedScripts();
563-
Map<Character.UnicodeScript, Integer> scriptFrequency = new EnumMap<Character.UnicodeScript, Integer>(Character.UnicodeScript.class);
564-
for (int i = text.start; i < text.end; i++) {
565-
int unicode = text.get(i).getUnicode();
566-
if (unicode > -1) {
567-
Character.UnicodeScript glyphScript = Character.UnicodeScript.of(unicode);
568-
if (scriptFrequency.containsKey(glyphScript)) {
569-
scriptFrequency.put(glyphScript, scriptFrequency.get(glyphScript) + 1);
570-
} else {
571-
scriptFrequency.put(glyphScript, 1);
559+
if (!otfFeaturesApplied && TypographyUtils.isTypographyModuleInitialized() && text.start < text.end) {
560+
if (hasOtfFont()) {
561+
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);
573+
for (int i = text.start; i < text.end; i++) {
574+
int unicode = text.get(i).getUnicode();
575+
if (unicode > -1) {
576+
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+
}
572582
}
573583
}
574-
}
575-
Integer max = 0;
576-
Map.Entry<Character.UnicodeScript, Integer> selectedEntry = null;
577-
for (Map.Entry<Character.UnicodeScript, Integer> entry : scriptFrequency.entrySet()) {
578-
Character.UnicodeScript entryScript = entry.getKey();
579-
if (entry.getValue() > max && !Character.UnicodeScript.COMMON.equals(entryScript) && !Character.UnicodeScript.UNKNOWN.equals(entryScript)
580-
&& !Character.UnicodeScript.INHERITED.equals(entryScript)) {
581-
max = entry.getValue();
582-
selectedEntry = entry;
583-
}
584-
}
585-
if (selectedEntry != null) {
586-
Character.UnicodeScript selectScript = ((Map.Entry<Character.UnicodeScript, Integer>) selectedEntry).getKey();
587-
if ((selectScript == Character.UnicodeScript.ARABIC || selectScript == Character.UnicodeScript.HEBREW) && parent instanceof LineRenderer) {
588-
setProperty(Property.BASE_DIRECTION, BaseDirection.DEFAULT_BIDI);
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+
}
589593
}
590-
if (supportedScripts != null && supportedScripts.contains(selectScript)) {
591-
script = selectScript;
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+
}
592602
}
593603
}
594-
}
595604

596-
if (hasOtfFont() && script != null) {
597-
TypographyUtils.applyOtfScript(font.getFontProgram(), text, script);
605+
if (script != null) {
606+
TypographyUtils.applyOtfScript(font.getFontProgram(), text, script, typographyConfig);
607+
}
598608
}
599609

600610
FontKerning fontKerning = (FontKerning) this.<FontKerning>getProperty(Property.FONT_KERNING, FontKerning.NO);
@@ -1446,5 +1456,4 @@ public void remove() {
14461456
}
14471457

14481458
}
1449-
14501459
}

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,24 @@ final class TypographyUtils {
111111
supportedScripts = (Collection<Character.UnicodeScript>) callMethod(TYPOGRAPHY_PACKAGE + SHAPER, GET_SUPPORTED_SCRIPTS, new Class[]{});
112112
} catch (Exception e) {
113113
supportedScripts = null;
114-
moduleFound = false;
115-
cachedClasses.clear();
116-
cachedMethods.clear();
117-
118114
logger.error(e.getMessage());
119115
}
120116
}
117+
moduleFound = supportedScripts != null;
118+
if (!moduleFound) {
119+
cachedClasses.clear();
120+
cachedMethods.clear();
121+
}
121122
TYPOGRAPHY_MODULE_INITIALIZED = moduleFound;
122123
SUPPORTED_SCRIPTS = supportedScripts;
123124
}
124125

125-
static void applyOtfScript(FontProgram fontProgram, GlyphLine text, Character.UnicodeScript script) {
126+
static void applyOtfScript(FontProgram fontProgram, GlyphLine text, Character.UnicodeScript script, Object typographyConfig) {
126127
if (!TYPOGRAPHY_MODULE_INITIALIZED) {
127128
logger.warn(typographyNotFoundException);
128129
} else {
129-
callMethod(TYPOGRAPHY_PACKAGE + SHAPER, APPLY_OTF_SCRIPT, new Class[]{TrueTypeFont.class, GlyphLine.class, Character.UnicodeScript.class},
130-
fontProgram, text, script);
131-
// Shaper.applyOtfScript((TrueTypeFont)fontProgram, text, script);
130+
callMethod(TYPOGRAPHY_PACKAGE + SHAPER, APPLY_OTF_SCRIPT, new Class[]{TrueTypeFont.class, GlyphLine.class, Character.UnicodeScript.class, Object.class},
131+
fontProgram, text, script, typographyConfig);
132132
}
133133
}
134134

@@ -238,6 +238,15 @@ static Collection<Character.UnicodeScript> getSupportedScripts() {
238238
}
239239
}
240240

241+
static Collection<Character.UnicodeScript> getSupportedScripts(Object typographyConfig) {
242+
if (!TYPOGRAPHY_MODULE_INITIALIZED) {
243+
logger.warn(typographyNotFoundException);
244+
return null;
245+
} else {
246+
return (Collection<Character.UnicodeScript>) callMethod(TYPOGRAPHY_PACKAGE + SHAPER, GET_SUPPORTED_SCRIPTS, (Object) null, new Class[] {Object.class}, typographyConfig);
247+
}
248+
}
249+
241250
static boolean isTypographyModuleInitialized() {
242251
return TYPOGRAPHY_MODULE_INITIALIZED;
243252
}
@@ -254,6 +263,8 @@ private static Object callMethod(String className, String methodName, Object tar
254263
logger.warn(MessageFormatUtil.format("Cannot find method {0} for class {1}", methodName, className));
255264
} catch (ClassNotFoundException e) {
256265
logger.warn(MessageFormatUtil.format("Cannot find class {0}", className));
266+
} catch (IllegalArgumentException e) {
267+
logger.warn(MessageFormatUtil.format("Illegal arguments passed to {0}#{1} method call: {2}", className, methodName, e.getMessage()));
257268
} catch (Exception e) {
258269
throw new RuntimeException(e.toString(), e);
259270
}

0 commit comments

Comments
 (0)