Skip to content

Commit a921c5a

Browse files
committed
Make lower/upper casing locale independent
DEVSIX-9092
1 parent 4de65ce commit a921c5a

File tree

80 files changed

+629
-171
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+629
-171
lines changed

barcodes/src/main/java/com/itextpdf/barcodes/BarcodeCodabar.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.barcodes;
2424

2525
import com.itextpdf.barcodes.exceptions.BarcodesExceptionMessageConstant;
26+
import com.itextpdf.commons.utils.StringNormalizer;
2627
import com.itextpdf.kernel.colors.Color;
2728
import com.itextpdf.kernel.font.PdfFont;
2829
import com.itextpdf.kernel.geom.Rectangle;
@@ -150,7 +151,7 @@ public BarcodeCodabar(PdfDocument document, PdfFont font) {
150151
* @return the bars
151152
*/
152153
public static byte[] getBarsCodabar(String text) {
153-
text = text.toUpperCase();
154+
text = StringNormalizer.toUpperCase(text);
154155
int len = text.length();
155156
if (len < 2) {
156157
throw new IllegalArgumentException(
@@ -186,7 +187,7 @@ public static byte[] getBarsCodabar(String text) {
186187
public static String calculateChecksum(String code) {
187188
if (code.length() < 2)
188189
return code;
189-
String text = code.toUpperCase();
190+
String text = StringNormalizer.toUpperCase(code);
190191
int sum = 0;
191192
int len = text.length();
192193
for (int k = 0; k < len; ++k) {

commons/src/main/java/com/itextpdf/commons/actions/contexts/ContextManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This file is part of the iText (R) project.
2424

2525
import com.itextpdf.commons.actions.NamespaceConstant;
2626
import com.itextpdf.commons.actions.ProductNameConstant;
27+
import com.itextpdf.commons.utils.StringNormalizer;
2728

2829
import java.util.Collection;
2930
import java.util.Collections;
@@ -131,7 +132,7 @@ void registerGenericContext(Collection<String> namespaces, Collection<String> pr
131132

132133
private static String normalize(String namespace) {
133134
// Conversion to lowercase is done to be compatible with possible changes in case of packages/namespaces
134-
return namespace.toLowerCase();
135+
return StringNormalizer.toLowerCase(namespace);
135136
}
136137

137138
private static class LengthComparator implements Comparator<String> {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2025 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.commons.utils;
24+
25+
import java.util.Locale;
26+
27+
/**
28+
* Utility class for string normalization.
29+
*/
30+
public final class StringNormalizer {
31+
32+
private StringNormalizer() {
33+
// Empty constructor
34+
}
35+
36+
/**
37+
* Converts a string to lowercase using Root locale.
38+
*
39+
* @param str a string to convert
40+
* @return a converted string
41+
*/
42+
public static String toLowerCase(String str) {
43+
if (str == null) {
44+
return null;
45+
}
46+
47+
return str.toLowerCase(Locale.ROOT);
48+
}
49+
50+
/**
51+
* Converts a string to uppercase using Root locale.
52+
*
53+
* @param str a string to convert
54+
* @return a converted string
55+
*/
56+
public static String toUpperCase(String str) {
57+
if (str == null) {
58+
return null;
59+
}
60+
61+
return str.toUpperCase(Locale.ROOT);
62+
}
63+
64+
/**
65+
* Converts a string to lowercase using Root locale and trims it.
66+
*
67+
* @param str a string to convert
68+
* @return a converted string
69+
*/
70+
public static String normalize(String str) {
71+
if (str == null) {
72+
return null;
73+
}
74+
75+
return toLowerCase(str).trim();
76+
}
77+
}

commons/src/test/java/com/itextpdf/commons/actions/contexts/ContextManagerTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.commons.actions.contexts;
2424

2525
import com.itextpdf.commons.actions.NamespaceConstant;
26+
import com.itextpdf.commons.utils.StringNormalizer;
2627
import com.itextpdf.test.ExtendedITextTest;
2728

2829
import java.util.Arrays;
@@ -37,8 +38,8 @@ public class ContextManagerTest extends ExtendedITextTest {
3738

3839
@Test
3940
public void getRecognisedNamespaceForSpecificNamespaceTest() {
40-
String outerNamespaces = NamespaceConstant.ITEXT.toLowerCase();
41-
String innerNamespaces = NamespaceConstant.PDF_HTML.toLowerCase();
41+
String outerNamespaces = StringNormalizer.toLowerCase(NamespaceConstant.ITEXT);
42+
String innerNamespaces = StringNormalizer.toLowerCase(NamespaceConstant.PDF_HTML);
4243

4344
Assertions.assertTrue(innerNamespaces.startsWith(outerNamespaces));
4445

@@ -89,7 +90,7 @@ public void unregisterNamespaceTest() {
8990

9091
Assertions.assertEquals(testNamespace,
9192
manager.getRecognisedNamespace(testNamespace + ".MyClass"));
92-
Assertions.assertEquals(testNamespaceWithCapitals.toLowerCase(),
93+
Assertions.assertEquals(StringNormalizer.toLowerCase(testNamespaceWithCapitals),
9394
manager.getRecognisedNamespace(testNamespaceWithCapitals + ".MyClass"));
9495

9596
manager.unregisterContext(testNamespaces);
@@ -102,7 +103,7 @@ public void unregisterNamespaceTest() {
102103
public void registeredNamespaceTest() {
103104
String registeredNamespace = NamespaceConstant.CORE_LAYOUT + "custompackage";
104105

105-
Assertions.assertEquals(NamespaceConstant.CORE_LAYOUT.toLowerCase(),
106+
Assertions.assertEquals(StringNormalizer.toLowerCase(NamespaceConstant.CORE_LAYOUT),
106107
ContextManager.getInstance().getRecognisedNamespace(registeredNamespace));
107108
}
108109
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2025 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.commons.utils;
24+
25+
import org.junit.jupiter.api.Assertions;
26+
import org.junit.jupiter.api.Tag;
27+
import org.junit.jupiter.api.Test;
28+
29+
@Tag("UnitTest")
30+
public class StringNormalizerTest {
31+
32+
@Test
33+
public void toLowerCaseTest() {
34+
Assertions.assertNull(StringNormalizer.toLowerCase(null));
35+
Assertions.assertEquals("some string", StringNormalizer.toLowerCase("SoMe StRiNg"));
36+
Assertions.assertEquals("some string", StringNormalizer.toLowerCase("SOME STRING"));
37+
}
38+
39+
@Test
40+
public void toUpperCaseTest() {
41+
Assertions.assertNull(StringNormalizer.toUpperCase(null));
42+
Assertions.assertEquals("SOME STRING", StringNormalizer.toUpperCase("SoMe StRiNg"));
43+
Assertions.assertEquals("SOME STRING", StringNormalizer.toUpperCase("some string"));
44+
}
45+
46+
@Test
47+
public void normalizeTest() {
48+
Assertions.assertNull(StringNormalizer.normalize(null));
49+
Assertions.assertEquals("some string", StringNormalizer.normalize(" \t\nSoMe StRiNg "));
50+
Assertions.assertEquals("some string", StringNormalizer.normalize(" \t\nSOME STRING "));
51+
}
52+
}

forms/src/main/java/com/itextpdf/forms/xfdf/XfdfObjectFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.forms.xfdf;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.forms.PdfAcroForm;
2627
import com.itextpdf.forms.fields.PdfFormCreator;
2728
import com.itextpdf.io.logs.IoLogMessageConstant;
@@ -509,7 +510,7 @@ private static void updateXfdfAnnotation(AnnotObject annotObject, PdfAnnotation
509510
}
510511

511512
private static void addCommonAnnotationAttributes(AnnotObject annot, PdfAnnotation pdfAnnotation) {
512-
annot.setName(pdfAnnotation.getSubtype().getValue().toLowerCase());
513+
annot.setName(StringNormalizer.toLowerCase(pdfAnnotation.getSubtype().getValue()));
513514

514515
if (pdfAnnotation.getColorObject() != null) {
515516
annot.addAttribute(new AttributeObject(XfdfConstants.COLOR, XfdfObjectUtils.convertColorToString(pdfAnnotation.getColorObject().toFloatArray())));

forms/src/main/java/com/itextpdf/forms/xfdf/XfdfObjectUtils.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.forms.xfdf;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.io.source.ByteUtils;
2627
import com.itextpdf.kernel.colors.Color;
2728
import com.itextpdf.kernel.geom.Rectangle;
@@ -258,7 +259,7 @@ static int convertFlagsFromString(String flagsString) {
258259
StringTokenizer st = new StringTokenizer(flagsString, delims);
259260
List<String> flagsList = new ArrayList<>();
260261
while (st.hasMoreTokens()) {
261-
flagsList.add(st.nextToken().toLowerCase());
262+
flagsList.add(StringNormalizer.toLowerCase(st.nextToken()));
262263
}
263264

264265
Map<String, Integer> flagMap = new HashMap<>();
@@ -349,7 +350,7 @@ static String convertColorToString(Color color) {
349350
* Converts float representation of the rgb color into a hex string representing the rgb color.
350351
*/
351352
private static String convertColorFloatToHex(float colorFloat) {
352-
String result = "0" + Integer.toHexString(((int) (colorFloat * 255 + 0.5))).toUpperCase();
353+
String result = "0" + StringNormalizer.toUpperCase(Integer.toHexString(((int) (colorFloat * 255 + 0.5))));
353354
return result.substring(result.length() - 2);
354355
}
355356

@@ -360,7 +361,7 @@ static String convertIdToHexString(String idString) {
360361
StringBuilder stb = new StringBuilder();
361362
char[] stringSymbols = idString.toCharArray();
362363
for (char ch : stringSymbols) {
363-
stb.append(Integer.toHexString((int) ch).toUpperCase());
364+
stb.append(StringNormalizer.toUpperCase(Integer.toHexString((int) ch)));
364365
}
365366
return stb.toString();
366367
}

forms/src/main/java/com/itextpdf/forms/xfdf/XfdfReader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.forms.xfdf;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.forms.PdfAcroForm;
2627
import com.itextpdf.forms.fields.PdfFormCreator;
2728
import com.itextpdf.forms.fields.PdfFormField;
@@ -199,7 +200,7 @@ private void addBorderStyleAttributes(PdfAnnotation annotation, AnnotObject anno
199200
borderStyle.put(PdfName.D, XfdfObjectUtils.convertDashesFromString(dashes));
200201
}
201202
if (style != null && !"cloudy".equals(style)) {
202-
borderStyle.put(PdfName.S, new PdfName(style.substring(0, 1).toUpperCase()));
203+
borderStyle.put(PdfName.S, new PdfName(StringNormalizer.toUpperCase(style.substring(0, 1))));
203204
}
204205
if (borderStyle.size() > 0) {
205206
annotation.put(PdfName.BS, borderStyle);

io/src/main/java/com/itextpdf/io/font/AdobeGlyphList.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.io.font;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.io.font.constants.FontResources;
2627
import com.itextpdf.io.util.ResourceUtil;
2728

@@ -99,7 +100,7 @@ public static int nameToUnicode(String name) {
99100
if (names2unicode.containsKey(name)) {
100101
v = (int) names2unicode.get(name);
101102
}
102-
if (v == -1 && name.length() == 7 && name.toLowerCase().startsWith("uni")) {
103+
if (v == -1 && name.length() == 7 && StringNormalizer.toLowerCase(name).startsWith("uni")) {
103104
try {
104105
return Integer.parseInt(name.substring(3), 16);
105106
} catch (Exception ignored) {

io/src/main/java/com/itextpdf/io/font/FontEncoding.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.io.font;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.io.util.ArrayUtil;
2627
import com.itextpdf.io.util.IntHashtable;
2728
import com.itextpdf.io.util.TextUtil;
@@ -347,7 +348,7 @@ protected void fillStandardEncoding() {
347348
* @return the normalized encoding
348349
*/
349350
protected static String normalizeEncoding(String enc) {
350-
String tmp = enc == null ? "" : enc.toLowerCase();
351+
String tmp = enc == null ? "" : StringNormalizer.toLowerCase(enc);
351352
switch (tmp) {
352353
case "":
353354
case "winansi":

io/src/main/java/com/itextpdf/io/font/FontProgramDescriptor.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.io.font;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.io.font.constants.FontMacStyleFlags;
2627

2728
import java.util.HashSet;
@@ -62,16 +63,16 @@ public class FontProgramDescriptor {
6263

6364
FontProgramDescriptor(FontNames fontNames, float italicAngle, boolean isMonospace) {
6465
this.fontName = fontNames.getFontName();
65-
this.fontNameLowerCase = this.fontName.toLowerCase();
66-
this.fullNameLowerCase = fontNames.getFullName()[0][3].toLowerCase();
66+
this.fontNameLowerCase = StringNormalizer.toLowerCase(this.fontName);
67+
this.fullNameLowerCase = StringNormalizer.toLowerCase(fontNames.getFullName()[0][3]);
6768
this.familyNameLowerCase = fontNames.getFamilyName() != null && fontNames.getFamilyName()[0][3] != null ?
68-
fontNames.getFamilyName()[0][3].toLowerCase() : null;
69+
StringNormalizer.toLowerCase(fontNames.getFamilyName()[0][3]) : null;
6970
// For font family2 let's take the last element in array. The family in the 1st element has high chance
7071
// to be the same as returned by getFamilyName. Ideally we should take different families based on OS
7172
// but it breaks the compatibility, produces different results on different OSs etc.
7273
String[][] familyName2 = fontNames.getFamilyName2();
7374
this.familyName2LowerCase = familyName2 != null && familyName2[familyName2.length - 1][3] != null ?
74-
familyName2[familyName2.length - 1][3].toLowerCase() : null;
75+
StringNormalizer.toLowerCase(familyName2[familyName2.length - 1][3]) : null;
7576
this.style = fontNames.getStyle();
7677
this.weight = fontNames.getFontWeight();
7778
this.macStyle = fontNames.getMacStyle();
@@ -144,7 +145,7 @@ public String getFamilyName2LowerCase() {
144145
private Set<String> extractFullFontNames(FontNames fontNames) {
145146
Set<String> uniqueFullNames = new HashSet<>();
146147
for (String[] fullName : fontNames.getFullName())
147-
uniqueFullNames.add(fullName[3].toLowerCase());
148+
uniqueFullNames.add(StringNormalizer.toLowerCase(fullName[3]));
148149
return uniqueFullNames;
149150
}
150151

@@ -153,7 +154,7 @@ private String extractFamilyNameEnglishOpenType(FontNames fontNames) {
153154
for (int k = 0; k < TT_FAMILY_ORDER.length; k += 3) {
154155
for (String[] name : fontNames.getFamilyName()) {
155156
if (TT_FAMILY_ORDER[k].equals(name[0]) && TT_FAMILY_ORDER[k + 1].equals(name[1]) && TT_FAMILY_ORDER[k + 2].equals(name[2])) {
156-
return name[3].toLowerCase();
157+
return StringNormalizer.toLowerCase(name[3]);
157158
}
158159
}
159160
}

io/src/main/java/com/itextpdf/io/font/FontProgramDescriptorFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.io.font;
2424

25+
import com.itextpdf.commons.utils.StringNormalizer;
2526
import com.itextpdf.io.exceptions.IOException;
2627
import com.itextpdf.io.font.constants.StandardFonts;
2728
import com.itextpdf.io.font.woff2.Woff2Converter;
@@ -48,7 +49,7 @@ public static FontProgramDescriptor fetchDescriptor(String fontName) {
4849
}
4950

5051
try {
51-
String fontNameLowerCase = baseName.toLowerCase();
52+
String fontNameLowerCase = StringNormalizer.toLowerCase(baseName);
5253
if (isBuiltinFonts14 || fontNameLowerCase.endsWith(".afm") || fontNameLowerCase.endsWith(".pfm")) {
5354
fontDescriptor = fetchType1FontDescriptor(fontName, null);
5455
} else if (isCidFont) {
@@ -116,7 +117,7 @@ private static FontProgramDescriptor fetchCachedDescriptor(String fontName, byte
116117
}
117118

118119
private static FontProgramDescriptor fetchTTCDescriptor(String baseName) throws java.io.IOException {
119-
int ttcSplit = baseName.toLowerCase().indexOf(".ttc,");
120+
int ttcSplit = StringNormalizer.toLowerCase(baseName).indexOf(".ttc,");
120121
if (ttcSplit > 0) {
121122
String ttcName;
122123
int ttcIndex;

0 commit comments

Comments
 (0)