 // starting with Mupdf-core 1.23, pdf_lookup_anchor() has been dropped.
 // Here we provide the original code ..

//  note that some functions have been renamed as OLD_  ...

// code grabbed from mupdf.1.22.2

#include <math.h>

static char *
OLD_pdf_format_link_uri(fz_context *ctx, fz_link_dest dest)
{
	char *uri = NULL;

	switch (dest.type)
	{
	default:
	case FZ_LINK_DEST_FIT:
		uri = fz_asprintf(ctx, "#page=%d&view=Fit", dest.loc.page + 1);
		break;
	case FZ_LINK_DEST_FIT_B:
		uri = fz_asprintf(ctx, "#page=%d&view=FitB", dest.loc.page + 1);
		break;
	case FZ_LINK_DEST_FIT_H:
		if (isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&view=FitH", dest.loc.page + 1);
		else
			uri = fz_asprintf(ctx, "#page=%d&view=FitH,%g", dest.loc.page + 1, dest.y);
		break;
	case FZ_LINK_DEST_FIT_BH:
		if (isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&view=FitBH", dest.loc.page + 1);
		else
			uri = fz_asprintf(ctx, "#page=%d&view=FitBH,%g", dest.loc.page + 1, dest.y);
		break;
	case FZ_LINK_DEST_FIT_V:
		if (isnan(dest.x))
			uri = fz_asprintf(ctx, "#page=%d&view=FitV", dest.loc.page + 1);
		else
			uri = fz_asprintf(ctx, "#page=%d&view=FitV,%g", dest.loc.page + 1, dest.x);
		break;
	case FZ_LINK_DEST_FIT_BV:
		if (isnan(dest.x))
			uri = fz_asprintf(ctx, "#page=%d&view=FitBV", dest.loc.page + 1);
		else
			uri = fz_asprintf(ctx, "#page=%d&view=FitBV,%g", dest.loc.page + 1, dest.x);
		break;
	case FZ_LINK_DEST_XYZ:
		if (!isnan(dest.zoom) && !isnan(dest.x) && !isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=%g,%g,%g", dest.loc.page + 1, dest.zoom, dest.x, dest.y);
		else if (!isnan(dest.zoom) && !isnan(dest.x) && isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=%g,%g,nan", dest.loc.page + 1, dest.zoom, dest.x);
		else if (!isnan(dest.zoom) && isnan(dest.x) && !isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=%g,nan,%g", dest.loc.page + 1, dest.zoom, dest.y);
		else if (!isnan(dest.zoom) && isnan(dest.x) && isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=%g,nan,nan", dest.loc.page + 1, dest.zoom);
		else if (isnan(dest.zoom) && !isnan(dest.x) && !isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=nan,%g,%g", dest.loc.page + 1, dest.x, dest.y);
		else if (isnan(dest.zoom) && !isnan(dest.x) && isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=nan,%g,nan", dest.loc.page + 1, dest.x);
		else if (isnan(dest.zoom) && isnan(dest.x) && !isnan(dest.y))
			uri = fz_asprintf(ctx, "#page=%d&zoom=nan,nan,%g", dest.loc.page + 1, dest.y);
		else
			uri = fz_asprintf(ctx, "#page=%d&zoom=nan,nan,nan", dest.loc.page + 1);
		break;
	case FZ_LINK_DEST_FIT_R:
		uri = fz_asprintf(ctx, "#page=%d&viewrect=%g,%g,%g,%g", dest.loc.page + 1,
			dest.x, dest.y, dest.w, dest.h);
		break;
	}

	return uri;
}

static pdf_obj *
OLD_resolve_dest_rec(fz_context *ctx, pdf_document *doc, pdf_obj *dest, int depth)
{
	if (depth > 10) /* Arbitrary to avoid infinite recursion */
		return NULL;

	if (pdf_is_name(ctx, dest) || pdf_is_string(ctx, dest))
	{
		dest = pdf_lookup_dest(ctx, doc, dest);
		dest = OLD_resolve_dest_rec(ctx, doc, dest, depth+1);
		return dest;
	}

	else if (pdf_is_array(ctx, dest))
	{
		return dest;
	}

	else if (pdf_is_dict(ctx, dest))
	{
		dest = pdf_dict_get(ctx, dest, PDF_NAME(D));
		return OLD_resolve_dest_rec(ctx, doc, dest, depth+1);
	}

	else if (pdf_is_indirect(ctx, dest))
		return dest;

	return NULL;
}

static pdf_obj *
OLD_resolve_dest(fz_context *ctx, pdf_document *doc, pdf_obj *dest)
{
	return OLD_resolve_dest_rec(ctx, doc, dest, 0);
}

static
char *
OLD_pdf_parse_link_dest(fz_context *ctx, pdf_document *doc, pdf_obj *dest)
{
	pdf_obj *arg1, *arg2, *arg3, *arg4;
	float arg1v, arg2v, arg3v, arg4v;
	fz_link_dest destination;
	pdf_obj *pageobj, *typeobj;
	fz_matrix page_ctm;
	const char *ld;
	int pageno;
	fz_point p;
	fz_rect rect;

	dest = OLD_resolve_dest(ctx, doc, dest);
	if (dest == NULL)
	{
		fz_warn(ctx, "undefined link destination");
		return NULL;
	}

	if (pdf_is_name(ctx, dest))
	{
		ld = pdf_to_name(ctx, dest);
		return fz_strdup(ctx, ld);
	}
	else if (pdf_is_string(ctx, dest))
	{
		ld = pdf_to_str_buf(ctx, dest);
		return fz_strdup(ctx, ld);
	}

	if (pdf_array_len(ctx, dest) < 1)
	{
		fz_warn(ctx, "invalid link destination");
		return NULL;
	}

	pageobj = pdf_array_get(ctx, dest, 0);
	if (pdf_is_int(ctx, pageobj))
	{
		pageno = pdf_to_int(ctx, pageobj);
		pageobj = pdf_lookup_page_obj(ctx, doc, pageno);
	}
	else
		pageno = pdf_lookup_page_number(ctx, doc, pageobj);

	destination.loc.chapter = 0;
	destination.loc.page = fz_clampi(pageno, 0, pdf_count_pages(ctx, doc) - 1);

	typeobj = pdf_array_get(ctx, dest, 1);
	if (typeobj == PDF_NAME(XYZ))
		destination.type = FZ_LINK_DEST_XYZ;
	else if (typeobj == PDF_NAME(Fit))
		destination.type = FZ_LINK_DEST_FIT;
	else if (typeobj == PDF_NAME(FitH))
		destination.type = FZ_LINK_DEST_FIT_H;
	else if (typeobj == PDF_NAME(FitV))
		destination.type = FZ_LINK_DEST_FIT_V;
	else if (typeobj == PDF_NAME(FitR))
		destination.type = FZ_LINK_DEST_FIT_R;
	else if (typeobj == PDF_NAME(FitB))
		destination.type = FZ_LINK_DEST_FIT_B;
	else if (typeobj == PDF_NAME(FitBH))
		destination.type = FZ_LINK_DEST_FIT_BH;
	else if (typeobj == PDF_NAME(FitBV))
		destination.type = FZ_LINK_DEST_FIT_BV;
	else
		destination.type = FZ_LINK_DEST_XYZ;

	arg1 = pdf_array_get(ctx, dest, 2);
	arg2 = pdf_array_get(ctx, dest, 3);
	arg3 = pdf_array_get(ctx, dest, 4);
	arg4 = pdf_array_get(ctx, dest, 5);

	arg1v = pdf_to_real(ctx, arg1);
	arg2v = pdf_to_real(ctx, arg2);
	arg3v = pdf_to_real(ctx, arg3);
	arg4v = pdf_to_real(ctx, arg4);

	pdf_page_obj_transform(ctx, pageobj, NULL, &page_ctm);

	switch (destination.type)
	{
	default:
	case FZ_LINK_DEST_FIT:
	case FZ_LINK_DEST_FIT_B:
		break;
	case FZ_LINK_DEST_FIT_H:
	case FZ_LINK_DEST_FIT_BH:
		p = fz_transform_point_xy(0, arg1v, page_ctm);
		destination.y = arg1 ? p.y : NAN;
		break;
	case FZ_LINK_DEST_FIT_V:
	case FZ_LINK_DEST_FIT_BV:
		p = fz_transform_point_xy(arg1v, 0, page_ctm);
		destination.x = arg1 ? p.x : NAN;
		break;
	case FZ_LINK_DEST_XYZ:
		p = fz_transform_point_xy(arg1v, arg2v, page_ctm);
		destination.x = arg1 ? p.x : NAN;
		destination.y = arg2 ? p.y : NAN;
		destination.zoom = arg3 ? (arg3v > 0 ? (arg3v * 100) : 100) : NAN;
		break;
	case FZ_LINK_DEST_FIT_R:
		rect.x0 = arg1v;
		rect.y0 = arg2v;
		rect.x1 = arg3v;
		rect.y1 = arg4v;
		fz_transform_rect(rect, page_ctm);
		destination.x = rect.x0;
		destination.y = rect.y0;
		destination.w = rect.x1 - rect.x0;
		destination.h = rect.y1 - rect.y0;
		break;
	}

	return OLD_pdf_format_link_uri(ctx, destination);
}

static
int Xpdf_lookup_anchor(fz_context *ctx, pdf_document *doc, const char *name, float *xp, float *yp) {
	pdf_obj *needle, *dest = NULL;
	char *uri;

	if (xp) *xp = 0;
	if (yp) *yp = 0;

	needle = pdf_new_string(ctx, name, strlen(name));
	fz_try(ctx)
		dest = pdf_lookup_dest(ctx, doc, needle);
	fz_always(ctx)
		pdf_drop_obj(ctx, needle);
	fz_catch(ctx)
		fz_rethrow(ctx);

	if (dest)
	{
		uri = OLD_pdf_parse_link_dest(ctx, doc, dest);
		return pdf_resolve_link(ctx, doc, uri, xp, yp);
	}

	if (!strncmp(name, "page=", 5))
		return fz_atoi(name + 5) - 1;

	return fz_atoi(name) - 1;
}