@cfsimplicity Julian Halliwell
if only they had more experience with pdfs...
Modern CSS support
Limited Header/Footer Customization
JS Support
Page Break Issues
Table Border Styles
Custom Font Support
Context aware chapter titles
Page numbering
Bookmarks
Table of Contents
Internal Links
Image Paths
Repeating Table Headers
Orientation change within the PDF
Missing, limited, or undocumented support for...
WKHTMLTOPDF
executable
PhantomJS (headless browser)
PhantomJS development is suspended until further notice
executable
executable -> Puppeteer CLI
Written for node, wrapped for java
Playwright
https://html2canvas.hertzen.com/
Javascript library that renders html into a canvas element on the fly and convert it to an image
Notable mentions
Apache PDF Box
(not from ortus)
Prince PDF
($500 / server licence)
Searching for docs....
The Adobe CFML cfdocument tag can render HTML that supports the following standards:
HTML 4.01
XML 1.0
DOM Level 1 and 2
CSS1 and CSS2 (For more information, see the "Supported CSS styles" section).
The Lucee CFML cfdocument tag can render HTML that supports the following standards:
HTML 4.01
XML 1.0
DOM Level 1 and 2
CSS 2.1, css 3 support in development
The magic is found in the CSS support
/* Use A4 paper */
@page { size: A4 }
/* Use A4 paper in landscape orientation */
@page { size: A4 landscape }
/* These two custom sizes are equivalent */
@page { size: 30cm 40cm }
@page { size: 40cm 30cm landscape }
/* Use square paper, this sets width and height */
@page { size: 30cm }
Name | Default alignment | In figure | |
---|---|---|---|
text-align | vertical-align | ||
@top | center | middle | yellow |
@bottom | center | middle | yellow |
@left | center | middle | red |
@right | center | middle | red |
@top-left | left | middle | green |
@page {
@bottom {
content: counter(page)
}
}
page-break-before:
auto | always | avoid
page-break-after:
auto | always | avoid
page-break-inside:
auto | always | avoid
table {
border-collapse: collapse;
table-layout: fixed;
}
modifies the table layout algorithm to repeat table headers and footers on subsequent pages
<cfdocument fontDirectory = "path/to/Calibri.ttf">
<style type="text/css">
body {
font-family: Calibri, sans-serif;
}
</style>
</cfdocument>
Note: Modern (Flying saucer) engine works using the font-family-name from the .ttf file with the same case.
Note 2: Possible problems with forign language fonts
@page {
@top {
content: string(doctitle)
}
}
h1 { string-set: doctitle content() }
Copying content from the document
Generated content in page regions may contain text content copied from the document using the string-set property:
@page docpage{
size:8.5in 11in;
margin: .4in .5in .75in .5in;
@bottom-left {
font-family: Arial, Helvetica, sans-serif;
font-size:11px;
content: "<cfoutput>#prc.invoiceTitle#</cfoutput>"
}
@bottom-right {
font-family: Arial, Helvetica, sans-serif;
font-size:11px;
content: "Page " counter(page) " of " counter(pages);
}
}
.normalpage {
page: docpage;
}
document bookmark=true {}
<a href="#anchor-id">What is ColdFusion (CFML)</a>
<h2 id="#anchor-id" class="h1 page">
What is ColdFusion (CFML)
</h2>
ul.toc a::after {
content: leader(".") target-counter(attr(href), page);
}
document localurl=true
<img
align="center"
style="max-width:100%"
src="/path/to/local_file.png"
/>
<img
align="center"
style="max-width:100%"
src="http(s)://yourimageurl.png"
/>
ColdFusion retrieves image files directly from
the local drive rather than by using HTTP, HTTPS, or proxy
table {
-fs-table-paginate
}
modifies the table layout algorithm to repeat table headers and footers on subsequent pages
-fs-keep-with-inline | will try to avoid breaking a block in such a way that only padding and borders appear on a page. |
-fs-page-sequence | Allows you to limit the scope of the page and pages counters to a portion of the document |
-fs-font-metric-src | use inside a font-face rule in case the font you want to embed in the PDF has a custom font metrics file; |
-fs-pdf-font-embed | use with the value embed inside a font-face rule to have Flying Saucer embed a font file within a PDF document, avoiding the need to call the addFont() method of the FontResolver class |
-fs-pdf-font-encoding | use inside a font-face rule to specify the enconding for a custom font you are embedding inside a PDF; takes the name of the encoding as value. |
-fs-table-cell-colspan | whole number. Replaces use of legacy colspan attribute for table columns. |
-fs-table-cell-rowspan | whole number. Replaces use of legacy rowspan attribute for table columns. |
-fs-table-paginate | when used with the value paginate, modifies the table layout algorithm to repeat table headers and footers on subsequent pages and improve the appearance of cells that break across pages (for example by closing and reopening borders), but that's all it does. If a table's minimum width is wider than the page, it will be chopped off. |
-fs-text-decoration-extent | Either line (default) or block. It controls how text decorations are drawn on a block level element. With line, the spec compliant behavior is used text decoration is drawn across line box. With block, text decoration is drawn across entire content area of block. |
https://flyingsaucerproject.github.io/flyingsaucer/r8/guide/users-guide-R8.html
AST
Abstract Syntax Tree
https://astexplorer.net/
Search for a java library to convert it
moduleSettings = {
cbmarkdown = {
// Looks for www or emails and converts them to links
autoLinkUrls : true,
// Creates anchor links for headings
anchorLinks : true,
// Set the anchor id
anchorSetId : true,
// Set the anchor id but also the name
achorSetName : true,
// Do we create the anchor for the full header or just before it. True is wrap, false is just create anchor tag
anchorWrapText : false,
// The class(es) to apply to the anchor
anchorClass : "anchor",
// raw html prefix. Added before heading text, wrapped or unwrapped
anchorPrefix : "",
// raw html suffix. Added before heading text, wrapped or unwrapped
anchorSuffix : "",
// Enable youtube embedded link transformer
enableYouTubeTransformer : false,
// override HTML to use for wrapping style.
codeStyleHTMLOpen : '<code class="code inline">',
// override HTML to use for wrapping style.
codeStyleHTMLClose : '</code>',
// add a class prefix to the "fenced" code blocks, i.e. ```js. Useful for supporting various syntax highlighters.
fencedCodeLanguageClassPrefix : "brush",
// Table options
tableOptions : {
// Treat consecutive pipes at the end of a column as defining spanning column.
columnSpans : true,
// Whether table body columns should be at least the number or header columns.
appendMissingColumns : true,
// Whether to discard body columns that are beyond what is defined in the header
discardExtraColumns : true,
// Class name to use on tables
className : "table",
// When true only tables whose header lines contain the same number of columns as the separator line will be recognized
headerSeparationColumnMatch : true
}
} // end markdown settings
};
vscode-cfml
{
"name": "CFML",
"scopeName": "embedding.cfml",
"patterns": [
{
"begin": "(?i)(?=^\\s*(/\\*|//|import\\b|(component|abstract\\s*component|final\\s*component|interface)(\\s+|{)))",
"contentName": "source.cfml.script",
"end": "(?=not)possible",
"patterns": [
{
"include": "#source-cfml-script"
}
]
},
{
"include": "#source-cfml-tag-comments"
},
{
"begin": "(?i)(?=<cf(component|interface)\\b)",
"contentName": "source.cfml",
"end": "(?=not)possible",
"patterns": [
{
"include": "#cfcomponent"
},
{
"include": "#cfinterface"
}
]
},
{
"begin": "(?=\\S)",
"contentName": "text.html.cfml",
cfml-parser
var braceOpen = 0;
var semi = 0;
var quotePos = 0;
var eqPos = 0;
var lineEnd = 0;
var inString = false;
var stringOpenChar = "";
var currentStatement = "";
var currentStatementStart = 1;
var commentStatement = "";
var sb = createObject("java", "java.lang.StringBuilder");
//parsing a cfscript tag uses startPosition and endPosition
if (arguments.startPosition != 0 && arguments.endPosition != 0) {
pos = arguments.startPosition;
contentLength = arguments.endPosition;
}
while(pos<=contentLength) {
c = mid(content, pos, 1);
if (c == "'" || c == """") {
if (inString && stringOpenChar == c) {
if (mid(content, pos, 2) != c&c) {
inString = false; //end string
} else {
//escaped string open char
sb.append(c);
sb.append(c);
pos = pos+2;
continue;
}
} else if (!inString) {
inString = true;
stringOpenChar = c;
}
sb.append(c);
} else if (!inString) {
if (c == "/" && mid(content, pos, 2) == "/*") {
//currentState = this.STATE.COMMENT;
commentStatement = new Comment(name="/*", startPosition=pos, parent=parent, file=arguments.file);
if (!isSimpleValue(parent)) {
parent.addChild(commentStatement);
}
endPos = find("*/", content, pos+3);
if (endPos == 0) {
//end of doc
endPos = contentLength;
}
commentStatement.setEndPosition(endPos);~