From 6a91fd663094aca0e55833bc60502f50c7e675a7 Mon Sep 17 00:00:00 2001 From: Antecer Date: Sat, 21 Nov 2020 23:51:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=BD=91=E9=A1=B5=E5=86=85?= =?UTF-8?q?=E5=B5=8C=E5=AD=97=E4=BD=93=E6=9B=BF=E6=8D=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/analyzeRule/QueryTTF.java | 605 ++++++++++++++++++ .../legado/app/model/analyzeRule/QueryTTF.kt | 602 ----------------- .../legado/app/model/webBook/BookContent.kt | 8 +- 3 files changed, 610 insertions(+), 605 deletions(-) create mode 100644 app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java delete mode 100644 app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java new file mode 100644 index 000000000..eb105ff89 --- /dev/null +++ b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java @@ -0,0 +1,605 @@ +package io.legado.app.model.analyzeRule; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class QueryTTF { + private class Header { + public int majorVersion; + public int minorVersion; + public int numOfTables; + public int searchRange; + public int entrySelector; + public int rangeShift; + } + + private class Directory { + public String tag; // table name + public int checkSum; // Check sum + public int offset; // Offset from beginning of file + public int length; // length of the table in bytes + } + + private class NameLayout { + public int format; + public int count; + public int stringOffset; + public List records = new LinkedList<>(); + } + + private class NameRecord { + public int platformID; // 平台标识符<0:Unicode, 1:Mac, 2:ISO, 3:Windows, 4:Custom> + public int encodingID; // 编码标识符 + public int languageID; // 语言标识符 + public int nameID; // 名称标识符 + public int length; // 名称字符串的长度 + public int offset; // 名称字符串相对于stringOffset的字节偏移量 + } + + private class HeadLayout { + public int majorVersion; + public int minorVersion; + public int fontRevision; + public int checkSumAdjustment; + public int magicNumber; + public int flags; + public int unitsPerEm; + public long created; + public long modified; + public short xMin; + public short yMin; + public short xMax; + public short yMax; + public int macStyle; + public int lowestRecPPEM; + public short fontDirectionHint; + public short indexToLocFormat; // <0:loca是2字节数组, 1:loca是4字节数组> + public short glyphDataFormat; + } + + private class MaxpLayout { + public int majorVersion; + public int minorVersion; + public int numGlyphs; // 字体中的字形数量 + public int maxPoints; + public int maxContours; + public int maxCompositePoints; + public int maxCompositeContours; + public int maxZones; + public int maxTwilightPoints; + public int maxStorage; + public int maxFunctionDefs; + public int maxInstructionDefs; + public int maxStackElements; + public int maxSizeOfInstructions; + public int maxComponentElements; + public int maxComponentDepth; + } + + private class CmapLayout { + public int version; + public int numTables; + public List records = new LinkedList<>(); + public Map tables = new HashMap<>(); + } + + private class CmapRecord { + public int platformID; + public int encodingID; + public int offset; + } + + private class CmapFormat { + public int format; + public int length; + public int language; + public byte[] glyphIdArray; + } + + private class CmapFormat4 extends CmapFormat { + public int segCountX2; + public int searchRange; + public int entrySelector; + public int rangeShift; + public int[] endCode; + public int reservedPad; + public int[] startCode; + public short[] idDelta; + public int[] idRangeOffset; + public int[] glyphIdArray; + } + + private class CmapFormat6 extends CmapFormat { + public int firstCode; + public int entryCount; + public int[] glyphIdArray; + } + + private class CmapFormat12 extends CmapFormat { + public int reserved; + public int length; + public int language; + public int numGroups; + public List> groups; + } + + private class GlyfLayout { + public short numberOfContours; // 非负值为简单字型,负值为符合字型 + public short xMin; + public short yMin; + public short xMax; + public short yMax; + public int[] endPtsOfContours; // length=numberOfContours + public int instructionLength; + public byte[] instructions; // length=instructionLength + public byte[] flags; + public short[] xCoordinates; // length = flags.length + public short[] yCoordinates; // length = flags.length + } + + private class ByteArrayReader { + public int Index; + public byte[] Buffer; + + public ByteArrayReader(byte[] buffer, int index) { + Buffer = buffer; + Index = index; + } + + public long ReadUIntX(long len) { + long result = 0; + for (long i = 0; i < len; ++i) { + result <<= 8; + result |= Buffer[Index++] & 0xFF; + } + return result; + } + + public long ReadUInt64() { + return ReadUIntX(8); + } + + public long ReadInt64() { + return ReadUIntX(8); + } + + public int ReadUInt32() { + return (int) ReadUIntX(4); + } + + public int ReadInt32() { + return (int) ReadUIntX(4); + } + + public int ReadUInt16() { + return (int) ReadUIntX(2); + } + + public short ReadInt16() { + return (short) ReadUIntX(2); + } + + public short ReadUInt8() { + return (short) ReadUIntX(1); + } + + + public String ReadStrings(int len, Charset charset) { + byte[] result = len > 0 ? new byte[len] : null; + for (int i = 0; i < len; ++i) result[i] = Buffer[Index++]; + return new String(result, charset); + } + + public byte GetByte() { + return Buffer[Index++]; + } + + public byte[] GetBytes(int len) { + byte[] result = len > 0 ? new byte[len] : null; + for (int i = 0; i < len; ++i) result[i] = Buffer[Index++]; + return result; + } + + public int[] GetUInt16Array(int len) { + int[] result = len > 0 ? new int[len] : null; + for (int i = 0; i < len; ++i) result[i] = ReadUInt16(); + return result; + } + + public short[] GetInt16Array(int len) { + short[] result = len > 0 ? new short[len] : null; + for (int i = 0; i < len; ++i) result[i] = ReadInt16(); + return result; + } + } + + private final ByteArrayReader FontReader; + private final Header FileHeader = new Header(); + private final List Directorys = new LinkedList<>(); + private final NameLayout Name = new NameLayout(); + private final HeadLayout Head = new HeadLayout(); + private final MaxpLayout Maxp = new MaxpLayout(); + private final List Loca = new LinkedList<>(); + private final CmapLayout Cmap = new CmapLayout(); + private final List Glyf = new LinkedList<>(); + private final Map UnicodeMap = new HashMap<>(); + private final Pair[] pps = new Pair[]{ + Pair.of(3, 10), + Pair.of(0, 4), + Pair.of(3, 1), + Pair.of(1, 0), + Pair.of(0, 3), + Pair.of(0, 1) + }; + + /** + * 构造函数 + * + * @param buffer 传入TTF字体二进制数组 + */ + public QueryTTF(byte[] buffer) { + FontReader = new ByteArrayReader(buffer, 0); + // 获取文件头 + FileHeader.majorVersion = FontReader.ReadUInt16(); + FileHeader.minorVersion = FontReader.ReadUInt16(); + FileHeader.numOfTables = FontReader.ReadUInt16(); + FileHeader.searchRange = FontReader.ReadUInt16(); + FileHeader.entrySelector = FontReader.ReadUInt16(); + FileHeader.rangeShift = FontReader.ReadUInt16(); + // 获取目录 + for (int i = 0; i < FileHeader.numOfTables; ++i) { + Directory d = new Directory(); + d.tag = FontReader.ReadStrings(4, StandardCharsets.US_ASCII); + d.checkSum = FontReader.ReadUInt32(); + d.offset = FontReader.ReadUInt32(); + d.length = FontReader.ReadUInt32(); + Directorys.add(d); + } + // 解析表 name (字体信息,包含版权、名称、作者等...) + for (Directory Temp : Directorys) { + if (Temp.tag.equals("name")) { + FontReader.Index = Temp.offset; + Name.format = FontReader.ReadUInt16(); + Name.count = FontReader.ReadUInt16(); + Name.stringOffset = FontReader.ReadUInt16(); + for (int i = 0; i < Name.count; ++i) { + NameRecord record = new NameRecord(); + record.platformID = FontReader.ReadUInt16(); + record.encodingID = FontReader.ReadUInt16(); + record.languageID = FontReader.ReadUInt16(); + record.nameID = FontReader.ReadUInt16(); + record.length = FontReader.ReadUInt16(); + record.offset = FontReader.ReadUInt16(); + Name.records.add(record); + } + } + } + // 解析表 head (获取 head.indexToLocFormat) + for (Directory Temp : Directorys) { + if (Temp.tag.equals("head")) { + FontReader.Index = Temp.offset; + Head.majorVersion = FontReader.ReadUInt16(); + Head.minorVersion = FontReader.ReadUInt16(); + Head.fontRevision = FontReader.ReadUInt32(); + Head.checkSumAdjustment = FontReader.ReadUInt32(); + Head.magicNumber = FontReader.ReadUInt32(); + Head.flags = FontReader.ReadUInt16(); + Head.unitsPerEm = FontReader.ReadUInt16(); + Head.created = FontReader.ReadUInt64(); + Head.modified = FontReader.ReadUInt64(); + Head.xMin = FontReader.ReadInt16(); + Head.yMin = FontReader.ReadInt16(); + Head.xMax = FontReader.ReadInt16(); + Head.yMax = FontReader.ReadInt16(); + Head.macStyle = FontReader.ReadUInt16(); + Head.lowestRecPPEM = FontReader.ReadUInt16(); + Head.fontDirectionHint = FontReader.ReadInt16(); + Head.indexToLocFormat = FontReader.ReadInt16(); + Head.glyphDataFormat = FontReader.ReadInt16(); + } + } + // 解析表 maxp (获取 maxp.numGlyphs) + for (Directory Temp : Directorys) { + if (Temp.tag.equals("maxp")) { + FontReader.Index = Temp.offset; + Maxp.majorVersion = FontReader.ReadUInt16(); + Maxp.minorVersion = FontReader.ReadUInt16(); + Maxp.numGlyphs = FontReader.ReadUInt16(); + Maxp.maxPoints = FontReader.ReadUInt16(); + Maxp.maxContours = FontReader.ReadUInt16(); + Maxp.maxCompositePoints = FontReader.ReadUInt16(); + Maxp.maxCompositeContours = FontReader.ReadUInt16(); + Maxp.maxZones = FontReader.ReadUInt16(); + Maxp.maxTwilightPoints = FontReader.ReadUInt16(); + Maxp.maxStorage = FontReader.ReadUInt16(); + Maxp.maxFunctionDefs = FontReader.ReadUInt16(); + Maxp.maxInstructionDefs = FontReader.ReadUInt16(); + Maxp.maxStackElements = FontReader.ReadUInt16(); + Maxp.maxSizeOfInstructions = FontReader.ReadUInt16(); + Maxp.maxComponentElements = FontReader.ReadUInt16(); + Maxp.maxComponentDepth = FontReader.ReadUInt16(); + } + } + // 解析表 loca (轮廓数据偏移地址表) + for (Directory Temp : Directorys) { + if (Temp.tag.equals("loca")) { + FontReader.Index = Temp.offset; + int offset = Head.indexToLocFormat == 0 ? 2 : 4; + for (long i = 0; i < Temp.length; i += offset) { + Loca.add(offset == 2 ? FontReader.ReadUInt16() << 1 : FontReader.ReadUInt32()); + } + } + } + // 解析表 cmap (Unicode编码轮廓索引对照表) + for (Directory Temp : Directorys) { + if (Temp.tag.equals("cmap")) { + FontReader.Index = Temp.offset; + Cmap.version = FontReader.ReadUInt16(); + Cmap.numTables = FontReader.ReadUInt16(); + + for (int i = 0; i < Cmap.numTables; ++i) { + CmapRecord record = new CmapRecord(); + record.platformID = FontReader.ReadUInt16(); + record.encodingID = FontReader.ReadUInt16(); + record.offset = FontReader.ReadUInt32(); + Cmap.records.add(record); + } + for (int i = 0; i < Cmap.numTables; ++i) { + int fmtOffset = Cmap.records.get(i).offset; + FontReader.Index = Temp.offset + fmtOffset; + int EndIndex = FontReader.Index; + + int format = FontReader.ReadUInt16(); + if (Cmap.tables.containsKey(fmtOffset)) continue; + if (format == 0) { + CmapFormat f = new CmapFormat(); + f.format = format; + f.length = FontReader.ReadUInt16(); + f.language = FontReader.ReadUInt16(); + f.glyphIdArray = FontReader.GetBytes(f.length - 6); + Cmap.tables.put(fmtOffset, f); + } else if (format == 4) { + CmapFormat4 f = new CmapFormat4(); + f.format = format; + f.length = FontReader.ReadUInt16(); + f.language = FontReader.ReadUInt16(); + f.segCountX2 = FontReader.ReadUInt16(); + int segCount = f.segCountX2 >> 1; + f.searchRange = FontReader.ReadUInt16(); + f.entrySelector = FontReader.ReadUInt16(); + f.rangeShift = FontReader.ReadUInt16(); + f.endCode = FontReader.GetUInt16Array(segCount); + f.reservedPad = FontReader.ReadUInt16(); + f.startCode = FontReader.GetUInt16Array(segCount); + f.idDelta = FontReader.GetInt16Array(segCount); + f.idRangeOffset = FontReader.GetUInt16Array(segCount); + f.glyphIdArray = FontReader.GetUInt16Array((EndIndex + f.length - FontReader.Index) >> 1); + Cmap.tables.put(fmtOffset, f); + } else if (format == 6) { + CmapFormat6 f = new CmapFormat6(); + f.format = format; + f.length = FontReader.ReadUInt16(); + f.language = FontReader.ReadUInt16(); + f.firstCode = FontReader.ReadUInt16(); + f.entryCount = FontReader.ReadUInt16(); + f.glyphIdArray = FontReader.GetUInt16Array(f.entryCount); + Cmap.tables.put(fmtOffset, f); + } else if (format == 12) { + CmapFormat12 f = new CmapFormat12(); + f.format = format; + f.reserved = FontReader.ReadUInt16(); + f.length = FontReader.ReadUInt32(); + f.language = FontReader.ReadUInt32(); + f.numGroups = FontReader.ReadUInt32(); + f.groups = new ArrayList<>(f.numGroups); + for (int n = 0; n < f.numGroups; ++n) { + f.groups.add(Triple.of(FontReader.ReadUInt32(), FontReader.ReadUInt32(), FontReader.ReadUInt32())); + } + Cmap.tables.put(fmtOffset, f); + } + } + } + } + // 解析表 glyf (字体轮廓数据表) + for (Directory Temp : Directorys) { + if (Temp.tag.equals("glyf")) { + FontReader.Index = Temp.offset; + for (int i = 0; i < Maxp.numGlyphs; ++i) { + FontReader.Index = Temp.offset + Loca.get(i); + + short numberOfContours = FontReader.ReadInt16(); + if (numberOfContours > 0) { + GlyfLayout g = new GlyfLayout(); + g.numberOfContours = numberOfContours; + g.xMin = FontReader.ReadInt16(); + g.yMin = FontReader.ReadInt16(); + g.xMax = FontReader.ReadInt16(); + g.yMax = FontReader.ReadInt16(); + g.endPtsOfContours = FontReader.GetUInt16Array(numberOfContours); + g.instructionLength = FontReader.ReadUInt16(); + g.instructions = FontReader.GetBytes(g.instructionLength); + int flagLength = g.endPtsOfContours[g.endPtsOfContours.length - 1] + 1; + // 获取轮廓点描述标志 + g.flags = new byte[flagLength]; + for (int n = 0; n < flagLength; ++n) { + g.flags[n] = FontReader.GetByte(); + if ((g.flags[n] & 0x08) != 0x00) { + for (int m = FontReader.ReadUInt8(); m > 0; --m) { + g.flags[++n] = g.flags[n - 1]; + } + } + } + // 获取轮廓点描述x轴相对值 + g.xCoordinates = new short[flagLength]; + for (int n = 0; n < flagLength; ++n) { + short same = (short) ((g.flags[n] & 0x10) != 0 ? 1 : -1); + if ((g.flags[n] & 0x02) != 0) { + g.xCoordinates[n] = (short) (same * FontReader.ReadUInt8()); + } else { + g.xCoordinates[n] = same == 1 ? (short) 0 : FontReader.ReadInt16(); + } + } + // 获取轮廓点描述y轴相对值 + g.yCoordinates = new short[flagLength]; + for (int n = 0; n < flagLength; ++n) { + short same = (short) ((g.flags[n] & 0x20) != 0 ? 1 : -1); + if ((g.flags[n] & 0x04) != 0) { + g.yCoordinates[n] = (short) (same * FontReader.ReadUInt8()); + } else { + g.yCoordinates[n] = same == 1 ? (short) 0 : FontReader.ReadInt16(); + } + } + // 相对坐标转绝对坐标 +// for (int n = 1; n < flagLength; ++n) { +// xCoordinates[n] += xCoordinates[n - 1]; +// yCoordinates[n] += yCoordinates[n - 1]; +// } + + Glyf.add(g); + } else { + // 复合字体暂未使用 + } + } + } + } + + // 建立Unicode&Glyf映射表 + for (int i = 0; i < 130000; ++i) { + int gid = GetGlyfIndex(i); + if (gid == 0) continue; + int thisLength = Glyf.get(gid).flags.length; + short[] thisGlyf = new short[thisLength << 1]; + System.arraycopy(Glyf.get(gid).xCoordinates, 0, thisGlyf, 0, thisLength); + System.arraycopy(Glyf.get(gid).yCoordinates, 0, thisGlyf, thisLength, thisLength); + UnicodeMap.put(i, thisGlyf); + } + } + + /** + * 获取字体信息 (1=字体名称) + * + * @param nameId 传入十进制字体信息索引 + * @return 返回查询结果字符串 + */ + public String GetNameById(int nameId) { + for (Directory Temp : Directorys) { + if (!Temp.tag.equals("name")) continue; + FontReader.Index = Temp.offset; + break; + } + for (NameRecord record : Name.records) { + if (record.nameID != nameId) continue; + FontReader.Index += Name.stringOffset + record.offset; + return FontReader.ReadStrings(record.length, record.platformID == 1 ? StandardCharsets.UTF_8 : StandardCharsets.UTF_16BE); + } + return "error"; + } + + /** + * 使用Unicode值查找轮廓索引 + * + * @param code 传入Unicode十进制值 + * @return 返回十进制轮廓索引 + */ + private int GetGlyfIndex(int code) { + if (code == 0) return 0; + int fmtKey = 0; + for (Pair item : pps) { + for (CmapRecord record : Cmap.records) { + if ((item.getLeft() == record.platformID) && (item.getRight() == record.encodingID)) { + fmtKey = record.offset; + break; + } + } + if (fmtKey > 0) break; + } + if (fmtKey == 0) return 0; + + int glyfID = 0; + CmapFormat table = Cmap.tables.get(fmtKey); + int fmt = table.format; + if (fmt == 0) { + if (code >= table.glyphIdArray.length) glyfID = 0; + else glyfID = table.glyphIdArray[code] & 0xFF; + } else if (fmt == 4) { + CmapFormat4 tab = (CmapFormat4) table; + if (code > tab.endCode[tab.endCode.length - 1]) return 0; + // 二分法查找数值索引 + int start = 0, middle, end = tab.endCode.length - 1; + while (start + 1 < end) { + middle = (start + end) / 2; + if (tab.endCode[middle] <= code) start = middle; + else end = middle; + } + if (tab.endCode[start] < code) ++start; + if (code < tab.startCode[start]) return 0; + if (tab.idRangeOffset[start] != 0) { + glyfID = tab.glyphIdArray[code - tab.startCode[start] + (tab.idRangeOffset[start] >> 1) - (tab.idRangeOffset.length - start)]; + } else glyfID = code + tab.idDelta[start]; + glyfID &= 0xFFFF; + + } else if (fmt == 6) { + CmapFormat6 tab = (CmapFormat6) table; + int index = code - tab.firstCode; + if (index < 0 || index >= tab.glyphIdArray.length) glyfID = 0; + else glyfID = tab.glyphIdArray[index]; + } else if (fmt == 12) { + CmapFormat12 tab = (CmapFormat12) table; + if (code > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0; + // 二分法查找数值索引 + int start = 0, middle, end = tab.numGroups - 1; + while (start + 1 < end) { + middle = (start + end) / 2; + if (tab.groups.get(middle).getLeft() <= code) start = middle; + else end = middle; + } + if (tab.groups.get(start).getLeft() <= code && code <= tab.groups.get(start).getMiddle()) { + glyfID = tab.groups.get(start).getRight() + code - tab.groups.get(start).getLeft(); + } + } + return glyfID; + } + + /** + * 使用轮廓数据获取Unicode值 + * + * @param inputGlyf 传入short[]轮廓数组 + * @return 返回Unicode十进制值 + */ + public int GetCodeByGlyf(short[] inputGlyf) { + int unicodeVal = 0; + for (Map.Entry g : UnicodeMap.entrySet()) { + if (inputGlyf.length != g.getValue().length) continue; + int i = inputGlyf.length; + while (i > 0) { + --i; + if (inputGlyf[i] != g.getValue()[i]) { + ++i; + break; + } + } + if (i == 0) unicodeVal = g.getKey(); + } + return unicodeVal; + } + + /** + * 使用Unicode值获取轮廓数据 + * + * @param code 传入Unicode十进制值 + * @return 返回short[]轮廓数组 + */ + public short[] GetGlyfByCode(int code) { + if (code <= 0) return new short[0]; + return UnicodeMap.getOrDefault(code, new short[0]); + } +} diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt deleted file mode 100644 index 710fdbe16..000000000 --- a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt +++ /dev/null @@ -1,602 +0,0 @@ -package io.legado.app.model.analyzeRule - -import io.legado.app.help.JsExtensions -import java.nio.charset.Charset -import kotlin.experimental.and - -/** - * 解析TTF字体 - * @see 获取详情 - * @see 基于Javascript的TTF解析器 - */ -@ExperimentalUnsignedTypes -class QueryTTF(var Font: ByteArray) : JsExtensions { - private class Header { - var majorVersion: UShort = 0u - var minorVersion: UShort = 0u - var numOfTables: UShort = 0u - var searchRange: UShort = 0u - var entrySelector: UShort = 0u - var rangeShift: UShort = 0u - } - - private class Directory { - var tag: String = "" - var checkSum: UInt = 0u - var offset: UInt = 0u - var length: UInt = 0u - } - - private class NameLayout { - var format: UShort = 0u - var count: UShort = 0u - var stringOffset: UShort = 0u - var records = ArrayList() - } - - private class NameRecord { - var platformID: UShort = 0u - var encodingID: UShort = 0u - var languageID: UShort = 0u - var nameID: UShort = 0u - var length: UShort = 0u - var offset: UShort = 0u - } - - private class HeadLayout { - var majorVersion: UShort = 0u - var minorVersion: UShort = 0u - var fontRevision: UInt = 0u - var checkSumAdjustment: UInt = 0u - var magicNumber: UInt = 0u - var flags: UShort = 0u - var unitsPerEm: UShort = 0u - var created: ULong = 0u - var modified: ULong = 0u - var xMin: Short = 0 - var yMin: Short = 0 - var xMax: Short = 0 - var yMax: Short = 0 - var macStyle: UShort = 0u - var lowestRecPPEM: UShort = 0u - var fontDirectionHint: Short = 0 - var indexToLocFormat: Short = 0 - var glyphDataFormat: Short = 0 - } - - private class MaxpLayout { - var majorVersion: UShort = 0u - var minorVersion: UShort = 0u - var numGlyphs: UShort = 0u // 字体中的字形数量 - var maxPoints: UShort = 0u - var maxContours: UShort = 0u - var maxCompositePoints: UShort = 0u - var maxCompositeContours: UShort = 0u - var maxZones: UShort = 0u - var maxTwilightPoints: UShort = 0u - var maxStorage: UShort = 0u - var maxFunctionDefs: UShort = 0u - var maxInstructionDefs: UShort = 0u - var maxStackElements: UShort = 0u - var maxSizeOfInstructions: UShort = 0u - var maxComponentElements: UShort = 0u - var maxComponentDepth: UShort = 0u - } - - private class CmapLayout { - var version: UShort = 0u - var numTables: UShort = 0u - var records = ArrayList() - var tables = mutableMapOf() - } - - private class CmapRecord { - var platformID: UShort = 0u - var encodingID: UShort = 0u - var offset: UInt = 0u - } - - private class CmapFormat0 { - var format: UShort = 0u - var length: UShort = 0u - var language: UShort = 0u - var glyphIdArray = ArrayList() - } - - private class CmapFormat4 { - var format: UShort = 0u - var length: UShort = 0u - var language: UShort = 0u - var segCountX2: UShort = 0u - var searchRange: UShort = 0u - var entrySelector: UShort = 0u - var rangeShift: UShort = 0u - lateinit var endCode: ArrayList // UInt16[] - var reservedPad: UShort = 0u - lateinit var startCode: ArrayList // UInt16[] - lateinit var idDelta: ArrayList - lateinit var idRangeOffset: ArrayList // UInt16[] - var glyphIdArray = ArrayList() - } - - private class CmapFormat6 { - var format: UShort = 0u - var length: UShort = 0u - var language: UShort = 0u - var firstCode: UShort = 0u - var entryCount: UShort = 0u - var glyphIdArray = ArrayList() - } - - private class CmapFormat12 { - var format: UShort = 0u - var reserved: UShort = 0u - var length: UInt = 0u - var language: UInt = 0u - var numGroups: UInt = 0u - lateinit var groups: ArrayList> - } - - private class GlyfLayout { - var numberOfContours: Short = 0 - var xMin: Short = 0 - var yMin: Short = 0 - var xMax: Short = 0 - var yMax: Short = 0 - lateinit var endPtsOfContours: ArrayList // UInt16[] - var instructionLength: UShort = 0u // UInt16 - lateinit var instructions: ArrayList - lateinit var flags: ArrayList - lateinit var xCoordinates: ArrayList - lateinit var yCoordinates: ArrayList - } - - private class ByteArrayReader(var Buffer: ByteArray, var Index: Int) { - fun ReadUIntX(len: Long): ULong { - var result: ULong = 0u - for (i in 0 until len) { - result.shl(8) - result = result or Buffer[Index++].toULong() - } - return result - } - - fun ReadUInt64(): ULong { - return ReadUIntX(8) - } - - fun ReadInt64(): Long { - return ReadUIntX(8).toLong() - } - - fun ReadUInt32(): UInt { - return ReadUIntX(4).toUInt() - } - - fun ReadInt32(): Int { - return ReadUIntX(4).toInt() - } - - fun ReadUInt16(): UShort { - return ReadUIntX(2).toUShort() - } - - fun ReadInt16(): Short { - return ReadUIntX(2).toShort() - } - - fun ReadStrings(len: Int, charset: Charset): String { - if (len <= 0) return "" - val result = ByteArray(len) - for (i in 0 until len) { - result[i] = Buffer[Index++] - } - return result.toString(charset) - } - - fun GetByte(): Byte { - return Buffer[Index++] - } - - fun GetBytes(len: Int): ArrayList { - if (len <= 0) return ArrayList(0) - val result = ArrayList(len) - for (i in 0 until len) { - result[i] = Buffer[Index++] - } - return result - } - - fun GetUInt16Array(len: Int): ArrayList { - if (len <= 0) return ArrayList(0) - val result = ArrayList(len) - for (i in 0 until len) { - result[i] = ReadUInt16() - } - return result - } - - fun GetInt16Array(len: Int): ArrayList { - if (len <= 0) return ArrayList(0) - val result = ArrayList(len) - for (i in 0 until len) { - result[i] = ReadInt16() - } - return result - } - } - - private var FontReader: ByteArrayReader - private var fileHeader = Header() - private var directorys = ArrayList() - private var name = NameLayout() - private var head = HeadLayout() - private var maxp = MaxpLayout() - private var loca = ArrayList() - private var cmap = CmapLayout() - private var glyf = ArrayList() - private var unicodeMap = mutableMapOf>() - - init { - FontReader = ByteArrayReader(Font, 0) - // 获取文件头 - fileHeader.majorVersion = FontReader.ReadUInt16() - fileHeader.minorVersion = FontReader.ReadUInt16() - fileHeader.numOfTables = FontReader.ReadUInt16() - fileHeader.searchRange = FontReader.ReadUInt16() - fileHeader.entrySelector = FontReader.ReadUInt16() - fileHeader.rangeShift = FontReader.ReadUInt16() - // 获取目录 - for (i in 0 until fileHeader.numOfTables.toInt()) { - val tag = FontReader.ReadStrings(4, Charsets.US_ASCII) - val t = Directory() - t.tag = tag - t.checkSum = FontReader.ReadUInt32() - t.offset = FontReader.ReadUInt32() - t.length = FontReader.ReadUInt32() - directorys.add(t) - } - // 解析表 name (字体信息,包含版权、名称、作者等...) - for (Temp in directorys) { - if (Temp.tag == "name") { - FontReader.Index = Temp.offset.toInt() - name.format = FontReader.ReadUInt16() - name.count = FontReader.ReadUInt16() - name.stringOffset = FontReader.ReadUInt16() - - for (i in 0 until name.count.toInt()) { - val record = NameRecord() - record.platformID = FontReader.ReadUInt16() - record.encodingID = FontReader.ReadUInt16() - record.languageID = FontReader.ReadUInt16() - record.nameID = FontReader.ReadUInt16() - record.length = FontReader.ReadUInt16() - record.offset = FontReader.ReadUInt16() - name.records.add(record) - } - } - } - // 解析表 head (获取 head.indexToLocFormat) - for (Temp in directorys) { - if (Temp.tag == "head") { - FontReader.Index = Temp.offset.toInt() - head.majorVersion = FontReader.ReadUInt16() - head.minorVersion = FontReader.ReadUInt16() - head.fontRevision = FontReader.ReadUInt32() - head.checkSumAdjustment = FontReader.ReadUInt32() - head.magicNumber = FontReader.ReadUInt32() - head.flags = FontReader.ReadUInt16() - head.unitsPerEm = FontReader.ReadUInt16() - head.created = FontReader.ReadUInt64() - head.modified = FontReader.ReadUInt64() - head.xMin = FontReader.ReadInt16() - head.yMin = FontReader.ReadInt16() - head.xMax = FontReader.ReadInt16() - head.yMax = FontReader.ReadInt16() - head.macStyle = FontReader.ReadUInt16() - head.lowestRecPPEM = FontReader.ReadUInt16() - head.fontDirectionHint = FontReader.ReadInt16() - head.indexToLocFormat = FontReader.ReadInt16() - head.glyphDataFormat = FontReader.ReadInt16() - } - } - // 解析表 maxp (获取 maxp.numGlyphs) - for (Temp in directorys) { - if (Temp.tag == "maxp") { - FontReader.Index = Temp.offset.toInt() - maxp.majorVersion = FontReader.ReadUInt16() - maxp.minorVersion = FontReader.ReadUInt16() - maxp.numGlyphs = FontReader.ReadUInt16() - maxp.maxPoints = FontReader.ReadUInt16() - maxp.maxContours = FontReader.ReadUInt16() - maxp.maxCompositePoints = FontReader.ReadUInt16() - maxp.maxCompositeContours = FontReader.ReadUInt16() - maxp.maxZones = FontReader.ReadUInt16() - maxp.maxTwilightPoints = FontReader.ReadUInt16() - maxp.maxStorage = FontReader.ReadUInt16() - maxp.maxFunctionDefs = FontReader.ReadUInt16() - maxp.maxInstructionDefs = FontReader.ReadUInt16() - maxp.maxStackElements = FontReader.ReadUInt16() - maxp.maxSizeOfInstructions = FontReader.ReadUInt16() - maxp.maxComponentElements = FontReader.ReadUInt16() - maxp.maxComponentDepth = FontReader.ReadUInt16() - } - } - // 解析表 loca (轮廓数据偏移地址表) - for (Temp in directorys) { - if (Temp.tag == "loca") { - FontReader.Index = Temp.offset.toInt() - val offset: UInt = if (head.indexToLocFormat.toInt() == 0) 2u else 4u - var i: UInt = 0u - while (i < Temp.length) { - loca.add(if (offset == 2u) FontReader.ReadUInt16().toUInt().shl(1) else FontReader.ReadUInt32()) - i += offset - } - } - } - // 解析表 cmap (Unicode编码轮廓索引对照表) - for (Temp in directorys) { - if (Temp.tag == "cmap") { - FontReader.Index = Temp.offset.toInt() - cmap.version = FontReader.ReadUInt16() - cmap.numTables = FontReader.ReadUInt16() - - for (i in 0 until cmap.numTables.toInt()) { - val record = CmapRecord() - record.platformID = FontReader.ReadUInt16() - record.encodingID = FontReader.ReadUInt16() - record.offset = FontReader.ReadUInt32() - cmap.records.add(record) - } - for (i in 0 until cmap.numTables.toInt()) { - val fmtOffset = cmap.records[i].offset - FontReader.Index = (Temp.offset + fmtOffset).toInt() - val EndIndex = FontReader.Index - - val format = FontReader.ReadUInt16() - - if (cmap.tables.contains(fmtOffset.toInt())) continue - when { - format.equals(0) -> { - val fmt = CmapFormat0() - fmt.format = format - fmt.length = FontReader.ReadUInt16() - fmt.language = FontReader.ReadUInt16() - fmt.glyphIdArray = FontReader.GetBytes(fmt.length.toInt() - 6) - cmap.tables[fmtOffset] = fmt - } - format.equals(4) -> { - val fmt = CmapFormat4() - fmt.format = format - fmt.length = FontReader.ReadUInt16() - fmt.language = FontReader.ReadUInt16() - fmt.segCountX2 = FontReader.ReadUInt16() - val segCount = fmt.segCountX2.toInt() / 2 - fmt.searchRange = FontReader.ReadUInt16() - fmt.entrySelector = FontReader.ReadUInt16() - fmt.rangeShift = FontReader.ReadUInt16() - fmt.endCode = FontReader.GetUInt16Array(segCount) - fmt.reservedPad = FontReader.ReadUInt16() - fmt.startCode = FontReader.GetUInt16Array(segCount) - fmt.idDelta = FontReader.GetInt16Array(segCount) - fmt.idRangeOffset = FontReader.GetUInt16Array(segCount) - fmt.glyphIdArray = FontReader.GetUInt16Array((fmt.length.toInt() - (FontReader.Index - EndIndex)) / 2) - cmap.tables[fmtOffset] = fmt - } - format.equals(6) -> { - val fmt = CmapFormat6() - fmt.format = format - fmt.length = FontReader.ReadUInt16() - fmt.language = FontReader.ReadUInt16() - fmt.firstCode = FontReader.ReadUInt16() - fmt.entryCount = FontReader.ReadUInt16() - fmt.glyphIdArray = FontReader.GetUInt16Array(fmt.entryCount.toInt()) - cmap.tables[fmtOffset] = fmt - } - format.equals(12) -> { - val fmt = CmapFormat12() - fmt.format = format - fmt.reserved = FontReader.ReadUInt16() - fmt.length = FontReader.ReadUInt32() - fmt.language = FontReader.ReadUInt32() - fmt.numGroups = FontReader.ReadUInt32() - for (n in 0 until fmt.numGroups.toLong()) { - fmt.groups.add(Triple(FontReader.ReadUInt32(), FontReader.ReadUInt32(), FontReader.ReadUInt32())) - } - cmap.tables[fmtOffset] = fmt - } - } - } - } - } - // 解析表 glyf (字体轮廓数据表) - for (Temp in directorys) { - if (Temp.tag == "glyf") { - FontReader.Index = Temp.offset.toInt() - for (i in 0 until maxp.numGlyphs.toInt()) { - FontReader.Index = (Temp.offset + loca[i]).toInt() - - val g = GlyfLayout() - g.numberOfContours = FontReader.ReadInt16() - g.xMin = FontReader.ReadInt16() - g.yMin = FontReader.ReadInt16() - g.xMax = FontReader.ReadInt16() - g.yMax = FontReader.ReadInt16() - if (g.numberOfContours > 0) { - g.endPtsOfContours = FontReader.GetUInt16Array(g.numberOfContours.toInt()) - g.instructionLength = FontReader.ReadUInt16() - g.instructions = FontReader.GetBytes(g.instructionLength.toInt()) - val flagLength = g.endPtsOfContours.last().toInt() + 1 - // 获取轮廓点描述标志 - g.flags = ArrayList(flagLength) - var n = 0 - while (n < flagLength) { - g.flags[n] = FontReader.GetByte() - if ((g.flags[n].and(0x08)).toInt() != 0x00) { - for (m in FontReader.GetByte() downTo 1) { - g.flags[++n] = g.flags[n - 1] - } - } - ++n - } - // 获取轮廓点描述x轴相对值 - g.xCoordinates = ArrayList(flagLength) - for (m in 0 until flagLength) { - val same = if ((g.flags[m].and(0x10)).toInt() != 0) 1 else -1 - if ((g.flags[m].and(0x02)).toInt() != 0) { - g.xCoordinates[m] = (same * FontReader.GetByte()).toShort() - } else { - g.xCoordinates[m] = if (same == 1) 0 else FontReader.ReadInt16() - } - } - // 获取轮廓点描述y轴相对值 - g.yCoordinates = ArrayList(flagLength) - for (m in 0 until flagLength) { - val same = if ((g.flags[m].and(0x20)).toInt() != 0) 1 else -1 - if ((g.flags[m].and(0x04)).toInt() != 0) { - g.yCoordinates[n] = (same * FontReader.GetByte()).toShort() - } else { - g.yCoordinates[n] = if (same == 1) 0 else FontReader.ReadInt16() - } - } - /* - // 相对坐标转绝对坐标 (因不绘制字体,这里用不上) - for(m in 1 until flagLength) - { - g.xCoordinates[m] = (g.xCoordinates[m] + g.xCoordinates[m - 1]).toShort() - g.yCoordinates[m] = (g.yCoordinates[m] + g.yCoordinates[m - 1]).toShort() - } - */ - - glyf.add(g) - } else { - // 复合字体暂未使用 - } - } - } - } - - // 建立Unicode&Glyf映射表 - for (i in 0..130000) { - val gid = GetGlyfIndex(i).toInt() - if (gid == 0) continue - if (unicodeMap.containsKey(gid)) continue - val thisGlyf = ArrayList() - thisGlyf.addAll(glyf[gid].xCoordinates) - thisGlyf.addAll(glyf[gid].yCoordinates) - unicodeMap[i] = thisGlyf - } - - } - - - /** - * 获取字体信息 (默认获取字体名称,索引位1) - */ - fun GetNameById(nameId: Int = 1): String { - for (Temp in directorys) { - if (Temp.tag != "name") continue - FontReader.Index = Temp.offset.toInt() - break - } - for (record in name.records) { - if (record.nameID.toInt() != nameId) continue - FontReader.Index = FontReader.Index + (name.stringOffset + record.offset).toInt() - return FontReader.ReadStrings(record.length.toInt(), if (record.platformID.toInt() == 1) Charsets.UTF_8 else Charsets.UTF_16BE) - } - return "error" - } - - var pps = arrayListOf>(Pair(3u, 10u), Pair(0u, 4u), Pair(3u, 1u), Pair(1u, 0u), Pair(0u, 3u), Pair(0u, 1u)) - - /** - * 使用Unicode值查找轮廓索引 - */ - private fun GetGlyfIndex(code: Int): Long { - if (code == 0) return 0 - var fmtKey: UInt = 0u - for (item in pps) { - for (record in cmap.records) { - if ((item.first == record.platformID) && (item.second == record.encodingID)) { - fmtKey = record.offset - break - } - } - if (fmtKey > 0u) break - } - if (fmtKey == 0u) return 0 - - var glyfID: Long = 0 - if (cmap.tables[fmtKey] is CmapFormat0) { - val tab = cmap.tables[fmtKey] as CmapFormat0 - if (code >= tab.glyphIdArray.size) glyfID = 0 - else glyfID = tab.glyphIdArray[code].toLong() - } else if (cmap.tables[fmtKey] is CmapFormat4) { - val tab = cmap.tables[fmtKey] as CmapFormat4 - if (code > tab.endCode.last().toInt()) return 0 - // 二分法查找数值索引 - var start = 0 - var middle: Int - var end = tab.endCode.size - 1 - while (start + 1 < end) { - middle = (start + end) / 2 - if (tab.endCode[middle] <= code.toUInt()) start = middle - else end = middle - } - if (tab.endCode[start] < code.toUInt()) ++start - if (code.toUInt() < tab.startCode[start]) return 0 - glyfID = if (tab.idRangeOffset[start].toInt() != 0) { - tab.glyphIdArray[code - tab.startCode[start].toInt() + (tab.idRangeOffset[start].toInt() / 2) - (tab.idRangeOffset.size - start)].toLong() - } else (code + tab.idDelta[start]).toLong() - glyfID = glyfID.and(0xFFFF) - } else if (cmap.tables[fmtKey] is CmapFormat6) { - val tab = cmap.tables[fmtKey] as CmapFormat6 - val index = code - tab.firstCode.toInt() - glyfID = if (index < 0 || index >= tab.glyphIdArray.size) 0 else tab.glyphIdArray[index].toLong() - } else if (cmap.tables[fmtKey] is CmapFormat12) { - val tab = (cmap.tables[fmtKey] as CmapFormat12) - if (code > tab.groups.last().second.toInt()) return 0 - // 二分法查找数值索引 - var start = 0 - var middle: Int - var end = tab.groups.size - 1 - while (start + 1 < end) { - middle = (start + end) / 2 - if (tab.groups[middle].first.toInt() <= code) start = middle - else end = middle - } - if (tab.groups[start].first.toInt() <= code && code <= tab.groups[start].second.toInt()) { - glyfID = (tab.groups[start].third.toInt() + code - tab.groups[start].first.toInt()).toLong() - } - } - return glyfID - } - - /** - * 使用轮廓数据获取Unicode值 - */ - public fun GetCodeByGlyf(inputGlyf: List): Int { - var unicodeVal = 0 - if (inputGlyf.isEmpty()) return 0 - for (g in unicodeMap) { - if (inputGlyf.size != g.value.size) continue - var isFound = true - for (i in inputGlyf.indices) { - if (inputGlyf[i] != g.value[i]) { - isFound = false - break - } - } - if (isFound) unicodeVal = g.key - } - return unicodeVal - } - - /** - * 使用Unicode值获取轮廓数据 - */ - public fun GetGlyfByCode(code: Int): ArrayList { - if (code <= 0) return ArrayList() - return unicodeMap.getOrDefault(code, ArrayList()) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index 40b39c8cf..3c68bece1 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt @@ -118,10 +118,12 @@ object BookContent { if (correctFont != null && font != null) { val queryTTF = QueryTTF(font!!) val cQueryTTF = QueryTTF(correctFont!!) - val contentArray = contentStr.toStringArray() + val contentArray = contentStr.toCharArray() contentArray.forEachIndexed { index, s -> - val code = cQueryTTF.GetCodeByGlyf(queryTTF.GetGlyfByCode(s.toInt())) - contentArray[index] = code.toString() + if(s> 58000.toChar()){ + val code = cQueryTTF.GetCodeByGlyf(queryTTF.GetGlyfByCode(s.toInt())) + contentArray[index] = code.toChar() + } } contentStr = contentArray.joinToString("") }