object to one paragraph
or stick multiple paragraphs in it.
In order to preserve style information, you shouldn't use direct
literal markup in your strings (though it doesn't really hurt
anything, it is not seen by the style system so you can't
influence the way tags are rendered after the fact). There is a
family of functions called gnc:html-markup-* that are shortcuts
to most of the common HTML markup (, , ,
-).
The markup functions can be composed and take any number of
arguments; the arguments are rendered and concatenated to become
the body of the body.
You can also markup using ANY tag with (gnc:html-markup tag
. body), which does the expected body by default (or
something else entirely if you have the "tag" style set
differently... more later) or (gnc:html-markup/attr tag attr
. body), which puts the attribute string 'attr' inside the start
tag: body. If there are attributes for the tag
specified as part of the current style, the attributes you
specify will be appended to the style's attributes.
gnc:html-markup puts an end tag after the body; you should use
gnc:html-markup/no-end and gnc:html-markup/attr/no-end if you
wish to force no end tag. Don't do this unless HTML syntax
forbids end tags, please.
Here is a short but complete HTML document demonstrating how to
create and render text:
(let ((doc (gnc:make-html-document))
(txt (gnc:make-html-text)))
(gnc:html-text-append! txt
(gnc:html-markup-h2 "This is a header")
(gnc:html-markup-p
"This is a paragraph of plain text with a "
(gnc:html-markup-b "bold")
" section in the middle."))
(gnc:html-document-add-object! doc txt)
(gnc:html-document-render doc))
html-document-render returns a string:
"
This is a header
This is a paragraph of plain text with a
bold section in the middle.
"
Any literals, like strings or numbers, that you include in an
html-text object get rendered according to the
in effect for their type, so please don't render them yourself
(unless you really want to).
See the section for more info, but as a rule of
thumb strings will be interpreted literally with HTML special
characters escaped for you (i.e. you don't need to put > in
the string, go ahead and put >), but if you want to translate to
gibberish on the fly or write a stylesheet to render strings in
tables as all caps you can do that too.
- tables. You can add data by rows or columns to an
and set row and column headers. You can set row and column
styles for tags and data, and even individual cell styles if you
want, so you can have pretty good control over how numbers and
rows are rendered even if you just add a column of numbers as
numbers. If you need to get and set the contents of individual
cells, you can do that too; the table is automatically grown to
whatever dimensions you need.
A simple example, taken from the account-summary report:
(let ((table (gnc:make-html-table)))
;; column 1: account names (just strings)
(gnc:html-table-append-column!
table
(map (lambda (acct)
(let ((name (gnc:account-get-name acct)))
(gnc:make-html-text
(gnc:html-markup-anchor
(string-append "gnc-register:account=" name)
(gnc:account-get-name acct)))))
accounts))
;; column 2: balances
(gnc:html-table-append-column!
table
(map (lambda (acct)
(gnc:account-get-balance acct))
accounts))
;; column headers
(gnc:html-table-set-column-headers!
table
(list (_ "Account name") (_ "Balance")))
(gnc:html-document-add-object! doc table))
The 'html-markup-anchor' bit just makes each account name into an
anchor that opens the appropriate register, like
My Bank Account
The contents of a cell can be any type of data or any
. There's a special kind of that's
only useful in tables: the . You want to use
this if you need to specify a rowspan or colspan that is not 1;
table cells have methods to set the rowspan and colspan.
- pie charts. Pie charts are simple affairs. They have data (a
list of numbers), a title and subtitle, a list of colors and a
list of labels. Colors and labels are matched to data points in
order.
- bar charts. Bar charts have "rows" and "columns" of data, where
the value of a data point determines the height of the bar and
you generally want more rows than columns:
X X Y
X Y X Y X : Column 0
X Y X Y Y : Column 1
X Y X Y
X Y X Y
r0c0 r0c1 r1c0 r1c1
Row 0 Row 1
Each column has a distinct color (gnc:html-bar-chart-set-column-colors!)
and there's a legend that associates column colors with column labels.
Row labels are printed under row groupings.
- scatter charts. There's a vector of "X", and a vector of "Y", of
the same length; a marker-style; a marker color; X label; and Y
label.
Styles
------
We have three different kinds of style control information available:
- markup styles, embodied by the class.
Whenever ANY HTML tag is rendered, a markup style is looked up
starting from the most local context and working back to the
and . The markup style associates
a tag name with an structure, which has
the following fields:
- the HTML tag to render (specified by 'tag). Note that this
may be different from the tag used to look up the style (the
one passed to html-markup).
- Any attributes to be used inside the start tag (listed
individually as 'attribute (list name value))
- The font face to use in the body ('font-face)
- The font size to use ('font-size)
- The font color ('font-color)
These properties are merged hierarchically, with the child (most
local context) taking precedence. For all fields except the
'attributes', it's a "child-take-all" strategy, i.e. the child's
style completely overrides the parent's. For 'attributes', as long as
the child does not explicitly specify a new HTML tag the attributes are
merged with the parent's, with the child's style winning if both
parent and child specify values for the same attribute.
Each type of is responsible for creating its own
"local style context" and representing local style info, so to set
the style for (for example) an HTML text object, you call
(gnc:html-text-set-style! obj "tag" style-info).
The style-info for any html-object is a bunch of field-value pairs:
;; render all bold text as big and green as well as bold
(gnc:html-text-set-style! my-txt "b"
'font-color "00ff00" 'font-size 7)
;; make sure all table cells are aligned right
(gnc:html-table-set-style! my-table "td"
'attribute '("align" "right"))
You can use ANY string as a markup tag, not just legit HTML ones.
By default, the HTML tag used is the same as the markup tag
(which is why the examples above don't specify a 'tag in the
style info.. it's OK to do so but redundant) but if you pass
another tag it's used instead; 'tag "" means use no tag at all.
So here's how to make text marked up with the tag "bigred" be big
and red:
(let ((doc (gnc:make-html-document))
(txt (gnc:make-html-text)))
;; add some text with bigred markup to the
;; text object
(gnc:html-text-append! txt
(gnc:html-markup "bigred"
(gnc:html-markup-p
"This is big red text.")
(gnc:html-markup-p
(gnc:html-markup-b
"This is big red BOLD text."))))
;; set the style for bigred text .. it doesn't have to
;; be before bigred markup is used, just before the
;; object is rendered.
(gnc:html-text-set-style! txt
"bigred" 'tag "" 'font-color "ff0000" 'font-size 7)
(gnc:html-document-add-object! doc txt)
(gnc:html-document-render doc))
This will render to
This is big red text.
This is big red BOLD text
- data styles, embodied by the class.
Whenever a rendering function needs to render a literal piece of
Scheme data, it invokes the data style to figure out how. As of
this writing, an record just contains a
function which knows how to render a particular type of data, but
this may change in the future.
Data styles and markup styles are both set through the
"-set-style" methods for various classes, with the difference
being that the type name is the key (i.e. "")
instead of the HTML tag name. For built-in Scheme types, there
are special cases: "", "", and "" are
defined. Since angle brackets aren't legal in HTML tags, there's
no namespace conflict; if your record type names (as returned by
(record-type-name (record-type-descriptor obj)) don't start and
end with angle brackets they will be added for setting and
getting data style information. You shouldn't have to worry about
this.
- "style sheets", embodied by the class. They
are more like templates than CSS stylesheets. Every
has a stylesheet, and that stylesheet controls
the HTML "frame" that the document is rendered into, as well as
setting up the highest level of markup styles and data styles for
rendering the document.
Every report now automatically has a "Stylesheet" option in the
"General" section which allows a stylesheet to be selected. At
the moment, the list of available stylesheets is fixed when the
report is created; this is a bug but I'm not sure how to fix it.
The default stylesheet (if none is specified in the report) puts
the trivial HTML frame around your 's
s:
Your Title Here
Your Objects Rendered Here
It has some basic options for setting the background color, table
formatting, background pixmap, and so on.
Other stylesheets haven't been written as of this writing, but
may put your objects in a cell of an HTML table with a fancy logo
in the top right, the document title in a "prepared for" section
with a date, letterhead or other graphics in the background, and
so on. Some or all of this should be customized in the style
sheet's parameter editor, which uses the same framework as the
report parameters editor and the Gnucash preferences editor.
Creating stylesheets:
You can create your own style sheets through the
gnc:define-html-style-sheet function. Defining a style sheet is
much like defining a report: you need an 'options' object and a
'renderer' function, plus a style-sheet version and name.
Since the stylesheet is really defining a complete HTML document
itself, you should use an created locally in the
stylesheet renderer to handle all the rendering. Just use the
default stylesheet for that document so you don't get off into
recursive meta-style land. It will DTRT. As you are creating
the document, grab the appropriate objects and styles from the
user document (the one you are writing a style sheet for, which
is passed as an argument to the renderer) and stuff them in the
local html-document.
This sounds more confusing than it actually is. Here's a very
simple style sheet that provides a configurable background color
for any report that uses it. Of course you could just add a bg
color as an option to every report; the advantage of doing it
here is that when you change the bgcolor in the stylesheet
options editor, it changes it in every report that uses that
style sheet.
There's a new "Style Sheet Dialog" in gnucash that lets you copy
and modify existing stylesheets from the GUI, so you can take a
distributed stylesheet, copy it with a new name, and change the
parameters to customize it.
(let ()
(define sample-options
(let* ((options (gnc:new-options))
(opt-register
(lambda (opt)
(gnc:register-option options opt))))
(opt-register
(gnc:make-color-option
(_ "Style Sheet Options")
(_ "Background Color") "a" (_ "Background color for reports.")
(list #xff #xff #xff 0)
255 #f))
options))
(define (sample-renderer options doc)
(let ((ssdoc (gnc:make-html-document))
(bgcolor
(gnc:color-option->html
(gnc:lookup-option options
(_ "Style Sheet Options")
(_ "Background Color")))))
;; this says that the HTML tag should have
;; the attributes bgcolor=COLOR, so it will get
;; rendered like
(gnc:html-document-set-style!
ssdoc "body" 'attributes
(with-output-to-string
(lambda ()
(display "bgcolor=")
(display bgcolor))))
;; if you want to set default markup and data styles for
;; documents using this stylesheet, do it here. This
;; style element dosn't do anything, it's just there
;; as an example:
(gnc:html-document-set-style! ssdoc "p" 'tag "p")
;; grab the markup and data styles and objects out of the
;; document... you have to do this in every style sheet!
(gnc:html-document-push-style
ssdoc (gnc:html-document-style doc))
(gnc:html-document-append-objects!
ssdoc (gnc:html-document-objects doc))
;; render the ssdocument (using the trivial stylesheet).
;; since the objects from 'doc' are now in ssdoc, this
;; renders the whole package.
(gnc:html-document-render ssdoc)))
(gnc:define-html-style-sheet
'version 1
'name "Sample Style Sheet"
'renderer sample-renderer
'options sample-options))