java实现woff字体解析,逆向反爬
作者:互联网
package com.liuwa.font; import com.google.typography.font.sfntly.Font; import com.google.typography.font.sfntly.FontFactory; import com.google.typography.font.sfntly.Tag; import com.google.typography.font.sfntly.table.core.CMap; import com.google.typography.font.sfntly.table.core.CMapFormat12; import com.google.typography.font.sfntly.table.core.CMapTable; import com.liuwa.exception.InvalidWoffException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.nio.ByteBuffer; import java.util.*; import java.util.zip.DataFormatException; import java.util.zip.Inflater; /** * woff 转换器 */ public class WoffConverter { private static Logger logger = LoggerFactory.getLogger(WoffConverter.class); private static final LinkedHashMap<String, Integer> woffHeaderFormat = new LinkedHashMap<String, Integer>() { { put("signature", 4); put("flavor", 4); put("length", 4); put("numTables", 2); put("reserved", 2); put("totalSfntSize", 4); put("majorVersion", 2); put("minorVersion", 2); put("metaOffset", 4); put("metaLength", 4); put("metaOrigLength", 4); put("privOffset", 4); put("privOrigLength", 4); } }; private static final LinkedHashMap<String, Integer> tableRecordEntryFormat = new LinkedHashMap<String, Integer>() { { put("tag", 4); put("offset", 4); put("compLength", 4); put("origLength", 4); put("origChecksum", 4); } }; private HashMap<String, Number> woffHeaders = new HashMap<String, Number>(); private ArrayList<HashMap<String, Number>> tableRecordEntries = new ArrayList<HashMap<String, Number>>(); private int offset = 0; private int readOffset = 0; private File woffFile; private byte[] ttfByteArray; private WoffConverter(){} public WoffConverter(File woffFile) throws InvalidWoffException, IOException, DataFormatException{ this.woffFile = woffFile; FileInputStream inputStream = new FileInputStream(woffFile); ByteArrayOutputStream ttfOutputStream = convertToTTFOutputStream(inputStream); ttfByteArray = ttfOutputStream.toByteArray(); } /** * woff 转 ttf byte[] * @return * @throws InvalidWoffException * @throws IOException * @throws DataFormatException */ public byte[] getTTFByteArray(){ return ttfByteArray; } /** * 获取unicode 字符列表 * @return */ public List<String> getUniCodeList(){ List<String> works = new ArrayList<String>(); try{ FontFactory fontFactory = FontFactory.getInstance(); Font font = fontFactory.loadFonts(ttfByteArray)[0]; CMapTable cmapTable = (CMapTable)font.tableMap().get(Tag.cmap); Iterator<CMap> it = cmapTable.iterator(); while(it.hasNext()){ CMap cmap = it.next(); if(cmap instanceof CMapFormat12){ Iterator<Integer> it1 = cmap.iterator(); while(it1.hasNext()){ String unicode = Integer.toHexString(it1.next()); // 目前大部分e/f 开头四位编码 if(unicode.length() > 3){ works.add("uni" + unicode); } } break; } } } catch (IOException | InvalidWoffException ex){ logger.error(ex.getMessage(), ex); } return works; } private ByteArrayOutputStream convertToTTFOutputStream(InputStream inputStream) throws InvalidWoffException, IOException, DataFormatException { getHeaders(new DataInputStream(inputStream)); if ((Integer) woffHeaders.get("signature") != 0x774F4646) { throw new InvalidWoffException("Invalid woff file"); } ByteArrayOutputStream ttfOutputStream = new ByteArrayOutputStream(); writeOffsetTable(ttfOutputStream); getTableRecordEntries(new DataInputStream(inputStream)); writeTableRecordEntries(ttfOutputStream); writeFontData(inputStream, ttfOutputStream); return ttfOutputStream; } /** * 获取头部 * @param woffFileStream * @throws IOException */ private void getHeaders(DataInputStream woffFileStream) throws IOException { readTableData(woffFileStream, woffHeaderFormat, woffHeaders); } /** * * @param ttfOutputStream * @throws IOException */ private void writeOffsetTable(ByteArrayOutputStream ttfOutputStream) throws IOException { ttfOutputStream.write(getBytes((Integer) woffHeaders.get("flavor"))); int numTables = (Integer) woffHeaders.get("numTables"); ttfOutputStream.write(getBytes((short)numTables)); int temp = numTables; int searchRange = 16; short entrySelector = 0; while (temp > 1) { temp = temp >> 1; entrySelector++; searchRange = (searchRange << 1); } short rangeShift = (short) (numTables * 16 - searchRange); ttfOutputStream.write(getBytes((short) searchRange)); ttfOutputStream.write(getBytes(entrySelector)); ttfOutputStream.write(getBytes(rangeShift)); offset += 12; } private void getTableRecordEntries(DataInputStream woffFileStream) throws IOException { int numTables = (Integer) woffHeaders.get("numTables"); for (int i = 0; i < numTables; i++) { HashMap<String, Number> tableDirectory = new HashMap<String, Number>(); readTableData(woffFileStream, tableRecordEntryFormat, tableDirectory); offset += 16; tableRecordEntries.add(tableDirectory); } } private void writeTableRecordEntries(ByteArrayOutputStream ttfOutputStream) throws IOException { for (HashMap<String, Number> tableRecordEntry : tableRecordEntries) { ttfOutputStream.write(getBytes((Integer) tableRecordEntry .get("tag"))); ttfOutputStream.write(getBytes((Integer) tableRecordEntry .get("origChecksum"))); ttfOutputStream.write(getBytes(offset)); ttfOutputStream.write(getBytes((Integer) tableRecordEntry .get("origLength"))); tableRecordEntry.put("outOffset", offset); offset += (Integer) tableRecordEntry.get("origLength"); if (offset % 4 != 0) { offset += 4 - (offset % 4); } } } private void writeFontData(InputStream woffFileStream, ByteArrayOutputStream ttfOutputStream) throws IOException, DataFormatException { for (HashMap<String, Number> tableRecordEntry : tableRecordEntries) { int tableRecordEntryOffset = (Integer) tableRecordEntry .get("offset"); int skipBytes = tableRecordEntryOffset - readOffset; if (skipBytes > 0) woffFileStream.skip(skipBytes); readOffset += skipBytes; int compressedLength = (Integer) tableRecordEntry.get("compLength"); int origLength = (Integer) tableRecordEntry.get("origLength"); byte[] fontData = new byte[compressedLength]; byte[] inflatedFontData = new byte[origLength]; int readBytes = 0; while (readBytes < compressedLength) { readBytes += woffFileStream.read(fontData, readBytes, compressedLength - readBytes); } readOffset += compressedLength; inflatedFontData = inflateFontData(compressedLength, origLength, fontData, inflatedFontData); ttfOutputStream.write(inflatedFontData); offset = (Integer) tableRecordEntry.get("outOffset") + (Integer) tableRecordEntry.get("origLength"); int padding = 0; if (offset % 4 != 0) padding = 4 - (offset % 4); ttfOutputStream.write(getBytes(0), 0, padding); } } private byte[] inflateFontData(int compressedLength, int origLength, byte[] fontData, byte[] inflatedFontData) { if (compressedLength != origLength) { Inflater decompressor = new Inflater(); decompressor.setInput(fontData, 0, compressedLength); try { decompressor.inflate(inflatedFontData, 0, origLength); } catch (DataFormatException e) { throw new InvalidWoffException("Malformed woff file"); } } else inflatedFontData = fontData; return inflatedFontData; } private byte[] getBytes(int i) { return ByteBuffer.allocate(4).putInt(i).array(); } private byte[] getBytes(short h) { return ByteBuffer.allocate(2).putShort(h).array(); } private void readTableData(DataInputStream woffFileStream, LinkedHashMap<String, Integer> formatTable, HashMap<String, Number> table) throws IOException { Iterator<String> headerKeys = formatTable.keySet().iterator(); while (headerKeys.hasNext()) { String key = headerKeys.next(); int size = formatTable.get(key); if (size == 2) { table.put(key, woffFileStream.readUnsignedShort()); } else if (size == 4) { table.put(key, woffFileStream.readInt()); } readOffset += size; } } }
标签:java,get,int,反爬,woff,private,put,new,ttfOutputStream 来源: https://www.cnblogs.com/rubekid/p/15330017.html