blob: f46183107e9d64aed4368c1e39d8b0fcc1660da0 [file] [log] [blame]
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package com.google.typography.font.compression;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.typography.font.sfntly.Font;
import com.google.typography.font.sfntly.Tag;
import com.google.typography.font.sfntly.table.core.CMap;
import com.google.typography.font.sfntly.table.core.CMapTable;
import com.google.typography.font.sfntly.table.core.MaximumProfileTable;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;
/**
* @author raph@google.com (Raph Levien)
*
* Experimental CMap encoder, based primarily on writing the _inverse_ encoding.
*/
public class CmapEncoder {
public static byte[] encode(Font font) {
int nGlyphs = font.<MaximumProfileTable>getTable(Tag.maxp).numGlyphs();
CMapTable cmapTable = font.getTable(Tag.cmap);
CMap cmap = getBestCMap(cmapTable);
Map<Integer, Integer> invEncoding = Maps.newHashMap();
List<Integer> exceptions = Lists.newArrayList();
for (Integer i : cmap) {
int glyphId = cmap.glyphId(i);
if (invEncoding.containsKey(glyphId)) {
exceptions.add(i);
exceptions.add(glyphId);
} else {
invEncoding.put(glyphId, i);
}
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
int last = -1;
for (int i = 0; i < nGlyphs; i++) {
if (invEncoding.containsKey(i)) {
int value = invEncoding.get(i);
int delta = value - last;
writeVShort(os, delta);
last = value;
} else {
writeVShort(os, 0);
}
}
for (int i : exceptions) {
writeVShort(os, i);
}
return os.toByteArray();
}
private static CMap getBestCMap(CMapTable cmapTable) {
for (CMap cmap : cmapTable) {
if (cmap.format() == CMap.CMapFormat.Format12.value()) {
return cmap;
}
}
for (CMap cmap : cmapTable) {
if (cmap.format() == CMap.CMapFormat.Format4.value()) {
return cmap;
}
}
return null;
}
// A simple signed varint encoding
static void writeVShort(ByteArrayOutputStream os, int value) {
if (value >= 0x2000 || value < -0x2000) {
os.write((byte)(0x80 | ((value >> 14) & 0x7f)));
}
if (value >= 0x40 || value < -0x40) {
os.write((byte)(0x80 | ((value >> 7) & 0x7f)));
}
os.write((byte)(value & 0x7f));
}
}