zoukankan      html  css  js  c++  java
  • 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.Table;
    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.google.typography.font.sfntly.table.core.PostScriptTable;
    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;
        }
    
        /**
         * 获取Cmap
         * @return
         */
        public LinkedHashMap<Integer, String> getCmap(){
            LinkedHashMap<Integer, String> ret = new LinkedHashMap<Integer, String>();
            try{
                FontFactory fontFactory = FontFactory.getInstance();
                Font font = fontFactory.loadFonts(ttfByteArray)[0];
    
                Map<Integer, ? extends Table> tableMap = font.tableMap();
                CMapTable cmapTable = (CMapTable)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()){
                            int val = it1.next();
                            String unicode = val < 128 ? String.valueOf((char) val) : ("uni" + Integer.toHexString(val));
    
                            ret.put(val, unicode);
                        }
                        break;
                    }
    
                }
    
            }
            catch (IOException | InvalidWoffException ex){
                logger.error(ex.getMessage(), ex);
            }
            return ret;
    
        }
    
    
        /**
         * 获取unicode 字符列表
         * @return
         */
        public List<String> getUniCodeList(){
            List<String> works = new ArrayList<String>();
            try{
                FontFactory fontFactory = FontFactory.getInstance();
                Font font = fontFactory.loadFonts(ttfByteArray)[0];
    
                Map<Integer, ? extends Table> tableMap = font.tableMap();
                if(tableMap.containsKey(Tag.CFF)){
    
                }
                else if(tableMap.containsKey(Tag.post)){
                    PostScriptTable postScriptTable = (PostScriptTable)tableMap.get(Tag.post);
                    for(int i=0; i< postScriptTable.numberOfGlyphs(); i++){
                        String glypName = postScriptTable.glyphName(i);
                        if(!glypName.startsWith("uni")){
                            continue;
                        }
                        works.add(glypName);
                    }
                }
    
            }
            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;
            }
        }
    }
  • 相关阅读:
    mybatis date类型比较
    搭建 c 语言环境 1_centos6 minimal 配置 c/c++ 编译环境
    2_eclipse配置c/c++环境
    1_eclipse导入hibernate 的DTD 文件
    1_eclipse配置c/c++开发环境
    2_修改Eclipse里面的快捷键
    1_修改注释内容
    8_对象创建、static 关键字、静态变量和成员变量的区别、文档
    7_匿名对象、封装(private)、this 关键字、构造方法
    6_面向对象基础、成员变量和局部变量的区别
  • 原文地址:https://www.cnblogs.com/rubekid/p/15330017.html
Copyright © 2011-2022 走看看