public void writeToStream(OutputStream os) throws IOException
    {
        if (glyphIds.isEmpty() && uniToGID.isEmpty())
        {
            LOG.info("font subset is empty");
        }
        
        addCompoundReferences();

        try (DataOutputStream out = new DataOutputStream(os))
        {
            long[] newLoca = new long[glyphIds.size() + 1];

            // generate tables in dependency order
            byte[] head = buildHeadTable();
            byte[] hhea = buildHheaTable();
            byte[] maxp = buildMaxpTable();
            byte[] name = buildNameTable();
            byte[] os2  = buildOS2Table();
            byte[] glyf = buildGlyfTable(newLoca);
            byte[] loca = buildLocaTable(newLoca);
            byte[] cmap = buildCmapTable();
            byte[] hmtx = buildHmtxTable();
            byte[] post = buildPostTable();

            // save to TTF in optimized order
            Map<String, byte[]> tables = new TreeMap<>();
            if (os2 != null)
            {
                tables.put(OS2WindowsMetricsTable.TAG, os2);
            }
            if (cmap != null)
            {
                tables.put(CmapTable.TAG, cmap);
            }
            tables.put(GlyphTable.TAG, glyf);
            tables.put(HeaderTable.TAG, head);
            tables.put(HorizontalHeaderTable.TAG, hhea);
            tables.put(HorizontalMetricsTable.TAG, hmtx);
            tables.put(IndexToLocationTable.TAG, loca);
            tables.put(MaximumProfileTable.TAG, maxp);
            if (name != null)
            {
                tables.put(NamingTable.TAG, name);
            }
            if (post != null)
            {
                tables.put(PostScriptTable.TAG, post);
            }

            // copy all other tables
            for (Map.Entry<String, TTFTable> entry : ttf.getTableMap().entrySet())
            {
                String tag = entry.getKey();
                TTFTable table = entry.getValue();

                if (!tables.containsKey(tag) && (keepTables == null || keepTables.contains(tag)))
                {
                    tables.put(tag, ttf.getTableBytes(table));
                }
            }

            // calculate checksum
            long checksum = writeFileHeader(out, tables.size());
            long offset = 12L + 16L * tables.size();
            for (Map.Entry<String, byte[]> entry : tables.entrySet())
            {
                checksum += writeTableHeader(out, entry.getKey(), offset, entry.getValue());
                offset += (entry.getValue().length + 3L) / 4 * 4;
            }
            checksum = 0xB1B0AFBAL - (checksum & 0xffffffffL);

            // update checksumAdjustment in 'head' table
            head[8] = (byte)(checksum >>> 24);
            head[9] = (byte)(checksum >>> 16);
            head[10] = (byte)(checksum >>> 8);
            head[11] = (byte)checksum;
            for (byte[] bytes : tables.values())
            {
                writeTableBody(out, bytes);
            }
        }
    }
@Test
    void testEmptySubset() throws IOException
    {
        TrueTypeFont x = new TTFParser().parse(new RandomAccessReadBufferedFile(
                "src/test/resources/ttf/LiberationSans-Regular.ttf"));
        TTFSubsetter ttfSubsetter = new TTFSubsetter(x);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ttfSubsetter.writeToStream(baos);
        try (TrueTypeFont subset = new TTFParser(true)
                .parse(new RandomAccessReadBuffer(baos.toByteArray())))
        {
            assertEquals(1, subset.getNumberOfGlyphs());
            assertEquals(0, subset.nameToGID(".notdef"));
            assertNotNull(subset.getGlyph().getGlyph(0));
        }
    }