#include #include inherit "module"; inherit "roxenlib"; #define PDF id->misc->pdf mixed newParagraph = Java.pkg["com/lowagie/text/Paragraph"]->_constructor; constant module_type = MODULE_PARSER; constant module_name = "PDF Markup Language"; constant module_doc = "Module for generating PDF content from markup"; constant cvs_version = "$Id: $"; constant thread_safe = 1; object fontFactory; // TYPE_FILE_LIST void create() { defvar("font_dirs", ({}), "Font Directories", TYPE_DIR_LIST, "A list of directories we should look in to find fonts."); } void start() { fontFactory = Java.pkg["com/lowagie/text/FontFactory"]; fontFactory->registerDirectories(); foreach(QUERY(font_dirs);;string dir) fontFactory->registerDirectory(dir); } string tag_pdffonts(string tag_name, mapping args, object id, object f, mapping defines, object fd) { object bfc = Java.pkg["com/lowagie/text/FontFactory"]; object af = bfc->getRegisteredFonts(); if(!af) return "no fonts available."; int nf = af->size(); string retval = ""; af = af->toArray(); for(int i = 0; i < nf; i++) { retval += (string_to_utf8((string)af[i]) + "
\n"); } return retval; } string tag_template(string tag_name, mapping args, object id, object f, mapping defines, object fd) { if(PDF->templates) throw("PDF templates can only be specified once.\n"); PDF->templates = ([]); if(args->first) { PDF->templates->first = load_template(id, args->first); } if(args->inside) { PDF->templates->inside = load_template(id, args->inside); } if(args->last) { PDF->templates->last = load_template(id, args->last); } } object load_template(object id, string path) { object temp; object tos = Java.pkg["java/io/ByteArrayOutputStream"](); //werror("Reading file from: %s %O", path, file_stat(path)); object treader = Java.pkg["com/lowagie/text/pdf/PdfReader"]->_constructor("(Ljava/lang/String;)V")(path); temp = PDF->writer->getImportedPage(treader, 1); return temp; } string tag_line(string tag_name, mapping args, object id, object f, mapping defines, object fd) { } string tag_boxdim(string tag_name, mapping args, object id, object f, mapping defines, object fd) { if(!PDF->boxdims) PDF->boxdims = ({}); PDF->boxdims += ({ args }); } string tag_pagebreak(string tag_name, mapping args, object id, object f, mapping defines, object fd) { page_break(id); return ""; } string container_running_paragraph(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { // werror("running_paragraph\n"); addRunningParagraph(id, args, contents); return ""; } string tag_running_image(string tag_name, mapping args, object id, object f, mapping defines, object fd) { addRunningImage(id, args); return ""; } string tag_fixed_image(string tag_name, mapping args, object id, object f, mapping defines, object fd) { addFixedImage(id, args); return ""; } string container_fixed_paragraph(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { //werror("container_fixed_paragraph()\n"); addFixedParagraph(id, args, contents); return ""; } string container_barcode(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { addBarcode(contents, args, id); return ""; } string container_runningbox(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { if(PDF->runningbox) throw("Only one running box may be specified per document.\n"); //werror("parsing a running box!\n"); // PDF->document->add(newParagraph("(Ljava/lang/String;Lcom/lowagie/text/Font;)V")("whee!", PDF->defaultfont)); parse_html(contents, (["boxdim": tag_boxdim, "fixedbox": lambda(mixed ... args){ throw("PDF: runningbox may not contain fixedboxes.\n");}]), ([]), id); mapping colargs = getRunningColumn(id); setColumn(colargs, id); parse_html(contents, (["pagebreak": tag_pagebreak, "image": tag_running_image]), (["barcode": container_barcode, "paragraph": container_running_paragraph, "ptable": container_table]), id); return ""; } string container_th(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { object font = getFont(PDF, args->font, (float)(args->size||12), args->color); object paragraph; font = Java.pkg["com/lowagie/text/Font"]->_constructor("(Lcom/lowagie/text/Font;)V")(font); paragraph = parseParagraph(id, paragraph, contents, font, args); object cell; cell = Java.pkg["com/lowagie/text/pdf/PdfPCell"]->_constructor("(Lcom/lowagie/text/pdf/PdfPCell;)V")(PDF->currenttable->getDefaultCell()); cell->setUseBorderPadding(1); cell->setUseDescender(1); cell->setUseAscender(1); if(args->height) cell->setFixedHeight((float)args->height); if(args->colspan) cell->setColspan((int)args->colspan); if(args->border) cell->setBorderWidth((float)args->border); if(args->valign) { int align; switch(lower_case(args->valign)) { case "top": align = Java.pkg["com/lowagie/text/Element"]->ALIGN_TOP; break; case "bottom": align = Java.pkg["com/lowagie/text/Element"]->ALIGN_BOTTOM; break; case "middle": align = Java.pkg["com/lowagie/text/Element"]->ALIGN_MIDDLE; break; } cell->setVerticalAlignment(align); } if(args->colspan) cell->setColspan((int)args->colspan); if(args->border) cell->setBorderWidth((float)args->border); if(contents && sizeof(contents)) { //werror("%O\n", sort(indices(cell))); cell->addElement(paragraph); } PDF->currenttable->_method("addCell", "(Lcom/lowagie/text/pdf/PdfPCell;)V")(cell); return ""; } string container_td(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { object font = getFont(PDF, args->font, (float)(args->size||12), args->color); object paragraph; font = Java.pkg["com/lowagie/text/Font"]->_constructor("(Lcom/lowagie/text/Font;)V")(font); paragraph = parseParagraph(id, paragraph, contents, font, args); object cell = Java.pkg["com/lowagie/text/pdf/PdfPCell"]->_constructor("(Lcom/lowagie/text/pdf/PdfPCell;)V")(PDF->currenttable->getDefaultCell()); cell->setUseBorderPadding(1); cell->setUseDescender(1); cell->setUseAscender(1); if(args->height) cell->setFixedHeight((float)args->height); if(args->colspan) cell->setColspan((int)args->colspan); if(args->border) cell->setBorderWidth((float)args->border); if(args->valign) { int align; switch(lower_case(args->valign)) { case "top": align = Java.pkg["com/lowagie/text/Element"]->ALIGN_TOP; break; case "bottom": align = Java.pkg["com/lowagie/text/Element"]->ALIGN_BOTTOM; break; case "middle": align = Java.pkg["com/lowagie/text/Element"]->ALIGN_MIDDLE; break; } cell->setVerticalAlignment(align); } if(contents && sizeof(contents)) cell->addElement(paragraph); PDF->currenttable->_method("addCell", "(Lcom/lowagie/text/pdf/PdfPCell;)V")(cell); if(args->colspan) PDF->current_cell += (int)args->colspan; else PDF->current_cell++; if((PDF->current_cell % PDF->table_columns) == 0) { if(((PDF->current_cell / PDF->table_columns) % 50) == 0) { //addTable(id, PDF->currenttable); } } return ""; } string container_table(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { if(!args->columns) error("table column count must be specified.\n"); PDF->table_columns = (int)args->columns; PDF->current_cell = 0; PDF->current_row = 0; PDF->currenttable = Java.pkg["com/lowagie/text/pdf/PdfPTable"]((int)args->columns); if(args->headerrows) PDF->currenttable->setHeaderRows((int)args->headerrows); if(args->footerrows) PDF->currenttable->setFooterRows((int)args->footerrows); if(args->cellpadding) PDF->currenttable->getDefaultCell()->setPadding((float)args->cellpadding); if(args->width) PDF->currenttable->setWidthPercentage((float)args->width); if(args->border) PDF->currenttable->getDefaultCell()->setBorderWidth((float)args->border); if(args->columnwidths) { object t; array widths = (args->columnwidths - " ") / ","; if(sizeof(widths)!= (int)args->columns) error("column width count must equal number of columns.\n"); t = Java.machine.new_float_array((int)args->columns); for(int i = 0; i < (int)args->columns; i++) { t[i] = (float)widths[i]; } PDF->currenttable->_method("setWidths", "([F)V")(t); PDF->currenttable->_method("setTotalWidth", "([F)V")(t); } parse_html(contents, ([]), (["td": container_td, "th": container_th]), id); Java.pkg["java/lang/System"]->gc(); addTable(id, PDF->currenttable); PDF->currenttable = 0; return ""; } string container_fixedbox(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { string page = lower_case(args->page); if(page == "first") page = "1"; if(args->force) page += "f"; if(!PDF->fixed_boxes) PDF->fixed_boxes = ([]); if(!page) throw("a fixed box must be placed on a particular page.\n"); if(!PDF->fixed_boxes[page]) PDF->fixed_boxes[page] = ({}); PDF->fixed_boxes[page] += ({args + (["contents": contents])}); return ""; } string container_pdf(string tag_name, mapping args, string contents, object id, object f, mapping defines, object fd) { // first, we set up the document (a4 size, by default, can be specified by pagesize attribute), pdfwriter and output stream. setup_pdf_objects(args, id); PDF->current_page = 1; PDF->document->open(); object font; // get the default font and size, if specified. if(args->defaultfont) font = getFont(PDF, args->defaultfont, (float)(args->defaultsize||12), args->color); else font = getFont(PDF, "helvetica", (float)(args->defaultsize||12), args->color); if(font) PDF->defaultfont = font; if(args->preparse) contents = parse_rxml(contents, id); if(args->runout) PDF->runout = 1; parse_html(contents, (["template": tag_template]), (["fixedbox": container_fixedbox]), id); parse_html(contents, ([]), (["runningbox": container_runningbox]), id); // apply_fixedboxes(id, 1); // okay, we're at the end, so if we've specified runout, we need to do that. if(PDF->runout) { mapping x = PDF->fixed_boxes + ([]); m_delete(x, "last"); m_delete(x, "inside"); m_delete(x, "first"); m_delete(x, "all"); array y = indices(x); int last_fb = 0; foreach(y;int i; mixed z) { if(has_suffix(z, "f")) z = z[0..(sizeof(z)-2)]; if((int)z > last_fb) last_fb = (int)z; } while(PDF->current_page <= last_fb) new_page(id); } set_last_template(id); PDF->document->close(); string pdf = get_pdf_stream(id); return pdf; } //! set the template for the current page, first or last depending on whether we're the first page or not. void set_last_template(object id) { object temp; apply_fixedboxes(id, 1); if(PDF->current_page > 1 && PDF->templates && PDF->templates->last) { temp = PDF->templates->last; } else if(PDF->templates && PDF->templates->first) { temp = PDF->templates->first; } if(!temp) return; object dc = PDF->writer->getDirectContentUnder(); dc->addTemplate(temp, 0.0,0.0); } void addTable(object id, object contents) { int x; /* object ptable = Java.pkg["com/lowagie/text/pdf/PdfPTable"](1); object dcell = ptable->getDefaultCell(); dcell->setPadding(6.0); dcell->setBorder(0.0); float f; f = contents->getWidthPercentage(); ptable->setWidthPercentage(f); dcell->setBorder(1.0); ptable->_method("addCell", "(Lcom/lowagie/text/pdf/PdfPTable;)V")(contents); */ contents->setExtendLastRow(0); PDF->currentcolumn->addElement(contents); int status; do { status = PDF->currentcolumn->go(); if(PDF->fixedbox) break; if(status != Java.pkg["com/lowagie/text/pdf/ColumnText"]->NO_MORE_TEXT) { mapping args = getRunningColumn(id); PDF->currentcolumn->setYLine(PDF->pagesize->top() - (float)args->uly); new_page(id); args = getRunningColumn(id); // FIXME: this is copied from setColumn(), which is different because apparently tables don't like it when their columntext object changes. float llx, lly, urx, ury; float ulx = (float)args->ulx; float uly = (float)args->uly; float lrx = (float)args->lrx; float lry = (float)args->lry; llx = ulx; lly = lry; urx = lrx; ury = uly; lly = PDF->pagesize->top() - lly; ury = PDF->pagesize->top() - ury; object dc = PDF->writer->getDirectContent(); if((int)args->border) { // werror("setting border: %O\n", args); dc->setLineWidth((float)4.0); if(args->dash) { array a = array_sscanf(args->dash, "%f,%*s%f,%s*%f"); dc->setLineDash(@a); } dc->rectangle(llx, lly, urx-llx, ury-lly); dc->stroke(); } float padding; if(args->padding) padding = (float)args->padding; PDF->columnwidth = ((urx-padding) - (llx+padding)); //werror("Column Width: %O\n", PDF->columnwidth); PDF->currentcolumn->setSimpleColumn(llx+padding, lly+padding, urx-padding, ury-padding); PDF->currentcolumn->setExtraParagraphSpace(24.0); } } while (status == Java.pkg["com/lowagie/text/pdf/ColumnText"]->NO_MORE_COLUMN); // contents->deleteBodyRows(); // contents->setSkipFirstHeader(1); } void addRunningParagraph(object id, mapping args, string|object contents, int|void retry) { object font; object paragraph; if(objectp(contents)) paragraph = contents; else { font = getFont(PDF, args->font, (float)(args->size||12), args->color); font = Java.pkg["com/lowagie/text/Font"]->_constructor("(Lcom/lowagie/text/Font;)V")(font); paragraph = parseParagraph(id, paragraph, contents, font, args); } PDF->currentcolumn->addElement(paragraph); int x = PDF->currentcolumn->go(); if(x == Java.pkg["com/lowagie/text/pdf/ColumnText"]->NO_MORE_COLUMN) { werror("column ended.\n"); page_break(id); if(!retry) addRunningParagraph(id, args, contents, 1); else werror("overflowing paragraph! dropping.\n"); } } void addRunningImage(object id, mapping args, int|void retry) { object img = getImage(args); PDF->currentcolumn->addElement(img); int x = PDF->currentcolumn->go(); if(x == Java.pkg["com/lowagie/text/pdf/ColumnText"]->NO_MORE_COLUMN) { werror("column ended.\n"); page_break(id); if(!retry) addRunningImage(id, args, 1); else werror("overflowing image! dropping.\n"); } } void page_break(object id) { new_page(id); mapping colargs = getRunningColumn(id); setColumn(colargs, id); } void addFixedParagraph(object id, mapping args, string|object contents) { // werror("addFixedParagraph: %O\n", contents); object paragraph; if(objectp(contents)) paragraph = contents; else { object font = getFont(PDF, args->font, (float)(args->size||12), args->color); contents = replace(contents, "¤tpage;", (string)PDF->current_page); font = Java.pkg["com/lowagie/text/Font"]->_constructor("(Lcom/lowagie/text/Font;)V")(font); paragraph = parseParagraph(id, paragraph, contents, font, args); } if(paragraph) { PDF->currentcolumn->addElement(paragraph); int x = PDF->currentcolumn->go(); if(x == Java.pkg["com/lowagie/text/pdf/ColumnText"]->NO_MORE_COLUMN) { // werror("column ended.\n"); } } } void addFixedImage(object id, mapping args) { object img = getImage(args); PDF->currentcolumn->addElement(img); int x = PDF->currentcolumn->go(); if(x == Java.pkg["com/lowagie/text/pdf/ColumnText"]->NO_MORE_COLUMN) { // werror("column ended.\n"); } } object getImage(mapping args) { object img; img = Java.pkg["com/lowagie/text/Image"]->_method("getInstance", "(Ljava/lang/String;)Lcom/lowagie/text/Image;")(args->src); if(args->alt) img->setAlt(args->alt); img->setWidthPercentage(0); img->setXYRatio(1.0); if(args->width) img->scaleAbsoluteWidth((float)args->width); if(args->height) img->scaleAbsoluteHeight((float)args->height); if(args->dpi) img->setDpi((int)args->dpi, (int)args->dpi); return img; } mapping getRunningColumn(object id) { if(PDF->current_page >= sizeof(PDF->boxdims)) return PDF->boxdims[-1]; else return PDF->boxdims[PDF->current_page-1]; } void setColumn(mapping args, object id) { object dc = PDF->writer->getDirectContent(); object column; column = Java.pkg["com/lowagie/text/pdf/ColumnText"](dc); float llx, lly, urx, ury; float ulx = (float)args->ulx; float uly = (float)args->uly; float lrx = (float)args->lrx; float lry = (float)args->lry; llx = ulx; lly = lry; urx = lrx; ury = uly; lly = PDF->pagesize->top() - lly; ury = PDF->pagesize->top() - ury; if(args->border && (int)args->border) { dc->setLineWidth((float)args->border); if(args->dash) { array a = array_sscanf(args->dash, "%f,%*s%f,%s*%f"); //werror("%O\n", a); dc->setLineDash(@a); } dc->rectangle(llx, lly, urx-llx, ury-lly); dc->stroke(); // werror("!!!\n!!!\ncolumns: %f, %f %f %f %f\n", PDF->pagesize->top(), llx, lly, urx, ury); } float padding; if(args->padding) padding = (float)args->padding; PDF->currentcolumn = 0; PDF->columnwidth = ((urx-padding) - (llx+padding)); //werror("Column Width: %O\n", PDF->columnwidth); column->setSimpleColumn(llx+padding, lly+padding, urx-padding, ury-padding); PDF->currentcolumn = column; // PDF->currentcolumn->setExtraParagraphSpace(24.0); } //! apply the headers, footers, and the template to the current page, if necessary, then start a new page. void new_page(object id) { // werror("new_page(%d)\n", PDF->current_page+1); object temp; apply_fixedboxes(id); if(PDF->current_page > 1 && PDF->templates && PDF->templates->inside) { temp = PDF->templates->inside; } else if(PDF->templates && PDF->templates->first) { temp = PDF->templates->first; } if(temp) { object dc = PDF->writer->getDirectContentUnder(); dc->addTemplate(temp, 0.0,0.0); } PDF->current_page++; PDF->document->newPage(); } // here's how we apply fixed boxes: // we always apply boxes marked "all" // if we're on the last page and we have boxes specified as "last", we use them. // if we've done this, then we skip any boxes also marked for the current page number. // (ie, last overrides a specific page placement) // otherwise, we place any boxes for the current page number // finally, if we're not the first page, and we're not the last page, we place any boxes // marked for "inside". void apply_fixedboxes(object id, int|void last) { // werror("apply_fixedboxes(): %O\n", PDF->current_page); if(!PDF->fixed_boxes) return; array selected_boxes = ({}); if(PDF->fixed_boxes->all) selected_boxes += PDF->fixed_boxes->all; if(last && PDF->fixed_boxes->last) selected_boxes += PDF->fixed_boxes->last; else if(PDF->fixed_boxes[(string)PDF->current_page]) selected_boxes += PDF->fixed_boxes[(string)PDF->current_page]; if(PDF->fixed_boxes[(string)PDF->current_page + "f"]) selected_boxes += PDF->fixed_boxes[(string)PDF->current_page + "f"]; if(!last && PDF->current_page > 1 && PDF->fixed_boxes->inside) selected_boxes += PDF->fixed_boxes->inside; foreach(selected_boxes;;mapping box) { object old_box = PDF->currentcolumn; setColumn(box, id); PDF->fixedbox = 1; parse_html(box->contents, (["image": tag_fixed_image]), (["ptable": container_table, "barcode": container_barcode, "paragraph": container_fixed_paragraph]), id); PDF->fixedbox = 0; PDF->currentcolumn = old_box; } } object getFont(mixed pdf, string fn, float size, string|void color) { object font; if(fn && sizeof(fn)) font = fontFactory->getFont(fn, "Identity-H", 1, (float)(size||12)); else { font = pdf->defaultfont; if(size) font->setSize((float)size); } // object font = fontFactory->getFont(fn, (float)size); if(color) setFontColor(font, color); return font; } void setFontColor(object font, string color) { if(color) { array c = array_sscanf(lower_case(color), "%*[#]%02x%02x%02x"); font->setColor(@c); } } mixed i_parse_tags(object parser, string data, mapping extra) { object id = extra->id; data = lower_case(data); data-=" "; switch(data) { case "": extra->bold++; break; case "": extra->bold--; break; case "": extra->underline++; break; case "": extra->underline--; break; case "": extra->italic++; break; case "": extra->italic--; break; } return ""; } mixed i_parse_data(object parser, string data, mapping extra) { // werror("i_parse_data(%O)\n", data); // werror("EXTRA: %O\n", sort(indices(extra->paragraph))); object id = extra->id; object font = extra->font; object thisfont = Java.pkg["com/lowagie/text/Font"]->_constructor("(Lcom/lowagie/text/Font;)V")(font); // uncomment this line if you want less html-like, more
-like behavior.
//    data = replace(data, ({"\r", "\n", "\t"}), ({" ", " ", " "}));

    int fontcalc;

    if(extra->bold >=1)
       fontcalc |= Java.pkg["com/lowagie/text/Font"]->BOLD;

    if(extra->italic >=1)
       fontcalc |= Java.pkg["com/lowagie/text/Font"]->ITALIC;

    if(extra->underline >=1)
       fontcalc |= Java.pkg["com/lowagie/text/Font"]->UNDERLINE;

    thisfont->setStyle(fontcalc);

    object phrase = Java.pkg["com/lowagie/text/Phrase"](data, thisfont);

    if(!extra->paragraph)
      extra->paragraph = newParagraph("(Ljava/lang/String;Lcom/lowagie/text/Font;)V")("", font);

    extra->paragraph->add(phrase);

    return "";
}

object parseParagraph(object id, object paragraph, string text, object font, mapping args)
{
	object parser = Parser.HTML();
	mapping extra = (["paragraph": paragraph, "id": id, "font": font]);
    parser->_set_tag_callback(i_parse_tags);
    parser->_set_data_callback(i_parse_data);
    parser->set_extra(extra);

    parser->finish(text);
//werror("PARAGRAPH: %O", extra);
//i_parse_data(parser, text, extra);
    if(!extra->paragraph) {werror("short circuiting!\n");return 0;}
/*
    if(args->spacebefore)
       extra->paragraph->setSpacingBefore((float)args->spacebefore);

    if(args->spaceafter)
       extra->paragraph->setSpacingAfter((float)args->spaceafter);
*/
    if(args->align)
       extra->paragraph->setAlignment(args->align);

    if(args->spacing)
       extra->paragraph->setLeading((float)args->spacing);

//   extra->paragraph->setExtraParagraphSpace(0.0);

    return extra->paragraph;
}

void addBarcode(string contents, mapping args, object id)
{
  object dc = PDF->writer->getDirectContent();
  object barcode;
  string bcclass;

  switch(lower_case(args->type))
  {
	case "128":
		bcclass = "com/lowagie/text/pdf/Barcode128";
		break;
	case "ean":
		bcclass = "com/lowagie/text/pdf/BarcodeEAN";
		break;
	case "postnet":
		bcclass = "com/lowagie/text/pdf/BarcodePostnet";
		break;
	case "codabar":
		bcclass = "com/lowagie/text/pdf/BarcodeCodabar";
		break;
	case "39":
	default:
		bcclass = "com/lowagie/text/pdf/Barcode39";
		break;
  }

  barcode = Java.pkg[bcclass]();
  barcode->setCode(contents);

  if(args->alttext) barcode->setAltText(args->alttext);
  if(args->height) barcode->setBarHeight((float)args->height);

  object img = barcode->createImageWithBarcode(dc, 0, 0);


  if(args->rotate) img->setRotationDegrees((float)args->rotate);

  object phrase = Java.pkg["com/lowagie/text/Phrase"]->_constructor("(Lcom/lowagie/text/Chunk;)V")(Java.pkg["com/lowagie/text/Chunk"](img, 0, 0));

  addFixedParagraph(id, args, phrase);
}

void setup_pdf_objects(mapping args, object id)
{
  array margins = ({0.0, 0.0, 0.0, 0.0});

  if(args->margin)
  {
    float m = (float)args->margin;
    margins = ({m, m, m, m});
  }
  id->misc->pdf = ([]);

  object rect;
  if(!args->pagesize || search(args->pagesize, ",")==-1)
    rect = Java.pkg["com/lowagie/text/PageSize"][upper_case(args->pagesize||"a4")];
  else
  {
	array x1 = ((args->pagesize-" ")/",");
    rect = Java.pkg["com/lowagie/text/Rectangle"]((float)x1[0], (float)x1[1]);
  }

  object d = Java.pkg["com/lowagie/text/Document"](rect, @margins);	
  object s = Java.pkg["java/io/ByteArrayOutputStream"]();
  object w = Java.pkg["com/lowagie/text/pdf/PdfWriter"]->getInstance(d, s);

  PDF->pagesize = rect;
  PDF->document = d;
  PDF->writer = w;
  PDF->stream = s;
}

string get_pdf_stream(object id)
{
	// get the contents of the pdf file into a string.
  int sba;
  object ba = PDF->stream->toByteArray()->_obj;
  sba = sizeof(ba);
  array b =  allocate(sba);
  for(int i = 0; i < sba; i++)
    b[i] = ba[i];

  string pdf;
  pdf = sprintf("%{%c%}", b);

  return pdf;
}

mapping query_container_callers()
{ 
 return (["pdf":container_pdf]);
}

mapping query_tag_callers()
{
  return (["pdffonts": tag_pdffonts]);
}