package railo.runtime.tag;
import java.awt.Dimension;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.StringUtil;
import railo.commons.pdf.PDFDocument;
import railo.commons.pdf.PDFException;
import railo.commons.pdf.PDFPageMark;
import railo.runtime.Info;
import railo.runtime.PageContextImpl;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.TemplateException;
import railo.runtime.ext.tag.BodyTagImpl;
import railo.runtime.op.Caster;
import railo.runtime.text.pdf.PDFUtil;
import railo.runtime.type.ReadOnlyStruct;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.SimpleBookmark;
public final class Document extends BodyTagImpl {
//private static final String STYLE_BG_INVISIBLE = "background-color: transparent; background-image: none;";
private Resource filename=null;
private boolean overwrite=false;
private String name=null;
private Dimension pagetype=PDFDocument.PAGETYPE_LETTER;
private double pageheight=0;
private double pagewidth=0;
private boolean isLandscape=false;
private double unitFactor=PDFDocument.UNIT_FACTOR_IN;
private int encryption=PDFDocument.ENC_NONE;
private String ownerpassword=null;
private String userpassword="empty";
private int scale=-1;
// TODO impl. tag Document backgroundvisible,fontembed,scale
private boolean backgroundvisible;
private int fontembed=PDFDocument.FONT_EMBED_YES;
private int permissions=0;
private PDFDocument _document;
private ArrayList<PDFDocument> documents=new ArrayList<PDFDocument>();
public Document() {
this._document=null;
}
@Override
public void release() {
super.release();
filename=null;
overwrite=false;
name=null;
pagetype=PDFDocument.PAGETYPE_LETTER;
pageheight=0;
pagewidth=0;
isLandscape=false;
unitFactor=PDFDocument.UNIT_FACTOR_IN;
encryption=PDFDocument.ENC_NONE;
ownerpassword=null;
userpassword="empty";
permissions=0;
scale=-1;
documents.clear();
_document=null;
backgroundvisible=false;
fontembed=PDFDocument.FONT_EMBED_YES;
}
private PDFDocument getDocument() {
//SerialNumber sn = pageContext.getConfig().getSerialNumber();
if(_document==null){
_document=new PDFDocument();
}
return _document;
}
/** set the value proxyserver
* Host name or IP address of a proxy server.
* @param proxyserver value to set
**/
public void setProxyserver(String proxyserver) {
getDocument().setProxyserver(proxyserver);
}
public void setProxyhost(String proxyserver) {
getDocument().setProxyserver(proxyserver);
}
/** set the value proxyport
* The port number on the proxy server from which the object is requested. Default is 80. When
* used with resolveURL, the URLs of retrieved documents that specify a port number are automatically
* resolved to preserve links in the retrieved document.
* @param proxyport value to set
**/
public void setProxyport(double proxyport) {
getDocument().setProxyport((int)proxyport);
}
/** set the value username
* When required by a proxy server, a valid username.
* @param proxyuser value to set
**/
public void setProxyuser(String proxyuser) {
getDocument().setProxyuser(proxyuser);
}
/** set the value password
* When required by a proxy server, a valid password.
* @param proxypassword value to set
**/
public void setProxypassword(String proxypassword) {
getDocument().setProxypassword(proxypassword);
}
public void setSaveasname(String saveAsName) {
// TODO impl
}
/**
* @param authUser the authUser to set
*/
public void setAuthuser(String authUser) {
getDocument().setAuthUser(authUser);
}
/**
* @param authPassword the authPassword to set
*/
public void setAuthpassword(String authPassword) {
getDocument().setAuthPassword(authPassword);
}
/**
* @param userAgent the userAgent to set
*/
public void setUseragent(String userAgent) {
getDocument().setUserAgent(userAgent);
}
/**
* @param format the format to set
* @throws ApplicationException
*/
public void setFormat(String format) throws ApplicationException {
format = StringUtil.toLowerCase(format.trim());
if(!"pdf".equals(format))
throw new ApplicationException("invalid format ["+format+"], only the following format is supported [pdf]");
}
/**
* @param filename the filename to set
* @throws PageException
*/
public void setFilename(String filename) throws PageException {
this.filename = ResourceUtil.toResourceNotExisting(pageContext, filename);
pageContext.getConfig().getSecurityManager().checkFileLocation(this.filename);
}
/**
* @param overwrite the overwrite to set
*/
public void setOverwrite(boolean overwrite) {
this.overwrite = overwrite;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param pagetype the pagetype to set
* @throws ApplicationException
*/
public void setPagetype(String strPagetype) throws ApplicationException {
strPagetype=StringUtil.toLowerCase(strPagetype.trim());
if("legal".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_LEGAL;
else if("letter".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_LETTER;
else if("a4".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_A4;
else if("a5".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_A5;
else if("b4".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B4;
else if("b5".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B5;
else if("b4-jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B4_JIS;
else if("b4 jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B4_JIS;
else if("b4_jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B4_JIS;
else if("b4jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B4_JIS;
else if("b5-jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B5_JIS;
else if("b5 jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B5_JIS;
else if("b5_jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B5_JIS;
else if("b5jis".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_B5_JIS;
else if("custom".equals(strPagetype)) pagetype=PDFDocument.PAGETYPE_CUSTOM;
else throw new ApplicationException("invalid page type ["+strPagetype+"], valid page types are [legal,letter,a4,a5,b4,b5,b4-jis,b5-jis,custom]");
}
/**
* @param pageheight the pageheight to set
* @throws ApplicationException
*/
public void setPageheight(double pageheight) throws ApplicationException {
if(pageheight<1) throw new ApplicationException("pageheight must be a positive number");
this.pageheight = pageheight;
}
/**
* @param pagewidth the pagewidth to set
* @throws ApplicationException
*/
public void setPagewidth(double pagewidth) throws ApplicationException {
if(pagewidth<1) throw new ApplicationException("pagewidth must be a positive number");
this.pagewidth = pagewidth;
}
/**
* @param orientation the orientation to set
* @throws ApplicationException
*/
public void setOrientation(String strOrientation) throws ApplicationException {
strOrientation=StringUtil.toLowerCase(strOrientation.trim());
if("portrait".equals(strOrientation)) isLandscape=false;
else if("landscape".equals(strOrientation)) isLandscape=true;
else throw new ApplicationException("invalid orientation ["+strOrientation+"], valid orientations are [portrait,landscape]");
}
/**
* @param marginbottom the marginbottom to set
*/
public void setMarginbottom(double marginbottom) {
getDocument().setMarginbottom(marginbottom);
}
/**
* @param marginleft the marginleft to set
*/
public void setMarginleft(double marginleft) {
getDocument().setMarginleft(marginleft);
}
/**
* @param marginright the marginright to set
*/
public void setMarginright(double marginright) {
getDocument().setMarginright(marginright);
}
/**
* @param margintop the margintop to set
*/
public void setMargintop(double margintop) {
getDocument().setMargintop(margintop);
}
/**
* @param bookmark the bookmark to set
*/
public void setBookmark(boolean bookmark) {
getDocument().setBookmark(bookmark);
}
public void setHtmlbookmark(boolean bookmark) {
getDocument().setHtmlBookmark(bookmark);
}
/**
* @param localUrl the localUrl to set
*/
public void setLocalurl(boolean localUrl) {
getDocument().setLocalUrl(localUrl);
}
/**
* @param unitFactor the unit to set
* @throws ApplicationException
*/
public void setUnit(String strUnit) throws ApplicationException {
strUnit=StringUtil.toLowerCase(strUnit.trim());
if("in".equals(strUnit)) unitFactor=PDFDocument.UNIT_FACTOR_IN;
else if("cm".equals(strUnit)) unitFactor=PDFDocument.UNIT_FACTOR_CM;
else if("point".equals(strUnit)) unitFactor=PDFDocument.UNIT_FACTOR_POINT;
else throw new ApplicationException("invalid unit ["+strUnit+"], valid units are [cm,in,point]");
}
/**
* @param encryption the encryption to set
* @throws ApplicationException
*/
public void setEncryption(String strEncryption) throws ApplicationException {
strEncryption=StringUtil.toLowerCase(strEncryption.trim());
if("none".equals(strEncryption)) encryption=PDFDocument.ENC_NONE;
else if("40-bit".equals(strEncryption)) encryption=PDFDocument.ENC_40BIT;
else if("40bit".equals(strEncryption)) encryption=PDFDocument.ENC_40BIT;
else if("40 bit".equals(strEncryption)) encryption=PDFDocument.ENC_40BIT;
else if("40_bit".equals(strEncryption)) encryption=PDFDocument.ENC_40BIT;
else if("128-bit".equals(strEncryption)) encryption=PDFDocument.ENC_128BIT;
else if("128bit".equals(strEncryption)) encryption=PDFDocument.ENC_128BIT;
else if("128 bit".equals(strEncryption)) encryption=PDFDocument.ENC_128BIT;
else if("128_bit".equals(strEncryption)) encryption=PDFDocument.ENC_128BIT;
else throw new ApplicationException("invalid encryption ["+strEncryption+"], valid encryption values are [none, 40-bit, 128-bit]");
}
/**
* @param ownerpassword the ownerpassword to set
* @throws ApplicationException
*/
public void setOwnerpassword(String ownerpassword) {
this.ownerpassword = ownerpassword;
}
/**
* @param userpassword the userpassword to set
*/
public void setUserpassword(String userpassword) {
this.userpassword = userpassword;
}
/**
* @param permissions the permissions to set
* @throws PageException
*/
public void setPermissions(String strPermissions) throws PageException {
permissions=PDFUtil.toPermissions(strPermissions);
}
/**
* @param scale the scale to set
* @throws ApplicationException
*/
public void setScale(double scale) throws ApplicationException {
if(scale<0) throw new ApplicationException("scale must be a positive number");
if(scale>100) throw new ApplicationException("scale must be a number less or equal than 100");
this.scale = (int) scale;
}
/**
* @param src the src to set
* @throws ApplicationException
*/
public void setSrc(String src) throws ApplicationException {
try {
getDocument().setSrc(src);
} catch (PDFException e) {
throw new ApplicationException(e.getMessage());
}
}
/**
* @param srcfile the srcfile to set
* @throws PageException
* @throws
*/
public void setSrcfile(String strSrcfile) throws PageException {
Resource srcfile = ResourceUtil.toResourceExisting(pageContext, strSrcfile);
pageContext.getConfig().getSecurityManager().checkFileLocation(srcfile);
try {
getDocument().setSrcfile(srcfile);
} catch (PDFException e) {
throw new ApplicationException(e.getMessage());
}
}
/**
* @param mimetype the mimetype to set
*/
public void setMimetype(String strMimetype) {
getDocument().setMimetype(strMimetype);
strMimetype = strMimetype.toLowerCase().trim();
}
public void setHeader(PDFPageMark header) {
getDocument().setHeader(header);
}
public void setFooter(PDFPageMark footer) {
getDocument().setFooter(footer);
}
public void setBackgroundvisible(boolean backgroundvisible) {
this.backgroundvisible=backgroundvisible;
}
public void setFontembed(String fontembed) throws PDFException {
Boolean fe=Caster.toBoolean(fontembed,null);
if(fe==null) {
fontembed=StringUtil.toLowerCase(fontembed.trim());
if("selective".equals(fontembed))
this.fontembed=PDFDocument.FONT_EMBED_SELECCTIVE;
else throw new PDFException("invalid value for fontembed ["+fontembed+"], valid values for fontembed are [yes,no,selective]");
}
else if(fe.booleanValue())this.fontembed=PDFDocument.FONT_EMBED_YES;
else this.fontembed=PDFDocument.FONT_EMBED_NO;
getDocument().setFontembed(this.fontembed);
}
public void addPDFDocument(PDFDocument document) {
// set proxy settings
if(_document!=null) {
if(_document.hasProxy()) {
document.setProxyserver(_document.getProxyserver());
document.setProxyport(_document.getProxyport());
document.setProxyuser(_document.getProxyuser());
document.setProxypassword(_document.getProxypassword());
}
document.setBookmark(_document.getBookmark());
document.setLocalUrl(_document.getLocalUrl());
}
documents.add(document);
}
@Override
public int doStartTag() throws PageException {
// SerialNumber sn = pageContext.getConfig().getSerialNumber();
//if(sn.getVersion()==SerialNumber.VERSION_COMMUNITY)
// throw new SecurityException("no access to this functionality with the "+sn.getStringVersion()+" version of railo");
ReadOnlyStruct cfdoc=new ReadOnlyStruct();
cfdoc.setEL("currentpagenumber", "{currentpagenumber}");
cfdoc.setEL("totalpagecount", "{totalpagecount}");
cfdoc.setEL("totalsectionpagecount", "{totalsectionpagecount}");
cfdoc.setEL("currentsectionpagenumber", "{currentsectionpagenumber}");
cfdoc.setReadOnly(true);
pageContext.variablesScope().setEL("cfdocument", cfdoc);
return EVAL_BODY_BUFFERED;
}
@Override
public void doInitBody() {
}
@Override
public int doAfterBody() {
getDocument().setBody(bodyContent.getString());
return SKIP_BODY;
}
@Override
public int doEndTag() throws PageException {
try {
_doEndTag();
}
catch (Exception e) {
throw Caster.toPageException(e);
}
return EVAL_PAGE;
}
public void _doEndTag() throws JspException, IOException, DocumentException {
// set root header/footer to sections
boolean doBookmarks=false;
boolean doHtmlBookmarks=false;
if(_document!=null){
PDFPageMark header = _document.getHeader();
PDFPageMark footer = _document.getFooter();
boolean hasHeader=header!=null;
boolean hasFooter=footer!=null;
if(hasFooter || hasHeader) {
Iterator<PDFDocument> it = documents.iterator();
PDFDocument doc;
while(it.hasNext()){
doc=it.next();
if(hasHeader && doc.getHeader()==null) doc.setHeader(header);
if(hasFooter && doc.getFooter()==null) doc.setFooter(footer);
}
}
doBookmarks=_document.getBookmark();
doHtmlBookmarks=_document.getHtmlBookmark();
}
if(filename!=null) {
if(filename.exists() && !overwrite)
throw new ApplicationException("file ["+filename+"] already exist","to allow overwrite the resource, set attribute [overwrite] to [true]");
OutputStream os= null;
try {
os= filename.getOutputStream();
render(os,doBookmarks,doHtmlBookmarks);
}
finally {
IOUtil.closeEL(os);
}
}
else if(!StringUtil.isEmpty(name)) {
render(null,doBookmarks,doHtmlBookmarks);
}
else {
HttpServletResponse rsp = pageContext. getHttpServletResponse();
if(rsp.isCommitted())
throw new ApplicationException("content is already flushed","you can't rewrite head of response after part of the page is flushed");
rsp.setContentType("application/pdf");
OutputStream os=getOutputStream();
try {
render(os,doBookmarks,doHtmlBookmarks);
}
finally {
IOUtil.flushEL(os);
IOUtil.closeEL(os);
((PageContextImpl)pageContext).getRootOut().setClosed(true);
}
throw new railo.runtime.exp.Abort(railo.runtime.exp.Abort.SCOPE_REQUEST);
}
}
private void render(OutputStream os, boolean doBookmarks, boolean doHtmlBookmarks) throws IOException, PageException, DocumentException {
byte[] pdf=null;
// merge multiple docs to 1
if(documents.size()>1) {
PDFDocument[] pdfDocs=new PDFDocument[documents.size()];
PdfReader[] pdfReaders = new PdfReader[pdfDocs.length];
Iterator<PDFDocument> it = documents.iterator();
int index=0;
// generate pdf with pd4ml
while(it.hasNext()) {
pdfDocs[index]=it.next();
pdfReaders[index]=
new PdfReader(pdfDocs[index].render(getDimension(),unitFactor,pageContext,doHtmlBookmarks));
index++;
}
// collect together
ByteArrayOutputStream baos = new ByteArrayOutputStream();
com.lowagie.text.Document document =
new com.lowagie.text.Document(pdfReaders[0].getPageSizeWithRotation(1));
PdfCopy copy = new PdfCopy(document,baos);
document.open();
String name;
ArrayList bookmarks=doBookmarks?new ArrayList():null;
try {
int size,totalPage=0;
Map parent;
for(int doc=0;doc<pdfReaders.length;doc++) {
size=pdfReaders[doc].getNumberOfPages();
PdfImportedPage ip;
// bookmarks
if(doBookmarks) {
name=pdfDocs[doc].getName();
if(!StringUtil.isEmpty(name)) {
bookmarks.add(parent=PDFUtil.generateGoToBookMark(name, totalPage+1));
}
else parent=null;
if(doHtmlBookmarks) {
java.util.List pageBM = SimpleBookmark.getBookmark(pdfReaders[doc]);
if(pageBM!=null) {
if(totalPage>0)SimpleBookmark.shiftPageNumbers(pageBM, totalPage, null);
if(parent!=null)PDFUtil.setChildBookmarks(parent,pageBM);
else bookmarks.addAll(pageBM);
}
}
}
totalPage++;
for(int page=1;page<=size;page++) {
if(page>1)totalPage++;
ip = copy.getImportedPage(pdfReaders[doc], page);
//ip.getPdfDocument().setHeader(arg0);
//ip.getPdfDocument().setFooter(arg0);
copy.addPage(ip);
}
}
if (doBookmarks && !bookmarks.isEmpty())copy.setOutlines(bookmarks);
}
finally {
document.close();
}
pdf=baos.toByteArray();
}
else if(documents.size()==1){
pdf=(documents.get(0)).render(getDimension(),unitFactor,pageContext,doHtmlBookmarks);
}
else {
pdf=getDocument().render(getDimension(),unitFactor,pageContext,doHtmlBookmarks);
}
// permission/encryption
if(PDFDocument.ENC_NONE!=encryption) {
PdfReader reader = new PdfReader(pdf);
com.lowagie.text.Document document = new com.lowagie.text.Document(reader.getPageSize(1));
document.addCreator("Railo "+Info.getVersionAsString()+" "+Info.getStateAsString());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfCopy copy = new PdfCopy(document,baos);
//PdfWriter writer = PdfWriter.getInstance(document, pdfOut);
copy.setEncryption(PDFDocument.ENC_128BIT==encryption , userpassword , ownerpassword , permissions);
document.open();
int size=reader.getNumberOfPages();
for(int page=1;page<=size;page++) {
copy.addPage(copy.getImportedPage(reader, page));
}
document.close();
pdf=baos.toByteArray();
}
// write out
if(os!=null)IOUtil.copy(new ByteArrayInputStream(pdf), os,true,false);
if(!StringUtil.isEmpty(name)) {
pageContext.setVariable(name,pdf);
}
}
private OutputStream getOutputStream() throws PageException, IOException {
try {
return ((PageContextImpl)pageContext).getResponseStream();
}
catch(IllegalStateException ise) {
throw new TemplateException("content is already send to user, flush");
}
}
private Dimension getDimension() throws ApplicationException {
// page size custom
if(isCustom(pagetype)) {
if(pageheight==0 || pagewidth==0)
throw new ApplicationException("when attribute pagetype has value [custom], the attributes [pageheight, pagewidth] must have a positive numeric value");
pagetype=new Dimension(PDFDocument.toPoint(pagewidth,unitFactor),PDFDocument.toPoint(pageheight,unitFactor));
}
// page orientation
if(isLandscape)pagetype=new Dimension(pagetype.height, pagetype.width);
return pagetype;
}
private boolean isCustom(Dimension d) throws ApplicationException {
if(d.height<=0 || d.width<=0)
throw new ApplicationException("if you define pagetype as custom, you have to define attribute pageheight and pagewith with a positive numeric value");
return (d.width+d.height)==2;
}
/**
* sets if has body or not
* @param hasBody
*/
public void hasBody(boolean hasBody) {
}
}