/*
* ConcourseConnect
* Copyright 2009 Concursive Corporation
* http://www.concursive.com
*
* This file is part of ConcourseConnect, an open source social business
* software and community platform.
*
* Concursive ConcourseConnect is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, version 3 of the License.
*
* Under the terms of the GNU Affero General Public License you must release the
* complete source code for any application that uses any part of ConcourseConnect
* (system header files and libraries used by the operating system are excluded).
* These terms must be included in any work that has ConcourseConnect components.
* If you are developing and distributing open source applications under the
* GNU Affero General Public License, then you are free to use ConcourseConnect
* under the GNU Affero General Public License.
*
* If you are deploying a web site in which users interact with any portion of
* ConcourseConnect over a network, the complete source code changes must be made
* available. For example, include a link to the source archive directly from
* your web site.
*
* For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
* products, and do not license and distribute their source code under the GNU
* Affero General Public License, Concursive provides a flexible commercial
* license.
*
* To anyone in doubt, we recommend the commercial license. Our commercial license
* is competitively priced and will eliminate any confusion about how
* ConcourseConnect can be used and distributed.
*
* ConcourseConnect is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>.
*
* Attribution Notice: ConcourseConnect is an Original Work of software created
* by Concursive Corporation
*/
package com.concursive.connect.web.modules.wiki.utils;
import com.concursive.commons.date.DateUtils;
import com.concursive.commons.db.DatabaseUtils;
import com.concursive.commons.phone.PhoneNumberBean;
import com.concursive.commons.phone.PhoneNumberUtils;
import com.concursive.commons.text.StringUtils;
import com.concursive.connect.Constants;
import com.concursive.connect.web.modules.documents.dao.FileItem;
import com.concursive.connect.web.modules.documents.dao.FileItemList;
import com.concursive.connect.web.modules.documents.dao.ImageInfo;
import com.concursive.connect.web.modules.profile.dao.Project;
import com.concursive.connect.web.modules.wiki.beans.WikiExportBean;
import com.concursive.connect.web.modules.wiki.dao.*;
import com.lowagie.text.*;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.List;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.awt.*;
import java.io.*;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* Class to manipulate wiki objects
*
* @author matt rajkowski
* @version $Id$
* @created September 26, 2006
*/
public class WikiPDFUtils {
private static final Log LOG = LogFactory.getLog(WikiPDFUtils.class);
public static String CRLF = "\n";
public final static String fs = System.getProperty("file.separator");
// Define styles
public final static Font titleFont = FontFactory.getFont(FontFactory.TIMES, 24, Font.NORMAL, new Color(0, 0, 0));
public final static Font titleSmallFont = FontFactory.getFont(FontFactory.TIMES, 12, Font.NORMAL, new Color(0, 0, 0));
public final static Font wikiFont = FontFactory.getFont(FontFactory.HELVETICA, 24, Font.ITALIC, new Color(0, 0, 0));
public final static Font sectionFont = FontFactory.getFont(FontFactory.HELVETICA, 20, Font.NORMAL, new Color(0, 0, 0));
public final static Font section2Font = FontFactory.getFont(FontFactory.HELVETICA, 18, Font.NORMAL, new Color(0, 0, 0));
public final static Font section3Font = FontFactory.getFont(FontFactory.HELVETICA, 16, Font.NORMAL, new Color(0, 0, 0));
public final static Font codeFont = FontFactory.getFont(FontFactory.COURIER, 10, Font.NORMAL, new Color(0, 0, 0));
public static boolean functionsToUse(Project project, Wiki wiki, File file, HashMap imageList) throws Exception {
// remove this.
Document document = new Document();
// Parse each line to see if paragraph, table, section heading, list
// Parse within the line to see if bold, italic, links, code fragment, images
// Define styles
Font exampleFont = FontFactory.getFont(FontFactory.HELVETICA, 24, Font.NORMAL, new Color(255, 0, 0));
// Add title and paragraphs
// TODO: Construct a title page... with logo
Paragraph title = new Paragraph(wiki.getSubject(), exampleFont);
//Chunk title = new Chunk(wiki.getSubject());
//title.setBackground(new Color(0xFF, 0xDE, 0xAD));
////title.setUnderline(new Color(0xFF, 0x00, 0x00), 3.0f, 0.0f, -4.0f, 0.0f, PdfContentByte.LINE_CAP_ROUND);
//title.setUnderline(0.2f, -2f);
LOG.debug("document.add(title)");
document.add(title);
// Link from
// a paragraph with a local goto
//Paragraph p1 = new Paragraph("We will do something special with this paragraph. If you click on ", FontFactory.getFont(FontFactory.HELVETICA, 12));
//p1.add(new Chunk("this word", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 255))).setLocalGoto("test"));
//p1.add(" you will automatically jump to another location in this document.");
// a paragraph with a local destination
//Paragraph p3 = new Paragraph("This paragraph contains a ");
//p3.add(new Chunk("local destination", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 255, 0))).setLocalDestination("test"));
// Ext A Href
//Paragraph paragraph = new Paragraph("Please visit my ");
//Anchor anchor1 = new Anchor("website (external reference)", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, new Color(0, 0, 255)));
//anchor1.setReference("http://www.lowagie.com/iText/");
//anchor1.setName("top");
//paragraph.add(anchor1);
//Image jpg = Image.getInstance("otsoe.jpg");
//document.add(jpg);
return false;
}
public static boolean exportToFile(WikiPDFContext context, Connection db) throws Exception {
LOG.debug("exportToFile-> begin");
// Context Objects
Wiki wiki = context.getWiki();
Project project = context.getProject();
File file = context.getFile();
WikiExportBean exportBean = context.getExportBean();
// Determine the content to parse
String content = wiki.getContent();
if (content == null) {
return false;
}
// Create a pdf
Document document = new Document(PageSize.LETTER);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// Meta data
document.addTitle(project.getTitle());
document.addSubject(wiki.getSubject());
document.addCreator("Concursive ConcourseConnect");
document.addAuthor("Wiki Contributor");
//writer.setPageEvent(new PageNumbersWatermark());
if (!exportBean.getIncludeTitle()) {
boolean hasTitle = StringUtils.hasText(wiki.getSubject());
HeaderFooter pageFooter = new HeaderFooter(new Phrase(project.getTitle() + (hasTitle ? ": " + wiki.getSubject() : "") + " - page "), new Phrase(""));
pageFooter.setAlignment(Element.ALIGN_CENTER);
document.setFooter(pageFooter);
}
document.open();
if (exportBean.getIncludeTitle()) {
//HeaderFooter pageHeader = new HeaderFooter(new Phrase(project.getTitle()), false);
//document.setHeader(pageHeader);
boolean hasTitle = (StringUtils.hasText(wiki.getSubject()));
HeaderFooter pageFooter = new HeaderFooter(new Phrase(project.getTitle() + (hasTitle ? ": " + wiki.getSubject() : "") + " - page "), new Phrase(""));
pageFooter.setAlignment(Element.ALIGN_CENTER);
document.setFooter(pageFooter);
// Draw a title page
Rectangle rectangle = new Rectangle(600, 30);
rectangle.setBackgroundColor(new Color(100, 100, 100));
LOG.debug("document.add(rectangle)");
document.add(rectangle);
document.add(new Paragraph(project.getTitle(), titleFont));
if (!"".equals(wiki.getSubject())) {
document.add(new Paragraph(wiki.getSubject(), titleFont));
}
document.add(Chunk.NEWLINE);
document.add(new Paragraph("Last Modified: " + wiki.getModified(), titleSmallFont));
document.newPage();
}
ArrayList<Integer> wikiListDone = new ArrayList<Integer>();
appendWiki(context, context.getWiki(), document, db, wikiListDone);
// Close everything
document.close();
writer.close();
LOG.debug("exportToFile-> finished");
return true;
}
private static void appendWiki(WikiPDFContext context, Wiki currentWiki, Document document, Connection db, ArrayList<Integer> wikiListDone) throws SQLException {
LOG.debug("appendWiki-> " + currentWiki.getSubject());
// Context Objects
Project project = context.getProject();
WikiExportBean exportBean = context.getExportBean();
// Track the wikis to get appended to the output
ArrayList<Integer> wikiListTodo = new ArrayList<Integer>();
try {
// Output the name of the Wiki
boolean hasTitle = StringUtils.hasText(currentWiki.getSubject());
if (hasTitle) {
Anchor wikiAnchor = new Anchor(currentWiki.getSubject(), wikiFont);
wikiAnchor.setName(currentWiki.getSubject().toLowerCase());
LOG.debug("Add anchor: " + currentWiki.getSubject().toLowerCase());
document.add(wikiAnchor);
LOG.debug("document.add(wikiAnchor)");
}
// Output the wiki content
parseContent(context, currentWiki, currentWiki.getContent(), document, null, db, wikiListTodo, wikiListDone, 0f);
wikiListDone.add(currentWiki.getId());
// See if any linked wikis should be appended
if (exportBean.getFollowLinks() && wikiListTodo.size() > 0) {
Iterator i = wikiListTodo.iterator();
while (i.hasNext()) {
Integer id = (Integer) i.next();
if (id > -1 && !wikiListDone.contains(id)) {
Wiki subwiki = new Wiki(db, id);
document.add(Chunk.NEXTPAGE);
appendWiki(context, subwiki, document, db, wikiListDone);
}
//i.remove();
}
}
} catch (Exception e) {
LOG.error("appendWiki", e);
}
}
private static boolean parseContent(WikiPDFContext context, Wiki wiki, String content, Document document, PdfPCell cell, Connection db, ArrayList<Integer> wikiListTodo, ArrayList<Integer> wikiListDone, float cellWidth) throws Exception {
LOG.debug("PARSING CONTENT: " + content);
// Parse the wiki page
int lastIndent = 0;
boolean preTag = false;
boolean pre = false;
boolean code = false;
boolean header = true;
try {
BufferedReader in = new BufferedReader(new StringReader(content));
String line = null;
ArrayList unorderedParents = null;
List thisList = null;
Paragraph codeParagraph = null;
while ((line = in.readLine()) != null) {
// Tables
if (line.startsWith("|")) {
// @todo Close all ordered lists, unordered lists, and paragraphs
// Parse the table
line = parseTable(context, wiki, line, document, db, wikiListTodo, wikiListDone, in);
if (line == null) {
continue;
}
}
// Forms
if (line.startsWith("[{form")) {
// @todo close any lists or paragraphs
// parseForm operates over all the lines that make up the form,
// it will have to look forward so it returns an unparsed line
parseForm(context, db, in, line, document, cell);
continue;
}
// Handle code blocks
// @todo chunk the content similar to WikiToHTMLUtils otherwise inaccurate
if (line.startsWith("<pre>") || line.startsWith("<code>")) {
if (!code && !pre) {
if (line.startsWith("<pre>")) {
preTag = true;
pre = true;
} else if (line.startsWith("<code>")) {
code = true;
}
codeParagraph = new Paragraph("", codeFont);
codeParagraph.setSpacingBefore(10);
if (pre && line.length() > ("<pre>").length()) {
int endOfLine = line.length();
if (line.endsWith("</pre>")) {
endOfLine = line.indexOf("</pre>");
}
// This line has some extra content that needs to be added
codeParagraph.add(new Chunk(line.substring(line.indexOf("<pre>") + 5, endOfLine) + Chunk.NEWLINE));
}
if (code && line.length() > ("<code>").length()) {
int endOfLine = line.length();
if (line.endsWith("</code>")) {
endOfLine = line.indexOf("</code>");
}
// This line has some extra content that needs to be added
codeParagraph.add(new Chunk(line.substring(line.indexOf("<code>") + 6, endOfLine) + Chunk.NEWLINE));
}
// See if this is a single line block
if (preTag && line.endsWith("</pre>")) {
// This is a single line block, so finish processing it
} else if (code && line.endsWith("</code>")) {
// This is a single line block, so finish processing it
} else {
// There are more lines to process, so do that
continue;
}
}
}
if (line.startsWith("</code>") || line.endsWith("</code>")) {
if (code) {
code = false;
if (line.indexOf("</code>") > 0 && !line.startsWith("<code>")) {
// This line has some extra content that needs to be added
codeParagraph.add(new Chunk(line.substring(0, line.indexOf("</code>")) + Chunk.NEWLINE));
}
// Draw the final content
PdfPTable codeTable = new PdfPTable(1);
codeTable.setWidthPercentage(100);
codeTable.setSpacingBefore(10);
PdfPCell codeCell = new PdfPCell(codeParagraph);
codeCell.setPadding(20);
codeCell.setBorderColor(new Color(100, 100, 100));
codeCell.setBackgroundColor(new Color(200, 200, 200));
// codeCell.setNoWrap(true);
codeTable.addCell(codeCell);
LOG.debug("document.add(codeTable)");
document.add(codeTable);
continue;
}
}
if (line.startsWith("</pre>") || line.endsWith("</pre>")) {
if (pre) {
preTag = false;
pre = false;
if (line.indexOf("</pre>") > 0 && !line.startsWith("<pre>")) {
// This line has some extra content that needs to be added
codeParagraph.add(new Chunk(line.substring(0, line.indexOf("</pre>")) + Chunk.NEWLINE));
}
// Draw the final content
PdfPTable codeTable = new PdfPTable(1);
codeTable.setWidthPercentage(100);
codeTable.setSpacingBefore(10);
PdfPCell codeCell = new PdfPCell(codeParagraph);
codeCell.setPadding(20);
codeCell.setBorderColor(new Color(100, 100, 100));
codeCell.setBackgroundColor(new Color(200, 200, 200));
// codeCell.setNoWrap(true);
codeTable.addCell(codeCell);
LOG.debug("document.add(codeTable)");
document.add(codeTable);
continue;
}
}
if (code || preTag) {
// Append the chunk
codeParagraph.add(new Chunk(line + Chunk.NEWLINE));
continue;
}
// Section
if (line.startsWith("=") && line.endsWith("=")) {
// @todo close any open lists or paragraphs
int hCount = parseHCount(line, "=");
if (hCount > 6) {
hCount = 6;
}
String section = line.substring(line.indexOf("=") + hCount, line.lastIndexOf("=") - hCount + 1);
header = true;
context.foundHeader(hCount);
String headerAnchor = null;
// Store the h2's with anchors for table of contents or index
if (hCount == 2) {
headerAnchor = StringUtils.toHtmlValue(section).replace(" ", "_");
context.getHeaderAnchors().put(headerAnchor, section);
}
if (hCount == 3) {
Paragraph title = new Paragraph(section.trim(), section2Font);
title.setSpacingBefore(10);
if (cell != null) {
LOG.debug("phrase.add(title)");
cell.addElement(title);
} else {
LOG.debug("document.add(title)");
document.add(title);
}
} else if (hCount > 3) {
Paragraph title = new Paragraph(section.trim(), section3Font);
title.setSpacingBefore(10);
if (cell != null) {
LOG.debug("phrase.add(title)");
cell.addElement(title);
} else {
LOG.debug("document.add(title)");
document.add(title);
}
} else {
Paragraph title = new Paragraph(section.trim(), sectionFont);
title.setSpacingBefore(10);
if (cell != null) {
LOG.debug("phrase.add(title)");
cell.addElement(title);
} else {
LOG.debug("document.add(title)");
document.add(title);
}
}
continue;
}
if (header) {
header = false;
if (line.trim().equals("")) {
// remove the extra space a user may leave after a header
continue;
}
}
// Determine if this is a bulleted list
if (line.startsWith("*") || line.startsWith("#")) {
// Initialize the list array
if (unorderedParents == null) {
unorderedParents = new ArrayList();
// if (phrase != null) {
// LOG.debug("phrase.add(new Paragraph(Chunk.NEWLINE))");
// phrase.add(new Paragraph(Chunk.NEWLINE));
// } else {
// LOG.debug("document.add(new Paragraph(Chunk.NEWLINE))");
// document.add(new Paragraph(Chunk.NEWLINE));
// }
}
// Get the indent level
boolean ol = line.startsWith("#");
int hCount = WikiPDFUtils.parseHCount(line, ol ? "#" : "*");
// Determine a shift in the tree
if (lastIndent == 0) {
if (ol) {
thisList = new List(ol, 20);
} else {
thisList = new List(ol, 10);
thisList.setListSymbol(new Chunk("\u2022", FontFactory.getFont(FontFactory.HELVETICA, 12)));
}
thisList.setIndentationLeft(36);
thisList.setIndentationRight(36);
unorderedParents.add(thisList);
} else {
if (hCount > lastIndent) {
if (ol) {
thisList = new List(ol, 20);
} else {
thisList = new List(ol, 10);
thisList.setListSymbol(new Chunk("\u2022", FontFactory.getFont(FontFactory.HELVETICA, 12)));
}
thisList.setIndentationLeft(36);
thisList.setIndentationRight(36);
((List) unorderedParents.get(unorderedParents.size() - 1)).add(thisList);
unorderedParents.add(thisList);
} else if (hCount < lastIndent) {
unorderedParents.remove(unorderedParents.size() - 1);
thisList = (List) unorderedParents.get(unorderedParents.size() - 1);
}
}
lastIndent = hCount;
// Append the item...
Paragraph thisItem = new Paragraph();
parseLine(context, line.substring(hCount).trim(), thisItem, db, wikiListTodo, cellWidth, cell);
thisList.add(new ListItem(thisItem));
continue;
}
// List is finished, so append it to the document before working on
// other paragraphs
if (unorderedParents != null) {
if (cell != null) {
LOG.debug("phrase.add((List) unorderedParents.get(0))");
cell.addElement((List) unorderedParents.get(0));
} else {
LOG.debug("document.add((List) unorderedParents.get(0))");
document.add((List) unorderedParents.get(0));
}
unorderedParents = null;
thisList = null;
lastIndent = 0;
}
// Otherwise a simple paragraph
Paragraph paragraph = new Paragraph();
parseLine(context, line, paragraph, db, wikiListTodo, cellWidth, cell);
if (cell != null) {
LOG.debug("phrase.add(paragraph)");
if (cell.getHorizontalAlignment() == Element.ALIGN_CENTER) {
paragraph.setAlignment(Element.ALIGN_CENTER);
}
paragraph.setSpacingBefore(5);
cell.addElement(paragraph);
} else {
LOG.debug("document.add(paragraph)");
paragraph.setSpacingBefore(5);
document.add(paragraph);
}
}
// Cleanup now that the lines are finished
if (pre || code) {
PdfPTable codeTable = new PdfPTable(1);
codeTable.setWidthPercentage(100);
codeTable.setSpacingBefore(10);
PdfPCell codeCell = new PdfPCell(codeParagraph);
codeCell.setPadding(20);
codeCell.setBorderColor(new Color(100, 100, 100));
codeCell.setBackgroundColor(new Color(200, 200, 200));
// codeCell.setNoWrap(true);
codeTable.addCell(codeCell);
LOG.debug("document.add(codeTable)");
document.add(codeTable);
}
if (unorderedParents != null) {
if (cell != null) {
LOG.debug("phrase.add((List) unorderedParents.get(0))");
cell.addElement((List) unorderedParents.get(0));
} else {
LOG.debug("document.add((List) unorderedParents.get(0))");
document.add((List) unorderedParents.get(0));
}
}
in.close();
} catch (Exception e) {
LOG.error("parseContent", e);
}
return true;
}
protected static int parseHCount(String line, String id) {
int count = 0;
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (id.equals(String.valueOf(c))) {
++count;
} else {
return count;
}
}
return 1;
}
private static String parseTable(WikiPDFContext context, Wiki wiki, String line, Document document, Connection db, ArrayList<Integer> wikiListTodo, ArrayList<Integer> wikiListDone, BufferedReader in) throws Exception {
if (line == null) {
return null;
}
PdfPTable pdfTable = null;
int columnCount = 0;
int rowCount = 0;
// Keep track of the table's custom styles
HashMap<Integer, String> cStyle = new HashMap<Integer, String>();
while (line != null && (line.startsWith("|") || line.startsWith("!"))) {
// Build a complete line
String lineToParse = line;
while (!line.endsWith("|")) {
line = in.readLine();
if (line == null) {
// there is an error in the line to process
return null;
}
if (line.startsWith("!")) {
lineToParse += CRLF + line.substring(1);
}
}
line = lineToParse;
// Determine if the row can output
boolean canOutput = true;
++rowCount;
String cellType = null;
Scanner sc = null;
if (line.startsWith("||") && line.endsWith("||")) {
cellType = "th";
sc = new Scanner(line).useDelimiter("[|][|]");
// sc = new Scanner(line.substring(2, line.length() - 2)).useDelimiter("[|][|]");
} else if (line.startsWith("|")) {
cellType = "td";
sc = new Scanner(line.substring(1, line.length() - 1)).useDelimiter("\\|(?=[^\\]]*(?:\\[|$))");
}
if (sc != null) {
if (rowCount == 1) {
// Count the columns, get the specified widths too...
while (sc.hasNext()) {
++columnCount;
sc.next();
}
// Reset the scanner now that the columns have been counted
if (line.startsWith("||") && line.endsWith("||")) {
sc = new Scanner(line).useDelimiter("[|][|]");
} else if (line.startsWith("|")) {
sc = new Scanner(line.substring(1, line.length() - 1)).useDelimiter("\\|(?=[^\\]]*(?:\\[|$))");
}
// Start the table
pdfTable = new PdfPTable(columnCount);
//pdfTable.setWidthPercentage(100);
pdfTable.setHorizontalAlignment(Element.ALIGN_LEFT);
pdfTable.setSpacingBefore(10);
pdfTable.setWidthPercentage(100);
pdfTable.setKeepTogether(true);
}
// Determine the column span
int colSpan = 1;
// Determine the cell being output
int cellCount = 0;
while (sc.hasNext()) {
String cellData = sc.next();
if (cellData.length() == 0) {
++colSpan;
continue;
}
// Track the cell count being output
++cellCount;
if (rowCount == 1) {
// Parse and validate the style input
LOG.debug("Checking style value: " + cellData);
if (cellData.startsWith("{") && cellData.endsWith("}")) {
String[] style = cellData.substring(1, cellData.length() - 1).split(":");
String attribute = style[0].trim();
String value = style[1].trim();
// Determine the width of each column and store it
if ("width".equals(attribute)) {
// Validate the width style
if (StringUtils.hasAllowedOnly("0123456789%.", value)) {
cStyle.put(cellCount, attribute + ": " + value + ";");
}
} else {
LOG.debug("Unsupported style: " + cellData);
}
canOutput = false;
}
}
// Output the header
if (canOutput) {
PdfPCell cell = new PdfPCell();
cell.setPadding(10);
cell.setBorderColor(new Color(100, 100, 100));
if ("th".equals(cellType)) {
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setBackgroundColor(new Color(0xC0, 0xC0, 0xC0));
}
if (colSpan > 1) {
cell.setColspan(colSpan);
}
// Output the data
if (" ".equals(cellData) || " ".equals(cellData)) {
// Put a blank space in blank cells for output consistency
cell.addElement(new Chunk(" "));
LOG.debug(" OUTPUTTING A BLANK");
} else {
// Output the cell as a complete wiki
float cellWidth = (100.0f / columnCount);
parseContent(context, wiki, cellData, document, cell, db, wikiListTodo, wikiListDone, cellWidth);
LOG.debug(" OUTPUTTING CONTENT");
}
pdfTable.addCell(cell);
}
}
}
// read another line to see if it's part of the table
line = in.readLine();
}
if (pdfTable != null) {
LOG.debug("document.add(pdfTable)");
document.add(pdfTable);
// document.add(Chunk.NEWLINE);
}
return line;
}
protected static String parseForm(WikiPDFContext context, Connection db, BufferedReader in, String line, Document document, PdfPCell cell) throws Exception {
if (line == null) {
return line;
}
CustomForm form = WikiToHTMLUtils.retrieveForm(in, line);
LOG.debug("parseForm");
for (CustomFormGroup group : form) {
LOG.debug(" group...");
// Start the table
PdfPTable pdfTable = new PdfPTable(2);
pdfTable.setHorizontalAlignment(Element.ALIGN_LEFT);
pdfTable.setSpacingBefore(10);
// pdfTable.setWidthPercentage(100);
pdfTable.setKeepTogether(true);
if (group.getDisplay() && StringUtils.hasText(group.getName())) {
// output the 1st row with a colspan of 2
if (StringUtils.hasText(group.getName())) {
Paragraph groupParagraph = new Paragraph(group.getName());
PdfPCell groupCell = new PdfPCell(groupParagraph);
groupCell.setHorizontalAlignment(Element.ALIGN_CENTER);
groupCell.setColspan(2);
groupCell.setPadding(20);
groupCell.setBorderColor(new Color(100, 100, 100));
groupCell.setBackgroundColor(new Color(200, 200, 200));
groupCell.setNoWrap(true);
pdfTable.addCell(groupCell);
}
}
for (CustomFormField field : group) {
LOG.debug(" field...");
if (field.hasValue()) {
// output the row (2 columns: label, value)
Paragraph fieldLabelParagraph = new Paragraph(field.getLabel());
PdfPCell fieldLabelCell = new PdfPCell(fieldLabelParagraph);
fieldLabelCell.setPadding(20);
fieldLabelCell.setBorderColor(new Color(100, 100, 100));
// fieldLabelCell.setNoWrap(true);
pdfTable.addCell(fieldLabelCell);
Paragraph fieldValueParagraph = new Paragraph(getFieldValue(context, field));
PdfPCell fieldValueCell = new PdfPCell(fieldValueParagraph);
fieldValueCell.setPadding(20);
fieldValueCell.setBorderColor(new Color(100, 100, 100));
// fieldValueCell.setNoWrap(true);
pdfTable.addCell(fieldValueCell);
}
}
LOG.debug("document.add(pdfTable)");
document.add(pdfTable);
}
return null;
}
private static boolean parseLine(WikiPDFContext context, String line, Paragraph main, Connection db, ArrayList<Integer> wikiListTodo, float cellWidth, PdfPCell cell) throws Exception {
LOG.debug("PARSING LINE: " + line);
// Context Objects
Project project = context.getProject();
WikiExportBean exportBean = context.getExportBean();
HashMap<String, ImageInfo> imageList = context.getImageList();
String fileLibrary = context.getFileLibrary();
boolean needsCRLF = true;
boolean bold = false;
boolean italic = false;
boolean bolditalic = false;
StringBuffer subject = new StringBuffer();
StringBuffer data = new StringBuffer();
int linkL = 0;
int linkR = 0;
int attr = 0;
int underlineAttr = 0;
// parse characters
for (int i = 0; i < line.length(); i++) {
char c1 = line.charAt(i);
String c = String.valueOf(c1);
// False attr/links
if (!"'".equals(c) && attr == 1) {
data.append("'").append(c);
attr = 0;
continue;
}
if (!"_".equals(c) && underlineAttr == 1) {
data.append("_").append(c);
underlineAttr = 0;
continue;
}
if (!"[".equals(c) && linkL == 1) {
data.append("[").append(c);
linkL = 0;
continue;
}
if (!"]".equals(c) && linkR == 1) {
data.append("]").append(c);
linkR = 0;
continue;
}
// Links
if ("[".equals(c)) {
++linkL;
continue;
}
if ("]".equals(c)) {
++linkR;
if (linkL == 2 && linkR == 2) {
LOG.debug("main.add(new Chunk(data.toString()))");
main.add(new Chunk(data.toString()));
data.setLength(0);
// Different type of links...
String link = subject.toString().trim();
if (link.startsWith("Image:") || link.startsWith("image:")) {
// @note From WikiImageLink.java
String image = link.substring(6);
String title = null;
int frame = -1;
int thumbnail = -1;
int left = -1;
int right = -1;
int center = -1;
int none = -1;
int imageLink = -1;
int alt = -1;
if (image.indexOf("|") > 0) {
// the image is first
image = image.substring(0, image.indexOf("|"));
// any directives are next
frame = link.indexOf("|frame");
thumbnail = link.indexOf("|thumb");
left = link.indexOf("|left");
right = link.indexOf("|right");
center = link.indexOf("|center");
none = link.indexOf("|none");
imageLink = link.indexOf("|link=");
alt = link.indexOf("|alt=");
// the optional caption is last
int last = link.lastIndexOf("|");
if (last > frame &&
last > thumbnail &&
last > left &&
last > right &&
last > center &&
last > none &&
last > imageLink &&
last > alt) {
title = link.substring(last + 1);
}
}
//A picture, including alternate text:
//[[Image:Wiki.png|The logo for this Wiki]]
//You can put the image in a frame with a caption:
//[[Image:Wiki.png|frame|The logo for this Wiki]]
// Access some image details
int width = 0;
int height = 0;
ImageInfo imageInfo = imageList.get(image + (thumbnail > -1 ? "-TH" : ""));
if (imageInfo != null) {
width = imageInfo.getWidth();
height = imageInfo.getHeight();
}
// Alt
String altText = null;
if (alt > -1) {
int startIndex = alt + 4;
int endIndex = link.indexOf("|", startIndex);
if (endIndex == -1) {
endIndex = link.length();
}
altText = link.substring(startIndex, endIndex);
}
// Looks like the image needs a link (which is always last)
String url = null;
if (imageLink > -1) {
// Get the entered link
int startIndex = imageLink + 6;
int endIndex = link.length();
String href = link.substring(startIndex, endIndex);
// Treat as a wikiLink to validate and to create a proper url
WikiLink wikiLink = new WikiLink(project.getId(), (altText != null ? href + " " + altText : href));
url = wikiLink.getUrl("");
if (!url.startsWith("http://") && !url.startsWith("https://")) {
// @todo Use a local link
// @todo Use an external link
}
}
// Determine if local or external image
if ((image.startsWith("http://") || image.startsWith("https://"))) {
// retrieve external image
String imageUrl = null;
try {
URL imageURL = new URL(image);
imageUrl = image;
} catch (Exception e) {
}
} else {
// local image
try {
// @todo image alignment and links
Image thisImage = Image.getInstance(getImageFilename(db, fileLibrary, project, image, (thumbnail > -1)));
LOG.debug("Drawing image for area: " + cellWidth);
if (cellWidth > 0f) {
LOG.debug(" Image is embedded in cell");
// Guess the size of the cell
float cellPixels = (500f * (cellWidth / 100f));
if (cellPixels > 0f && (float) width > cellPixels) {
// Shrink image to fit the cell
LOG.debug(" Scaling to fit");
thisImage.scaleToFit(cellPixels, 500f);
} else {
// Align image to left instead of scaling it to fit
thisImage.setAlignment(Image.LEFT);
}
LOG.debug("cell.addElement(thisImage)");
cell.addElement(thisImage);
} else {
LOG.debug(" Image is inline");
if (width > 500) {
LOG.debug(" Scaling to fit");
thisImage.scaleToFit(500f, 500f);
}
LOG.debug("main.add(thisImage)");
main.add(thisImage);
}
// thisImage.setAlignment();
// thisImage.Alignment = Image.TEXTWRAP | Image.ALIGN_RIGHT;
// main.add(thisImage);
} catch (FileNotFoundException fnfe) {
LOG.warn("WikiPDFUtils-> Image was not found in the FileLibrary (" + getImageFilename(db, fileLibrary, project, image, (thumbnail > -1)) + ")... will continue.");
}
}
/*
if (frame > -1 || thumbnail > -1) {
sb.append("</div>");
sb.append("<div id=\"caption\" style=\"margin-bottom: 5px; margin-left: 5px; margin-right: 5px; text-align: left;\">");
}
if (thumbnail > -1) {
sb.append("<div style=\"float:right\"><a target=\"_blank\" href=\"ProjectManagementWiki.do?command=Img&pid=" + wiki.getProjectId() + "&subject=" + StringUtils.replace(StringUtils.jsEscape(image), "%20", "+") + "\"><img src=\"images/magnify-clip.png\" width=\"15\" height=\"11\" alt=\"Enlarge\" border=\"0\" /></a></div>");
}
if (frame > -1 || thumbnail > -1) {
if (title != null) {
sb.append(StringUtils.toHtml(title));
}
sb.append(
" </div>\n" +
"</div>");
}
*/
/*
if (none > -1) {
sb.append("<br clear=\"all\">");
}
*/
if (i + 1 == line.length() && (right > -1 || left > -1) || none > -1) {
needsCRLF = false;
}
} else {
// This is most likely a Wiki link
String title = subject.toString().trim();
if (link.indexOf("|") > 0) {
link = link.substring(0, link.indexOf("|")).trim();
title = title.substring(title.indexOf("|") + 1);
}
if (link.indexOf("http://") > -1 || link.indexOf("https://") > -1) {
String label = link;
if (link.indexOf(" ") > 0) {
label = link.substring(link.indexOf(" ") + 1);
link = link.substring(0, link.indexOf(" "));
}
Anchor anchor1 = new Anchor(label, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, new Color(0, 0, 255)));
anchor1.setReference(link);
anchor1.setName("top");
main.add(anchor1);
} else {
// Place a wiki link
if (exportBean.getFollowLinks()) {
// See if target link exists before creating a link to it
int linkedWikiId = -1;
if (StringUtils.hasText(title) && !title.startsWith("|")) {
Wiki subwiki = WikiList.queryBySubject(db, title, project.getId());
if (subwiki.getId() > -1) {
linkedWikiId = subwiki.getId();
}
}
// Display the linked item
if (linkedWikiId > -1) {
// Display as an anchor
Anchor linkToWiki = new Anchor(title, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 255)));
linkToWiki.setReference("#" + title.toLowerCase());
LOG.debug("Link to: #" + title.toLowerCase());
main.add(linkToWiki);
LOG.debug(" main.add(linkToWiki)");
// main.add(new Chunk(title, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 255))).setLocalGoto(link));
// Add this wiki to the to do list...
if (!wikiListTodo.contains(linkedWikiId)) {
wikiListTodo.add(linkedWikiId);
}
} else {
// Display without the link
main.add(new Chunk(title, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 255))));
}
} else {
// Not following links, so display... perhaps as an external link someday
main.add(new Chunk(title, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 255))));
}
}
}
subject.setLength(0);
linkL = 0;
linkR = 0;
}
continue;
}
if (!"[".equals(c) && linkL == 2 && !"]".equals(c)) {
subject.append(c);
continue;
}
// Attribute properties
if ("'".equals(c)) {
++attr;
continue;
}
if (!"'".equals(c) && attr > 1) {
if (attr == 2) {
if (!italic) {
main.add(new Chunk(data.toString()));
data.setLength(0);
data.append(c);
italic = true;
} else {
data.append(c);
main.add(new Chunk(data.toString(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.ITALIC, new Color(0, 0, 0))));
data.setLength(0);
italic = false;
}
attr = 0;
continue;
}
if (attr == 3) {
if (!bold) {
main.add(new Chunk(data.toString()));
data.setLength(0);
data.append(c);
bold = true;
} else {
data.append(c);
main.add(new Chunk(data.toString(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, new Color(0, 0, 0))));
data.setLength(0);
bold = false;
}
attr = 0;
continue;
}
if (attr == 5) {
if (!bolditalic) {
main.add(new Chunk(data.toString()));
data.setLength(0);
data.append(c);
bolditalic = true;
} else {
data.append(c);
main.add(new Chunk(data.toString(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLDITALIC, new Color(0, 0, 0))));
data.setLength(0);
bolditalic = false;
}
attr = 0;
continue;
}
}
data.append(c);
}
for (int x = 0; x < linkR; x++) {
data.append("]");
}
for (int x = 0; x < linkL; x++) {
data.append("[");
}
if (attr == 1) {
data.append("'");
}
if (italic) {
main.add(new Chunk(data.toString(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.ITALIC, new Color(0, 0, 0))));
} else if (bold) {
main.add(new Chunk(data.toString(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, new Color(0, 0, 0))));
} else if (bolditalic) {
main.add(new Chunk(data.toString(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLDITALIC, new Color(0, 0, 0))));
} else {
main.add(new Chunk(data.toString()));
}
data.setLength(0);
return needsCRLF;
}
public static String getFieldValue(WikiPDFContext context, CustomFormField field) {
// Return output based on type
switch (field.getType()) {
case CustomFormField.TEXTAREA:
return StringUtils.replace(field.getValue(), "^", CRLF);
case CustomFormField.SELECT:
return field.getValue();
case CustomFormField.CHECKBOX:
if ("true".equals(field.getValue())) {
return "Yes";
} else {
return "No";
}
case CustomFormField.CALENDAR:
String calendarValue = field.getValue();
if (StringUtils.hasText(calendarValue)) {
try {
String convertedDate = DateUtils.getUserToServerDateTimeString(null, DateFormat.SHORT, DateFormat.LONG, field.getValue());
Timestamp timestamp = DatabaseUtils.parseTimestamp(convertedDate);
Locale locale = Locale.getDefault();
int dateFormat = DateFormat.SHORT;
SimpleDateFormat dateFormatter = (SimpleDateFormat) SimpleDateFormat.getDateInstance(dateFormat, locale);
calendarValue = dateFormatter.format(timestamp);
} catch (Exception e) {
LOG.error(e);
}
}
return calendarValue;
case CustomFormField.PERCENT:
return field.getValue() + "%";
case CustomFormField.INTEGER:
// Determine the value to display in the field
String integerValue = StringUtils.toHtmlValue(field.getValue());
if (StringUtils.hasText(integerValue)) {
try {
NumberFormat formatter = NumberFormat.getInstance();
integerValue = formatter.format(StringUtils.getIntegerNumber(field.getValue()));
} catch (Exception e) {
LOG.warn("Could not integer format: " + field.getValue());
}
}
return integerValue;
case CustomFormField.FLOAT:
// Determine the value to display in the field
String decimalValue = field.getValue();
if (StringUtils.hasText(decimalValue)) {
try {
NumberFormat formatter = NumberFormat.getInstance();
decimalValue = formatter.format(StringUtils.getDoubleNumber(field.getValue()));
} catch (Exception e) {
LOG.warn("Could not decimal format: " + field.getValue());
}
}
return decimalValue;
case CustomFormField.CURRENCY:
// Use a currency for formatting
String currencyCode = field.getValueCurrency();
if (currencyCode == null) {
currencyCode = field.getCurrency();
}
if (!StringUtils.hasText(currencyCode)) {
currencyCode = "USD";
}
try {
NumberFormat formatter = NumberFormat.getCurrencyInstance();
if (currencyCode != null) {
Currency currency = Currency.getInstance(currencyCode);
formatter.setCurrency(currency);
}
return (formatter.format(StringUtils.getDoubleNumber(field.getValue())));
} catch (Exception e) {
LOG.error(e.getMessage());
}
return field.getValue();
case CustomFormField.EMAIL:
return field.getValue();
case CustomFormField.PHONE:
PhoneNumberBean phone = new PhoneNumberBean();
phone.setNumber(field.getValue());
PhoneNumberUtils.format(phone, Locale.getDefault());
return phone.toString();
case CustomFormField.URL:
String value = StringUtils.toHtmlValue(field.getValue());
if (StringUtils.hasText(value)) {
if (!value.contains("://")) {
value = "http://" + value;
}
if (value.contains("://")) {
return value;
}
}
return value;
case CustomFormField.IMAGE:
String image = field.getValue();
if (StringUtils.hasText(image)) {
return "WikiImage:" + image;
}
return image;
default:
return field.getValue();
}
}
private static String getImageFilename(Connection db, String fileLibrary, Project project, String subject, boolean showThumbnail) throws SQLException {
// Load the file for download
FileItemList fileItemList = new FileItemList();
fileItemList.setLinkModuleId(Constants.PROJECT_WIKI_FILES);
fileItemList.setLinkItemId(project.getId());
fileItemList.setFilename(subject);
fileItemList.buildList(db);
FileItem fileItem = null;
if (fileItemList.size() > 0) {
fileItem = (FileItem) fileItemList.get(0);
return fileLibrary + "1" + fs + "projects" + fs + getDatePath(fileItem.getModified()) + (showThumbnail ? fileItem.getThumbnailFilename() : fileItem.getFilename());
}
return null;
}
protected static String getDatePath(java.sql.Timestamp fileDate) {
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy");
String datePathToUse1 = formatter1.format(fileDate);
SimpleDateFormat formatter2 = new SimpleDateFormat("MMdd");
String datePathToUse2 = formatter2.format(fileDate);
return datePathToUse1 + fs + datePathToUse2 + fs;
}
}