PDFlib Cookbook

cookbook

textflow/create_interactive_index

In a Textflow define some terms to be indexed and create a sorted index from the indexed terms.

Download Java Code  Switch to PHP Code  Show Output 

/* 
 * 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.
 *
 * Required software: PDFlib/PDFlib+PDI/PPS 10
 * 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=NotoSerif-Regular fontsize=12 " + 
             "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);
            
            
            /* ----------------------------------------------------------------
             * 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=NotoSerif-Regular 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=NotoSerif-Regular 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);
        }
    }
}