+ FILE *f;
+ GString *pdfbuf, *pgstrm, *zpgstrm, *tmpstr;
+ int n_obj_catalog, n_obj_pages_offs, n_page, n_obj_bgpix, n_obj_prefix;
+ int i, startxref;
+ struct XrefTable xref;
+ GList *pglist;
+ struct Page *pg;
+ gboolean annot, uses_pdf;
+ gboolean use_hiliter;
+ struct PdfInfo pdfinfo;
+ struct PdfObj *obj;
+ GList *pdffonts, *list;
+ struct PdfFont *font;
+ char *tmpbuf;
+
+ f = fopen(filename, "wb");
+ if (f == NULL) return FALSE;
+ setlocale(LC_NUMERIC, "C");
+ annot = FALSE;
+ xref.data = NULL;
+ uses_pdf = FALSE;
+ pdffonts = NULL;
+ for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
+ pg = (struct Page *)pglist->data;
+ if (pg->bg->type == BG_PDF) uses_pdf = TRUE;
+ }
+
+ if (uses_pdf && bgpdf.status != STATUS_NOT_INIT &&
+ bgpdf.file_contents!=NULL && !strncmp(bgpdf.file_contents, "%PDF-1.", 7)) {
+ // parse the existing PDF file
+ pdfbuf = g_string_new_len(bgpdf.file_contents, bgpdf.file_length);
+ if (pdfbuf->str[7]<'4') pdfbuf->str[7] = '4'; // upgrade to 1.4
+ annot = pdf_parse_info(pdfbuf, &pdfinfo, &xref);
+ if (!annot) {
+ g_string_free(pdfbuf, TRUE);
+ if (xref.data != NULL) g_free(xref.data);
+ }
+ }
+
+ if (!annot) {
+ pdfbuf = g_string_new("%PDF-1.4\n%\370\357\365\362\n");
+ xref.n_alloc = xref.last = 0;
+ xref.data = NULL;
+ }
+
+ // catalog and page tree
+ n_obj_catalog = xref.last+1;
+ n_obj_pages_offs = xref.last+4;
+ make_xref(&xref, n_obj_catalog, pdfbuf->len);
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Type /Catalog /Pages %d 0 R >> endobj\n",
+ n_obj_catalog, n_obj_catalog+1);
+ make_xref(&xref, n_obj_catalog+1, pdfbuf->len);
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Type /Pages /Kids [", n_obj_catalog+1);
+ for (i=0;i<journal.npages;i++)
+ g_string_append_printf(pdfbuf, "%d 0 R ", n_obj_pages_offs+i);
+ g_string_append_printf(pdfbuf, "] /Count %d >> endobj\n", journal.npages);
+ make_xref(&xref, n_obj_catalog+2, pdfbuf->len);
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Type /ExtGState /CA %.2f >> endobj\n",
+ n_obj_catalog+2, ui.hiliter_opacity);
+ xref.last = n_obj_pages_offs + journal.npages-1;
+
+ for (pglist = journal.pages, n_page = 0; pglist!=NULL;
+ pglist = pglist->next, n_page++) {
+ pg = (struct Page *)pglist->data;
+
+ // draw the background and page into pgstrm
+ pgstrm = g_string_new("");
+ g_string_printf(pgstrm, "q 1 0 0 -1 0 %.2f cm 1 J 1 j ", pg->height);
+ n_obj_bgpix = -1;
+ n_obj_prefix = -1;
+ if (pg->bg->type == BG_SOLID)
+ pdf_draw_solid_background(pg, pgstrm);
+ else if (pg->bg->type == BG_PDF && annot &&
+ pdfinfo.pages[pg->bg->file_page_seq-1].contents!=NULL) {
+ make_xref(&xref, xref.last+1, pdfbuf->len);
+ n_obj_prefix = xref.last;
+ tmpstr = make_pdfprefix(pdfinfo.pages+(pg->bg->file_page_seq-1),
+ pg->width, pg->height);
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Length %zu >> stream\n%s\nendstream\nendobj\n",
+ n_obj_prefix, tmpstr->len, tmpstr->str);
+ g_string_free(tmpstr, TRUE);
+ g_string_prepend(pgstrm, "Q Q Q ");
+ }
+ else if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF)
+ n_obj_bgpix = pdf_draw_bitmap_background(pg, pgstrm, &xref, pdfbuf);
+ // draw the page contents
+ use_hiliter = FALSE;
+ pdf_draw_page(pg, pgstrm, &use_hiliter, &xref, &pdffonts);
+ g_string_append_printf(pgstrm, "Q\n");
+
+ // deflate pgstrm and write it
+ zpgstrm = do_deflate(pgstrm->str, pgstrm->len);
+ g_string_free(pgstrm, TRUE);
+
+ make_xref(&xref, xref.last+1, pdfbuf->len);
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Length %zu /Filter /FlateDecode>> stream\n",
+ xref.last, zpgstrm->len);
+ g_string_append_len(pdfbuf, zpgstrm->str, zpgstrm->len);
+ g_string_free(zpgstrm, TRUE);
+ g_string_append(pdfbuf, "endstream\nendobj\n");
+
+ // write the page object
+
+ make_xref(&xref, n_obj_pages_offs+n_page, pdfbuf->len);
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Type /Page /Parent %d 0 R /MediaBox [0 0 %.2f %.2f] ",
+ n_obj_pages_offs+n_page, n_obj_catalog+1, pg->width, pg->height);
+ if (n_obj_prefix>0) {
+ obj = get_pdfobj(pdfbuf, &xref, pdfinfo.pages[pg->bg->file_page_seq-1].contents);
+ if (obj->type != PDFTYPE_ARRAY) {
+ free_pdfobj(obj);
+ obj = dup_pdfobj(pdfinfo.pages[pg->bg->file_page_seq-1].contents);
+ }
+ g_string_append_printf(pdfbuf, "/Contents [%d 0 R ", n_obj_prefix);
+ if (obj->type == PDFTYPE_REF)
+ g_string_append_printf(pdfbuf, "%d %d R ", obj->intval, obj->num);
+ if (obj->type == PDFTYPE_ARRAY) {
+ for (i=0; i<obj->num; i++) {
+ show_pdfobj(obj->elts[i], pdfbuf);
+ g_string_append_c(pdfbuf, ' ');
+ }
+ }
+ free_pdfobj(obj);
+ g_string_append_printf(pdfbuf, "%d 0 R] ", xref.last);
+ }
+ else g_string_append_printf(pdfbuf, "/Contents %d 0 R ", xref.last);
+ g_string_append(pdfbuf, "/Resources ");
+
+ if (n_obj_prefix>0)
+ obj = dup_pdfobj(pdfinfo.pages[pg->bg->file_page_seq-1].resources);
+ else obj = NULL;
+ if (obj!=NULL && obj->type!=PDFTYPE_DICT)
+ { free_pdfobj(obj); obj=NULL; }
+ if (obj==NULL) {
+ obj = g_malloc(sizeof(struct PdfObj));
+ obj->type = PDFTYPE_DICT;
+ obj->num = 0;
+ obj->elts = NULL;
+ obj->names = NULL;
+ }
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/PDF"));
+ if (n_obj_bgpix>0)
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/ImageC"));
+ if (use_hiliter)
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/ExtGState", PDFTYPE_DICT, "/XoHi", mk_pdfref(n_obj_catalog+2));
+ if (n_obj_bgpix>0)
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/XObject", PDFTYPE_DICT, "/ImBg", mk_pdfref(n_obj_bgpix));
+ for (list=pdffonts; list!=NULL; list = list->next) {
+ font = (struct PdfFont *)list->data;
+ if (font->used_in_this_page) {
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/Text"));
+ tmpbuf = g_strdup_printf("/F%d", font->n_obj);
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/Font", PDFTYPE_DICT, tmpbuf, mk_pdfref(font->n_obj));
+ g_free(tmpbuf);
+ }
+ }
+ show_pdfobj(obj, pdfbuf);
+ free_pdfobj(obj);
+ g_string_append(pdfbuf, " >> endobj\n");
+ }
+
+ // after the pages, we insert fonts
+ for (list = pdffonts; list!=NULL; list = list->next) {
+ font = (struct PdfFont *)list->data;
+ embed_pdffont(pdfbuf, &xref, font);
+ g_free(font->filename);
+ g_free(font->fontname);
+ g_free(font);
+ }
+ g_list_free(pdffonts);
+
+ // PDF trailer
+ startxref = pdfbuf->len;
+ if (annot) g_string_append_printf(pdfbuf,
+ "xref\n%d %d\n", n_obj_catalog, xref.last-n_obj_catalog+1);
+ else g_string_append_printf(pdfbuf,
+ "xref\n0 %d\n0000000000 65535 f \n", xref.last+1);
+ for (i=n_obj_catalog; i<=xref.last; i++)
+ g_string_append_printf(pdfbuf, "%010d 00000 n \n", xref.data[i]);
+ g_string_append_printf(pdfbuf,
+ "trailer\n<< /Size %d /Root %d 0 R ", xref.last+1, n_obj_catalog);
+ if (annot) {
+ g_string_append_printf(pdfbuf, "/Prev %d ", pdfinfo.startxref);
+ // keeping encryption info somehow doesn't work.
+ // xournal can't annotate encrypted PDFs anyway...
+/*
+ obj = get_dict_entry(pdfinfo.trailerdict, "/Encrypt");
+ if (obj!=NULL) {
+ g_string_append_printf(pdfbuf, "/Encrypt ");
+ show_pdfobj(obj, pdfbuf);
+ }
+*/
+ }
+ g_string_append_printf(pdfbuf,
+ ">>\nstartxref\n%d\n%%%%EOF\n", startxref);
+
+ g_free(xref.data);
+ if (annot) {
+ free_pdfobj(pdfinfo.trailerdict);
+ if (pdfinfo.pages!=NULL)
+ for (i=0; i<pdfinfo.npages; i++) {
+ free_pdfobj(pdfinfo.pages[i].resources);
+ free_pdfobj(pdfinfo.pages[i].mediabox);
+ free_pdfobj(pdfinfo.pages[i].contents);
+ }
+ }
+
+ setlocale(LC_NUMERIC, "");
+ if (fwrite(pdfbuf->str, 1, pdfbuf->len, f) < pdfbuf->len) {
+ fclose(f);
+ g_string_free(pdfbuf, TRUE);
+ return FALSE;
+ }
+ fclose(f);
+ g_string_free(pdfbuf, TRUE);
+ return TRUE;