<commit_before>#include "fitz.h"
#include "mupdf.h"

/* we need to combine all sub-streams into one for the content stream interpreter */

static fz_error
pdf_loadpagecontentsarray(fz_buffer **bigbufp, pdf_xref *xref, fz_obj *list)
{
	fz_error error;
	fz_buffer *big;
	fz_buffer *one;
	int i;

	pdf_logpage("multiple content streams: %d\n", fz_arraylen(list));

	/* TODO: openstream, read, close into big buffer at once */

	big = fz_newbuffer(32 * 1024);

	for (i = 0; i < fz_arraylen(list); i++)
	{
		fz_obj *stm = fz_arrayget(list, i);
		error = pdf_loadstream(&one, xref, fz_tonum(stm), fz_togen(stm));
		if (error)
		{
			fz_dropbuffer(big);
			return fz_rethrow(error, "cannot load content stream part %d/%d (%d %d R)", i + 1, fz_arraylen(list), fz_tonum(stm), fz_togen(stm));
		}

		if (big->len + one->len + 1 > big->cap)
			fz_resizebuffer(big, big->len + one->len + 1);
		memcpy(big->data + big->len, one->data, one->len);
		big->data[big->len + one->len] = ' ';
		big->len += one->len + 1;

		fz_dropbuffer(one);
	}

	*bigbufp = big;
	return fz_okay;
}

static fz_error
pdf_loadpagecontents(fz_buffer **bufp, pdf_xref *xref, fz_obj *obj)
{
	fz_error error;

	if (fz_isarray(obj))
	{
		error = pdf_loadpagecontentsarray(bufp, xref, obj);
		if (error)
			return fz_rethrow(error, "cannot load content stream array (%d 0 R)", fz_tonum(obj));
	}
	else if (pdf_isstream(xref, fz_tonum(obj), fz_togen(obj)))
	{
		error = pdf_loadstream(bufp, xref, fz_tonum(obj), fz_togen(obj));
		if (error)
			return fz_rethrow(error, "cannot load content stream (%d 0 R)", fz_tonum(obj));
	}
	else
	{
		fz_warn("page contents missing, leaving page blank");
		*bufp = fz_newbuffer(0);
	}

	return fz_okay;
}

/* We need to know whether to install a page-level transparency group */

static int pdf_resourcesuseblending(fz_obj *rdb);

static int
pdf_extgstateusesblending(fz_obj *dict)
{
	fz_obj *obj;

	obj = fz_dictgets(dict, "BM");
	if (fz_isname(obj) && strcmp(fz_toname(obj), "Normal"))
		return 1;

	return 0;
}

static int
pdf_patternusesblending(fz_obj *dict)
{
	fz_obj *obj;

	obj = fz_dictgets(dict, "Resources");
	if (fz_isdict(obj) && pdf_resourcesuseblending(obj))
		return 1;

	obj = fz_dictgets(dict, "ExtGState");
	if (fz_isdict(obj) && pdf_extgstateusesblending(obj))
		return 1;

	return 0;
}

static int
pdf_xobjectusesblending(fz_obj *dict)
{
	fz_obj *obj;

	obj = fz_dictgets(dict, "Resources");
	if (fz_isdict(obj) && pdf_resourcesuseblending(obj))
		return 1;

	return 0;
}

static int
pdf_resourcesuseblending(fz_obj *rdb)
{
	fz_obj *dict;
	fz_obj *tmp;
	int i;

	/* stop on cyclic resource dependencies */
	if (fz_dictgets(rdb, ".seen"))
		return 0;

	tmp = fz_newnull();
	fz_dictputs(rdb, ".seen", tmp);
	fz_dropobj(tmp);

	dict = fz_dictgets(rdb, "ExtGState");
	for (i = 0; i < fz_dictlen(dict); i++)
		if (pdf_extgstateusesblending(fz_dictgetval(dict, i)))
			goto found;

	dict = fz_dictgets(rdb, "Pattern");
	for (i = 0; i < fz_dictlen(dict); i++)
		if (pdf_patternusesblending(fz_dictgetval(dict, i)))
			goto found;

	dict = fz_dictgets(rdb, "XObject");
	for (i = 0; i < fz_dictlen(dict); i++)
		if (pdf_xobjectusesblending(fz_dictgetval(dict, i)))
			goto found;

	fz_dictdels(rdb, ".seen");
	return 0;

found:
	fz_dictdels(rdb, ".seen");
	return 1;
}

fz_error
pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict)
{
	fz_error error;
	pdf_page *page;
	fz_obj *obj;
	fz_bbox bbox;
<commit_msg>Cache the result of resourcesuseblending in the object structure to gracefully deal with cyclic dependency chains in resource dictionaries.<commit_after>#include "fitz.h"
#include "mupdf.h"

/* we need to combine all sub-streams into one for the content stream interpreter */

static fz_error
pdf_loadpagecontentsarray(fz_buffer **bigbufp, pdf_xref *xref, fz_obj *list)
{
	fz_error error;
	fz_buffer *big;
	fz_buffer *one;
	int i;

	pdf_logpage("multiple content streams: %d\n", fz_arraylen(list));

	/* TODO: openstream, read, close into big buffer at once */

	big = fz_newbuffer(32 * 1024);

	for (i = 0; i < fz_arraylen(list); i++)
	{
		fz_obj *stm = fz_arrayget(list, i);
		error = pdf_loadstream(&one, xref, fz_tonum(stm), fz_togen(stm));
		if (error)
		{
			fz_dropbuffer(big);
			return fz_rethrow(error, "cannot load content stream part %d/%d (%d %d R)", i + 1, fz_arraylen(list), fz_tonum(stm), fz_togen(stm));
		}

		if (big->len + one->len + 1 > big->cap)
			fz_resizebuffer(big, big->len + one->len + 1);
		memcpy(big->data + big->len, one->data, one->len);
		big->data[big->len + one->len] = ' ';
		big->len += one->len + 1;

		fz_dropbuffer(one);
	}

	*bigbufp = big;
	return fz_okay;
}

static fz_error
pdf_loadpagecontents(fz_buffer **bufp, pdf_xref *xref, fz_obj *obj)
{
	fz_error error;

	if (fz_isarray(obj))
	{
		error = pdf_loadpagecontentsarray(bufp, xref, obj);
		if (error)
			return fz_rethrow(error, "cannot load content stream array (%d 0 R)", fz_tonum(obj));
	}
	else if (pdf_isstream(xref, fz_tonum(obj), fz_togen(obj)))
	{
		error = pdf_loadstream(bufp, xref, fz_tonum(obj), fz_togen(obj));
		if (error)
			return fz_rethrow(error, "cannot load content stream (%d 0 R)", fz_tonum(obj));
	}
	else
	{
		fz_warn("page contents missing, leaving page blank");
		*bufp = fz_newbuffer(0);
	}

	return fz_okay;
}

/* We need to know whether to install a page-level transparency group */

static int pdf_resourcesuseblending(fz_obj *rdb);

static int
pdf_extgstateusesblending(fz_obj *dict)
{
	fz_obj *obj;

	obj = fz_dictgets(dict, "BM");
	if (fz_isname(obj) && strcmp(fz_toname(obj), "Normal"))
		return 1;

	return 0;
}

static int
pdf_patternusesblending(fz_obj *dict)
{
	fz_obj *obj;

	obj = fz_dictgets(dict, "Resources");
	if (fz_isdict(obj) && pdf_resourcesuseblending(obj))
		return 1;

	obj = fz_dictgets(dict, "ExtGState");
	if (fz_isdict(obj) && pdf_extgstateusesblending(obj))
		return 1;

	return 0;
}

static int
pdf_xobjectusesblending(fz_obj *dict)
{
	fz_obj *obj;

	obj = fz_dictgets(dict, "Resources");
	if (fz_isdict(obj) && pdf_resourcesuseblending(obj))
		return 1;

	return 0;
}

static int
pdf_resourcesuseblending(fz_obj *rdb)
{
	fz_obj *dict;
	fz_obj *tmp;
	int i;

	/* stop on cyclic resource dependencies */
	if (fz_dictgets(rdb, ".useBM"))
		return fz_tobool(fz_dictgets(rdb, ".useBM"));

	tmp = fz_newbool(0);
	fz_dictputs(rdb, ".useBM", tmp);
	fz_dropobj(tmp);

	dict = fz_dictgets(rdb, "ExtGState");
	for (i = 0; i < fz_dictlen(dict); i++)
		if (pdf_extgstateusesblending(fz_dictgetval(dict, i)))
			goto found;

	dict = fz_dictgets(rdb, "Pattern");
	for (i = 0; i < fz_dictlen(dict); i++)
		if (pdf_patternusesblending(fz_dictgetval(dict, i)))
			goto found;

	dict = fz_dictgets(rdb, "XObject");
	for (i = 0; i < fz_dictlen(dict); i++)
		if (pdf_xobjectusesblending(fz_dictgetval(dict, i)))
			goto found;

	return 0;

found:
	tmp = fz_newbool(1);
	fz_dictputs(rdb, ".useBM", tmp);
	fz_dropobj(tmp);
	return 1;
}

fz_error
pdf_loadpage(pdf_page **pagep, pdf_xref *xref, fz_obj *dict)
{
	fz_error error;
	pdf_page *page;
	fz_obj *obj;
	fz_bbox bbox;

{'fraction_non_alphanumeric': 0.14771595275366445, 'fraction_numerical': 0.007826953180589156, 'mean_word_length': 4.727791361043195, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7010384', 'n_tokens_mistral': 3183, 'n_tokens_neox': 2996, 'n_words': 693}