BASKET
Search
PDFlib

text_output/widows_and_orphans

Download Java Code    Switch to PHP Code      Show Output PDF

/* $Id: widows_and_orphans.java,v 1.4 2016/07/12 10:27:37 stm Exp $

 * Textflow starter:

 * Create multi-column text output which may span multiple pages

 *

 * Required software: PDFlib/PDFlib+PDI/PPS 9

 * Required data: none

 */

package com.pdflib.cookbook.pdflib.text_output;


import com.pdflib.pdflib;

import com.pdflib.PDFlibException;


public class widows_and_orphans {

    public static void main(String argv[]) {

        pdflib p = null;


        String outfile = "widows_and_orphans.pdf";

        String title = "Widows and Orphans";

        int exitcode = 0;


        try {

            p = new pdflib();


            /* This means we must check return values of load_font() etc. */

            p.set_option("errorpolicy=return");


            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.4 $");

           

            double pagewidth = 500;

            double pageheight = 500;

           

            double header_fontsize = 24;


            /*

             * Text that together with the specified fontsize below provokes

             * an orphan.

             */

            String orphan_explanation = repeatString("This text provokes an \"orphan\" at the end of the "

                + "first fitbox. ", 10);

            String orphan = repeatString("Unless action is taken, the first line of this paragraph appears "

                + "as an orphan at the end of the first fitbox. ", 2);

            String orphan_more_text = repeatString("This is text after the paragraph with the orphan. ", 2);

            final String orphan_text = orphan_explanation

                + "<nextline leading=80%><nextparagraph leading=100%>"

                + orphan

                + "<nextline leading=80%><nextparagraph leading=100%>"

                + orphan_more_text;

             

            /*

             * Format the text that provokes an orphan without any special

             * action to avoid widows and orphans.

             */

            format_text_simple(p, orphan_text, 14.1,

                pagewidth, pageheight,

                header_fontsize,

                "Orphan");

           

            /*

             * Format the same text with an algorithm that avoids widows and

             * orphans.

             */

            format_text_widow_orphan_aware(p, orphan_text, 14.1,

                pagewidth, pageheight,

                header_fontsize,

                "Orphan avoided");

           

            /*

             * Text that together with the specified fontsize below provokes

             * a widow.

             */

            String widow_explanation = repeatString("This text provokes a \"widow\" at the start of the "

                + "second fitbox. ", 7);

            String widow = repeatString("Unless action is taken, the last line of this paragraph appears "

                + "as a widow at the start of the second fitbox. ", 2);

            String widow_more_text = repeatString("This is text after the paragraph with the widow. ", 2);

            final String widow_text = widow_explanation

                + "<nextline leading=80%><nextparagraph leading=100%>"

                + widow

                + "<nextline leading=80%><nextparagraph leading=100%>"

                + widow_more_text;

           

            /*

             * Format the text that provokes a widow without any special

             * action to avoid widows and orphans.

             */

            format_text_simple(p, widow_text, 14,

                pagewidth, pageheight,

                header_fontsize,

                "Widow");

           

            /*

             * Format the same text with an algorithm that avoids widows and

             * orphans.

             */

            format_text_widow_orphan_aware(p, widow_text, 14.1,

                pagewidth, pageheight,

                header_fontsize,

                "Widow avoided");

           

            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");

            exitcode = 1;

        }

        catch (Exception e) {

            System.err.println(e.getMessage());

            exitcode = 1;

        }

        finally {

            if (p != null) {

                p.delete();

            }

            System.exit(exitcode);

        }

    }


    /**

     * Format the textflow into the fitboxes over multiple pages, without doing

     * anything to avoid widows and orphans.

     *

     * @param p

     *            the PDFlib object

     * @param text

     *            text with inline options for textflow

     * @param fontsize

     *            size of font for text

     * @param pagewidth

     *            width of the page

     * @param pageheight

     *            height of the page

     * @param header_fontsize

     *            fontsize to use for the page header

     * @param title

     *            title for pages

     *           

     * @throws PDFlibException

     */

    private static void format_text_simple(pdflib p,

        String text, double fontsize,

        double pagewidth, double pageheight,

        double header_fontsize, String title)

            throws PDFlibException, Exception {

       

        final String optlist = "fontname=Helvetica "

            + "fontsize=" + fontsize + " encoding=unicode "

            + "alignment=left adjustmethod=nofit";

       

        int tf = p.create_textflow(text, optlist);

        if (tf == -1) {

            throw new Exception("Error: Unable to create textflow: " +

                p.get_errmsg());

        }

       

        double fitbox_width = pagewidth * 0.5;

        double fitbox_height = pageheight * 0.5;

       

        double llx = (pagewidth - fitbox_width) / 2;

        double lly = (pageheight - fitbox_height) / 2;

        double urx = llx + fitbox_width;

        double ury = lly + fitbox_height;

       

        String header_options = "fontname=Helvetica encoding=unicode fontsize="

            + header_fontsize + " position={center} boxsize={"

            + pagewidth + " " + header_fontsize + "}";

       

        /*

         * Simple algorithm to format the Textflow into as many fitboxes

         * as necessary.

         */

        int pagecount;

        String result;

        for (result = "_boxfull", pagecount = 1;

                result.equals("_boxfull"); pagecount += 1) {

           

            p.begin_page_ext(pagewidth, pageheight, "");


            p.fit_textline(title + " (page " + pagecount + ")", 0,

                pageheight - 2 * header_fontsize, header_options);


            result = p.fit_textflow(tf, llx, lly, urx, ury, "showborder=true");


            p.end_page_ext("");

        }

       

        p.delete_textflow(tf);

    }

   

    /**

     * Format the textflow into the fitboxes over multiple pages, while

     * applying an algorithm that avoids widows and orphans.

     *

     * @param p

     *            the PDFlib object

     * @param text

     *            text with inline options for textflow

     * @param fontsize

     *            size of font for text

     * @param pagewidth

     *            width of the page

     * @param pageheight

     *            height of the page

     * @param header_fontsize

     *            fontsize to use for the page header

     * @param title

     *            title of first page

     *           

     * @throws PDFlibException

     */

    private static void format_text_widow_orphan_aware(pdflib p,

        String text, double fontsize,

        double pagewidth, double pageheight,

        double header_fontsize, String title)

            throws PDFlibException, Exception {

       

        /*

         * Same option list as in format_text_simple(), but with

         * minlinecount=2 to avoid orphans.

         */

        final String optlist = "minlinecount=2 fontname=Helvetica "

            + "fontsize=" + fontsize + " encoding=unicode "

            + "alignment=left adjustmethod=nofit";


        int tf = p.create_textflow(text, optlist);

        if (tf == -1) {

            throw new Exception("Error: Unable to create textflow: " +

                        p.get_errmsg());

        }

     

        double fitbox_width = pagewidth * 0.5;

        double fitbox_height = pageheight * 0.5;

       

        double llx = (pagewidth - fitbox_width) / 2;

        double lly = (pageheight - fitbox_height) / 2;

        double urx = llx + fitbox_width;

        double ury = lly + fitbox_height;

       

        String header_options = "fontname=Helvetica encoding=unicode fontsize="

            + header_fontsize + " position={center} boxsize={"

            + pagewidth + " " + header_fontsize + "}";

       

        int pagecount;

        String result;

        for (result = "_boxfull", pagecount = 1;

                result.equals("_boxfull"); pagecount += 1) {

           

            p.begin_page_ext(pagewidth, pageheight, "");


            /*

             * Fit the the remaining Textflow into the first fitbox in blind

             * mode (option blind=true), i.e. without creating any real output,

             * while setting "minlinecount=2" to avoid an orphan. Query the

             * number of lines in the fitbox by using keyword "boxlinecount".

             */

            result = p.fit_textflow(tf, llx, lly, urx, ury, "blind=true");


            int boxlinecount = (int) p.info_textflow(tf, "boxlinecount");


            /*

             * Count how many times the textflow must be rewound: At least

             * one time for the first blind fit, and another time if a second

             * blind fit is performed because the fitbox is full.

             */

            int rewindcount = 1;

           

            if (result.equals("_boxfull")) {

               

                /*

                 * Fit the next part of the Textflow into the second fitbox in

                 * blind mode. We don't care here that the second fitbox will

                 * actually be placed on the next page in the output file.

                 */

                p.fit_textflow(tf, llx, lly, urx, ury, "blind=true");


                /*

                 * Query the number of lines of the first paragraph in the

                 * second fitbox. If the count is equal to one, we found a

                 * single-line "widow". In order to avoid that, we reduce the

                 * number of lines for the first fitbox by one.

                 */

                int firstparalinecount = (int) p.info_textflow(tf,

                    "firstparalinecount");

                if (firstparalinecount == 1) {

                    boxlinecount -= 1;

                }

               

                rewindcount += 1;

            }


            /* Place header line */

            p.fit_textline(title + " (page " + pagecount + ")", 0,

                pageheight - 2 * header_fontsize, header_options);


            /*

             * Now do the actual output on the page.

             * Rewind the Textflow one or two steps (determined by

             * rewindcount) and set the calculated maximum number of lines.

             */

            result = p.fit_textflow(tf, llx, lly, urx, ury,

                "rewind=-" + rewindcount

                + " maxlines=" + boxlinecount + " showborder=true");


            p.end_page_ext("");

        }

       

        p.delete_textflow(tf);

    }

   

    /**

     * Repeat a string n times.

     *

     * @param s

     *            the string to repeat

     * @param n

     *            how many times to repeat

     * @return n times the string s

     */

    private static String repeatString(String s, int n) {

        return new String(new char[n]).replace("\0", s);

    }

}