/* * Create interactive index: * In a Textflow define some terms to be indexed and create a sorted index from * the indexed terms * * For indicating an indexed term in a Textflow use the inline options * "matchbox" and "matchbox end" to create a matchbox at the position to which * the index entry will refer to. The matchbox name will be similar to the * indexed term. Place the Textflow. Then, create the index by collecting all * matchboxes. Each index entry will consist of the matchbox name (indexed term) * and the respective page number. Provide the page number with a link * annotation to jump to the respective page. Matchboxes are used here a * second time to indicate the active link area on the page number. * * Note that compiling this code with JDK 1.5 will result in a compiler warning * "... uses unchecked or unsafe operations ...". The reason is that we prefer * JDK 1.4 compatible code over the use of JDK 1.5 generics. * * Required software: PDFlib/PDFlib+PDI/PPS 9 * Required data: none */ package com.pdflib.cookbook.pdflib.textflow; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import com.pdflib.pdflib; import com.pdflib.PDFlibException; public class create_interactive_index { public static void main (String argv[]) { pdflib p = null; String searchpath = "../input"; String outfile = "create_interactive_index.pdf"; String title = "Create Interactive Index"; final double llx = 20, lly = 20, urx = 200, ury = 200; final int pagewidth = 250, pageheight = 230; int exitcode = 0; int i, pageno, entryno, tf = -1, idx = -1; String mname, result; double mcount, minfo; /* Option list to indicate the start of a matchbox */ String startopts = ""; /* Option list to indicate the end of a matchbox */ final String endopts = "matchbox=end"; /* Standard option list for adding a Textflow. * "avoidemptybegin" deletes empty lines at the beginning of a fitbox. * "charref" enables the substitution of numeric and character entity * or glyph name references, e.g. of the character reference "­" * for a soft hyphen. */ final String stdopts = "fontname=Helvetica fontsize=12 encoding=unicode " + "leading=120% charref avoidemptybegin "; int ntexts = 10; /* The text array contains pairs of strings. Each first string will be used * as indexed term, i.e. the text of an index marker. */ String texts[] = { "Long Distance Glider", "\nWith this paper rocket you can send all your messages even when " + "sitting in a hall or in the cinema pretty near the back.\n\n", "Giant Wing", "\nAn unbelievable sailplane! It is amazingly robust and can even " + "do aerobatics. But it best suited to gliding.\n\n", "Cone Head Rocket", "\nThis paper arrow can be thrown with big swing. We launched it " + "from the roof of a hotel. It stayed in the air a long time and " + "covered a considerable distance.\n\n", "Super Dart", "\nThe super dart can fly giant loops with a radius of 4 or 5 " + "metres and cover very long distances. Its heavy cone point is " + "slightly bowed upwards to get the lift required for loops.\n\n", "German Bi-Plane", "\nBrand-new and ready for take-off. If you have lessons in the " + "history of aviation you can show your interest by letting it " + "land on your teacher's desk.\n\n", }; /* Index entry containing a name and a page number. For sorting the index * entries a compare method is provided. */ class IndexEntry implements Comparable { String name; int page; public int compareTo(IndexEntry other) { return name.compareTo(other.name); } }; /* * List of all index entries */ List index = new LinkedList(); try { p = new pdflib(); p.set_option("searchpath={" + searchpath + "}"); /* This means we must check return values of load_font() etc. */ p.set_option("errorpolicy=return"); /* Start the output document */ if (p.begin_document(outfile, "") == -1) throw new Exception("Error: " + p.get_errmsg()); p.set_info("Creator", "PDFlib Cookbook"); p.set_info("Title", title); /* ---------------------------------------------------------------- * Add the text Textflow and define a matchbox on each indexed term * ---------------------------------------------------------------- */ /* Supply the standard options to the Textflow. This has to be done * only once. Further calls of add_textflow() for this Textflow will use * these settings by default. */ tf = p.add_textflow(-1, "", stdopts); if (tf == -1) throw new Exception("Error: " + p.get_errmsg()); /* Loop over all texts. Add each text and define a matchbox on each * indexed term. The matchbox name is set to the indexed term. */ for (i = 0; i < ntexts; i+=2) { /* Add text and start a matchbox indicating an indexed term. * Colorize the matchbox rectangle (only for illustration purposes) */ startopts = "matchbox={name={" + texts[i] + "} " + "fillcolor={rgb 0 0.95 0.95}}"; tf = p.add_textflow(tf, texts[i], startopts); if (tf == -1) throw new Exception("Error: " + p.get_errmsg()); /* Add text and finish the matchbox */ tf = p.add_textflow(tf, texts[i+1], endopts); if (tf == -1) throw new Exception("Error: " + p.get_errmsg()); } /* -------------------------------------------------------------------- * Place the text and retrieve all matchboxes (indexed terms) to create * the index entries from * -------------------------------------------------------------------- */ /* Initialize the current page number to be output in the index */ pageno = 0; /* Initialize the number of index entries */ entryno = 0; /* Loop until all of the text is placed; create new pages as long as * more text needs to be placed. */ do { p.begin_page_ext(pagewidth, pageheight, ""); pageno++; /* Place the text */ result = p.fit_textflow(tf, llx, lly, urx, ury, ""); /* Place a page number at the lower right corner of the page */ p.fit_textline(String.valueOf(pageno), pagewidth - 20, 10, "fontname=Helvetica encoding=unicode fontsize=12 " + "fillcolor={rgb 0 0.95 0.95}"); /* Create the index by creating an index entry from each matchbox on * the page. Create an index entry by retrieving the matchbox name * as well as the current page number. * * (In our solution multiple index entries may refer to the same * indexed term. An indexer for general use would combine entries * for the same term into a single index entry with multiple * page numbers. Implement this by creating a chain of multiple * matchboxes for each indexed term.) */ /* Query the number of matchboxes on the page; the "num" parameter * is set to 0 and will be ignored */ mcount = p.info_matchbox("*", 0, "count"); for (i = 1; i <= mcount; i++) { /* Get the matchbox name */ minfo = p.info_matchbox("*", i, "name"); mname = p.get_string((int) minfo, ""); /* Retrieve the name of the matchbox to be used as the indexed * term and the page number to be used as the page number in the * index entry */ IndexEntry newEntry = new IndexEntry(); newEntry.name = new String(mname); newEntry.page = pageno; index.add(newEntry); entryno++; } p.end_page_ext(""); /* "_boxfull" means we must continue because there is more text; * "_nextpage" is interpreted as "start new column" */ } while (result.equals("_boxfull") || result.equals("_nextpage")); /* Check for errors */ if (!result.equals("_stop")) { /* "_boxempty" happens if the box is very small and doesn't * hold any text at all. */ if (result.equals( "_boxempty")) throw new Exception ("Error: Textflow box too small"); else { /* Any other return value is a user exit caused by * the "return" option; this requires dedicated code to * deal with. */ throw new Exception ("User return '" + result + "' found in Textflow"); } } p.delete_textflow(tf); /* ------------------------------------------------------------- * Sort the list of index entries. Convert it to an array first. * ------------------------------------------------------------- */ IndexEntry sortedIndex[] = new IndexEntry[index.size()]; sortedIndex = index.toArray(sortedIndex); Arrays.sort(sortedIndex); /* --------------------------------------------------------------------- * Construct the contents of the index page(s) based on the collected * pairs containing the indexed term plus the corresponding page number * --------------------------------------------------------------------- */ /* Supply the standard options to the index Textflow. This has to be * done only once for each Textflow. Further calls of add_textflow() for * this Textflow will use these settings by default. */ idx = p.add_textflow(-1, "", stdopts); if (idx == -1) throw new Exception("Error: " + p.get_errmsg()); /* Add the heading "Index" to the index Textflow */ idx = p.add_textflow(idx, "Index\n\n", ""); if (idx == -1) throw new Exception("Error: " + p.get_errmsg()); /* Add the collected and sorted index entries to the index Textflow */ for (i = 0; i < sortedIndex.length; i++) { /* Add the indexed term of the index entry */ idx = p.add_textflow(idx, sortedIndex[i].name + " ", "fillcolor={gray 0}"); if (idx == -1) throw new Exception("Error: " + p.get_errmsg()); /* Add the page number of the index entry. In addition, define a * matchbox with a sequence number as the name. This matchbox will * be used later to define a link annotation on it to jump to the * respective page. */ idx = p.add_textflow(idx, String.valueOf(sortedIndex[i].page), "fillcolor={rgb 0 0.95 0.95} matchbox={name=" + i + "}"); if (idx == -1) throw new Exception("Error: " + p.get_errmsg()); idx = p.add_textflow(idx, "\n", endopts); if (idx == -1) throw new Exception("Error: " + p.get_errmsg()); } /* --------------------------------------------------------------------- * Place the index Textflow with each entry consisting of a text, a page * number, and a link annotation on the page number * --------------------------------------------------------------------- */ /* Initialize the current number of the index entry */ entryno = 0; /* Loop until all index entries are placed; create new pages as long as * more index entries need to be placed */ do { p.begin_page_ext(pagewidth, pageheight, ""); pageno++; /* Fit the index Textflow */ result = p.fit_textflow(idx, llx, lly, urx, ury, ""); /* Place a page number */ p.fit_textline(String.valueOf(pageno), pagewidth - 20, 10, "fontname=Helvetica encoding=unicode fontsize=12 " + "fillcolor={rgb 0 0.95 0.95}"); /* Collect the index entries by retrieving the number of matchboxes * on the current page */ mcount = p.info_matchbox("*", 1, "count"); for (i = 1; i <= mcount; i++) { /* Get the matchbox name which corresponds to the text of the * index entry */ minfo = p.info_matchbox("*", i, "name"); mname = p.get_string((int) minfo, ""); int action = p.create_action("GoTo", "destination={page=" + (sortedIndex[entryno].page) + "}"); /* With the "GoTo" action, create a Link annotation on the * matchbox defined above. 0 rectangle coordinates will be * replaced with matchbox coordinates. */ p.create_annotation(0, 0, 0, 0, "Link", "action={activate " + action + "} linewidth=0 " + "usematchbox={" + mname + "}"); entryno++; } p.end_page_ext(""); } while (result.equals("_boxfull") || result.equals("_nextpage")); /* Check for errors */ if (!result.equals("_stop")) { /* "_boxempty" happens if the box is very small and doesn't * hold any text at all. */ if (result.equals( "_boxempty")) throw new Exception ("Error: Textflow box too small"); else { /* Any other return value is a user exit caused by * the "return" option; this requires dedicated code to * deal with. */ throw new Exception ("User return '" + result + "' found in Textflow"); } } p.delete_textflow(idx); p.end_document(""); } catch (PDFlibException e) { System.err.println("PDFlib exception occurred:"); System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() + ": " + e.get_errmsg()); exitcode = 1; } catch (Exception e) { System.err.println(e.toString()); exitcode = 1; } finally { if (p != null) { p.delete(); } System.exit(exitcode); } } }