Beispiel #1
0
static void tx_dialog(void)
{
	int ff = pdf_field_flags(ctx, tx_widget->obj);
	const char *label = pdf_field_label(ctx, tx_widget->obj);
	int tx_h = (ff & PDF_TX_FIELD_IS_MULTILINE) ? 10 : 1;
	int lbl_h = ui_break_lines((char*)label, NULL, 20, 394, NULL);
	int is;

	ui_dialog_begin(400, (ui.gridsize+4)*3 + ui.lineheight*(tx_h+lbl_h-2));
	{
		ui_layout(T, X, NW, 2, 2);
		ui_label("%s", label);
		is = ui_input(&tx_input, 200, tx_h);

		ui_layout(B, X, NW, 2, 2);
		ui_panel_begin(0, ui.gridsize, 0, 0, 0);
		{
			ui_layout(R, NONE, S, 0, 0);
			if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE))
				ui.dialog = NULL;
			ui_spacer();
			if (ui_button("Okay") || is == UI_INPUT_ACCEPT)
			{
				pdf_set_text_field_value(ctx, tx_widget, tx_input.text);
				if (pdf_update_page(ctx, tx_widget->page))
					render_page();
				ui.dialog = NULL;
			}
		}
		ui_panel_end();
	}
	ui_dialog_end();
}
Beispiel #2
0
static void reload(void)
{
    fz_drop_outline(ctx, outline);
    fz_drop_document(ctx, doc);

    doc = fz_open_document(ctx, filename);
    if (fz_needs_password(ctx, doc))
    {
        if (!fz_authenticate_password(ctx, doc, password))
        {
            fprintf(stderr, "Invalid password.\n");
            exit(1);
        }
    }

    fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);

    fz_try(ctx)
    outline = fz_load_outline(ctx, doc);
    fz_catch(ctx)
    outline = NULL;

    pdf = pdf_specifics(ctx, doc);
    if (pdf)
        pdf_enable_js(ctx, pdf);

    currentpage = fz_clampi(currentpage, 0, fz_count_pages(ctx, doc) - 1);

    render_page();
    update_title();
}
Beispiel #3
0
G_MODULE_EXPORT gboolean
cb_expose(GtkWidget *widget,
  GdkEvent  *event,
  gpointer data)
{
  return render_page(widget);
}
Beispiel #4
0
G_MODULE_EXPORT gboolean
cb_draw(GtkWidget *widget,
  cairo_t  *cr,
  gpointer data)
{
  return render_page(widget);
}
Beispiel #5
0
void _pan_zoom(int mode) {
	int x, y, xx, yy, dx, dy, ig;
	unsigned int w, h, uig;
	Window wig;
	XEvent ev;
	XQueryPointer(dpy, root, &wig, &wig, &xx, &yy, &ig, &ig , &uig);
	XGetGeometry(dpy, presWin, &wig, &x, &y, &w, &h, &uig, &uig);
	XGrabPointer(dpy, root, true, PointerMotionMask | ButtonReleaseMask,
			GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
	while (true) {
		XMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask, &ev);
		if (ev.type == ButtonRelease) break;
		dx = ev.xbutton.x_root - xx; xx = ev.xbutton.x_root;
		dy = ev.xbutton.y_root - yy; yy = ev.xbutton.y_root;
		XClearWindow(dpy, topWin);
		if (mode == 1) {
			XMoveWindow(dpy, presWin, x+=dx, y+=dy);
		}
		else if (mode == 3) {
			if ((w+=dx) > winMax) w = winMax;
			if (w < winMin) w = winMin;
			if ((h+=dy) > winMax) h = winMax;
			if (h < winMin) h = winMin;
			XResizeWindow(dpy, presWin, w, h);
			render_page(cur, presWin, true);
		}
		while (XCheckMaskEvent(dpy, PointerMotionMask, &ev));
	}
	XUngrabPointer(dpy, CurrentTime);
}
Beispiel #6
0
int __tagcat(struct req *req, const char *tagcat, int page, char *tmpl,
	     bool istag)
{
	const unsigned int posts_per_page = req->opts.index_stories;
	struct post *posts[posts_per_page];
	struct str *tag;
	int nposts;

	if (!tagcat)
		return R404(req, NULL);

	tag = STR_DUP(tagcat);

	req_head(req, "Content-Type", "text/html");

	page = MAX(page, 0);

	__store_title(&req->vars, tagcat);
	__store_pages(&req->vars, page);
	__store_tag(&req->vars, tagcat);

	sidebar(req);

	vars_scope_push(&req->vars);

	nposts = index_get_posts(posts, tag, istag, NULL, NULL,
				 page * posts_per_page, posts_per_page);

	load_posts(req, posts, nposts, nposts == posts_per_page);

	str_putref(tag);

	req->body = render_page(req, tmpl);
	return 0;
}
Beispiel #7
0
static void do_forms(float xofs, float yofs)
{
    pdf_ui_event event;
    fz_point p;
    int i;

    for (i = 0; i < annot_count; ++i)
        ui_draw_image(&annot_tex[i], xofs - page_tex.x, yofs - page_tex.y);

    if (!pdf || search_active)
        return;

    p.x = xofs - page_tex.x + ui.x;
    p.y = xofs - page_tex.x + ui.y;
    fz_transform_point(&p, &page_inv_ctm);

    if (ui.down && !ui.active)
    {
        event.etype = PDF_EVENT_TYPE_POINTER;
        event.event.pointer.pt = p;
        event.event.pointer.ptype = PDF_POINTER_DOWN;
        if (pdf_pass_event(ctx, pdf, (pdf_page*)page, &event))
        {
            if (pdf->focus)
                ui.active = do_forms;
            pdf_update_page(ctx, (pdf_page*)page);
            render_page();
            ui_needs_update = 1;
        }
    }
    else if (ui.active == do_forms && !ui.down)
    {
        ui.active = NULL;
        event.etype = PDF_EVENT_TYPE_POINTER;
        event.event.pointer.pt = p;
        event.event.pointer.ptype = PDF_POINTER_UP;
        if (pdf_pass_event(ctx, pdf, (pdf_page*)page, &event))
        {
            pdf_update_page(ctx, (pdf_page*)page);
            render_page();
            ui_needs_update = 1;
        }
    }
}
Beispiel #8
0
int main(int argc, char const *argv[])
{
	char *path;
	PopplerDocument *doc;
	GError *err;
	gchar *gbuf;
	char *buf;
	page_t page_meta;
	int file_length, n;

	g_type_init();

	if (argc != 2) {
		return 1;
	}

	err = NULL;
	buf = open_pdf_file(argv[1], &file_length);

	sandboxify();

	doc = poppler_document_new_from_data(buf, file_length, NULL, &err);
	if (err != NULL) {
		fprintf(stderr, "Unable to open file: %s\n", err->message);
		return 2;
	}

	n = poppler_document_get_n_pages(doc);

	for (int i = 0; i < n; i++) {
		PopplerPage *page = poppler_document_get_page(doc, i);

		page_meta.pagenum = i;
		page_meta.text = poppler_page_get_text(page);
		page_meta.svg_len = 0;
		page_meta.svg = malloc(SVG_BUFLEN);
		if (!page_meta.svg)
			ERROR("Cannot allocate svg buffer, not enought memory?");
		page_meta.free_space = SVG_BUFLEN;

		render_page(&page_meta, page);

		if (page_meta.text)
			free(page_meta.text);
		g_object_unref(page);
	}

	if (munmap(buf, file_length) == -1)
		PERROR("munmap()");

	return 0;
}
Beispiel #9
0
void _redraw(const char *arg) {
	if (arg && arg[0] == 'n') {
		int ig;
		unsigned int w, h, uig;
		Window wig;
		XGetGeometry(dpy, topWin, &wig, &ig, &ig, &w, &h, &uig, &uig);
		XMoveResizeWindow(dpy, presWin, 0, 0, w, h);
	}
	render_page(cur, presWin, false);
#ifdef module_sorter
	sorter_draw(cur);
#endif
	if (!arg) return;
	// TODO notes ...
}
Beispiel #10
0
int xlib_mainloop() {
	XEvent ev;
	running = true;
	render_page(cur, presWin, false);
	while (running && !XNextEvent(dpy,&ev)) {
#ifdef module_sorter
		if (sorter_event(&ev)) continue;
#endif
		//if (notes_event(&ev)) continue;
		if (ev.type < LASTEvent && handler[ev.type])
			handler[ev.type](&ev);
		XSync(dpy,true);
	}
	// TODO show a blank/black page first with no fade in.
	return 0;
}
Beispiel #11
0
static void ch_dialog(void)
{
	const char *label;
	const char *value;
	const char **options;
	int n, choice;
	int label_h;

	label = pdf_field_label(ctx, ch_widget->obj);
	label_h = ui_break_lines((char*)label, NULL, 20, 394, NULL);
	n = pdf_choice_widget_options(ctx, ch_widget->page->doc, ch_widget, 0, NULL);
	options = fz_malloc_array(ctx, n, sizeof(char*));
	pdf_choice_widget_options(ctx, ch_widget->page->doc, ch_widget, 0, options);
	value = pdf_field_value(ctx, ch_widget->obj);

	ui_dialog_begin(400, (ui.gridsize+4)*3 + ui.lineheight*(label_h-1));
	{
		ui_layout(T, X, NW, 2, 2);

		ui_label("%s", label);
		choice = ui_select("Widget/Ch", value, options, n);
		if (choice >= 0)
			pdf_set_choice_field_value(ctx, ch_widget, options[choice]);

		ui_layout(B, X, NW, 2, 2);
		ui_panel_begin(0, ui.gridsize, 0, 0, 0);
		{
			ui_layout(R, NONE, S, 0, 0);
			if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE))
				ui.dialog = NULL;
			ui_spacer();
			if (ui_button("Okay"))
			{
				if (pdf_update_page(ctx, ch_widget->page))
					render_page();
				ui.dialog = NULL;
			}
		}
		ui_panel_end();
	}
	ui_dialog_end();

	fz_free(ctx, options);
}
Beispiel #12
0
G_MODULE_EXPORT void
cb_file_set(
  GtkFileChooserButton *fc,
  gpointer data)
{
  gchar *file,*url;

  file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
  if (doc != NULL) {
    g_object_unref(doc);
  }
  /* file://ファイルのフルパスとしてURL作成 */
  url = g_strconcat("file://",file,NULL);
  /* poppler documentの作成(PDFファイルの読み込み) */
  doc = poppler_document_new_from_file(url,NULL,NULL);
  g_free(url);
  /* DrawingAreaへ描画 */
  render_page(darea1);
}
Beispiel #13
0
static void do_sign(void)
{
	pdf_pkcs7_signer *signer = NULL;

	fz_var(signer);

	fz_try(ctx)
	{
		signer = pkcs7_openssl_read_pfx(ctx, cert_filename, cert_password.text);
		pdf_sign_signature(ctx, pdf, sig_widget, signer);
		ui_show_warning_dialog("Signed document successfully.");
	}
	fz_always(ctx)
	{
		if (signer)
			signer->drop(signer);
	}
	fz_catch(ctx)
		ui_show_warning_dialog("%s", fz_caught_message(ctx));

	if (pdf_update_page(ctx, sig_widget->page))
		render_page();
}
Beispiel #14
0
static void do_canvas(void)
{
    static int saved_scroll_x = 0;
    static int saved_scroll_y = 0;
    static int saved_ui_x = 0;
    static int saved_ui_y = 0;

    float x, y;

    if (oldpage != currentpage || oldzoom != currentzoom || oldrotate != currentrotate)
    {
        render_page();
        update_title();
        oldpage = currentpage;
        oldzoom = currentzoom;
        oldrotate = currentrotate;
    }

    if (ui.x >= canvas_x && ui.x < canvas_x + canvas_w && ui.y >= canvas_y && ui.y < canvas_y + canvas_h)
    {
        ui.hot = doc;
        if (!ui.active && ui.middle)
        {
            ui.active = doc;
            saved_scroll_x = scroll_x;
            saved_scroll_y = scroll_y;
            saved_ui_x = ui.x;
            saved_ui_y = ui.y;
        }
    }

    if (ui.hot == doc)
    {
        scroll_x -= ui.scroll_x * ui.lineheight * 3;
        scroll_y -= ui.scroll_y * ui.lineheight * 3;
    }

    if (ui.active == doc)
    {
        scroll_x = saved_scroll_x + saved_ui_x - ui.x;
        scroll_y = saved_scroll_y + saved_ui_y - ui.y;
    }

    if (page_tex.w <= canvas_w)
    {
        scroll_x = 0;
        x = canvas_x + (canvas_w - page_tex.w) / 2;
    }
    else
    {
        scroll_x = fz_clamp(scroll_x, 0, page_tex.w - canvas_w);
        x = canvas_x - scroll_x;
    }

    if (page_tex.h <= canvas_h)
    {
        scroll_y = 0;
        y = canvas_y + (canvas_h - page_tex.h) / 2;
    }
    else
    {
        scroll_y = fz_clamp(scroll_y, 0, page_tex.h - canvas_h);
        y = canvas_y - scroll_y;
    }

    ui_draw_image(&page_tex, x - page_tex.x, y - page_tex.y);

    do_forms(x, y);

    if (!search_active)
    {
        do_links(links, x, y);
        do_page_selection(x, y, x+page_tex.w, y+page_tex.h);
        if (search_hit_page == currentpage && search_hit_count > 0)
            do_search_hits(x, y);
    }
}
Beispiel #15
0
/** Reads an skp file, renders it to pdf and writes the output to a pdf file
 * @param inputPath The skp file to be read.
 * @param outputDir Output dir.
 */
static bool process_pdf(const SkString& inputPath, const SkString& outputDir) {
    SkDebugf("Loading PDF:  %s\n", inputPath.c_str());

    SkString inputFilename = SkOSPath::SkBasename(inputPath.c_str());

    SkAutoTDelete<SkPdfRenderer> renderer(SkPdfRenderer::CreateFromFile(inputPath.c_str()));
    if (NULL == renderer.get()) {
        SkDebugf("Failure loading file %s\n", inputPath.c_str());
        return false;
    }

    if (FLAGS_showMemoryUsage) {
        SkDebugf("Memory usage after load: %u\n", (unsigned int) renderer->bytesUsed());
    }

    // TODO(edisonn): bench timers
    if (FLAGS_benchLoad > 0) {
        for (int i = 0 ; i < FLAGS_benchLoad; i++) {
            SkAutoTDelete<SkPdfRenderer> benchRenderer(
                    SkPdfRenderer::CreateFromFile(inputPath.c_str()));
            if (NULL == benchRenderer.get()) {
                SkDebugf("Failed to load on %ith attempt\n", i);
            } else if (FLAGS_showMemoryUsage) {
                SkDebugf("Memory usage after load %i number : %u\n", i,
                         (unsigned int) benchRenderer->bytesUsed());
            }
        }
    }

    if (!renderer->pages()) {
        // This should never happen, since CreateFromFile will return NULL if there are no pages.
        SkASSERT(false);
        SkDebugf("ERROR: Empty PDF Document %s\n", inputPath.c_str());
        return false;
    }

    bool success = true;
    for (int i = 0; i < FLAGS_benchRender + 1; i++) {
        // TODO(edisonn) if (i == 1) start timer
        if (strcmp(FLAGS_pages[0], "all") == 0) {
            for (int pn = 0; pn < renderer->pages(); ++pn) {
                success &= render_page(outputDir, inputFilename, *renderer,
                        FLAGS_noExtensionForOnePagePdf && renderer->pages() == 1 ? -1 : pn);
            }
        } else if (strcmp(FLAGS_pages[0], "reverse") == 0) {
            for (int pn = renderer->pages() - 1; pn >= 0; --pn) {
                success &= render_page(outputDir, inputFilename, *renderer,
                        FLAGS_noExtensionForOnePagePdf && renderer->pages() == 1 ? -1 : pn);
            }
        } else if (strcmp(FLAGS_pages[0], "first") == 0) {
            success &= render_page(outputDir, inputFilename, *renderer,
                    FLAGS_noExtensionForOnePagePdf && renderer->pages() == 1 ? -1 : 0);
        } else if (strcmp(FLAGS_pages[0], "last") == 0) {
            success &= render_page(outputDir, inputFilename, *renderer,
                    FLAGS_noExtensionForOnePagePdf && renderer->pages() == 1 ? -1
                    : renderer->pages() - 1);
        } else {
            int pn = atoi(FLAGS_pages[0]);
            success &= render_page(outputDir, inputFilename, *renderer,
                    FLAGS_noExtensionForOnePagePdf && renderer->pages() == 1 ? -1 : pn);
        }
    }

    if (!success) {
        SkDebugf("Failures for file %s\n", inputPath.c_str());
    }

    return success;
}
Beispiel #16
0
void do_widget_canvas(fz_irect canvas_area)
{
	pdf_widget *widget;
	fz_rect bounds;
	fz_irect area;

	if (!pdf)
		return;

	for (widget = pdf_first_widget(ctx, page); widget; widget = pdf_next_widget(ctx, widget))
	{
		bounds = pdf_bound_widget(ctx, widget);
		bounds = fz_transform_rect(bounds, view_page_ctm);
		area = fz_irect_from_rect(bounds);

		if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&area))
		{
			if (!widget->is_hot)
				pdf_annot_event_enter(ctx, widget);
			widget->is_hot = 1;

			ui.hot = widget;
			if (!ui.active && ui.down)
			{
				ui.active = widget;
				pdf_annot_event_down(ctx, widget);
				if (selected_annot != widget)
				{
					if (selected_annot && pdf_annot_type(ctx, selected_annot) == PDF_ANNOT_WIDGET)
						pdf_annot_event_blur(ctx, selected_annot);
					selected_annot = widget;
					pdf_annot_event_focus(ctx, widget);
				}
			}
		}
		else
		{
			if (widget->is_hot)
				pdf_annot_event_exit(ctx, widget);
			widget->is_hot = 0;
		}

		/* Set is_hot and is_active to select current appearance */
		widget->is_active = (ui.active == widget && ui.down);

		if (showform)
		{
			glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
			glEnable(GL_BLEND);
			glColor4f(0, 0, 1, 0.1f);
			glRectf(area.x0, area.y0, area.x1, area.y1);
			glDisable(GL_BLEND);
		}

		if (ui.active == widget || (!ui.active && ui.hot == widget))
		{
			glLineStipple(1, 0xAAAA);
			glEnable(GL_LINE_STIPPLE);
			glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
			glEnable(GL_BLEND);
			glColor4f(1, 1, 1, 1);
			glBegin(GL_LINE_LOOP);
			glVertex2f(area.x0-0.5f, area.y0-0.5f);
			glVertex2f(area.x1+0.5f, area.y0-0.5f);
			glVertex2f(area.x1+0.5f, area.y1+0.5f);
			glVertex2f(area.x0-0.5f, area.y1+0.5f);
			glEnd();
			glDisable(GL_BLEND);
			glDisable(GL_LINE_STIPPLE);
		}

		if (ui.hot == widget && ui.active == widget && !ui.down)
		{
			pdf_annot_event_up(ctx, widget);

			if (pdf_field_flags(ctx, widget->obj) & PDF_FIELD_IS_READ_ONLY)
				continue;

			switch (pdf_widget_type(ctx, widget))
			{
			default:
				break;
			case PDF_WIDGET_TYPE_CHECKBOX:
			case PDF_WIDGET_TYPE_RADIOBUTTON:
				pdf_toggle_widget(ctx, widget);
				break;
			case PDF_WIDGET_TYPE_TEXT:
				show_tx_dialog(widget);
				break;
			case PDF_WIDGET_TYPE_COMBOBOX:
			case PDF_WIDGET_TYPE_LISTBOX:
				ui.dialog = ch_dialog;
				ch_widget = widget;
				break;
			case PDF_WIDGET_TYPE_SIGNATURE:
				show_sig_dialog(widget);
				break;
			}
		}
	}

	if (pdf_update_page(ctx, page))
		render_page();
}
Beispiel #17
0
int main(int argc, char **argv)
#endif
{
	const GLFWvidmode *video_mode;
	char filename[2048];
	char *password = "";
	float layout_w = DEFAULT_LAYOUT_W;
	float layout_h = DEFAULT_LAYOUT_H;
	float layout_em = DEFAULT_LAYOUT_EM;
	char *layout_css = NULL;
	int c;

	while ((c = fz_getopt(argc, argv, "p:r:W:H:S:U:")) != -1)
	{
		switch (c)
		{
		default: usage(argv[0]); break;
		case 'p': password = fz_optarg; break;
		case 'r': currentzoom = fz_atof(fz_optarg); break;
		case 'W': layout_w = fz_atof(fz_optarg); break;
		case 'H': layout_h = fz_atof(fz_optarg); break;
		case 'S': layout_em = fz_atof(fz_optarg); break;
		case 'U': layout_css = fz_optarg; break;
		}
	}

	if (fz_optind < argc)
	{
		fz_strlcpy(filename, argv[fz_optind], sizeof filename);
	}
	else
	{
#ifdef _WIN32
		win_install();
		if (!win_open_file(filename, sizeof filename))
			exit(0);
#else
		usage(argv[0]);
#endif
	}

	title = strrchr(filename, '/');
	if (!title)
		title = strrchr(filename, '\\');
	if (title)
		++title;
	else
		title = filename;

	memset(&ui, 0, sizeof ui);

	search_input.p = search_input.text;
	search_input.q = search_input.p;
	search_input.end = search_input.p;

	if (!glfwInit()) {
		fprintf(stderr, "cannot initialize glfw\n");
		exit(1);
	}

	video_mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
	screen_w = video_mode->width;
	screen_h = video_mode->height;

	glfwSetErrorCallback(on_error);

	window = glfwCreateWindow(DEFAULT_WINDOW_W, DEFAULT_WINDOW_H, filename, NULL, NULL);
	if (!window) {
		fprintf(stderr, "cannot create glfw window\n");
		exit(1);
	}

	glfwMakeContextCurrent(window);

	ctx = fz_new_context(NULL, NULL, 0);
	fz_register_document_handlers(ctx);

	if (layout_css)
	{
		fz_buffer *buf = fz_read_file(ctx, layout_css);
		fz_write_buffer_byte(ctx, buf, 0);
		fz_set_user_css(ctx, (char*)buf->data);
		fz_drop_buffer(ctx, buf);
	}

	has_ARB_texture_non_power_of_two = glfwExtensionSupported("GL_ARB_texture_non_power_of_two");
	if (!has_ARB_texture_non_power_of_two)
		fz_warn(ctx, "OpenGL implementation does not support non-power of two texture sizes");

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);

	ui.fontsize = DEFAULT_UI_FONTSIZE;
	ui.baseline = DEFAULT_UI_BASELINE;
	ui.lineheight = DEFAULT_UI_LINEHEIGHT;

	ui_init_fonts(ctx, ui.fontsize);

	doc = fz_open_document(ctx, filename);
	if (fz_needs_password(ctx, doc))
	{
		if (!fz_authenticate_password(ctx, doc, password))
		{
			fprintf(stderr, "Invalid password.\n");
			exit(1);
		}
	}

	outline = fz_load_outline(ctx, doc);
	pdf = pdf_specifics(ctx, doc);
	if (pdf)
		pdf_enable_js(ctx, pdf);

	fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);

	render_page();
	update_title();
	shrinkwrap();

	glfwSetFramebufferSizeCallback(window, on_reshape);
	glfwSetCursorPosCallback(window, on_mouse_motion);
	glfwSetMouseButtonCallback(window, on_mouse_button);
	glfwSetScrollCallback(window, on_scroll);
	glfwSetCharModsCallback(window, on_char);
	glfwSetKeyCallback(window, on_key);
	glfwSetWindowRefreshCallback(window, on_display);

	glfwGetFramebufferSize(window, &window_w, &window_h);

	ui_needs_update = 1;

	while (!glfwWindowShouldClose(window))
	{
		glfwWaitEvents();
		if (ui_needs_update)
			run_main_loop();
	}

	ui_finish_fonts(ctx);

	fz_drop_link(ctx, links);
	fz_drop_page(ctx, page);
	fz_drop_outline(ctx, outline);
	fz_drop_document(ctx, doc);
	fz_drop_context(ctx);

	glfwTerminate();

	return 0;
}