PDFlib Cookbook

cookbook

text_output/create_interactive_index

Download Java Code      Switch to PHP Code      Show Output PDF

/* $Id: create_interactive_index.java,v 1.6 2013/02/26 11:10:24 stm Exp $ 
 * 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.text_output;

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 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<IndexEntry> {
        String name;
        int page;
        public int compareTo(IndexEntry other) {
            return name.compareTo(other.name);
        }
    };
    
    /*
     * List of all index entries
     */
    List<IndexEntry> index = new LinkedList<IndexEntry>();
    
    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 + " $Revision: 1.6 $");
        
        
        /* ----------------------------------------------------------------
         * 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.print("PDFlib exception occurred:\n");
            System.err.print("[" + e.get_errnum() + "] " + e.get_apiname() +
                ": " + e.get_errmsg() + "\n");
        } catch (Exception e) {
            System.err.println(e.toString());
        } finally {
            if (p != null) {
                p.delete();
            }
        }
    }
}