Showing posts with label ColdFusion 9. Show all posts
Showing posts with label ColdFusion 9. Show all posts

Thursday, May 27, 2010

CF9 Question: Do Spreadsheet Functions Support "Blank Cells"?

I saw a question about spreadsheets this week that had me scratching my head. When you create a new worksheet in Excel, all of the cells are blank. So if you enter a long string in any cell, the text will overflow into the adjacent cell. (If the adjacent cell is blank.)




However, if you do something similar in ColdFusion 9, the text does not overflow into the next cell. Notice how the extra text in cell A1 is hidden, even though the adjacent cell is technically empty.



<cfscript>
    cfSheet = SpreadsheetNew("foo");
    SpreadsheetAddRow(cfSheet, "this text should overflow,,short text", 1);
    SpreadsheetSetColumnWidth(cfSheet, 1, 15); 
    SpreadsheetSetColumnWidth(cfSheet, 2, 15); 
    SpreadsheetSetColumnWidth(cfSheet, 3, 15); 
    
    saveToFile = ExpandPath("test.xls");
    SpreadsheetWrite(cfsheet, saveToFile, true);
    
    // how about a WriteLine() function folks ... ;) ?
    WriteOutput("Saved file: "& saveToFile &"<hr>");
</cfscript>

<cfheader name="Content-Disposition" value="inline; filename=#saveToFile#">
<cfcontent type="application/vnd.ms-excel" file="#saveToFile#" deleteFile="true" />


In the underlying POI library there is a special cell type for blank cells: CELL_TYPE_BLANK. In loose terms, it represents a raw cell that has never had a value. This is different from a cell whose value is set to an empty string. The value may be an empty string, but the cell still has a value. So it has a type of CELL_TYPE_STRING.

If you loop over the test file created earlier, you will see all of the cells are CELL_TYPE_STRING. Which at least explains why the first cell's text does not overflow into the next cell.

<cfscript>
    // open the workbook
    source = ExpandPath("test.xls");
    input  = createObject("java", "java.io.FileInputStream").init( source );
    wb     = createObject("java", "org.apache.poi.ss.usermodel.WorkbookFactory").create( input );
    // get the first sheet
    sheet  = wb.getSheetAt(0);

    // loop over each row in the sheet
    rows = sheet.rowIterator();
    while(rows.hasNext()) {
        // get the current row
        r = rows.next();

        // loop over each cell in the sheet
        cells = r.cellIterator();
        while (cells.hasNext()) {
            // get the current cell
            c = cells.next();

            // check the cell type (ingore error handling for brevity)
            if (c.getCellType() == c.CELL_TYPE_BLANK) {
                type = "CELL_TYPE_BLANK";
            }
            else if (c.getCellType() == c.CELL_TYPE_STRING) {
                type = "CELL_TYPE_STRING";
            }
            else {
                type = "other";
            }

            // display the position, cell type / value
            // note: Adding +1 because POI indexes are 0-based
            WriteOutput("["& r.getRowNum()+1 &"]["& c.getColumnIndex()+1 &"]=");
            WriteOutput(type &" / "& c.toString() &"<hr>");
        }
    }
    // clean up 
    input.close();
</cfscript>

But this raises the question, does ColdFusion 9 support "blank" cells, and if so how do you create one? I am honestly not sure. You could work around it by tapping into the undocumented getWorkBook() method of ColdFusion spreadsheet objects. It returns a reference to the underlying POI workbook. Using the workbook, you can then grab the desired rows and cells and change the cell type to CELL_TYPE_BLANK.

<cfscript>
    cfSheet = SpreadsheetNew("foo");
    SpreadsheetAddRow(cfSheet, "this text should overflow,,short text", 1);
    SpreadsheetSetColumnWidth(cfSheet, 1, 15); 
    SpreadsheetSetColumnWidth(cfSheet, 2, 15); 
    SpreadsheetSetColumnWidth(cfSheet, 3, 15); 
    
    // work around to make cell B1 a "blank" cell   
    // POI row/cell indexes are 0-based !!
    poiSheet = cfSheet.getWorkBook().getSheet("foo");
    poiCell = poiSheet.getRow( 0 ).getCell( 1 ); 
    poiCell.setCellType( poiCell.CELL_TYPE_BLANK );
    
       
    saveToFile = ExpandPath("newTest.xls");
    SpreadsheetWrite(cfsheet, saveToFile, true);
    WriteOutput("Saved file: "& saveToFile &"<br>");
</cfscript>
                 
<cfheader name="Content-Disposition" value="inline; filename=#saveToFile#">
<cfcontent type="application/vnd.ms-excel" file="#saveToFile#" deleteFile="true" />

However I have not found a documented way to do this. So does anyone know the answer to the million dollar question: does ColdFusion 9 provide a documented method for creating blank cells? My guess would be no. But I would be very happy to be proven wrong. Any spreadsheet function gurus out there?

...Read More

Monday, February 15, 2010

ColdFusion 9: CFPDF - Adding Headers and Footers (and a Few Quirks) - Part 2

In Part 1 I posted some examples of text and image headers. CF9 also introduced some new constants for working with page numbers and labels.


Page Numbers
Though "Page X of Y" headers/footers are not exactly earth-shattering, it is a little easier to add them to existing pdf's with the new constants _PAGENUMBER and _LASTPAGENUMBER.

<!--- Sample Document --->
<cfdocument format="pdf" name="pdfInput">
    <cfloop from="1" to="10" index="x">
        The Blank Page 
        <cfif x lt 10>
            <cfdocumentitem type="pagebreak" />
        </cfif>
    </cfloop>
</cfdocument>

<!--- Add Page X of Y Footers --->
<cfpdf action="addFooter"
   source="pdfInput"
   name="pdfOutput"
   text="Page _PAGENUMBER of _LASTPAGENUMBER" 
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

NumberFormat
With the help of the neat numberFormat attribute, you can change boring decimal page numbers into Roman numeral format (both upper and lower case). The supported values are: LOWERCASEROMAN, NUMERIC, UPPERCASEROMAN.

<!--- Sample Document --->
<cfdocument format="pdf" name="pdfInput">
    <cfloop from="1" to="10" index="x">
        The Blank Page 
        <cfif x lt 10>
            <cfdocumentitem type="pagebreak" />
        </cfif>
    </cfloop>
</cfdocument>

<!--- Add Page X of Y in Roman Numerals --->
<cfpdf action="addFooter"
    source="pdfInput"
    name="pdfOutput"
    numberFormat="UpperCaseRoman"
    text="Page _PAGENUMBER of _LASTPAGENUMBER" 
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

Page Labels
If your pdf contains custom Page Labels, you can also add that text to headers/footers using the _PAGELABEL and/or _LASTPAGELABEL constants. Obviously if your pdf doesn't have custom page labels, you will probably just see the default decimal page numbers instead. But I am pretty sure you can add custom labels with ddx (or alternately with iText).


<!--- Add Page Label to Header --->
<cfpdf action="addHeader"
    source="testDocumentWithLabels.pdf"
    name="pdfOutput"
    text="<b>Label For Page (_PAGENUMBER):</b> _PAGELABEL" 
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

Things You Cannot Do ...
Perhaps this is an obvious point, but these settings have no affect on information already embedded within the pdf. In other words, you cannot change existing pdfs with page numbers already embedded in the file. However, if you use cfpdf to generate the headers/footers you can certainly remove them and add new ones.

Printing Headers/Footers
For cases when you do not always want headers/footers to be printed, you can use the showOnPrint option to control visibility when printing. For example, you could tweak some of the previous snippets to display both the graphic and text headers on screen, but use showOnPrint="false" to make only the text header "printable". Obviously you can also use the pages attribute to apply that behavior only to select pages.

<!--- 
    Display the graphics header only on screen
--->
<cfpdf action="addHeader" 
    source="inputFile.pdf" 
    destination="pdfWithImageHeader.pdf" 
    topMargin="2.6"
    leftMargin="0"
    rightMargin="0"
    align="center"
    image="http://www.google.com/logos/olympics10-prsskating-hp.png"
    overwrite="true"
    showonprint = "false"
/>

....

<!--- 
    Display the text header both on screen and when printed
--->
<cfpdf action="addHeader" 
    source="pdfWithImageHeader.pdf" 
    name="pdfOutput" 
    topMargin="1.7"
    leftMargin="0"
    rightMargin="0"
    align="center"
    text="#headerText#"
    showonprint = "true"
/>

As you can see, it is not extremely complex. But hopefully these examples make a good addendum to the limited documentation on the addHeader / addFooters features.

...Read More

ColdFusion 9: CFPDF - Adding Headers and Footers (and a Few Quirks) - Part 1

With the explosion of new features in CFPDF, there is a lot to cover in the documentation. So it is understandably a little lacking on extra examples that go beyond the very basics. While belatedly exploring some of the extended features, I ran into a few quirks with addHeader / addFooter. Probably not news to some of you. But I thought I would share my examples and results in case it saves someone else a little time.


Text Headers and Footers
The basic concept of adding a textual headers and footers was simple enough. Just use the appropriate action, addHeader or addFooter, along with a simple text value.  Setting the text alignment is equally simple, just provide an align value (left,center, right) with the desired margins.  It is worth noting that if you omit the margin, CF uses its default.  While I would not swear to it, the default seems to be 1 inch. At least for letter sized pages.

Note: If there is too much text to display with the margin bounds, it may flow right off the page. So if working with a lot of text, you may need to adjust it with some html/css.

<!--- Align plain text header --->
<cfpdf  action="addHeader" 
    source="inputFile.pdf" 
    name="pdfOutput" 
    align="left"
    leftMargin="0.5"
    text="Left Aligned. One-half (0.5) inch from left side"
/>  

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />
Styling Text Headers and Footers
Applying html/css styles to the text is very similar to using a text watermark (added in CF 8.0.1). I did encounter one peculiarity with fonts. The names seem to be case sensitive. So on windows the font name "castellar" did nothing, but "Castellar" (with an uppercase "C") did produce the desired font.


<!--- 
Add Styled header 
--->
<cfsavecontent variable="headerText">
    <span style="font-size: 25pt; font-family: Castellar;">
       <b style="color: #0080ff;">Vancouver</b>
       <b style="color: #0000ff;">2010</b>
       <b style="color: #000080;">Olympics</b>
    </span>
</cfsavecontent>

<cfpdf action="addHeader" 
    source="inputFile.pdf" 
    name="pdfOutput" 
    topMargin="1"
    text="#headerText#"
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

HTML/CSS Limitations
Not all html tags are supported, such as <div>, <a>, etcetera. I am assuming the same limitations apply to action="addWatermark". So whatever quirks one has, the others probably do as well. 

Now in case you see an error message complaining about an "Invalid Document..", check the stack trace for the real error. Most likely it will say something about either an invalid tag or syntax error in your html/css.


An error occurred during the ADDHEADER operation in the cfpdf tag.  
Error: Invalid Document C:\ColdFusion9\wwwroot\test\pdf\inputFile.pdf specified for source or directory.  
...

Stack Trace:
com.adobe.internal.pdftoolkit.core.exceptions.PDFInvalidDocumentException: Error during text formatting, internalProcessElement: InvalidInput: Illegal element tag "div".
....

Differences between Watermarks and Headers/Footers
One difference I have noticed is how text is sized. CF 8.0.1 added the width and height attributes to support text watermarks. From what I have observed, the width and height values seem to control the size of the watermark text, rather than any explicit font sizes in html/css.  Since the width and height attributes are not allowed with actions addHeader and addFooter,  that text can be controlled by css font sizes.

Text and Image Opacity
As with watermarks, you can also use the opacity attribute to change transparency of the header/footer. Simply use a value between 0 (transparent) and 10 (fully opaque). This example creates a styled footer whose text is 50% transparent. Opacity applies to both text and images.



<cfsavecontent variable="text">
    <span style="font-size: 18pt; font-family: Century Gothic;">
       <b style="color: #0080ff;">Vancouver</b>
       <b style="color: #0000ff;">2010</b>
       <b style="color: #000080;">Olympics</b>
    </span>
</cfsavecontent>

<cfpdf action="addFooter" 
    source="inputFile.pdf" 
    name="pdfOutput" 
    bottomMargin="0.5"
    text="#text#"
    align="center"
    opacity="5"
/>    

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

Image Headers and Footers
Using images for headers and footers is also supported. The image source can be a file path, url, CF image object, array of bytes, or a base64 string (using isBase64="true").

Figuring out image headers/footers took a little while. It is not that the syntax is particularly cryptic. But it was not really clear to me, from the documentation, what should happen in various cases, such as when using an over-sized or undersized image.

Based on my tests, if an image is too large to fit within the given margin dimensions, it is automatically re-sized. So if the margin is too small, you can end up with a skewed or squished image. I also observed some strange alignment issues in a few of my tests. Though I am not yet sure why. In any case, you may want to ensure your images are correctly sized beforehand, to avoid unexpected resizing behavior by cfpdf.

If you view the image url below, its size should be around 7.4 x 2.4 inches. To center align the image, I used the code below. The top margin is set to "2.6" (inches). Tall enough to fit the image without resizing, plus a small amount of padding at the top. Both the left and right margin are set to zero (0) so the image is centered across the entire page width.

Now you may notice that visually this image does not appear completely centered. But that is due to the layout of the underlying graphic. But you can adjust the position by changing the alignment and leftMargin.


<!---
    Sample Document
--->
<cfdocument format="pdf" fileName="inputFile.pdf" pageType="Letter" marginTop="2.6" overwrite="true">
    <style type="text/css" media="screen">
        div { font-family: Castellar; padding: 10px; color: 204a64; } 
    </style>
    <cfloop from="1" to="10" index="x">
        <div> 
            Alpine Skiing * Biathlon * BobSleigh * Cross-Country Skiing * Figure Skating * 
            Ice Hockey * Luge * Curling * Snowboard * Speed Skating
        </div>
        <cfif x lt 10>
            <cfdocumentitem type="pagebreak" />
        </cfif>
    </cfloop>
</cfdocument>

<!--- 
    Add image header (Note: Using image URL just for demonstration purposes)
--->
<cfpdf action="addHeader" 
    source="inputFile.pdf" 
    destination="pdfWithImageHeader.pdf" 
    topMargin="2.6"
    leftMargin="0"
    rightMargin="0"
    align="center"
    image="http://www.google.com/logos/olympics10-prsskating-hp.png"
    overwrite="true"
/>

Multiple Headers / Footers
It is also possible to layer headers/footers by adding text on top of an existing image. To demonstrate, read in the file created in the previous code snippet and adjust the margin size so the text is placed in the desired position. The results should look something like this



<!--- 
Add text header on top of existing image header
--->
<cfsavecontent variable="headerText">
    <span style="font-size: 25pt; font-family: Castellar;">
       <b style="color: #0080ff;">Vancouver</b>
       <b style="color: #0000ff;">2010</b>
       <b style="color: #000080;">Olympics</b>
    </span>
</cfsavecontent>

<cfpdf action="addHeader" 
    source="pdfWithImageHeader.pdf" 
    name="pdfOutput" 
    topMargin="1.7"
    leftMargin="0"
    rightMargin="0"
    align="center"
    text="#headerText#"
/>

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

Remove Headers/Footers
In the process of running all of these tests, I discovered something worth noting about images.  Using an image obviously increases the file size. In some cases substantially (I have not gotten around to doing any size comparisons between cfpdf and iText yet). Since adding a new header/footer does not remove any existing ones, if you mistakenly add an image twice, it will increase the file size .. twice. But you can always use the action="removeHeaderFooter" first. (Yes, it does remove both headers and footers).


<!--- 
    Remove headers and footers
--->
<cfpdf action="removeHeaderFooter" 
        source="pdfWithImageHeader.pdf" 
        name="pdfOutput" 
    />

<!--- Display results --->
<cfcontent type="application/pdf" variable="#ToBinary(pdfOutput)#" />

On a side note, if you use cfdocument to generate header and footers, as opposed to the new cfpdf actions, be aware there is a "Known Issue" mentioned in the CF9 release notes.


Bug 76078:
Headers or footers added while generating PDF using cfdocument are not marked and are not recognized as headers or footers. Therefore, cfpdf action="removeheaderfooter" will not be able to remove them from source PDF.

ColdFusion 9: CFPDF - Adding Headers and Footers (and a Few Quirks) - Part 2

...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep