Skip to content

Commit bfdd38e

Browse files
committed
Support Character encodings related rules in UA-2
DEVSIX-9004
1 parent eb025ca commit bfdd38e

File tree

12 files changed

+421
-33
lines changed

12 files changed

+421
-33
lines changed

commons/src/main/java/com/itextpdf/commons/datastructures/Tuple2.java

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

25+
import java.util.Objects;
26+
2527
/**
2628
* Simple tuple container that holds two elements.
2729
*
@@ -61,6 +63,38 @@ public T2 getSecond() {
6163
return second;
6264
}
6365

66+
/**
67+
* {@inheritDoc}
68+
*
69+
* <p>
70+
* Note, that in case current class is overridden, equals should also be overridden.
71+
*
72+
* @param obj {@inheritDoc}
73+
*
74+
* @return {@inheritDoc}
75+
*/
76+
@Override
77+
public boolean equals(Object obj) {
78+
if (this == obj) {
79+
return true;
80+
}
81+
if (obj == null || getClass() != obj.getClass()) {
82+
return false;
83+
}
84+
Tuple2<T1, T2> that = (Tuple2<T1, T2>) obj;
85+
return Objects.equals(this.first, that.first) && Objects.equals(this.second, that.second);
86+
}
87+
88+
/**
89+
* {@inheritDoc}
90+
*
91+
* @return {@inheritDoc}
92+
*/
93+
@Override
94+
public int hashCode() {
95+
return Objects.hash((Object) first, (Object) second);
96+
}
97+
6498
/**
6599
* {@inheritDoc}
66100
*/

commons/src/test/java/com/itextpdf/commons/datastructures/Tuple2Test.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.commons.datastructures;
2424

2525
import com.itextpdf.test.ExtendedITextTest;
26-
27-
import org.junit.jupiter.api.Test;
2826
import org.junit.jupiter.api.Tag;
27+
import org.junit.jupiter.api.Test;
28+
2929
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
3031
import static org.junit.jupiter.api.Assertions.assertNull;
3132

3233
@Tag("UnitTest")
@@ -51,4 +52,48 @@ public void testTuple2_TestWithNullFirstValue() {
5152
assertNull(tuple.getFirst());
5253
assertEquals(Integer.valueOf(1), tuple.getSecond());
5354
}
55+
56+
@Test
57+
public void equalsTest() {
58+
Tuple2<String, Integer> tuple1 = new Tuple2<>("test", 1);
59+
Tuple2<String, Integer> tuple2 = new Tuple2<>("test", 1);
60+
assertEquals(tuple1, tuple2);
61+
}
62+
63+
@Test
64+
public void equalsSameTest() {
65+
Tuple2<String, Integer> tuple = new Tuple2<>("test", 1);
66+
assertEquals(tuple, tuple);
67+
}
68+
69+
@Test
70+
public void equalsNullTest() {
71+
Tuple2<String, Integer> tuple = new Tuple2<>("test", 1);
72+
assertNotEquals(tuple, null);
73+
}
74+
75+
@Test
76+
public void notEqualsTest() {
77+
Tuple2<String, Integer> tuple1 = new Tuple2<>("test", 1);
78+
Tuple2<String, Integer> tuple2 = new Tuple2<>("test", 2);
79+
Tuple2<String, Integer> tuple3 = new Tuple2<>("test2", 2);
80+
assertNotEquals(tuple1, tuple2);
81+
assertNotEquals(tuple2, tuple3);
82+
assertNotEquals(tuple1, tuple3);
83+
}
84+
85+
@Test
86+
public void equalsWithCustomTest() {
87+
Tuple2<String, Integer> tuple1 = new Tuple2<>("test", 1);
88+
Tuple2<String, Integer> tuple2 = new CustomTuple2<>("test", 1);
89+
Tuple2<String, Integer> tuple3 = new CustomTuple2<>("test", 1);
90+
assertNotEquals(tuple1, tuple2);
91+
assertEquals(tuple2, tuple3);
92+
}
93+
94+
private static class CustomTuple2<T1, T2> extends Tuple2<T1, T2> {
95+
public CustomTuple2(T1 test, T2 i) {
96+
super(test, i);
97+
}
98+
}
5499
}

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

Lines changed: 21 additions & 8 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.datastructures.Tuple2;
2526
import com.itextpdf.io.exceptions.IOException;
2627
import com.itextpdf.io.exceptions.IoExceptionMessageConstant;
2728
import com.itextpdf.io.font.constants.FontStretches;
@@ -124,25 +125,34 @@ static class PostTable {
124125
}
125126

126127
static class CmapTable {
128+
/**
129+
* Collection of the pairs representing Platform ID and Encoding ID of the “cmap” subtables
130+
* present in the font program.
131+
*/
132+
List<Tuple2<Integer, Integer>> cmapEncodings = new ArrayList<>();
127133
/**
128134
* The map containing the code information for the table 'cmap', encoding 1.0.
129135
* The key is the code and the value is an {@code int[2]} where position 0
130136
* is the glyph number and position 1 is the glyph width normalized to 1000 units.
137+
*
131138
* @see TrueTypeFont#UNITS_NORMALIZATION
132139
*/
133140
Map<Integer, int[]> cmap10;
134141
/**
135142
* The map containing the code information for the table 'cmap', encoding 3.1 in Unicode.
136143
* The key is the code and the value is an {@code int[2]} where position 0
137144
* is the glyph number and position 1 is the glyph width normalized to 1000 units.
145+
*
138146
* @see TrueTypeFont#UNITS_NORMALIZATION
139147
*/
140148
Map<Integer, int[]> cmap31;
141149
Map<Integer, int[]> cmapExt;
142150
boolean fontSpecific = false;
143151
}
144152

145-
/** The file name. */
153+
/**
154+
* The file name.
155+
*/
146156
protected String fileName;
147157
/**
148158
* The file in use.
@@ -418,7 +428,8 @@ private void initializeSfntTables() throws java.io.IOException {
418428

419429
/**
420430
* Reads the font data.
421-
* @param all if true, all tables will be read, otherwise only 'head', 'name', and 'os/2'.
431+
*
432+
* @param all if {@code true}, all tables will be read, otherwise only 'head', 'name', and 'os/2'
422433
*/
423434
protected void loadTables(boolean all) throws java.io.IOException {
424435
readNameTable();
@@ -537,8 +548,9 @@ protected IntHashtable readKerning(int unitsPerEm) throws java.io.IOException {
537548
* Read the glyf bboxes from 'glyf' table.
538549
*
539550
* @param unitsPerEm {@link HeaderTable#unitsPerEm}
551+
*
540552
* @throws IOException the font is invalid
541-
* @throws java.io.IOException the font file could not be read
553+
* @throws java.io.IOException the font file could not be read
542554
*/
543555
protected int[][] readBbox(int unitsPerEm) throws java.io.IOException {
544556
int tableLocation[];
@@ -586,7 +598,7 @@ protected int[][] readBbox(int unitsPerEm) throws java.io.IOException {
586598
int start = locaTable[glyph];
587599
if (start != locaTable[glyph + 1]) {
588600
raf.seek(tableGlyphOffset + start + 2);
589-
bboxes[glyph] = new int[] {
601+
bboxes[glyph] = new int[]{
590602
FontProgram.convertGlyphSpaceToTextSpace(raf.readShort()) / unitsPerEm,
591603
FontProgram.convertGlyphSpaceToTextSpace(raf.readShort()) / unitsPerEm,
592604
FontProgram.convertGlyphSpaceToTextSpace(raf.readShort()) / unitsPerEm,
@@ -611,7 +623,7 @@ protected int readNumGlyphs() throws java.io.IOException {
611623
* Extracts the names of the font in all the languages available.
612624
*
613625
* @throws IOException on error
614-
* @throws java.io.IOException on error
626+
* @throws java.io.IOException on error
615627
*/
616628
private void readNameTable() throws java.io.IOException {
617629
int[] table_location = tables.get("name");
@@ -661,7 +673,7 @@ private void readNameTable() throws java.io.IOException {
661673
* Read horizontal header, table 'hhea'.
662674
*
663675
* @throws IOException the font is invalid.
664-
* @throws java.io.IOException the font file could not be read.
676+
* @throws java.io.IOException the font file could not be read.
665677
*/
666678
private void readHheaTable() throws java.io.IOException {
667679
int[] table_location = tables.get("hhea");
@@ -691,7 +703,7 @@ private void readHheaTable() throws java.io.IOException {
691703
* Read font header, table 'head'.
692704
*
693705
* @throws IOException the font is invalid.
694-
* @throws java.io.IOException the font file could not be read.
706+
* @throws java.io.IOException the font file could not be read.
695707
*/
696708
private void readHeadTable() throws java.io.IOException {
697709
int[] table_location = tables.get("head");
@@ -719,7 +731,7 @@ private void readHeadTable() throws java.io.IOException {
719731
* Depends on {@link HeaderTable#unitsPerEm} property.
720732
*
721733
* @throws IOException the font is invalid.
722-
* @throws java.io.IOException the font file could not be read.
734+
* @throws java.io.IOException the font file could not be read.
723735
*/
724736
private void readOs_2Table() throws java.io.IOException {
725737
int[] table_location = tables.get("OS/2");
@@ -825,6 +837,7 @@ private void readCmapTable() throws java.io.IOException {
825837
for (int k = 0; k < num_tables; ++k) {
826838
int platId = raf.readUnsignedShort();
827839
int platSpecId = raf.readUnsignedShort();
840+
cmaps.cmapEncodings.add(new Tuple2<>(platId, platSpecId));
828841
int offset = raf.readInt();
829842
if (platId == 3 && platSpecId == 0) {
830843
cmaps.fontSpecific = true;

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

Lines changed: 44 additions & 8 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.datastructures.Tuple2;
2526
import com.itextpdf.commons.utils.MessageFormatUtil;
2627
import com.itextpdf.io.exceptions.IOException;
2728
import com.itextpdf.io.exceptions.IoExceptionMessageConstant;
@@ -32,6 +33,8 @@ This file is part of the iText (R) project.
3233
import com.itextpdf.io.font.otf.OpenTypeGdefTableReader;
3334
import com.itextpdf.io.logs.IoLogMessageConstant;
3435
import com.itextpdf.io.util.IntHashtable;
36+
import org.slf4j.Logger;
37+
import org.slf4j.LoggerFactory;
3538

3639
import java.util.ArrayList;
3740
import java.util.LinkedHashMap;
@@ -41,13 +44,11 @@ This file is part of the iText (R) project.
4144
import java.util.Set;
4245
import java.util.SortedSet;
4346
import java.util.stream.Collectors;
44-
import org.slf4j.Logger;
45-
import org.slf4j.LoggerFactory;
4647

4748
public class TrueTypeFont extends FontProgram {
4849

4950

50-
private OpenTypeParser fontParser;
51+
private OpenTypeParser fontParser;
5152

5253
protected int[][] bBoxes;
5354

@@ -104,6 +105,7 @@ public boolean hasKernPairs() {
104105
*
105106
* @param first the first glyph
106107
* @param second the second glyph
108+
*
107109
* @return the kerning to be applied
108110
*/
109111
@Override
@@ -218,6 +220,37 @@ public Set<Integer> mapGlyphsCidsToGids(Set<Integer> glyphs) {
218220
.collect(Collectors.toSet());
219221
}
220222

223+
/**
224+
* Checks whether current {@link TrueTypeFont} program contains the “cmap” subtable
225+
* with provided platform ID and encoding ID.
226+
*
227+
* @param platformID platform ID
228+
* @param encodingID encoding ID
229+
*
230+
* @return {@code true} if “cmap” subtable with provided platform ID and encoding ID is present in the font program,
231+
* {@code false} otherwise
232+
*/
233+
public boolean isCmapPresent(int platformID, int encodingID) {
234+
OpenTypeParser.CmapTable cmaps = fontParser.getCmapTable();
235+
if (cmaps == null) {
236+
return false;
237+
}
238+
return cmaps.cmapEncodings.contains(new Tuple2<>(platformID, encodingID));
239+
}
240+
241+
/**
242+
* Gets the number of the “cmap” subtables for the current {@link TrueTypeFont} program.
243+
*
244+
* @return the number of the “cmap” subtables
245+
*/
246+
public int getNumberOfCmaps() {
247+
OpenTypeParser.CmapTable cmaps = fontParser.getCmapTable();
248+
if (cmaps == null) {
249+
return 0;
250+
}
251+
return cmaps.cmapEncodings.size();
252+
}
253+
221254
protected void readGdefTable() throws java.io.IOException {
222255
int[] gdef = fontParser.tables.get("GDEF");
223256
if (gdef != null) {
@@ -238,7 +271,8 @@ protected void readGsubTable() throws java.io.IOException {
238271
protected void readGposTable() throws java.io.IOException {
239272
int[] gpos = fontParser.tables.get("GPOS");
240273
if (gpos != null) {
241-
gposTable = new GlyphPositioningTableReader(fontParser.raf, gpos[0], gdefTable, codeToGlyph, fontMetrics.getUnitsPerEm());
274+
gposTable = new GlyphPositioningTableReader(fontParser.raf, gpos[0], gdefTable, codeToGlyph,
275+
fontMetrics.getUnitsPerEm());
242276
}
243277
}
244278

@@ -408,9 +442,9 @@ public void updateUsedGlyphs(SortedSet<Integer> usedGlyphs, boolean subset, List
408442
if (subsetRanges != null) {
409443
compactRange = toCompactRange(subsetRanges);
410444
} else if (!subset) {
411-
compactRange = new int[] {0, 0xFFFF};
445+
compactRange = new int[]{0, 0xFFFF};
412446
} else {
413-
compactRange = new int[] {};
447+
compactRange = new int[]{};
414448
}
415449

416450
for (int k = 0; k < compactRange.length; k += 2) {
@@ -427,9 +461,11 @@ public void updateUsedGlyphs(SortedSet<Integer> usedGlyphs, boolean subset, List
427461
/**
428462
* Normalizes given ranges by making sure that first values in pairs are lower than second values and merges overlapping
429463
* ranges in one.
464+
*
430465
* @param ranges a {@link List} of integer arrays, which are constituted by pairs of ints that denote
431-
* each range limits. Each integer array size shall be a multiple of two.
432-
* @return single merged array consisting of pairs of integers, each of them denoting a range.
466+
* each range limits. Each integer array size shall be a multiple of two
467+
*
468+
* @return single merged array consisting of pairs of integers, each of them denoting a range
433469
*/
434470
private static int[] toCompactRange(List<int[]> ranges) {
435471
List<int[]> simp = new ArrayList<>();

io/src/test/java/com/itextpdf/io/font/TrueTypeFontTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ public void checkSxHeightTtfTest() throws IOException {
117117
Assertions.assertEquals(536, xHeight);
118118
}
119119

120+
@Test
121+
public void containsCmapTest() throws IOException {
122+
TrueTypeFont fontProgram = (TrueTypeFont) FontProgramFactory.createFont(SOURCE_FOLDER + "glyphs-fmt-6.ttf");
123+
Assertions.assertEquals(1, fontProgram.getNumberOfCmaps());
124+
Assertions.assertTrue(fontProgram.isCmapPresent(0, 3));
125+
Assertions.assertFalse(fontProgram.isCmapPresent(1, 0));
126+
}
127+
120128
private void checkCmapTableEntry(FontProgram fontProgram, char uniChar, int expectedGlyphId) {
121129

122130
Glyph glyph = fontProgram.getGlyph(uniChar);

pdfa/src/main/java/com/itextpdf/pdfa/checker/PdfA1Checker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ protected void checkContentStream(PdfStream contentStream) {
384384
@Override
385385
protected void checkNonSymbolicTrueTypeFont(PdfTrueTypeFont trueTypeFont) {
386386
String encoding = trueTypeFont.getFontEncoding().getBaseEncoding();
387-
// non-symbolic true type font will always has an encoding entry in font dictionary in itext
387+
// non-symbolic true type font will always have an encoding entry in font dictionary in itext
388388
if (!PdfEncodings.WINANSI.equals(encoding) && !PdfEncodings.MACROMAN.equals(encoding) || trueTypeFont.getFontEncoding().hasDifferences()) {
389389
throw new PdfAConformanceException(PdfaExceptionMessageConstant.ALL_NON_SYMBOLIC_TRUE_TYPE_FONT_SHALL_SPECIFY_MAC_ROMAN_OR_WIN_ANSI_ENCODING_AS_THE_ENCODING_ENTRY, trueTypeFont);
390390
}

pdfa/src/main/java/com/itextpdf/pdfa/checker/PdfA2Checker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ protected void checkNumberOfDeviceNComponents(PdfSpecialCs.DeviceN deviceN) {
422422
@Override
423423
protected void checkNonSymbolicTrueTypeFont(PdfTrueTypeFont trueTypeFont) {
424424
String encoding = trueTypeFont.getFontEncoding().getBaseEncoding();
425-
// non-symbolic true type font will always has an encoding entry in font dictionary in itext
425+
// non-symbolic true type font will always have an encoding entry in font dictionary in itext
426426
if (!PdfEncodings.WINANSI.equals(encoding) && !PdfEncodings.MACROMAN.equals(encoding)) {
427427
throw new PdfAConformanceException(PdfaExceptionMessageConstant.ALL_NON_SYMBOLIC_TRUE_TYPE_FONT_SHALL_SPECIFY_MAC_ROMAN_ENCODING_OR_WIN_ANSI_ENCODING, trueTypeFont);
428428
}

0 commit comments

Comments
 (0)