static fz_error
pdf_loadobjstm(pdf_xref *xref, int num, int gen, char *buf, int cap)
{
    fz_error error;
    fz_stream *stm;
    fz_obj *objstm;
    int *numbuf;
    int *ofsbuf;

    fz_obj *obj;
    int first;
    int count;
    int i, n;
    pdf_token_e tok;

    pdf_logxref("loadobjstm (%d %d R)\n", num, gen);

    error = pdf_loadobject(&objstm, xref, num, gen);
    if (error)
        return fz_rethrow(error, "cannot load object stream object (%d %d R)", num, gen);

    count = fz_toint(fz_dictgets(objstm, "N"));
    first = fz_toint(fz_dictgets(objstm, "First"));

    pdf_logxref("\tcount %d\n", count);

    numbuf = fz_malloc(count * sizeof(int));
    ofsbuf = fz_malloc(count * sizeof(int));

    error = pdf_openstream(&stm, xref, num, gen);
    if (error)
    {
        error = fz_rethrow(error, "cannot open object stream (%d %d R)", num, gen);
        goto cleanupbuf;
    }

    for (i = 0; i < count; i++)
    {
        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
            goto cleanupstm;
        }
        numbuf[i] = atoi(buf);

        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
            goto cleanupstm;
        }
        ofsbuf[i] = atoi(buf);
    }

    fz_seek(stm, first, 0);

    for (i = 0; i < count; i++)
    {
        fz_seek(stm, first + ofsbuf[i], 0);

        error = pdf_parsestmobj(&obj, xref, stm, buf, cap);
        if (error)
        {
            error = fz_rethrow(error, "cannot parse object %d in stream (%d %d R)", i, num, gen);
            goto cleanupstm;
        }

        if (numbuf[i] < 1 || numbuf[i] >= xref->len)
        {
            fz_dropobj(obj);
            error = fz_throw("object id (%d 0 R) out of range (0..%d)", numbuf[i], xref->len - 1);
            goto cleanupstm;
        }

        if (xref->table[numbuf[i]].type == 'o' && xref->table[numbuf[i]].ofs == num)
        {
            if (xref->table[numbuf[i]].obj)
                fz_dropobj(xref->table[numbuf[i]].obj);
            xref->table[numbuf[i]].obj = obj;
        }
        else
        {
            fz_dropobj(obj);
        }
    }

    fz_close(stm);
    fz_free(ofsbuf);
    fz_free(numbuf);
    fz_dropobj(objstm);
    return fz_okay;

cleanupstm:
    fz_close(stm);
cleanupbuf:
    fz_free(ofsbuf);
    fz_free(numbuf);
    fz_dropobj(objstm);
    return error; /* already rethrown */
}
fz_error
pdf_loadobjstm(pdf_xref *xref, int oid, int gen, char *buf, int cap)
{
    fz_error error;
    fz_stream *stm;
    fz_obj *objstm;
    int *oidbuf;
    int *ofsbuf;

    fz_obj *obj;
    int first;
    int count;
    int i, n;
    pdf_token_e tok;

    pdf_logxref("loadobjstm (%d %d R)\n", oid, gen);

    error = pdf_loadobject(&objstm, xref, oid, gen);
    if (error)
        return fz_rethrow(error, "cannot load object stream object");

    count = fz_toint(fz_dictgets(objstm, "N"));
    first = fz_toint(fz_dictgets(objstm, "First"));

    pdf_logxref("  count %d\n", count);

    oidbuf = fz_malloc(count * sizeof(int));
    if (!oidbuf)
    {
        error = fz_rethrow(-1, "out of memory: object id buffer");
        goto cleanupobj;
    }

    ofsbuf = fz_malloc(count * sizeof(int));
    if (!ofsbuf)
    {
        error = fz_rethrow(-1, "out of memory: offset buffer");
        goto cleanupoid;
    }

    error = pdf_openstream(&stm, xref, oid, gen);
    if (error)
    {
        error = fz_rethrow(error, "cannot open object stream");
        goto cleanupofs;
    }

    for (i = 0; i < count; i++)
    {
        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream");
            goto cleanupstm;
        }
        oidbuf[i] = atoi(buf);

        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream");
            goto cleanupstm;
        }
        ofsbuf[i] = atoi(buf);
    }

    error = fz_seek(stm, first, 0);
    if (error)
    {
        error = fz_rethrow(error, "cannot seek in object stream");
        goto cleanupstm;
    }

    for (i = 0; i < count; i++)
    {
        /* FIXME: seek to first + ofsbuf[i] */

        error = pdf_parsestmobj(&obj, xref, stm, buf, cap);
        if (error)
        {
            error = fz_rethrow(error, "cannot parse object %d in stream", i);
            goto cleanupstm;
        }

        if (oidbuf[i] < 1 || oidbuf[i] >= xref->len)
        {
            fz_dropobj(obj);
            error = fz_throw("object id (%d 0 R) out of range (0..%d)", oidbuf[i], xref->len - 1);
            goto cleanupstm;
        }

        if (xref->table[oidbuf[i]].obj)
            fz_dropobj(xref->table[oidbuf[i]].obj);
        xref->table[oidbuf[i]].obj = obj;
    }

    fz_dropstream(stm);
    fz_free(ofsbuf);
    fz_free(oidbuf);
    fz_dropobj(objstm);
    return fz_okay;

cleanupstm:
    fz_dropstream(stm);
cleanupofs:
    fz_free(ofsbuf);
cleanupoid:
    fz_free(oidbuf);
cleanupobj:
    fz_dropobj(objstm);
    return error; /* already rethrown */
}