textflow/widows_and_orphans
Create multi-column text output which may span multiple pages.
Download Java Code Switch to PHP Code Show Output
/*
* Textflow starter:
* Create multi-column text output which may span multiple pages
*
* Required software: PDFlib/PDFlib+PDI/PPS 10
* Required data: none
*/
package com.pdflib.cookbook.pdflib.textflow;
import com.pdflib.pdflib;
import com.pdflib.PDFlibException;
public class widows_and_orphans {
public static void main(String argv[]) {
/* This is where the data files are. Adjust as necessary. */
String searchpath = "../input";
pdflib p = null;
String outfile = "widows_and_orphans.pdf";
String title = "Widows and Orphans";
int exitcode = 0;
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");
if (p.begin_document(outfile, "") == -1)
throw new Exception("Error: " + p.get_errmsg());
p.set_info("Creator", "PDFlib Cookbook");
p.set_info("Title", title);
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, 13.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, 13.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, 13.6,
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, 13.6,
pagewidth, pageheight,
header_fontsize,
"Widow avoided");
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);
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=NotoSerif-Regular "
+ "fontsize=" + fontsize + " 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=NotoSerif-Regular "
+ "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=NotoSerif-Regular "
+ "fontsize=" + fontsize + " 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=NotoSerif-Regular "
+ "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);
}
}