From 204787bc73fb30cb59296ebae4dd0c177d2a2b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 23 Mar 2021 10:05:27 +0100 Subject: [PATCH] gnujpdf - various CMAP subtable formats support for drawing text --- lib/gnujpdf.jar | Bin 201017 -> 206250 bytes libsrc/gnujpdf/src/gnu/jpdf/TtfParser.java | 264 ++++++++++++++++++++- 2 files changed, 256 insertions(+), 8 deletions(-) diff --git a/lib/gnujpdf.jar b/lib/gnujpdf.jar index 41cb871e4a48295c94b10443fa8a6f1919d4b207..3b42e4ef3e394b6fc3f6c7d57f87e553e694a399 100644 GIT binary patch delta 13145 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 apv;d{pG(3=^0v?5>f8)vNJu|Ls`6iRbzpe_ diff --git a/libsrc/gnujpdf/src/gnu/jpdf/TtfParser.java b/libsrc/gnujpdf/src/gnu/jpdf/TtfParser.java index 2fb65f920..b438d1ba5 100644 --- a/libsrc/gnujpdf/src/gnu/jpdf/TtfParser.java +++ b/libsrc/gnujpdf/src/gnu/jpdf/TtfParser.java @@ -198,15 +198,84 @@ public class TtfParser { int version = readUnsignedShort(input); int numberSubtables = readUnsignedShort(input); - for (int i = 0; i < numberSubtables; i++) { - int platFormId = readUnsignedShort(input); - int platFormSpecificId = readUnsignedShort(input); - long offset = readUnsignedLong(input); - seek(input, tableOffsets.get("cmap") + offset); + List platFormIds = new ArrayList<>(); + List platFormSpecificIds = new ArrayList<>(); + List offsets = new ArrayList<>(); + for (int i = 0; i < numberSubtables; i++) { + platFormIds.add(readUnsignedShort(input)); + platFormSpecificIds.add(readUnsignedShort(input)); + offsets.add(readUnsignedLong(input)); + } + + for (int i = 0; i < numberSubtables; i++) { + long offset = offsets.get(i); + seek(input, tableOffsets.get("cmap") + offset); int format = readUnsignedShort(input); switch (format) { - case 4: + case 0: //byte enconding table + { + int length = readUnsignedShort(input); + int languageCode = readUnsignedShort(input); + for (int c = 0; c < 256; c++) { + ctg.put(c, readUnsignedByte(input)); + } + break; + } + case 2: //high byte mapping through table + { + int length = readUnsignedShort(input); + int languageCode = readUnsignedShort(input); + int subHeaderKeys[] = new int[256]; + int maxSubHeaderIndex = 0; + for (int c = 0; c < 256; c++) { + subHeaderKeys[c] = readUnsignedShort(input); + maxSubHeaderIndex = Math.max(maxSubHeaderIndex, subHeaderKeys[i] / 8); + } + + List firstCodes = new ArrayList<>(); + List entryCountCodes = new ArrayList<>(); + List idDeltas = new ArrayList<>(); + List idRangeOffsets = new ArrayList<>(); + + for (int c = 0; c <= maxSubHeaderIndex; c++) { + firstCodes.add(readUnsignedShort(input)); + entryCountCodes.add(readUnsignedShort(input)); + idDeltas.add(readShort(input)); + idRangeOffsets.add(readUnsignedShort(input) - (maxSubHeaderIndex + 1 - i - 1) * 8 - 2); + } + + long startGlyphIndexOffset = input.getFilePointer(); + for (int c = 0; c <= maxSubHeaderIndex; c++) { + int firstCode = firstCodes.get(c); + int idRangeOffset = idRangeOffsets.get(c); + int idDelta = idDeltas.get(c); + int entryCount = entryCountCodes.get(c); + input.seek(startGlyphIndexOffset + idRangeOffset); + for (int j = 0; j < entryCount; ++j) { + int charCode = i; + charCode = (charCode << 8) + (firstCode + j); + + int p = readUnsignedShort(input); + if (p > 0) { + p = (p + idDelta) % 65536; + if (p < 0) { + p += 65536; + } + } + + if (p >= numGlyphs) { + continue; + } + + ctg.put(charCode, p); + } + } + + break; + } + case 4: //segment mapping to delta values + { int length = readUnsignedShort(input); int languageCode = readUnsignedShort(input); int segCountX2 = readUnsignedShort(input); @@ -263,9 +332,172 @@ public class TtfParser { } } break; - } + } + case 6: { + int length = readUnsignedShort(input); + int languageCode = readUnsignedShort(input); + int firstCode = readUnsignedShort(input); + int entryCount = readUnsignedShort(input); + if (entryCount == 0) { + break; + } + int[] glyphIdArray = readUnsignedShortArray(entryCount, input); + for (int c = 0; c < entryCount; c++) { + ctg.put(firstCode + c, glyphIdArray[c]); + } + break; + } + case 8: { //mixed 16-bit and 32-bit coverage + readUnsignedShort(input); //reserved, set to 0 + long length = readUnsignedLong(input); + long languageCode = readUnsignedLong(input); + final long LEAD_OFFSET = 0xD800l - (0x10000 >> 10); + final long SURROGATE_OFFSET = 0x10000l - (0xD800 << 10) - 0xDC00; + int[] is32 = readUnsignedByteArray(8192, input); + long nbGroups = readUnsignedLong(input); - break; + if (nbGroups > 65536) { + throw new IOException("CMap ( Subtype8 ) is invalid"); + } + + for (long c = 0; c < nbGroups; c++) { + long firstCode = readUnsignedLong(input); + long endCode = readUnsignedLong(input); + long startGlyph = readUnsignedLong(input); + + if (firstCode > endCode || 0 > firstCode) { + throw new IOException("Range invalid"); + } + + for (long j = firstCode; j <= endCode; ++j) { + // -- Convert the Character code in decimal + if (j > Integer.MAX_VALUE) { + throw new IOException("[Sub Format 8] Invalid character code " + j); + } + if ((int) j / 8 >= is32.length) { + throw new IOException("[Sub Format 8] Invalid character code " + j); + } + + int currentCharCode; + if ((is32[(int) j / 8] & (1 << ((int) j % 8))) == 0) { + currentCharCode = (int) j; + } else { + long lead = LEAD_OFFSET + (j >> 10); + long trail = 0xDC00 + (j & 0x3FF); + + long codepoint = (lead << 10) + trail + SURROGATE_OFFSET; + if (codepoint > Integer.MAX_VALUE) { + throw new IOException("[Sub Format 8] Invalid character code " + codepoint); + } + currentCharCode = (int) codepoint; + } + + long glyphIndex = startGlyph + (j - firstCode); + if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE) { + throw new IOException("CMap contains an invalid glyph index"); + } + + ctg.put(currentCharCode, (int) glyphIndex); + } + } + break; + } + case 10: { //trimmed array + readUnsignedShort(input); //reserved, set to 10 + long length = readUnsignedLong(input); + long languageCode = readUnsignedLong(input); + long startCode = readUnsignedLong(input); + long numChars = readUnsignedLong(input); + if (numChars > Integer.MAX_VALUE) { + //throw new IOException("Invalid number of Characters"); + } + + if (startCode < 0 || startCode > 0x0010FFFF || (startCode + numChars) > 0x0010FFFF + || ((startCode + numChars) >= 0x0000D800 && (startCode + numChars) <= 0x0000DFFF)) { + //throw new IOException("Invalid Characters codes"); + } + for (long c = startCode; c < startCode + numChars; c++) { + ctg.put((int) c, readUnsignedShort(input)); + } + break; + } + case 12: { //segmented coverage + readUnsignedShort(input); //reserved, set to 0 + long length = readUnsignedLong(input); + long languageCode = readUnsignedLong(input); + long nbGroups = readUnsignedLong(input); + for (long c = 0; c < nbGroups; ++c) { + long firstCode = readUnsignedLong(input); + long endCode = readUnsignedLong(input); + long startGlyph = readUnsignedLong(input); + + if (firstCode < 0 || firstCode > 0x0010FFFF + || firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF) { + throw new IOException("Invalid characters codes"); + } + + if (endCode > 0 && endCode < firstCode + || endCode > 0x0010FFFF + || endCode >= 0x0000D800 && endCode <= 0x0000DFFF) { + throw new IOException("Invalid characters codes"); + } + + for (long j = 0; j <= endCode - firstCode; ++j) { + long glyphIndex = startGlyph + j; + if (glyphIndex >= numGlyphs) { + //warn("Format 12 cmap contains an invalid glyph index"); + break; + } + + if (firstCode + j > 0x10FFFF) { + //warn("Format 12 cmap contains character beyond UCS-4"); + } + + ctg.put((int) (firstCode + j), (int) glyphIndex); + } + } + break; + } + case 13: { //many-to-one mappings + + readUnsignedShort(input); //reserved, set to 0 + long length = readUnsignedLong(input); + long languageCode = readUnsignedLong(input); + long nbGroups = readUnsignedLong(input); + for (long c = 0; c < nbGroups; c++) { + long firstCode = readUnsignedLong(input); + long endCode = readUnsignedLong(input); + long glyphId = readUnsignedLong(input); + + if (glyphId > numGlyphs) { + //warn("Format 13 cmap contains an invalid glyph index"); + break; + } + + if (firstCode < 0 || firstCode > 0x0010FFFF || (firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF)) { + throw new IOException("Invalid Characters codes"); + } + + if ((endCode > 0 && endCode < firstCode) || endCode > 0x0010FFFF + || (endCode >= 0x0000D800 && endCode <= 0x0000DFFF)) { + throw new IOException("Invalid Characters codes"); + } + + for (long j = 0; j <= endCode - firstCode; ++j) { + if (firstCode + j > Integer.MAX_VALUE) { + throw new IOException("Character Code greater than Integer.MAX_VALUE"); + } + + if (firstCode + j > 0x10FFFF) { + //warn("Format 13 cmap contains character beyond UCS-4"); + } + + ctg.put((int) (firstCode + j), (int) glyphId); + } + } + break; + } + } } if (!ctg.containsKey(0)) { ctg.put(0, 0); @@ -471,6 +703,22 @@ public class TtfParser { return b; } + private int[] readUnsignedByteArray(int size, RandomAccessFile input) throws IOException { + int ret[] = new int[size]; + for (int i = 0; i < size; i++) { + ret[i] = readUnsignedByte(input); + } + return ret; + } + + private int[] readUnsignedShortArray(int size, RandomAccessFile input) throws IOException { + int ret[] = new int[size]; + for (int i = 0; i < size; i++) { + ret[i] = readUnsignedShort(input); + } + return ret; + } + private byte readByte(RandomAccessFile input) throws IOException { return (byte) readUnsignedByte(input); }