diff mupdf-source/source/fitz/subset-cff.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/source/fitz/subset-cff.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,2514 @@
+// Copyright (C) 2004-2025 Artifex Software, Inc.
+//
+// This file is part of MuPDF.
+//
+// MuPDF is free software: you can redistribute it and/or modify it under the
+// terms of the GNU Affero General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+//
+// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
+//
+// Alternative licensing terms are available from the licensor.
+// For commercial licensing, see <https://www.artifex.com/> or contact
+// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
+// CA 94129, USA, for further information.
+
+#include "mupdf/fitz.h"
+
+#include <math.h>
+
+/*
+	For the purposes of this code, and to save my tiny brain from
+	overload, we will adopt the following notation:
+
+	1) The PDF file contains bytes of data. These bytes are looked
+	up in the MuPDF font handling to resolve to 'glyph ids' (gids).
+	These account for all the different encodings etc in use,
+	including the encoding table within the font.
+
+	(For CIDFonts, Cid = Gid, and there is no encoding table).
+
+	2) We are given the list of gids that are used in the document.
+
+	Unlike for simple TTFs, we don't map these down to the bottom of the
+	range, we just remove the definitions for them.
+
+	For now, I'm leaving zero size charstrings for subsetted glyphs.
+	This may need to be changed to be predefined charstrings that
+	just set a zero width if this is illegal.
+
+	Similarly, for now, we don't attempt to compact either the local
+	or global subrs.
+*/
+
+/*
+	In CFF files, we have:
+
+	Charset: Maps from gid <-> glyph name
+
+	Encoding: Maps from code <-> gid
+		plus supplemental code -> glyph name (which must have been used already in the map)
+*/
+
+
+/* Contains 1-4, to tell us the size of offsets used. */
+typedef uint8_t offsize_t;
+
+typedef struct
+{
+	/* Index position and length in the original */
+	uint32_t index_offset;
+	uint32_t index_size;
+
+	/* Fields read from the index */
+	uint16_t count;
+	offsize_t offsize;
+	const uint8_t *offset; /* A pointer to the offset table, not to the data table! */
+
+	/* The offset of the byte before the data. The offset of the first
+	 * object is always 1. Add the offset of any given object to this
+	 * and you get the offset within the block. */
+	uint32_t data_offset;
+} index_t;
+
+typedef struct
+{
+	uint8_t scanned;
+	uint16_t num;
+} usage_t;
+
+typedef struct
+{
+	int len;
+	int max;
+	usage_t *list;
+} usage_list_t;
+
+typedef struct
+{
+	uint8_t *base;
+	size_t len;
+
+	int symbolic;
+	int is_cidfont;
+
+	uint8_t major;
+	uint8_t minor;
+	uint8_t headersize;
+	offsize_t offsize;
+	offsize_t new_offsize;
+
+	index_t name_index;
+	index_t top_dict_index;
+	index_t string_index;
+	index_t global_index;
+	index_t charstrings_index;
+	index_t local_index;
+	index_t fdarray_index;
+	uint16_t gsubr_bias;
+	uint16_t subr_bias;
+	uint32_t top_dict_index_offset;
+	uint32_t string_index_offset;
+	uint32_t global_index_offset;
+	uint32_t encoding_offset;
+	uint32_t encoding_len;
+	uint32_t charset_offset;
+	uint32_t charset_len;
+	uint32_t charstrings_index_offset;
+	uint32_t private_offset;
+	uint32_t private_len;
+	uint32_t local_index_offset;
+	uint32_t fdselect_offset;
+	uint32_t fdselect_len;
+	uint32_t fdarray_index_offset;
+	uint32_t charstring_type;
+
+	uint16_t unpacked_charset_len;
+	uint16_t unpacked_charset_max;
+	uint16_t *unpacked_charset;
+
+	struct
+	{
+		fz_buffer *rewritten_dict;
+		fz_buffer *rewritten_private;
+		uint32_t offset;
+		uint32_t len;
+		uint32_t fixup;
+		uint32_t local_index_offset;
+		index_t local_index;
+		usage_list_t local_usage;
+		uint16_t subr_bias;
+		fz_buffer *local_subset;
+	} *fdarray;
+
+	struct
+	{
+		uint32_t charset;
+		uint32_t encoding;
+		uint32_t charstrings;
+		uint32_t privat;
+		uint32_t fdselect;
+		uint32_t fdarray;
+	} top_dict_fixup_offsets;
+
+	fz_buffer *charstrings_subset;
+	fz_buffer *top_dict_subset;
+	fz_buffer *private_subset;
+	fz_buffer *local_subset;
+	fz_buffer *global_subset;
+
+	usage_list_t local_usage;
+	usage_list_t global_usage;
+	usage_list_t gids_to_keep;
+	usage_list_t extra_gids_to_keep;
+
+	uint16_t *gid_to_cid;
+	uint8_t *gid_to_font;
+} cff_t;
+
+/* cid -> gid */
+static const uint8_t standard_encoding[256] =
+{
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+	33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+	49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+	65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+	81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+	0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123,
+	0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136,
+	137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0,
+	0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0
+};
+
+/* Simple functions for bigendian fetching/putting */
+
+static uint32_t get16(const uint8_t *d)
+{
+	return (d[0]<<8)|d[1];
+}
+
+static void put32(uint8_t *d, uint32_t v)
+{
+	d[0] = v>>24;
+	d[1] = v>>16;
+	d[2] = v>>8;
+	d[3] = v;
+}
+
+static void put16(uint8_t *d, uint32_t v)
+{
+	d[0] = v>>8;
+	d[1] = v;
+}
+
+static void put8(uint8_t *d, uint32_t v)
+{
+	d[0] = v;
+}
+
+static uint32_t
+get_offset(const uint8_t *d, offsize_t os)
+{
+	uint32_t v = *d++;
+	if (os > 1)
+		v = (v<<8) | *d++;;
+	if (os > 2)
+		v = (v<<8) | *d++;;
+	if (os > 3)
+		v = (v<<8) | *d++;;
+
+	return v;
+}
+
+static void
+put_offset(uint8_t *d, offsize_t os, uint32_t v)
+{
+	if (os > 3)
+		d[3] = v, v >>= 8;
+	if (os > 2)
+		d[2] = v, v >>= 8;
+	if (os > 1)
+		d[1] = v, v >>= 8;
+	d[0] = v;
+}
+
+static uint8_t
+offsize_for_offset(uint32_t offset)
+{
+	if (offset < 256)
+		return 1;
+	if (offset < 65536)
+		return 2;
+	if (offset < (1<<24))
+		return 3;
+	return 4;
+}
+
+uint16_t
+subr_bias(fz_context *ctx, cff_t *cff, uint16_t count)
+{
+	if (cff->charstring_type == 1)
+		return 0;
+	else if (count < 1240)
+		return 107;
+	else if (count < 33900)
+		return 1131;
+	else
+		return 32768;
+}
+
+/* Index functions */
+
+/* "Load" an index and check it for plausibility (no overflows etc) */
+static uint32_t
+index_load(fz_context *ctx, index_t *index, const uint8_t *base, uint32_t len, uint32_t offset)
+{
+	uint32_t data_offset, i, v, prev;
+	offsize_t os;
+	const uint8_t *data = base + offset;
+	const uint8_t *data0 = data;
+
+	/* Non-existent tables leave the index empty */
+	if (offset == 0 || len == 0)
+	{
+		memset(index, 0, sizeof(*index));
+		return 0;
+	}
+
+	index->index_offset = offset;
+
+	if (offset >= len || len-offset < 2)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated index");
+
+	index->count = get16(data);
+
+	if (index->count == 0)
+		return offset+2;
+
+	if (offset + 4 >= len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated index");
+
+	os = index->offsize = data[2];
+	if (index->offsize < 1 || index->offsize > 4)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Illegal offsize");
+
+	index->offset = data + 3;
+
+	data_offset = 3 + (index->count+1) * os - 1;
+	index->data_offset = data_offset + offset;
+
+	if (data_offset > len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated index");
+
+	data += 3;
+	prev = get_offset(data, os);
+	if (prev != 1)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Corrupt index");
+	data += os;
+	for (i = index->count; i > 0; i--)
+	{
+		v = get_offset(data, os);
+		data += os;
+		if (v < prev)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "Index not monotonic");
+		prev = v;
+	}
+	if (v > len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated index");
+
+	data += prev - 1;
+	index->index_size = data - data0;
+
+	return index->index_size + offset;
+}
+
+static uint32_t
+index_get(fz_context *ctx, index_t *index, int idx)
+{
+	int os;
+	uint32_t v;
+
+	if (idx < 0 || idx > index->count || index->count == 0)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Index bounds");
+
+	os = index->offsize;
+	idx *= os;
+	v = get_offset(&index->offset[idx], index->offsize);
+
+	return index->data_offset + v;
+}
+
+/* DICT handling structures and functions */
+
+#define HIOP(A) (A+22)
+
+typedef enum
+{
+	/* Top DICT Operators */
+	DICT_OP_version = 0,
+	DICT_OP_Notice = 1,
+	DICT_OP_Copyright = HIOP(0),
+	DICT_OP_FullName = 2,
+	DICT_OP_FamilyName = 3,
+	DICT_OP_Weight = 4,
+	DICT_OP_isFixedPitch = HIOP(1),
+	DICT_OP_ItalicAngle = HIOP(2),
+	DICT_OP_UnderlinePosition = HIOP(3),
+	DICT_OP_UnderlineThickness = HIOP(4),
+	DICT_OP_PaintType = HIOP(5),
+	DICT_OP_CharstringType = HIOP(6),
+	DICT_OP_FontMatrix = HIOP(7),
+	DICT_OP_UniqueID = 13,
+	DICT_OP_FontBBox = 5,
+	DICT_OP_StrokeWidth = HIOP(8),
+	DICT_OP_XUID = 14,
+	DICT_OP_charset = 15,
+	DICT_OP_Encoding = 16,
+	DICT_OP_CharStrings = 17,
+	DICT_OP_Private = 18,
+	DICT_OP_SyntheticBase = HIOP(20),
+	DICT_OP_Postscript = HIOP(21),
+	DICT_OP_BaseFontName = HIOP(22),
+	DICT_OP_BaseFontBlend = HIOP(23),
+
+	/* CIDFont Operators */
+	DICT_OP_ROS = HIOP(30),
+	DICT_OP_CIDFontVersion = HIOP(31),
+	DICT_OP_CIDFontRevision = HIOP(32),
+	DICT_OP_CIDFontType = HIOP(33),
+	DICT_OP_CIDCount = HIOP(34),
+	DICT_OP_UIDBase = HIOP(35),
+	DICT_OP_FDArray = HIOP(36),
+	DICT_OP_FDSelect = HIOP(37),
+	DICT_OP_FontName = HIOP(38),
+
+	/* Private DICT Operators */
+	DICT_OP_BlueValues = 6,
+	DICT_OP_OtherBlues = 7,
+	DICT_OP_FamilyBlues = 8,
+	DICT_OP_FamilyOtherBlues = 9,
+	DICT_OP_BlueScale = HIOP(9),
+	DICT_OP_BlueShift = HIOP(10),
+	DICT_OP_BlueFuzz = HIOP(11),
+	DICT_OP_StdHW = 10,
+	DICT_OP_StdVW = 11,
+	DICT_OP_StemSnapH = HIOP(12),
+	DICT_OP_StemSnapV = HIOP(13),
+	DICT_OP_ForceBold = HIOP(14),
+	DICT_OP_LanguageGroup = HIOP(17),
+	DICT_OP_ExpansionFactor = HIOP(18),
+	DICT_OP_initialRandomSeed = HIOP(19),
+	DICT_OP_Subrs = 19,
+	DICT_OP_defaultWidthX = 20,
+	DICT_OP_nominalWidthX = 21
+} dict_operator;
+
+typedef enum
+{
+	da_int = 0,
+	da_real = 1,
+	da_operator = 2
+} dict_arg_type;
+
+typedef struct {
+	dict_arg_type type;
+	union {
+		uint32_t i;
+		float f;
+	} u;
+} dict_arg;
+
+#define DICT_MAX_ARGS 48
+
+typedef struct {
+	const uint8_t *base;
+	size_t len;
+	uint32_t offset;
+	uint32_t end_offset;
+	uint8_t *val;
+	int eod;
+	int num_args;
+	dict_arg arg[DICT_MAX_ARGS+1];
+} dict_iterator;
+
+static uint8_t
+dict_get_byte(fz_context *ctx, dict_iterator *di)
+{
+	uint8_t b;
+
+	if (di->offset == di->end_offset)
+		di->eod = 1;
+	if (di->eod)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Overlong DICT data");
+	b = di->base[di->offset++];
+
+	return b;
+}
+
+static dict_arg
+dict_get_arg(fz_context *ctx, dict_iterator *di)
+{
+	uint8_t b0, b1, b2, b3, b4;
+	dict_arg d;
+
+	b0 = dict_get_byte(ctx, di);
+	if (b0 == 12)
+	{
+		b1 = dict_get_byte(ctx, di);
+		d.type = da_operator;
+		d.u.i = HIOP(b1);
+		return d;
+	}
+	else if (b0 <= 21)
+	{
+		d.type = da_operator;
+		d.u.i = b0;
+		return d;
+	}
+	else if (b0 <= 27)
+	{
+malformed:
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Malformed DICT");
+	}
+	else if (b0 == 28)
+	{
+		b1 = dict_get_byte(ctx, di);
+		b2 = dict_get_byte(ctx, di);
+		d.type = da_int;
+		d.u.i = (b1<<8) | b2;
+	}
+	else if (b0 == 29)
+	{
+		b1 = dict_get_byte(ctx, di);
+		b2 = dict_get_byte(ctx, di);
+		b3 = dict_get_byte(ctx, di);
+		b4 = dict_get_byte(ctx, di);
+		d.type = da_int;
+		d.u.i = (b1<<24) | (b2<<16)  | (b3<<8)  | b4;
+	}
+	else if (b0 == 30)
+	{
+		char cheap[32+5];
+		unsigned int i;
+
+		for (i = 0; i < sizeof(cheap)-5; )
+		{
+			static const char *dict = "0123456789.EE -f";
+			uint8_t b = dict_get_byte(ctx, di);
+
+			if ((b>>4) == 0xf)
+				break;
+			cheap[i++] = dict[b>>4];
+			if ((b>>4) == 0xc)
+				cheap[i++] = '-';
+
+			b &= 15;
+			if (b == 0xf)
+				break;
+			cheap[i++] = dict[b];
+			if (b == 0xc)
+				cheap[i++] = '-';
+		}
+		cheap[i++] = 0;
+		d.type = da_real;
+		d.u.f = fz_atof(cheap);
+	}
+	else if (b0 == 31)
+	{
+		goto malformed;
+	}
+	else if (b0 <= 246)
+	{
+		d.type = da_int;
+		d.u.i = b0-139;
+	}
+	else if (b0 <= 250)
+	{
+		b1 = dict_get_byte(ctx, di);
+		d.type = da_int;
+		d.u.i = ((b0-247)<<8) + b1 + 108;
+	}
+	else if (b0 <= 254)
+	{
+		b1 = dict_get_byte(ctx, di);
+		d.type = da_int;
+		d.u.i = -((b0-251)<<8) - b1 - 108;
+	}
+	else
+		goto malformed;
+
+	return d;
+}
+
+static dict_operator
+dict_next(fz_context *ctx, dict_iterator *di)
+{
+	int n;
+
+	if (di->offset >= di->end_offset)
+	{
+		di->eod = 1;
+		return 0;
+	}
+
+	n = 0;
+	while (di->offset < di->end_offset)
+	{
+		di->arg[n] = dict_get_arg(ctx, di);
+		if (di->arg[n].type == da_operator)
+		{
+			/* Sorted! Terminate loop. */
+			break;
+		}
+		if (n == DICT_MAX_ARGS)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "Too many operands");
+		n++;
+	}
+	di->num_args = n;
+
+	return (dict_operator)di->arg[n].u.i;
+}
+
+static dict_operator
+dict_init(fz_context *ctx, dict_iterator *di, const uint8_t *base, size_t len, uint32_t offset, uint32_t end)
+{
+	di->base = base;
+	di->len = len;
+	di->offset = offset;
+	di->end_offset = end;
+	di->eod = (di->offset == di->end_offset);
+
+	if (di->offset > len || end > len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Malformed DICT");
+
+	return dict_next(ctx, di);
+}
+
+static int
+dict_more(dict_iterator *di)
+{
+	return !di->eod;
+}
+
+static uint32_t
+dict_arg_int(fz_context *ctx, dict_iterator *di, int idx)
+{
+	if (idx < 0 || idx >= di->num_args)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Missing dict arg");
+
+	if (di->arg[idx].type != da_int)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "DICT arg not an int");
+
+	return di->arg[idx].u.i;
+}
+
+static void
+dict_write_arg(fz_context *ctx, fz_output *out, dict_arg d)
+{
+	int si;
+	uint32_t i = d.u.i;
+
+	if (d.type == da_operator)
+	{
+		if (i >= HIOP(0))
+		{
+			fz_write_byte(ctx, out, 12);
+			i -= HIOP(0);
+		}
+		fz_write_byte(ctx, out, i);
+		return;
+	}
+
+	if (d.type == da_real)
+	{
+		char text[32];
+		unsigned int k, j;
+		uint8_t v;
+
+		fz_snprintf(text, sizeof(text)-1, "%g", d.u.f);
+
+		fz_write_byte(ctx, out, 30);
+		j = 4;
+		v = 0;
+		for (k = 0; k < sizeof(text)-1;)
+		{
+			char c = text[k++];
+
+			if (c >= '0' && c <= '9')
+				v |= (c - '0')<<j;
+			else if (c == '.')
+				v |= 0xa<<j;
+			else if (c == 'e' || c == 'E')
+			{
+				if (text[k] == '-')
+				{
+					v |= 0xc<<j;
+					k++;
+				}
+				else
+				{
+					v |= 0xb<<j;
+				}
+			}
+			else if (c == '-')
+			{
+				v |= 0xe<<j;
+			}
+			else if (c == 0)
+				break;
+
+			if (j == 0)
+			{
+				fz_write_byte(ctx, out, v);
+				v = 0;
+			}
+			j ^= 4;
+		}
+		if (j == 4)
+			v = 0xff;
+		else
+			v |= 0xf;
+		fz_write_byte(ctx, out, v);
+		return;
+	}
+
+	/* Must be an int. */
+	si = (int)i;
+	if (-107 <= si && si <= 107)
+		fz_write_byte(ctx, out, si+139);
+	else if (108 <= si && si <= 1131)
+	{
+		si -= 108;
+		fz_write_byte(ctx, out, (si>>8)+247);
+		fz_write_byte(ctx, out, si);
+	}
+	else if (-1131 <= si && si <= -108)
+	{
+		si = -si - 108;
+		fz_write_byte(ctx, out, (si>>8)+251);
+		fz_write_byte(ctx, out, si);
+	}
+	else if (-32768 <= si && si <= 32767)
+	{
+		fz_write_byte(ctx, out, 28);
+		fz_write_byte(ctx, out, si>>8);
+		fz_write_byte(ctx, out, si);
+	}
+	else
+	{
+		fz_write_byte(ctx, out, 29);
+		fz_write_byte(ctx, out, si>>24);
+		fz_write_byte(ctx, out, si>>16);
+		fz_write_byte(ctx, out, si>>8);
+		fz_write_byte(ctx, out, si);
+	}
+}
+
+static void
+dict_write_args(fz_context *ctx, fz_output *out, dict_iterator *di)
+{
+	int i;
+
+	for (i = 0; i <= di->num_args; i++)
+	{
+		dict_write_arg(ctx, out, di->arg[i]);
+	}
+}
+
+static void
+do_subset(fz_context *ctx, cff_t *cff, fz_buffer **buffer, usage_list_t *keep_list, index_t *index, int keep_notdef)
+{
+	uint8_t *d, *strings;
+	uint32_t i, offset, end;
+	uint32_t required, offset_size, fill;
+	uint32_t num_charstrings = index->count;
+	int gid;
+	int num_gids = keep_list->len;
+	const usage_t *gids = keep_list->list;
+
+	if (num_charstrings == 0)
+		return;
+
+	/* First count the required size. */
+	offset = index_get(ctx, index, 0);
+	required = 0;
+	gid = 0;
+	for (i = 0; i < num_charstrings; offset = end, i++)
+	{
+		end = index_get(ctx, index, i+1);
+		if (gid < num_gids && i == gids[gid].num)
+		{
+			/* Keep this */
+			gid++;
+		}
+		else if (keep_notdef && i == 0)
+		{
+			/* Keep this. */
+		}
+		else
+		{
+			/* Drop this */
+			required += 1;
+			continue;
+		}
+		required += end-offset;
+	}
+
+	/* So we need 'required' bytes of space for the strings themselves */
+	/* Do not forget to increment by one byte! This is because the
+	last entry in the offset table points to one byte beyond the end of
+	the required string data. Consider if the required string data occupies
+	255 bytes, then each offset for each of the required entries can be
+	represented by a single byte, but the last table entry would need to
+	point to offset 256, which cannot be represented by a single byte. */
+	offset_size = offsize_for_offset(required + 1);
+
+	required += 2 + 1 + (num_charstrings+1)*offset_size;
+
+	*buffer = fz_new_buffer(ctx, required);
+	d = (*buffer)->data;
+	(*buffer)->len = required;
+
+	/* Write out the index header */
+	put16(d, num_charstrings); /* count */
+	d +=2;
+	put8(d, offset_size); /* offset size */
+	d += 1;
+
+
+	/* Now copy the charstrings themselves */
+	strings = d + offset_size * (num_charstrings+1) - 1;
+	gid = 0;
+	fill = 1;
+	offset = index_get(ctx, index, 0);
+	for (i = 0; i < num_charstrings; offset = end, i++)
+	{
+		end = index_get(ctx, index, i+1);
+		if (gid < num_gids && gids[gid].num == i)
+		{
+			/* Keep this */
+			gid++;
+		}
+		else if (keep_notdef && i == 0)
+		{
+			/* Keep this */
+		}
+		else
+		{
+			/* Drop this */
+			put_offset(d, offset_size, fill);
+			d += offset_size;
+			strings[fill++] = 0x0e; /* endchar */
+			continue;
+		}
+
+		memcpy(strings + fill, &cff->base[offset], end-offset);
+		put_offset(d, offset_size, fill);
+		d += offset_size;
+		fill += end-offset;
+	}
+	put_offset(d, offset_size, fill);
+}
+
+static void
+subset_charstrings(fz_context *ctx, cff_t *cff)
+{
+	do_subset(ctx, cff, &cff->charstrings_subset, &cff->gids_to_keep, &cff->charstrings_index, 1);
+}
+
+static void
+subset_locals(fz_context *ctx, cff_t *cff)
+{
+	do_subset(ctx, cff, &cff->local_subset, &cff->local_usage, &cff->local_index, 0);
+}
+
+static void
+subset_globals(fz_context *ctx, cff_t *cff)
+{
+	do_subset(ctx, cff, &cff->global_subset, &cff->global_usage, &cff->global_index, 0);
+}
+
+static void
+subset_fdarray_locals(fz_context *ctx, cff_t *cff)
+{
+	uint16_t i, n = cff->fdarray_index.count;
+
+	for (i = 0; i < n; i++)
+		do_subset(ctx, cff, &cff->fdarray[i].local_subset, &cff->fdarray[i].local_usage, &cff->fdarray[i].local_index, 0);
+}
+
+/* Charstring "executing" functions */
+
+static int
+usage_list_find(fz_context *ctx, usage_list_t *list, int value)
+{
+	/* are we on the list already? */
+	int lo = 0;
+	int hi = list->len;
+
+	while (lo < hi)
+	{
+		int mid = (lo + hi)>>1;
+		int v = list->list[mid].num;
+		if (v < value)
+			lo = mid+1;
+		else if (v > value)
+			hi = mid;
+		else
+			return mid;
+	}
+	return lo;
+}
+
+static int
+usage_list_contains(fz_context *ctx, usage_list_t *list, int value)
+{
+	int lo = usage_list_find(ctx, list, value);
+
+	return (lo < list->len && list->list[lo].num == value);
+}
+
+static void
+usage_list_add(fz_context *ctx, usage_list_t *list, int value)
+{
+	int lo = usage_list_find(ctx, list, value);
+
+	if (lo < list->len && list->list[lo].num == value)
+		return;
+
+	if (list->len == list->max)
+	{
+		int newmax = list->max * 2;
+
+		if (newmax == 0)
+			newmax = 32;
+
+		list->list = fz_realloc(ctx, list->list, sizeof(*list->list) * newmax);
+		list->max = newmax;
+	}
+
+	memmove(&list->list[lo+1], &list->list[lo], (list->len - lo) * sizeof(*list->list));
+	list->list[lo].num = value;
+	list->list[lo].scanned = 0;
+	list->len++;
+}
+
+static void
+drop_usage_list(fz_context *ctx, usage_list_t *list)
+{
+	if (!list)
+		return;
+	fz_free(ctx, list->list);
+	list->list = NULL;
+}
+
+static void
+mark_subr_used(fz_context *ctx, cff_t *cff, int subr, int global, int local_subr_bias, usage_list_t *local_usage)
+{
+	usage_list_t *list = global ? &cff->global_usage : local_usage;
+
+	subr += global ? cff->gsubr_bias : local_subr_bias;
+
+	usage_list_add(ctx, list, subr);
+}
+
+static void
+use_sub_char(fz_context *ctx, cff_t *cff, int code)
+{
+	/* code is a character code in 'standard encoding'. We
+	 * need to map that to whatever glyph that would be in
+	 * standard encoding, and mark that glyph as being used. */
+	uint32_t i, gid;
+
+	if (code < 0 || code > 255)
+		return;
+	i = standard_encoding[code];
+	if (i == 0)
+		return;
+
+	for (gid = 0; gid < cff->unpacked_charset_len; gid++)
+	{
+		if (cff->unpacked_charset[gid] == i)
+			break;
+	}
+	if (gid == cff->unpacked_charset_len)
+	{
+		fz_warn(ctx, "subsidiary char out of range");
+		return;
+	}
+
+	if (usage_list_contains(ctx, &cff->gids_to_keep, gid))
+		return;
+
+	usage_list_add(ctx, &cff->extra_gids_to_keep, gid);
+}
+
+#define ATLEAST(n) if (sp < n) goto atleast_fail;
+#define POP(n) if (sp < n) goto atleast_fail;
+#define PUSH(n) \
+do { if (sp + n > (int)(sizeof(stack)/sizeof(*stack))) fz_throw(ctx, FZ_ERROR_FORMAT, "Stack overflow"); sp += n; } while (0)
+
+static void
+execute_charstring(fz_context *ctx, cff_t *cff, const uint8_t *pc, const uint8_t *end, uint16_t subr_bias, usage_list_t *local_usage)
+{
+	double trans[32] = { 0 };
+	double stack[513];
+	int sp = 0;
+	int stem_hints = 0;
+	uint8_t c;
+
+	/* 0 => starting, 1 => had hstem, 2 => anything else */
+	int start = 0;
+
+	while (pc < end)
+	{
+		c = *pc++;
+
+		/* An operator other than one of the hint ones immediately
+		 * disqualifies us from being in the hint extension state. */
+		if (c < 32 && (c != 1 && c != 18 && c != 19 && c != 20))
+			start = 2;
+
+		switch (c)
+		{
+		case 0:
+		case 2:
+		case 9:
+		case 13:
+		case 17:
+			fz_throw(ctx, FZ_ERROR_FORMAT, "Reserved charstring byte");
+			break;
+
+		/* Deal with all the hints together */
+		case 18: /* hstemhm */
+		case 1: /* hstem */
+			start = 1;
+		case 23: /* vstemhm */
+		case 3: /* vstem */
+			stem_hints += (sp/2);
+			goto clear;
+
+		case 19: /* hintmask */
+		case 20: /* cntrmask */
+			if (start == 1)
+				stem_hints += (sp/2);
+			pc += (stem_hints+7)>>3;
+			if (pc > end)
+				goto overflow;
+			start = 2;
+			goto clear;
+
+		/* The operators all clear the stack. */
+		case 4: /* vmoveto */
+		case 5: /* rlineto */
+		case 6: /* hlineto */
+		case 7: /* vlineto */
+		case 8: /* rrcurveto */
+		case 15: /* vsindex */
+		case 21: /* rmoveto */
+		case 22: /* hmoveto */
+		case 24: /* rcurveline */
+		case 25: /* rlinecurve */
+		case 26: /* vvcurveto */
+		case 27: /* hhcurveto */
+		case 30: /* vhcurveto */
+		case 31: /* hvcurveto */
+clear:
+			sp = 0;
+			break;
+
+
+
+		case 10: /* callsubr */
+			ATLEAST(1);
+			mark_subr_used(ctx, cff, stack[sp-1], 0, subr_bias, local_usage);
+			sp--;
+			break;
+		case 11: /* return */
+			pc = end;
+			sp = 0;
+			break;
+		case 12: /* escape */
+		{
+			if (pc == end)
+			{
+overflow:
+				fz_throw(ctx, FZ_ERROR_FORMAT, "Buffer overflow in charstring");
+			}
+			c = *pc++;
+			switch (c)
+			{
+			case 0: /* dotsection: deprecated, nop */
+				sp = 0;
+				break;
+			case 3: /* and */
+				ATLEAST(2);
+				stack[sp-2] = (stack[sp-1] != 0 && stack[sp-2] != 0);
+				sp--;
+				break;
+			case 4: /* or */
+				ATLEAST(2);
+				stack[sp-2] = (stack[sp-1] != 0 || stack[sp-2] != 0);
+				sp--;
+				break;
+			case 5: /* not */
+				ATLEAST(1);
+				stack[sp-1] = (stack[sp-1] == 0);
+				break;
+
+			case 9: /* abs */
+				ATLEAST(1);
+				if (stack[sp-1] < 0)
+					stack[sp-1] = -stack[sp-1];
+				break;
+
+			case 10: /* add */
+				ATLEAST(2);
+				stack[sp-2] += stack[sp-1];
+				sp--;
+				break;
+
+			case 11: /* sub */
+				ATLEAST(2);
+				stack[sp-2] -= stack[sp-1];
+				sp--;
+				break;
+
+			case 12: /* div */
+				ATLEAST(2);
+				if (stack[sp-2] != 0)
+					stack[sp-2] /= stack[sp-1];
+				sp--;
+				break;
+
+			case 14: /* neg */
+				ATLEAST(1);
+				stack[sp-1] = -stack[sp-1];
+				break;
+
+			case 15: /* eq */
+				ATLEAST(2);
+				stack[sp-2] = (stack[sp-1] == stack[sp-2]);
+				sp--;
+				break;
+			case 18: /* drop */
+				POP(1);
+				break;
+
+			case 20: /* put */
+				ATLEAST(2);
+				if ((int)stack[sp-1] < 0 || (unsigned int)stack[sp-1] > sizeof(trans)/sizeof(*trans))
+					fz_throw(ctx, FZ_ERROR_FORMAT, "Transient array over/underflow");
+				trans[(int)stack[sp-1]] = stack[sp-2];
+				sp -= 2;
+				break;
+			case 21: /* get */
+				ATLEAST(1);
+				if ((int)stack[sp-1] < 0 || (unsigned int)stack[sp-1] > sizeof(trans)/sizeof(*trans))
+					fz_throw(ctx, FZ_ERROR_FORMAT, "Transient array over/underflow");
+				stack[sp-1] = trans[(int)stack[sp-1]];
+				break;
+
+			case 22: /* ifelse */
+				ATLEAST(4);
+				if (stack[sp-2] > stack[sp-1])
+					stack[sp-4] = stack[sp-3];
+				sp -= 3;
+				break;
+			case 23: /* random */
+				PUSH(1);
+				stack[sp-1] = 0.5;
+				break;
+
+			case 24: /* mul */
+				ATLEAST(2);
+				stack[sp-2] *= stack[sp-1];
+				break;
+
+			case 26: /* sqrt */
+				ATLEAST(1);
+				if (stack[sp-1] >= 0)
+					stack[sp-1] = sqrtf(stack[sp-1]);
+				break;
+
+			case 27: /* dup */
+				ATLEAST(1);
+				PUSH(1);
+				stack[sp-1] = stack[sp-2];
+				break;
+
+			case 28: /* exch */
+			{
+				double d;
+				ATLEAST(2);
+				d = stack[sp-1];
+				stack[sp-1] = stack[sp-2];
+				stack[sp-2] = d;
+				break;
+			}
+			case 29: /* index */
+			{
+				int i;
+				ATLEAST(1);
+				i = (int)stack[sp-1];
+				ATLEAST(i+1);
+				if (i < 0 || i > sp-1)
+					i = 0;
+				stack[sp-1] = stack[sp-2-i];
+				break;
+			}
+			case 30: /* roll */
+			{
+				int N, J;
+				ATLEAST(2);
+				J = stack[sp-1];
+				N = stack[sp-2];
+				if (N == 0)
+					break;
+				if (N < 0)
+					fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid roll");
+				ATLEAST(2+N);
+				if (J < 0)
+				{
+					J = N - ((-J) % N);
+					if (J == 0)
+						break;
+				}
+				while (J--)
+				{
+					double t = stack[sp-2];
+					int i;
+					for (i = N-1; i > 0; i--)
+					{
+						stack[sp-2-i] = stack[sp-3-i];
+					}
+					stack[sp-2-N] = t;
+				}
+				break;
+			}
+
+
+			case 34: /* hflex */
+			case 35: /* flex */
+			case 36: /* hflex1 */
+			case 37: /* flex1 */
+				sp = 0;
+				break;
+
+
+			default:
+				fz_throw(ctx, FZ_ERROR_FORMAT, "Reserved charstring byte");
+			}
+			break;
+		}
+		case 14: /* endchar */
+			pc = end;
+			if (sp >= 4)
+			{
+				use_sub_char(ctx, cff, stack[sp-1]);
+				use_sub_char(ctx, cff, stack[sp-2]);
+			}
+			sp = 0;
+			break;
+		case 16: /* blend */
+			/* Consumes a lot of operators, leaves n, where n = stack[sp-1]. */
+			ATLEAST(1);
+			sp = stack[sp-1];
+			break;
+		case 29: /* callgsubr */
+			ATLEAST(1);
+			mark_subr_used(ctx, cff, stack[sp-1], 1, subr_bias, local_usage);
+			sp--;
+			break;
+		case 28: /* shortint */
+			if (pc + 2 >= end)
+			{
+				pc = end;
+				break;
+			}
+			PUSH(1);
+			stack[sp-1] = (pc[0]<<8) | pc[1];
+			pc += 2;
+			break;
+		case 255: /* number */
+			if (pc + 4 >= end)
+			{
+				pc = end;
+				break;
+			}
+			PUSH(1);
+			stack[sp-1] = ((pc[0]<<24) | (pc[1]<<16) | (pc[2]<<8) | pc[3]) / 65536.0;
+			pc += 4;
+			break;
+		case 247: case 248: case 249: case 250: /* number */
+			PUSH(1);
+			stack[sp-1] = (c-247) * 256 + 108;
+			if (pc >= end)
+				break;
+			stack[sp-1] += *pc++;
+			break;
+		case 251: case 252: case 253: case 254: /* number */
+			PUSH(1);
+			stack[sp-1] = -((c-251) * 256 + 108);
+			if (pc >= end)
+				break;
+			stack[sp-1] -= *pc++;
+			break;
+		default: /* 32-246 */
+			PUSH(1);
+			stack[sp-1] = c-139;
+			break;
+		}
+
+	}
+	return;
+atleast_fail:
+	fz_throw(ctx, FZ_ERROR_FORMAT, "Insufficient operators on the stack: op=%d", c);
+}
+
+
+usage_list_t *
+get_font_locals(fz_context *ctx, cff_t *cff, int gid, int is_pdf_cidfont, uint16_t *subr_bias)
+{
+	usage_t *gids = cff->gids_to_keep.list;
+	int num_gids = cff->gids_to_keep.len;
+
+	if (is_pdf_cidfont && cff->is_cidfont)
+	{
+		uint8_t font = 0;
+		if (gid < num_gids && gids[gid].num < cff->charstrings_index.count)
+			font = cff->gid_to_font[gids[gid].num];
+		else if (gid == 0)
+			font = cff->gid_to_font[gid];
+		if (font >= cff->fdarray_index.count)
+			font = 0;
+
+		if (subr_bias)
+			*subr_bias = cff->fdarray[font].subr_bias;
+		return &cff->fdarray[font].local_usage;
+	}
+
+	if (subr_bias)
+		*subr_bias = cff->subr_bias;
+	return &cff->local_usage;
+}
+
+static void
+scan_charstrings(fz_context *ctx, cff_t *cff, int is_pdf_cidfont)
+{
+	uint32_t offset, end;
+	int num_charstrings = (int)cff->charstrings_index.count;
+	int i, gid, font;
+	usage_t *gids = cff->gids_to_keep.list;
+	int num_gids = cff->gids_to_keep.len;
+	int changed;
+	uint16_t subr_bias;
+	usage_list_t *local_usage = NULL;
+
+	/* Scan through the charstrings.*/
+	offset = index_get(ctx, &cff->charstrings_index, 0);
+	gid = 0;
+	for (i = 0; i < num_charstrings; offset = end, i++)
+	{
+		end = index_get(ctx, &cff->charstrings_index, i+1);
+		if (gid < num_gids && i == gids[gid].num)
+		{
+			/* Keep this */
+			gid++;
+		}
+		else if (i == 0)
+		{
+			/* Keep this. */
+		}
+		else
+		{
+			/* Drop this */
+			continue;
+		}
+		local_usage = get_font_locals(ctx, cff, gid, is_pdf_cidfont, &subr_bias);
+		execute_charstring(ctx, cff, &cff->base[offset], &cff->base[end], subr_bias, local_usage);
+	}
+
+	/* Now we search the 'extra' ones, the 'subrs' (local) and 'gsubrs' (globals)
+	 * that are used. Searching each of these might find more that need to be
+	 * searched, so we use a loop. */
+	do
+	{
+		changed = 0;
+		/* Extra (subsidiary) glyphs */
+		for (i = 0; i < cff->extra_gids_to_keep.len; i++)
+		{
+			if (cff->extra_gids_to_keep.list[i].scanned)
+				continue;
+			cff->extra_gids_to_keep.list[i].scanned = 1;
+			gid = cff->extra_gids_to_keep.list[i].num;
+			usage_list_add(ctx, &cff->gids_to_keep, gid);
+			offset = index_get(ctx, &cff->charstrings_index, gid);
+			end = index_get(ctx, &cff->charstrings_index, gid+1);
+
+			local_usage = get_font_locals(ctx, cff, gid, is_pdf_cidfont, &subr_bias);
+			execute_charstring(ctx, cff, &cff->base[offset], &cff->base[end], subr_bias, local_usage);
+			changed = 1;
+		}
+
+		/* Now, run through the locals, seeing what locals and globals they call.  */
+		for (i = 0; i < cff->local_usage.len; i++)
+		{
+			if (cff->local_usage.list[i].scanned)
+				continue;
+			cff->local_usage.list[i].scanned = 1;
+			gid = cff->local_usage.list[i].num;
+			offset = index_get(ctx, &cff->local_index, gid);
+			end = index_get(ctx, &cff->local_index, gid+1);
+
+			local_usage = get_font_locals(ctx, cff, gid, is_pdf_cidfont, &subr_bias);
+			execute_charstring(ctx, cff, &cff->base[offset], &cff->base[end], subr_bias, local_usage);
+			changed = 1;
+		}
+
+		/* Now, run through the per-font locals, seeing what per-font locals and globals they call.  */
+		for (font = 0; font < cff->fdarray_index.count; font++)
+		{
+			for (i = 0; i < cff->fdarray[font].local_usage.len; i++)
+			{
+				gid = cff->fdarray[font].local_usage.list[i].num;
+
+				if (cff->fdarray[font].local_usage.list[i].scanned)
+					continue;
+				cff->fdarray[font].local_usage.list[i].scanned = 1;
+				gid = cff->fdarray[font].local_usage.list[i].num;
+				offset = index_get(ctx, &cff->fdarray[font].local_index, gid);
+				end = index_get(ctx, &cff->fdarray[font].local_index, gid+1);
+
+				local_usage = get_font_locals(ctx, cff, gid, is_pdf_cidfont, &subr_bias);
+				execute_charstring(ctx, cff, &cff->base[offset], &cff->base[end], subr_bias, local_usage);
+				changed = 1;
+			}
+		}
+
+		/* Now, run through the globals, seeing what globals they call.  */
+		for (i = 0; i < cff->global_usage.len; i++)
+		{
+			if (cff->global_usage.list[i].scanned)
+				continue;
+			cff->global_usage.list[i].scanned = 1;
+			gid = cff->global_usage.list[i].num;
+			offset = index_get(ctx, &cff->global_index, gid);
+			end = index_get(ctx, &cff->global_index, gid+1);
+
+			local_usage = get_font_locals(ctx, cff, gid, is_pdf_cidfont, &subr_bias);
+			execute_charstring(ctx, cff, &cff->base[offset], &cff->base[end], subr_bias, local_usage);
+			changed = 1;
+		}
+	}
+	while (changed);
+}
+
+static void
+get_encoding_len(fz_context *ctx, cff_t *cff)
+{
+	uint32_t encoding_offset = cff->encoding_offset;
+	const uint8_t *d = cff->base + encoding_offset;
+	uint8_t fmt;
+	uint8_t n;
+	uint32_t size;
+
+	if (encoding_offset < 2)
+	{
+		cff->encoding_len = 0;
+		return;
+	}
+
+	if (encoding_offset + 2 > cff->len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt encoding");
+
+	fmt = *d++;
+	n = *d++;
+
+	switch (fmt & 127)
+	{
+	case 0:
+		size = 2 + n;
+		break;
+	case 1:
+		size = 2 + n * 2;
+		break;
+	case 2:
+		size = 2 + n * 3;
+		break;
+	default:
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Bad format encoding");
+	}
+
+	if (encoding_offset + size > cff->len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt encoding");
+
+	if (fmt & 128)
+	{
+		if (encoding_offset + size + 1 > cff->len)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt encoding");
+
+		n = *d++;
+		size += 1 + n*3;
+
+		if (encoding_offset + size > cff->len)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt encoding");
+	}
+	cff->encoding_len = size;
+}
+
+static void
+get_charset_len(fz_context *ctx, cff_t *cff)
+{
+	uint32_t charset_offset = cff->charset_offset;
+	const uint8_t *d = cff->base + charset_offset;
+	const uint8_t *d0 = d;
+	uint8_t fmt;
+	uint32_t i, n;
+
+	if (charset_offset < 2)
+	{
+		cff->charset_len = 0;
+		return;
+	}
+
+	if (charset_offset + 1 > cff->len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+
+	fmt = *d++;
+	n = cff->charstrings_index.count;
+
+	if (fmt == 0)
+	{
+		cff->unpacked_charset = fz_malloc(ctx, sizeof(uint16_t) * n);
+		cff->unpacked_charset_len = cff->unpacked_charset_max = n;
+		cff->unpacked_charset[0] = 0;
+		for (i = 1; i < n; i++)
+		{
+			cff->unpacked_charset[i] = get16(d);
+			d += 2;
+		}
+	}
+	else if (fmt == 1)
+	{
+		cff->unpacked_charset = fz_malloc(ctx, sizeof(uint16_t) * 256);
+		cff->unpacked_charset_max = 256;
+		cff->unpacked_charset_len = 1;
+		cff->unpacked_charset[0] = 0;
+		n--;
+		while (n > 0)
+		{
+			uint16_t first;
+			uint32_t nleft;
+			if (d + 3>= cff->base + cff->len)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+			first = get16(d);
+			nleft = d[2] + 1;
+			d += 3;
+			if (nleft > n)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+			n -= nleft;
+			while (nleft)
+			{
+				if (cff->unpacked_charset_len == cff->unpacked_charset_max)
+				{
+					cff->unpacked_charset = fz_realloc(ctx, cff->unpacked_charset, sizeof(uint16_t) * 2 * cff->unpacked_charset_max);
+					cff->unpacked_charset_max *= 2;
+				}
+				cff->unpacked_charset[cff->unpacked_charset_len++] = first;
+				first++;
+				nleft--;
+			}
+		}
+	}
+	else if (fmt == 2)
+	{
+		cff->unpacked_charset = fz_malloc(ctx, sizeof(uint16_t) * 256);
+		cff->unpacked_charset_max = 256;
+		cff->unpacked_charset_len = 1;
+		cff->unpacked_charset[0] = 0;
+		n--;
+		while (n > 0)
+		{
+			uint16_t first;
+			uint32_t nleft;
+			if (d + 4 >= cff->base + cff->len)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+			first = get16(d);
+			nleft = get16(d+2) + 1;
+			d += 4;
+			if (nleft > n)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+			n -= nleft;
+			while (nleft)
+			{
+				if (cff->unpacked_charset_len == cff->unpacked_charset_max)
+				{
+					cff->unpacked_charset = fz_realloc(ctx, cff->unpacked_charset, sizeof(uint16_t) * 2 * cff->unpacked_charset_max);
+					cff->unpacked_charset_max *= 2;
+				}
+				cff->unpacked_charset[cff->unpacked_charset_len++] = first;
+				first++;
+				nleft--;
+			}
+		}
+	}
+	else
+	{
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Bad charset format");
+	}
+
+	cff->charset_len = (uint32_t)(d - d0);
+}
+
+static void
+read_fdselect(fz_context *ctx, cff_t *cff)
+{
+	uint32_t fdselect_offset = cff->fdselect_offset;
+	const uint8_t *d = cff->base + fdselect_offset;
+	const uint8_t *d0 = d;
+	uint8_t fmt;
+	uint16_t n, m, i, first, last, k;
+
+	if (fdselect_offset == 0)
+	{
+		cff->fdselect_len = 0;
+		return;
+	}
+
+	if (fdselect_offset + 1 > cff->len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt fdselect");
+
+	fmt = *d++;
+	n = cff->charstrings_index.count;
+
+	cff->gid_to_font = fz_calloc(ctx, n, sizeof(*cff->gid_to_font));
+
+	if (fmt == 0)
+	{
+		for (i = 0; i < n; i++)
+		{
+			if (d >= cff->base + cff->len)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt fdselect");
+			cff->gid_to_font[i] = d[0];
+			d++;
+		}
+	}
+	else if (fmt == 3)
+	{
+		if (d + 2 >= cff->base + cff->len)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt fdselect");
+		m = get16(d);
+		d += 2;
+		if (m > cff->charstrings_index.count)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt fdselect");
+
+		for (i = 0; i < m; i++)
+		{
+			if (d + 5 >= cff->base + cff->len)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt fdselect");
+			first = get16(d);
+			last = get16(d + 3);
+			if (first >= cff->charstrings_index.count || last > cff->charstrings_index.count || first >= last)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt fdselect");
+			for (k = first; k < last; k++)
+				cff->gid_to_font[k] = d[2];
+			d += 3;
+		}
+	}
+
+	cff->fdselect_len = (uint32_t)(d - d0);
+}
+
+static void
+load_charset_for_cidfont(fz_context *ctx, cff_t *cff)
+{
+	uint32_t charset_offset = cff->charset_offset;
+	const uint8_t *d = cff->base + charset_offset;
+	uint8_t fmt;
+	uint32_t n = cff->charstrings_index.count;
+	uint32_t i;
+
+	if (charset_offset + 1 > cff->len)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+
+	fmt = *d++;
+
+	cff->gid_to_cid = fz_calloc(ctx, n, sizeof(*cff->gid_to_cid));
+	cff->gid_to_cid[0] = 0;
+
+	if (fmt == 0)
+	{
+		for (i = 1; i < n; i++)
+		{
+			cff->gid_to_cid[i] = get16(d);
+			d += 2;
+		}
+	}
+	else if (fmt == 1)
+	{
+		for (i = 1; i < n;)
+		{
+			uint16_t first;
+			int32_t nleft;
+			if (d + 3 >= cff->base + cff->len)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+			first = get16(d);
+			nleft = d[2] + 1;
+			d += 3;
+			while (nleft-- && i < n)
+			{
+				cff->gid_to_cid[i++] = first++;
+			}
+		}
+	}
+	else if (fmt == 2)
+	{
+		for (i = 1; i < n;)
+		{
+			uint16_t first;
+			int32_t nleft;
+			if (d + 4 >= cff->base + cff->len)
+				fz_throw(ctx, FZ_ERROR_FORMAT, "corrupt charset");
+			first = get16(d);
+			nleft = get16(d+2) + 1;
+			d += 4;
+			while (nleft-- && i < n)
+			{
+				cff->gid_to_cid[i++] = first++;
+			}
+		}
+	}
+	else
+	{
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Bad charset format");
+	}
+}
+
+static void
+write_offset(fz_context *ctx, fz_output *out, uint8_t os, uint32_t v)
+{
+	if (os > 3)
+		fz_write_byte(ctx, out, v>>24);
+	if (os > 2)
+		fz_write_byte(ctx, out, v>>16);
+	if (os > 1)
+		fz_write_byte(ctx, out, v>>8);
+	fz_write_byte(ctx, out, v);
+}
+
+static void
+output_name_index(fz_context *ctx, cff_t *cff, fz_output *out)
+{
+	uint32_t name0 = index_get(ctx, &cff->name_index, 0);
+	uint32_t name1 = index_get(ctx, &cff->name_index, 1);
+	uint8_t os;
+
+	/* Turn name1 back into an offset from the index. */
+	name1 -= name0;
+	name1++;
+	os = offsize_for_offset(name1);
+
+	fz_write_uint16_be(ctx, out, 1); /* Count */
+	fz_write_byte(ctx, out, os); /* offsize */
+	write_offset(ctx, out, os, 1); /* index[0] = 1 */
+	write_offset(ctx, out, os, name1); /* index[1] = end */
+	fz_write_data(ctx, out, cff->base + name0, name1-1);
+}
+
+static void
+output_top_dict_index(fz_context *ctx, cff_t *cff, fz_output *out)
+{
+	uint32_t top_dict_len = (uint32_t)cff->top_dict_subset->len;
+	uint8_t os = offsize_for_offset((uint32_t)(1 + top_dict_len));
+
+	fz_write_uint16_be(ctx, out, 1); /* Count */
+	fz_write_byte(ctx, out, os); /* offsize */
+	write_offset(ctx, out, os, 1);
+	write_offset(ctx, out, os, (uint32_t)(1 + cff->top_dict_subset->len));
+
+	/* And copy the updated top dict. */
+	fz_write_data(ctx, out, cff->top_dict_subset->data, cff->top_dict_subset->len);
+}
+
+static uint32_t
+rewrite_fdarray(fz_context *ctx, cff_t *cff, uint32_t offset0)
+{
+	/* fdarray_index will start at offset0. */
+	uint16_t i;
+	uint16_t n = cff->fdarray_index.count;
+	uint32_t len = 0;
+	uint8_t os;
+	size_t offset;
+
+	if (cff->fdarray == NULL)
+		fz_throw(ctx, FZ_ERROR_FORMAT, "Expected to rewrite an fdarray");
+
+	/* Count how many bytes the index will require. */
+	for (i = 0; i < n; i++)
+	{
+		len += (uint32_t)cff->fdarray[i].rewritten_dict->len;
+	}
+	os = offsize_for_offset(len+1);
+	len += 2 + 1 + (n+1)*os;
+
+	/* Now offset0 + len points to where the private dicts
+	 * will go. Run through, fixing up the offsets in the
+	 * font dicts (this won't change the length). */
+	offset = offset0 + len;
+	for (i = 0; i < n; i++)
+	{
+		assert(cff->fdarray[i].rewritten_dict->data[cff->fdarray[i].fixup] == 29);
+		assert(cff->fdarray[i].rewritten_dict->data[cff->fdarray[i].fixup+5] == 29);
+		put32(&cff->fdarray[i].rewritten_dict->data[cff->fdarray[i].fixup+1], (uint32_t)cff->fdarray[i].rewritten_private->len);
+		put32(&cff->fdarray[i].rewritten_dict->data[cff->fdarray[i].fixup+6], (uint32_t)offset);
+		offset += cff->fdarray[i].rewritten_private->len;
+		if (cff->fdarray[i].local_subset)
+		{
+			offset += cff->fdarray[i].local_subset->len;
+		}
+		else
+		{
+			offset += 2;
+		}
+	}
+
+	return (uint32_t)offset;
+}
+
+static void
+update_dicts(fz_context *ctx, cff_t *cff, uint32_t offset)
+{
+	uint8_t *top_dict_data = cff->top_dict_subset->data;
+	uint32_t top_dict_len = (uint32_t)cff->top_dict_subset->len;
+
+	/* Update the offsets */
+	/*	Header
+		Name Index
+		Top Dict Index
+			(Top Dict)
+		String Index
+		Global Subr Index
+		Encodings
+		Charsets
+		FDSelect
+		CharStrings Index
+		Font DICT Index
+			(Font Dict)
+		Private DICT
+		Local Subr Index
+	*/
+	offset += 2 + 1 + 2 * offsize_for_offset(top_dict_len+1); /* offset = start of top_dict_index data */
+	offset += top_dict_len; /* offset = end of top_dict */
+	if (cff->string_index.index_size)
+		offset += cff->string_index.index_size;
+	else
+		offset += 2;
+	if (cff->global_subset)
+		offset += (uint32_t)cff->global_subset->len;
+	else if (cff->global_index.index_size)
+		offset += cff->global_index.index_size;
+	else
+		offset += 2;
+	if (cff->top_dict_fixup_offsets.encoding)
+	{
+		assert(top_dict_data[cff->top_dict_fixup_offsets.encoding] == 29);
+		put32(top_dict_data + cff->top_dict_fixup_offsets.encoding+1, offset);
+		offset += cff->encoding_len;
+	}
+	if (cff->top_dict_fixup_offsets.charset)
+	{
+		assert(top_dict_data[cff->top_dict_fixup_offsets.charset] == 29);
+		put32(top_dict_data + cff->top_dict_fixup_offsets.charset+1, offset);
+		offset += cff->charset_len;
+	}
+	if (cff->top_dict_fixup_offsets.fdselect)
+	{
+		assert(top_dict_data[cff->top_dict_fixup_offsets.fdselect] == 29);
+		put32(top_dict_data + cff->top_dict_fixup_offsets.fdselect+1, offset);
+		offset += cff->fdselect_len;
+	}
+	assert(top_dict_data[cff->top_dict_fixup_offsets.charstrings] == 29);
+	put32(top_dict_data + cff->top_dict_fixup_offsets.charstrings+1, offset);
+	if (cff->charstrings_subset)
+		offset += (uint32_t)cff->charstrings_subset->len;
+	else if (cff->charstrings_index.index_size)
+		offset += cff->charstrings_index.index_size;
+	else
+		offset += 2;
+	if (cff->top_dict_fixup_offsets.fdarray)
+	{
+		assert(top_dict_data[cff->top_dict_fixup_offsets.fdarray] == 29);
+		put32(top_dict_data + cff->top_dict_fixup_offsets.fdarray+1, offset);
+		offset = rewrite_fdarray(ctx, cff, offset);
+	}
+	if (cff->top_dict_fixup_offsets.privat)
+	{
+		assert(top_dict_data[cff->top_dict_fixup_offsets.privat] == 29);
+		put32(top_dict_data + cff->top_dict_fixup_offsets.privat+1, (uint32_t)cff->private_subset->len);
+		put32(top_dict_data + cff->top_dict_fixup_offsets.privat+6, offset);
+	}
+}
+
+static void
+read_top_dict(fz_context *ctx, cff_t *cff, int idx)
+{
+	dict_iterator di;
+	dict_operator k;
+	uint32_t top_dict_offset = index_get(ctx, &cff->top_dict_index, idx);
+	uint32_t top_dict_end = index_get(ctx, &cff->top_dict_index, idx+1);
+
+	for (k = dict_init(ctx, &di, cff->base, cff->len, top_dict_offset, top_dict_end); dict_more(&di); k = dict_next(ctx, &di))
+	{
+		switch (k)
+		{
+		case DICT_OP_ROS:
+			cff->is_cidfont = 1;
+			break;
+		case DICT_OP_charset:
+			cff->charset_offset = dict_arg_int(ctx, &di, 0);
+			break;
+		case DICT_OP_Encoding:
+			cff->encoding_offset = dict_arg_int(ctx, &di, 0);
+			break;
+		case DICT_OP_CharstringType:
+			cff->charstring_type = 1;
+			break;
+		case DICT_OP_CharStrings:
+			cff->charstrings_index_offset = dict_arg_int(ctx, &di, 0);
+			break;
+		case DICT_OP_Private:
+			cff->private_len = dict_arg_int(ctx, &di, 0);
+			cff->private_offset = dict_arg_int(ctx, &di, 1);
+			break;
+		case DICT_OP_FDSelect:
+			cff->fdselect_offset = dict_arg_int(ctx, &di, 0);
+			break;
+		case DICT_OP_FDArray:
+			cff->fdarray_index_offset = dict_arg_int(ctx, &di, 0);
+			break;
+		default:
+			break;
+		}
+	}
+
+	for (k = dict_init(ctx, &di, cff->base, cff->len, cff->private_offset, cff->private_offset + cff->private_len); dict_more(&di); k = dict_next(ctx, &di))
+	{
+		switch (k)
+		{
+		case DICT_OP_Subrs:
+			cff->local_index_offset = dict_arg_int(ctx, &di, 0) + cff->private_offset;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void
+make_new_top_dict(fz_context *ctx, cff_t *cff)
+{
+	dict_iterator di;
+	dict_operator k;
+	uint32_t top_dict_offset = index_get(ctx, &cff->top_dict_index, 0);
+	uint32_t top_dict_end = index_get(ctx, &cff->top_dict_index, 1);
+	fz_output *out = NULL;
+
+	cff->top_dict_subset = fz_new_buffer(ctx, 1024);
+
+	fz_var(out);
+
+	fz_try(ctx)
+	{
+		out = fz_new_output_with_buffer(ctx, cff->top_dict_subset);
+
+		for (k = dict_init(ctx, &di, cff->base, cff->len, top_dict_offset, top_dict_end); dict_more(&di); k = dict_next(ctx, &di))
+		{
+			switch (k)
+			{
+			case DICT_OP_charset:
+				if (cff->charset_offset < 2)
+					di.arg[0].u.i = cff->charset_offset;
+				else
+				{
+					di.arg[0].u.i = 0x80000000;
+					cff->top_dict_fixup_offsets.charset = fz_tell_output(ctx, out);
+				}
+				break;
+			case DICT_OP_Encoding:
+				if (cff->encoding_offset < 2)
+					di.arg[0].u.i = cff->encoding_offset;
+				else
+				{
+					di.arg[0].u.i = 0x80000000;
+					cff->top_dict_fixup_offsets.encoding = fz_tell_output(ctx, out);
+				}
+				break;
+			case DICT_OP_CharStrings:
+				di.arg[0].u.i = 0x80000000;
+				cff->top_dict_fixup_offsets.charstrings = fz_tell_output(ctx, out);
+				break;
+			case DICT_OP_Private:
+				di.arg[0].u.i = 0x80000000;
+				di.arg[1].u.i = 0x80000000;
+				cff->top_dict_fixup_offsets.privat = fz_tell_output(ctx, out);
+				break;
+			case DICT_OP_FDSelect:
+				di.arg[0].u.i = 0x80000000;
+				cff->top_dict_fixup_offsets.fdselect = fz_tell_output(ctx, out);
+				break;
+			case DICT_OP_FDArray:
+				di.arg[0].u.i = 0x80000000;
+				cff->top_dict_fixup_offsets.fdarray = fz_tell_output(ctx, out);
+				break;
+			default:
+				break;
+			}
+			dict_write_args(ctx, out, &di);
+		}
+
+		fz_close_output(ctx, out);
+	}
+	fz_always(ctx)
+		fz_drop_output(ctx, out);
+	fz_catch(ctx)
+		fz_rethrow(ctx);
+}
+
+static void
+make_new_private_dict(fz_context *ctx, cff_t *cff)
+{
+	dict_iterator di;
+	dict_operator k;
+	fz_output *out = NULL;
+	int64_t len;
+
+	cff->private_subset = fz_new_buffer(ctx, 1024);
+
+	fz_var(out);
+
+	fz_try(ctx)
+	{
+		int subrs = 0;
+		out = fz_new_output_with_buffer(ctx, cff->private_subset);
+
+		for (k = dict_init(ctx, &di, cff->base, cff->len, cff->private_offset, cff->private_offset + cff->private_len); dict_more(&di); k = dict_next(ctx, &di))
+		{
+			switch (k)
+			{
+			case DICT_OP_Subrs:
+				subrs = 1;
+				break;
+			default:
+				dict_write_args(ctx, out, &di);
+			}
+		}
+
+		if (subrs != 0)
+		{
+			/* Everything is in the DICT except for the local subr offset. Insert
+			 * that now. This is tricky, because what is the offset? It depends on
+			 * the size of the dict we are creating now, and the size of the dict
+			 * we are creating now depends on the size of the offset! */
+			/* Length so far */
+			len = fz_tell_output(ctx, out);
+			/* We have to encode an offset, plus the Subrs token (19). Offset
+			 * can take up to 5 bytes. */
+			if (len+2 < 107)
+			{
+				/* We can code it with a single byte encoding */
+				len += 2;
+				fz_write_byte(ctx, out, len + 139);
+			}
+			else if (len+3 < 1131)
+			{
+				/* We can code it with a 2 byte encoding */
+				/* (b0-247) * 256 + b1 + 108 == len+3 */
+				len = len+3 - 108;
+				fz_write_byte(ctx, out, (len>>8) + 247);
+				fz_write_byte(ctx, out, len);
+			}
+			else if (len+4 < 32767)
+			{
+				/* We can code it with a 3 byte encoding */
+				len += 4;
+				fz_write_byte(ctx, out, 28);
+				fz_write_byte(ctx, out, len>>8);
+				fz_write_byte(ctx, out, len);
+			}
+			else
+			{
+				/* We can code it with a 5 byte encoding */
+				len += 5;
+				fz_write_byte(ctx, out, 29);
+				fz_write_byte(ctx, out, len>>24);
+				fz_write_byte(ctx, out, len>>16);
+				fz_write_byte(ctx, out, len>>8);
+				fz_write_byte(ctx, out, len);
+			}
+			fz_write_byte(ctx, out, DICT_OP_Subrs);
+		}
+
+		fz_close_output(ctx, out);
+	}
+	fz_always(ctx)
+		fz_drop_output(ctx, out);
+	fz_catch(ctx)
+		fz_rethrow(ctx);
+}
+
+static void
+read_fdarray_and_privates(fz_context *ctx, cff_t *cff)
+{
+	dict_iterator di;
+	dict_operator k;
+	uint16_t i;
+	uint16_t n = cff->fdarray_index.count;
+	int subrs;
+	int64_t len;
+
+	cff->fdarray = fz_calloc(ctx, n, sizeof(*cff->fdarray));
+
+	for (i = 0; i < n; i++)
+	{
+		uint32_t offset = index_get(ctx, &cff->fdarray_index, i);
+		uint32_t end = index_get(ctx, &cff->fdarray_index, i+1);
+		fz_output *out = NULL;
+
+		cff->fdarray[i].rewritten_dict = fz_new_buffer(ctx, 1024);
+
+		fz_var(out);
+
+		fz_try(ctx)
+		{
+			out = fz_new_output_with_buffer(ctx, cff->fdarray[i].rewritten_dict);
+
+			for (k = dict_init(ctx, &di, cff->base, cff->len, offset, end); dict_more(&di); k = dict_next(ctx, &di))
+			{
+				switch (k)
+				{
+				case DICT_OP_Private:
+					cff->fdarray[i].len = di.arg[0].u.i;
+					cff->fdarray[i].offset = di.arg[1].u.i;
+					di.arg[0].u.i = 0x80000000;
+					di.arg[1].u.i = 0x80000000;
+					cff->fdarray[i].fixup = fz_tell_output(ctx, out);
+					break;
+				default:
+					break;
+				}
+				dict_write_args(ctx, out, &di);
+			}
+
+			fz_close_output(ctx, out);
+		}
+		fz_always(ctx)
+			fz_drop_output(ctx, out);
+		fz_catch(ctx)
+			fz_rethrow(ctx);
+
+
+		offset = cff->fdarray[i].offset;
+		end = cff->fdarray[i].offset + cff->fdarray[i].len;
+
+		fz_try(ctx)
+		{
+			cff->fdarray[i].rewritten_private = fz_new_buffer(ctx, 1024);
+
+			out = fz_new_output_with_buffer(ctx, cff->fdarray[i].rewritten_private);
+			cff->fdarray[i].local_index_offset = 0;
+
+			subrs = 0;
+
+			for (k = dict_init(ctx, &di, cff->base, cff->len, offset, end); dict_more(&di); k = dict_next(ctx, &di))
+			{
+				switch (k)
+				{
+				case DICT_OP_Subrs:
+					subrs = 1;
+					cff->fdarray[i].local_index_offset = dict_arg_int(ctx, &di, 0) + offset;
+					break;
+				default:
+					dict_write_args(ctx, out, &di);
+					break;
+				}
+			}
+
+			if (subrs != 0)
+			{
+				/* Everything is in the DICT except for the local subr offset. Insert
+				 * that now. This is tricky, because what is the offset? It depends on
+				 * the size of he dict we are creating now, and the size of the dict
+				 * we are creating now depends on the size of the offset! */
+				/* Length so far */
+				len = fz_tell_output(ctx, out);
+				/* We have to encode an offset, plus the Subrs token (19). Offset
+				 * can take up to 5 bytes. */
+				if (len+2 < 107)
+				{
+					/* We can code it with a single byte encoding */
+					len += 2;
+					fz_write_byte(ctx, out, len + 139);
+				}
+				else if (len+3 < 1131)
+				{
+					/* We can code it with a 2 byte encoding */
+					/* (b0-247) * 256 + b1 + 108 == len+3 */
+					len = len+3 - 108;
+					fz_write_byte(ctx, out, (len>>8) + 247);
+					fz_write_byte(ctx, out, len);
+				}
+				else if (len+4 < 32767)
+				{
+					/* We can code it with a 3 byte encoding */
+					len += 4;
+					fz_write_byte(ctx, out, 28);
+					fz_write_byte(ctx, out, len>>8);
+					fz_write_byte(ctx, out, len);
+				}
+				else
+				{
+					/* We can code it with a 5 byte encoding */
+					len += 5;
+					fz_write_byte(ctx, out, 29);
+					fz_write_byte(ctx, out, len>>24);
+					fz_write_byte(ctx, out, len>>16);
+					fz_write_byte(ctx, out, len>>8);
+					fz_write_byte(ctx, out, len);
+				}
+				fz_write_byte(ctx, out, DICT_OP_Subrs);
+			}
+
+			fz_close_output(ctx, out);
+		}
+		fz_always(ctx)
+			fz_drop_output(ctx, out);
+		fz_catch(ctx)
+			fz_rethrow(ctx);
+
+		if (cff->fdarray[i].local_index_offset != 0)
+		{
+			index_load(ctx, &cff->fdarray[i].local_index, cff->base, (uint32_t)cff->len, cff->fdarray[i].local_index_offset);
+			cff->fdarray[i].subr_bias = subr_bias(ctx, cff, cff->fdarray[i].local_index.count);
+		}
+	}
+}
+
+static void
+output_fdarray(fz_context *ctx, fz_output *out, cff_t *cff)
+{
+	uint16_t i;
+	uint16_t n = cff->fdarray_index.count;
+	uint8_t os;
+	uint32_t offset = 1;
+	uint32_t len = 0;
+
+	for (i = 0; i < n; i++)
+	{
+		len += (uint32_t)cff->fdarray[i].rewritten_dict->len;
+	}
+	os = offsize_for_offset(len+1);
+
+	fz_write_uint16_be(ctx, out, cff->fdarray_index.count); /* Count */
+	fz_write_byte(ctx, out, os); /* offsize */
+
+	/* First we write out the offsets of the rewritten dicts. */
+	for (i = 0; i < n; i++)
+	{
+		write_offset(ctx, out, os, offset);
+		offset += (uint32_t)cff->fdarray[i].rewritten_dict->len;
+	}
+	write_offset(ctx, out, os, offset);
+
+	/* Now write the dicts themselves. */
+	for (i = 0; i < n; i++)
+	{
+		fz_write_data(ctx, out, cff->fdarray[i].rewritten_dict->data, cff->fdarray[i].rewritten_dict->len);
+	}
+
+	/* Now we can write out the private dicts, unchanged from the original file. */
+	for (i = 0; i < n; i++)
+	{
+		fz_write_data(ctx, out, cff->fdarray[i].rewritten_private->data, cff->fdarray[i].rewritten_private->len);
+		if (cff->fdarray[i].local_subset)
+			fz_write_data(ctx, out, cff->fdarray[i].local_subset->data, cff->fdarray[i].local_subset->len);
+		else
+			fz_write_uint16_be(ctx, out, 0);
+	}
+}
+
+/* Nasty O(n^2) thing. */
+static uint16_t
+cid_to_gid(fz_context *ctx, cff_t *cff, uint16_t cid)
+{
+	uint32_t n = cff->charstrings_index.count;
+	uint32_t i;
+
+	for (i = 0; i < n; i++)
+	{
+		if (cff->gid_to_cid[i] == cid)
+			return i;
+	}
+	return 0;
+}
+
+
+fz_buffer *
+fz_subset_cff_for_gids(fz_context *ctx, fz_buffer *orig, int *gids, int num_gids, int symbolic, int is_pdf_cidfont)
+{
+	cff_t cff = { 0 };
+	fz_buffer *newbuf = NULL;
+	uint8_t *base;
+	size_t len;
+	fz_output *out = NULL;
+	int i;
+	uint16_t n, k;
+
+	fz_var(newbuf);
+	fz_var(out);
+
+	if (orig == NULL)
+		return NULL;
+
+	base = orig->data;
+	len = orig->len;
+
+	fz_try(ctx)
+	{
+		cff.base = base;
+		cff.len = len;
+
+		cff.symbolic = symbolic;
+
+		if (len < 4)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "Truncated CFF");
+
+		cff.major = base[0];
+		cff.minor = base[1];
+		cff.headersize = base[2];
+		cff.offsize = base[3];
+
+		if (cff.offsize > 4)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid offsize in CFF");
+
+		if (len > UINT32_MAX)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "CFF too large");
+
+		/* First, the name index */
+		cff.top_dict_index_offset = index_load(ctx, &cff.name_index, base, (uint32_t)len, cff.headersize);
+
+		/* Next, the top dict index */
+		cff.string_index_offset = index_load(ctx, &cff.top_dict_index, base, (uint32_t)len, cff.top_dict_index_offset);
+
+		/* Next, the string index */
+		cff.global_index_offset = index_load(ctx, &cff.string_index, base, (uint32_t)len, cff.string_index_offset);
+
+		/* Next the Global subr index */
+		index_load(ctx, &cff.global_index, base, (uint32_t)len, cff.global_index_offset);
+
+		/* Default value, possibly updated by top dict entries */
+		cff.charstring_type = 2;
+
+		/* CFF files can contain several fonts, but we only want the first one. */
+		read_top_dict(ctx, &cff, 0);
+
+		cff.gsubr_bias = subr_bias(ctx, &cff, cff.global_index.count);
+
+		if (cff.charstrings_index_offset == 0)
+			fz_throw(ctx, FZ_ERROR_FORMAT, "Missing charstrings table");
+
+		index_load(ctx, &cff.charstrings_index, base, (uint32_t)len, cff.charstrings_index_offset);
+		index_load(ctx, &cff.local_index, base, (uint32_t)len, cff.local_index_offset);
+		cff.subr_bias = subr_bias(ctx, &cff, cff.local_index.count);
+		index_load(ctx, &cff.fdarray_index, base, (uint32_t)len, cff.fdarray_index_offset);
+
+		get_encoding_len(ctx, &cff);
+		get_charset_len(ctx, &cff);
+
+		if (is_pdf_cidfont && cff.is_cidfont)
+		{
+			read_fdselect(ctx, &cff);
+			read_fdarray_and_privates(ctx, &cff);
+		}
+
+		/* Move our list of gids into our own storage. */
+		if (is_pdf_cidfont && cff.is_cidfont)
+		{
+			/* For CIDFontType0 FontDescriptor with a CFF that uses CIDFont operators,
+			 * we are given CIDs here, not GIDs. Accordingly
+			 * we need to look them up in the CharSet.
+			 */
+			load_charset_for_cidfont(ctx, &cff);
+			for (i = 0; i < num_gids; i++)
+				usage_list_add(ctx, &cff.gids_to_keep, cid_to_gid(ctx, &cff, gids[i]));
+		}
+		else
+		{
+			/* For CIDFontType0 FontDescriptor with a CFF that DOES NOT use CIDFont operators,
+			 * and for Type1 FontDescriptors, we are given GIDs directly.
+			 */
+			for (i = 0; i < num_gids; i++)
+				usage_list_add(ctx, &cff.gids_to_keep, gids[i]);
+		}
+
+		/* Scan charstrings. */
+		scan_charstrings(ctx, &cff, is_pdf_cidfont);
+
+		/* Now subset the data. */
+		subset_charstrings(ctx, &cff);
+		if (is_pdf_cidfont && cff.is_cidfont)
+			subset_fdarray_locals(ctx, &cff);
+		subset_locals(ctx, &cff);
+		subset_globals(ctx, &cff);
+
+		/* FIXME: cull the strings? */
+
+		/*	Now, rewrite the font.
+
+			There are various sections for this, as follows:
+
+				SECTION			CIDFonts	Dict
+					(Subsection)	only		Contains
+									absolute
+									offsets?
+				Header
+				Name Index
+				Top Dict Index
+					(Top Dict)			Y
+				String Index
+				Global Subr Index
+				Encodings
+				Charsets
+				FDSelect		Y
+				CharStrings Index
+				Font DICT Index		Y
+					(Font Dict)			N
+				Private DICT				N
+				Local Subr Index
+
+		The size of global offsets varies according to how large the file is,
+		therefore we need to take care.
+
+		The 'suffix' of sections from String Index onwards are independent of
+		this global offset size, so we finalise those sections first.
+
+		We can then use this size to inform our choice of offset size for the
+		top dictionary.
+
+		So, layout the sections from the end backwards.
+		*/
+
+		/* Local Subr Index */
+		/* Private DICT */
+		make_new_private_dict(ctx, &cff);
+		/* Font DICT - CIDFont only */
+		/* Charstrings - already done */
+		/* FDSelect - CIDFont only */
+		/* Charsets - unchanged */
+		/* Encoding - unchanged */
+		/* Globals */
+		/* Strings - unchanged */
+		make_new_top_dict(ctx, &cff);
+
+		newbuf = fz_new_buffer(ctx, 1024);
+		out = fz_new_output_with_buffer(ctx, newbuf);
+
+		/* Copy header */
+		fz_write_byte(ctx, out, cff.major);
+		fz_write_byte(ctx, out, cff.minor);
+		fz_write_byte(ctx, out, 4);
+		fz_write_byte(ctx, out, cff.offsize);
+
+		output_name_index(ctx, &cff, out);
+		update_dicts(ctx, &cff, fz_tell_output(ctx, out));
+		output_top_dict_index(ctx, &cff, out);
+
+		/* Copy strings index */
+		if (cff.string_index.index_size)
+			fz_write_data(ctx, out, base + cff.string_index.index_offset, cff.string_index.index_size);
+		else
+			fz_write_uint16_be(ctx, out, 0);
+		/* Copy globals index (if there is one) */
+		if (cff.global_subset)
+			fz_write_data(ctx, out, cff.global_subset->data, cff.global_subset->len);
+		else if (cff.global_index.index_size)
+			fz_write_data(ctx, out, base + cff.global_index.index_offset, cff.global_index.index_size);
+		else
+			fz_write_uint16_be(ctx, out, 0);
+		/* Copy encoding */
+		if (cff.encoding_offset > 2)
+			fz_write_data(ctx, out, base + cff.encoding_offset, cff.encoding_len);
+		/* Copy charset */
+		if (cff.charset_offset > 2)
+			fz_write_data(ctx, out, base + cff.charset_offset, cff.charset_len);
+		if (cff.fdselect_offset)
+			fz_write_data(ctx, out, base + cff.fdselect_offset, cff.fdselect_len);
+		/* Copy charstrings */
+		if (cff.charstrings_subset)
+			fz_write_data(ctx, out, cff.charstrings_subset->data, cff.charstrings_subset->len);
+		else if (cff.charstrings_index.index_size)
+			fz_write_data(ctx, out, base + cff.charstrings_index.index_offset, cff.charstrings_index.index_size);
+		else
+			fz_write_uint16_be(ctx, out, 0);
+		if (cff.fdarray)
+			output_fdarray(ctx, out, &cff);
+		/* Copy Private dict */
+		fz_write_data(ctx, out, cff.private_subset->data, cff.private_subset->len);
+		/* Copy the local table - subsetted if there is one, original if not, or maybe none! */
+		if (cff.local_subset)
+			fz_write_data(ctx, out, cff.local_subset->data, cff.local_subset->len);
+		else if (cff.local_index.index_size)
+			fz_write_data(ctx, out, base + cff.local_index.index_offset, cff.local_index.index_size);
+
+		fz_close_output(ctx, out);
+	}
+	fz_always(ctx)
+	{
+		fz_drop_output(ctx, out);
+		fz_drop_buffer(ctx, cff.private_subset);
+		fz_drop_buffer(ctx, cff.charstrings_subset);
+		fz_drop_buffer(ctx, cff.top_dict_subset);
+		fz_drop_buffer(ctx, cff.local_subset);
+		fz_drop_buffer(ctx, cff.global_subset);
+		fz_free(ctx, cff.gid_to_cid);
+		fz_free(ctx, cff.gid_to_font);
+		drop_usage_list(ctx, &cff.local_usage);
+		drop_usage_list(ctx, &cff.global_usage);
+		drop_usage_list(ctx, &cff.gids_to_keep);
+		drop_usage_list(ctx, &cff.extra_gids_to_keep);
+		if (cff.fdarray)
+		{
+			n = cff.fdarray_index.count;
+			for (k = 0; k < n; k++)
+			{
+				fz_drop_buffer(ctx, cff.fdarray[k].rewritten_dict);
+				fz_drop_buffer(ctx, cff.fdarray[k].rewritten_private);
+				fz_drop_buffer(ctx, cff.fdarray[k].local_subset);
+				drop_usage_list(ctx, &cff.fdarray[k].local_usage);
+			}
+			fz_free(ctx, cff.fdarray);
+		}
+		fz_free(ctx, cff.unpacked_charset);
+	}
+	fz_catch(ctx)
+	{
+		fz_drop_buffer(ctx, newbuf);
+		fz_rethrow(ctx);
+	}
+
+	return newbuf;
+}