- From: Jon Bosak <bosak@atlantic-83.Eng.Sun.COM>
- Date: Fri, 19 Jul 1996 00:32:17 -0700
- To: www-style@w3.org
- cc: bosak@atlantic-83.Eng.Sun.COM
Readers of this list may be interested in the following message, which was posted this evening to comp.text.sgml. Jon ------------------------------------------------------------------------ Newsgroups: comp.text.sgml Subject: DSSSL style sheet for HTML 3.2 hardcopy Below is a first attempt at a DSSSL (dsssl-o) style sheet for producing hardcopy output from documents validated against the HTML 3.2 DTD. It supports most of the features of HTML 3.2 that make any sense in printed form and adds a couple of features of its own, notably headers, footers, and the autonumbering of heads and table captions. The style sheet was tested using an alpha version of James Clark's DSSSL engine. Since precompiled binaries of the alpha were available only for Windows 95, that was the platform for which the style sheet was written. Very minor and obvious adjustments should be sufficient to adapt it to other platforms. Thanks are due to Anders Berglund for putting together the foundation of the style sheet, and of course to James Clark for taking the time to answer a number of foolish questions and for producing yet another basic piece of the Grand Solution. Since this style sheet is my first attempt to write in DSSSL, Scheme, or any other functional language, I would be grateful for suggestions on how to improve it. Jon --- Jon Bosak, Online Information Technology Architect SunSoft, 2550 Garcia Ave., MPK17-101, Mountain View, CA 94043 A Davenport Group Sponsor -- see http://www.ora.com/davenport/ ------------------------------- cut here -------------------------------- <!doctype style-sheet system "style-sh.dtd"> ;; ###################################################################### ;; ;; DSSSL style sheet for HTML 3.2 print output ;; ;; 1996.07.18 ;; ;; Jon Bosak, SunSoft, based on work by Anders Berglund, EBT, ;; with critical assistance from James Clark ;; ;; ###################################################################### ;; Features in HTML 3.2 that are not implemented in the style sheet: ;; ;; automatic table column widths ;; % on width attribute for TABLE ;; attributes on TH and TD: align, valign, rowspan, colspan ;; attributes on TABLE: width, align, border, cellspacing, cellpadding ;; start attribute on OL ;; value attribute on LI ;; noshade attribute on HR ;; ;; See also "Non-Printing Elements" below ;; ;; Features in the style sheet that are not in HTML 3.2: ;; ;; page headers that display the HEAD TITLE content ;; page footers that display the page number ;; optional autonumbering of heads and table captions ;; support for named units (pt, pi, cm, mm) in size attributes ;; ============================== UNITS ================================ (define-unit mm .001m) (define-unit cm .01m) (define-unit in 25.4cm) (define-unit pi (/ 1in 6)) (define-unit pt (/ 1in 72)) (define-unit px (/ 1in 96)) ;; see below for definition of "em" ;; =========================== PARAMETERS ============================== ;; Visual acuity levels are "normal", "presbyopic", and ;; "large-type"; set the line following to choose the level (define *visual-acuity* "normal") ;; (define *visual-acuity* "presbyopic") ;; (define *visual-acuity* "large-type") (define *bf-size* (case *visual-acuity* (("normal") 10pt) (("presbyopic") 12pt) (("large-type") 24pt))) (define *mf-size* *bf-size*) (define-unit em *bf-size*) ;; these font selections are for Windows 95 (define *title-font-family* "Arial") (define *body-font-family* "Times New Roman") (define *mono-font-family* "Courier New") (define *dingbat-font-family* "Wingdings") ;; these "bullet strings" are a hack that is completely dependent on ;; the Wingdings font family selected above; consider this a ;; placeholder for suitable ISO 10646 characters (define *disk-bullet* "l") (define *circle-bullet* "¡") (define *square-bullet* "o") (define *small-diamond-bullet* "w") (define *line-spacing-factor* 1.1) (define *head-before-factor* 0.75) (define *head-after-factor* 0.5) (define *autonum-level* 6) ;; zero disables autonumbering (define *page-width* 8.5in) (define *page-height* 11in) (define *left-right-margin* 6pi) (define *top-margin* (if (equal? *visual-acuity* "large-type") 7.5pi 6pi)) (define *bottom-margin* (if (equal? *visual-acuity* "large-type") 7.5pi 6pi)) (define *header-margin* (if (equal? *visual-acuity* "large-type") 4.5pi 3pi)) (define *footer-margin* 3pi) (define *text-width* (- *page-width* (* *left-right-margin* 2))) (define *body-start-indent* 6pi) (define *body-width* (- *text-width* *body-start-indent*)) (define *para-sep* (/ *bf-size* 2.0)) (define *block-sep* (* *para-sep* 2.0)) (define *hsize-bump-factor* 1.2) (define *ss-size-factor* 0.6) (define *ss-shift-factor* 0.4) ;; ============================ FUNCTIONS ============================== (define (expt b n) (if (= n 0) 1 (* b (expt b (- n 1))))) (define upperalpha (list #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z)) (define loweralpha (list #\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z)) (define (ISALPHA? c) (if (or (member c upperalpha) (member c loweralpha)) #t #f)) (define (EQUIVLOWER c a1 a2) (cond ((null? a1) '()) ((char=? c (car a1)) (car a2)) ((char=? c (car a2)) c) (else (EQUIVLOWER c (cdr a1) (cdr a2))))) (define (char-downcase c) (EQUIVLOWER c upperalpha loweralpha)) (define (LOCASE slist) (if (null? slist) '() (cons (char-downcase (car slist)) (LOCASE (cdr slist))))) (define (STR2LIST s) (let ((start 0) (len (string-length s))) (let loop ((i start) (l len)) (if (= i len) '() (cons (string-ref s i) (loop (+ i 1) l)))))) (define (LIST2STR x) (apply string x)) (define (STRING-DOWNCASE s) (LIST2STR (LOCASE (STR2LIST s)))) (define (UNAME-START-INDEX u last) (let ((c (string-ref u last))) (if (ISALPHA? c) (if (= last 0) 0 (UNAME-START-INDEX u (- last 1))) (+ last 1)))) ;; this doesn't deal with "%" yet (define (PARSEDUNIT u) (if (string? u) (let ((strlen (string-length u))) (if (> strlen 2) (let ((u-s-i (UNAME-START-INDEX u (- strlen 1)))) (if (= u-s-i 0) ;; there's no number here 1pi ;; so return something that might work (if (= u-s-i strlen) ;; there's no unit name here (* (string->number u) 1px) ;; so default to pixels (3.2) (let* ((unum (string->number (substring u 0 u-s-i))) (uname (STRING-DOWNCASE (substring u u-s-i strlen)))) (case uname (("mm") (* unum 1mm)) (("cm") (* unum 1cm)) (("in") (* unum 1in)) (("pi") (* unum 1pi)) (("pc") (* unum 1pi)) (("pt") (* unum 1pt)) (("px") (* unum 1px)) (("barleycorn") (* unum 2pi)) ;; extensible! (else (cond ((number? unum) (* unum 1px)) ((number? (string->number u)) (* (string->number u) 1px)) (else u)))))))) (if (number? (string->number u)) (* (string->number u) 1px) 1pi))) 1pi)) (define (INLIST?) (or (have-ancestor? "OL") (have-ancestor? "UL") (have-ancestor? "DIR") (have-ancestor? "MENU") (have-ancestor? "DL"))) (define (HSIZE n) (* *bf-size* (expt *hsize-bump-factor* n))) (define (OLSTEP) (case (modulo (length (hierarchical-number-recursive "OL")) 4) ((1) 1.2em) ((2) 1.2em) ((3) 1.6em) ((0) 1.4em))) (define (ULSTEP) 1em) (define (PQUAD) (case (attribute-string "align") (("LEFT") 'start) (("CENTER") 'center) (("RIGHT") 'end) (else (inherited-quadding)))) (define (HQUAD) (cond ((string? (attribute-string "align")) (PQUAD)) ((have-ancestor? "CENTER") 'center) ((have-ancestor? "DIV") (inherited-quadding)) (else 'start))) (define (BULLSTR sty) (case sty (("CIRCLE") *circle-bullet*) (("SQUARE") *square-bullet*) (else *disk-bullet*))) ;; ====================== NON-PRINTING ELEMENTS ======================== ;; Note that HEAD includes TITLE, ISINDEX, BASE, META, STYLE, ;; SCRIPT, and LINK as possible children (element HEAD (empty-sosofo)) (element FORM (empty-sosofo)) (element APPLET (empty-sosofo)) (element PARAM (empty-sosofo)) (element TEXTFLOW (empty-sosofo)) (element MAP (empty-sosofo)) (element AREA (empty-sosofo)) ;; ============================ TOP LEVEL ============================== (element HTML (make simple-page-sequence font-family-name: *body-font-family* font-size: *bf-size* line-spacing: (* *bf-size* *line-spacing-factor*) left-header: (make sequence font-size: (- *bf-size* 1pt) line-spacing: (* (- *bf-size* 1pt) *line-spacing-factor*) font-posture: 'italic (process-first-descendant "TITLE")) right-footer: (make sequence font-size: (- *bf-size* 1pt) line-spacing: (* (- *bf-size* 1pt) *line-spacing-factor*) (literal "Page ") (page-number-sosofo)) top-margin: *top-margin* bottom-margin: *bottom-margin* left-margin: *left-right-margin* right-margin: *left-right-margin* header-margin: *header-margin* footer-margin: *footer-margin* page-width: *page-width* page-height: *page-height* input-whitespace-treatment: 'collapse quadding: 'justify (process-children-trim))) (element BODY (process-children-trim)) ;; ========================= BLOCK ELEMENTS ========================== ;; .......................... Generic DIV ............................ (element DIV (let ((align (attribute-string "align"))) (make display-group quadding: (case align (("LEFT") 'start) (("CENTER") 'center) (("RIGHT") 'end) (else 'justify)) (process-children-trim)))) ;; "CENTER is a shorthand for DIV with ALIGN=CENTER" (3.2 DTD) (element CENTER (make display-group quadding: 'center (process-children-trim))) ;; ................ Headings H1, H2, H3, H4, H5, H6 ................. (element H1 (make paragraph font-family-name: *title-font-family* font-weight: 'bold font-size: (HSIZE 4) line-spacing: (* (HSIZE 4) *line-spacing-factor*) space-before: (* (HSIZE 4) *head-before-factor*) space-after: (* (HSIZE 4) *head-after-factor*) start-indent: *body-start-indent* first-line-start-indent: (- *body-start-indent*) quadding: (HQUAD) keep-with-next?: #t (process-children-trim))) (element H2 (make paragraph font-family-name: *title-font-family* font-weight: 'bold font-size: (HSIZE 3) line-spacing: (* (HSIZE 3) *line-spacing-factor*) space-before: (* (HSIZE 3) *head-before-factor*) space-after: (* (HSIZE 3) *head-after-factor*) start-indent: *body-start-indent* first-line-start-indent: (- *body-start-indent*) quadding: (HQUAD) keep-with-next?: #t (literal (if (>= *autonum-level* 2) (let ((x (element-number-list (list "H1" "H2")))) (string-append (format-number (list-ref x 1) "1") ". ")) (string-append ""))) (process-children-trim))) (element H3 (make paragraph font-family-name: *title-font-family* font-weight: 'bold font-size: (HSIZE 2) line-spacing: (* (HSIZE 2) *line-spacing-factor*) space-before: (* (HSIZE 2) *head-before-factor*) space-after: (* (HSIZE 2) *head-after-factor*) start-indent: *body-start-indent* first-line-start-indent: (- *body-start-indent*) quadding: (HQUAD) keep-with-next?: #t (literal (if (>= *autonum-level* 3) (let ((x (element-number-list (list "H1" "H2" "H3")))) (string-append (format-number (list-ref x 1) "1") "." (format-number (list-ref x 2) "1") " ")) (string-append ""))) (process-children-trim))) (element H4 (make paragraph font-family-name: *title-font-family* font-weight: 'bold font-size: (HSIZE 1) line-spacing: (* (HSIZE 1) *line-spacing-factor*) space-before: (* (HSIZE 1) *head-before-factor*) space-after: (* (HSIZE 1) *head-after-factor*) start-indent: *body-start-indent* quadding: (HQUAD) keep-with-next?: #t (literal (if (>= *autonum-level* 4) (let ((x (element-number-list (list "H1" "H2" "H3" "H4")))) (string-append (format-number (list-ref x 1) "1") "." (format-number (list-ref x 2) "1") "." (format-number (list-ref x 3) "1") " ")) (string-append ""))) (process-children-trim))) (element H5 (make paragraph font-family-name: *title-font-family* font-weight: 'bold font-size: (HSIZE 0) line-spacing: (* (HSIZE 0) *line-spacing-factor*) space-before: (* (HSIZE 0) *head-before-factor*) space-after: (* (HSIZE 0) *head-after-factor*) start-indent: *body-start-indent* quadding: (HQUAD) keep-with-next?: #t (literal (if (>= *autonum-level* 5) (let ((x (element-number-list (list "H1" "H2" "H3" "H4" "H5")))) (string-append (format-number (list-ref x 1) "1") "." (format-number (list-ref x 2) "1") "." (format-number (list-ref x 3) "1") "." (format-number (list-ref x 4) "1") " ")) (string-append ""))) (process-children-trim))) (element H6 (make paragraph font-family-name: *title-font-family* font-posture: 'italic font-size: (HSIZE 0) line-spacing: (* (HSIZE 0) *line-spacing-factor*) space-before: (* (HSIZE 0) *head-before-factor*) space-after: (* (HSIZE 0) *head-after-factor*) start-indent: *body-start-indent* quadding: (HQUAD) keep-with-next?: #t (literal (if (>= *autonum-level* 6) (let ((x (element-number-list (list "H1" "H2" "H3" "H4" "H5" "H6")))) (string-append (format-number (list-ref x 1) "1") "." (format-number (list-ref x 2) "1") "." (format-number (list-ref x 3) "1") "." (format-number (list-ref x 4) "1") "." (format-number (list-ref x 5) "1") " ")) (string-append ""))) (process-children-trim))) ;; .......................... Paragraphs ............................. (define p-style (style font-size: *bf-size* line-spacing: (* *bf-size* *line-spacing-factor*))) (element P (make paragraph use: p-style space-before: *para-sep* start-indent: *body-start-indent* quadding: (PQUAD) (process-children-trim))) (element ADDRESS (make paragraph use: p-style font-posture: 'italic space-before: *para-sep* start-indent: *body-start-indent* (process-children-trim))) (element BLOCKQUOTE (make paragraph font-size: (* *bf-size* (/ 5.0 6.0)) line-spacing: (* *bf-size* *line-spacing-factor* (/ 5.0 6.0)) space-before: *para-sep* start-indent: (+ *body-start-indent* 1em) end-indent: 1em (process-children-trim))) (define (MONOPARA) (make paragraph use: p-style space-before: *para-sep* start-indent: (+ *body-start-indent* 1em) lines: 'asis font-family-name: *mono-font-family* font-size: *mf-size* input-whitespace-treatment: 'preserve (process-children-trim))) (element PRE (MONOPARA)) (element XMP (MONOPARA)) (element LISTING (MONOPARA)) (element PLAINTEXT (MONOPARA)) (element BR (make display-group (empty-sosofo))) ;; ................. Lists: UL, OL, DIR, MENU, DL .................... (element UL (make display-group space-before: (if (INLIST?) *para-sep* *block-sep*) space-after: (if (INLIST?) *para-sep* *block-sep*) start-indent: (if (INLIST?) (inherited-start-indent) *body-start-indent*))) (element (UL LI) (make paragraph use: p-style space-before: (if (attribute-string "compact" (ancestor "UL")) 0 *para-sep*) start-indent: (+ (inherited-start-indent) (ULSTEP)) first-line-start-indent: (- (ULSTEP)) (make line-field font-family-name: *dingbat-font-family* font-size: (- *bf-size* 2pt) field-width: (ULSTEP) (literal (let ((litype (attribute-string "type")) (ultype (attribute-string "type" (ancestor "UL")))) (cond (litype (BULLSTR litype)) (ultype (BULLSTR ultype)) (else *disk-bullet*))))) (process-children-trim))) (element (UL LI UL LI) (make paragraph use: p-style space-before: (if (attribute-string "compact" (ancestor "UL")) 0 *para-sep*) start-indent: (+ (inherited-start-indent) (ULSTEP)) first-line-start-indent: (- (ULSTEP)) (make line-field font-family-name: *dingbat-font-family* font-size: *bf-size* field-width: (ULSTEP) (literal (let ((litype (attribute-string "type")) (ultype (attribute-string "type" (ancestor "UL")))) (cond (litype (BULLSTR litype)) (ultype (BULLSTR ultype)) (else *small-diamond-bullet*))))) (process-children-trim))) (element (UL LI P) (make paragraph use: p-style start-indent: (+ (inherited-start-indent) (OLSTEP)) first-line-start-indent: (- (OLSTEP)) (process-children-trim))) (element OL (make display-group space-before: (if (INLIST?) *para-sep* *block-sep*) space-after: (if (INLIST?) *para-sep* *block-sep*) start-indent: (if (INLIST?) (inherited-start-indent) *body-start-indent*))) (element (OL LI) (make paragraph use: p-style space-before: (if (attribute-string "compact" (ancestor "OL")) 0 *para-sep*) start-indent: (+ (inherited-start-indent) (OLSTEP)) first-line-start-indent: (- (OLSTEP)) (make line-field field-width: (OLSTEP) (literal (case (modulo (length (hierarchical-number-recursive "OL")) 4) ((1) (string-append (format-number (child-number) "1") ".")) ((2) (string-append (format-number (child-number) "a") ".")) ((3) (string-append "(" (format-number (child-number) "i") ")")) ((0) (string-append "(" (format-number (child-number) "a") ")"))))) (process-children-trim))) (element (OL LI P) (make paragraph use: p-style start-indent: (+ (inherited-start-indent) (OLSTEP)) first-line-start-indent: (- (OLSTEP)) (process-children-trim))) ;; Note that DIR cannot properly have block children. Here DIR is ;; interpreted as an unmarked list without extra vertical ;; spacing. (element DIR (make display-group space-before: (if (INLIST?) *para-sep* *block-sep*) space-after: (if (INLIST?) *para-sep* *block-sep*) start-indent: (if (INLIST?) (inherited-start-indent) *body-start-indent*))) (element (DIR LI) (make paragraph use: p-style start-indent: (+ (inherited-start-indent) (* 2.0 (ULSTEP))) first-line-start-indent: (- (ULSTEP)) (process-children-trim))) ;; Note that MENU cannot properly have block children. Here MENU is ;; interpreted as a small-bulleted list with no extra vertical ;; spacing. (element MENU (make display-group space-before: (if (INLIST?) *para-sep* *block-sep*) space-after: (if (INLIST?) *para-sep* *block-sep*) start-indent: (if (INLIST?) (inherited-start-indent) *body-start-indent*))) (element (MENU LI) (make paragraph use: p-style start-indent: (+ (inherited-start-indent) (ULSTEP)) first-line-start-indent: (- (ULSTEP)) (make line-field font-family-name: *dingbat-font-family* font-size: *bf-size* field-width: (ULSTEP) (literal "w")) (process-children-trim))) ;; Note that DLs do not nest (element DL (make display-group space-before: (if (INLIST?) *para-sep* *block-sep*) space-after: (if (INLIST?) *para-sep* *block-sep*) start-indent: (if (INLIST?) (+ (inherited-start-indent) 2em) (+ *body-start-indent* 2em)) (make paragraph))) (element DT (let ((compact (attribute-string "compact" (ancestor "DL")))) (if compact (make line-field field-width: 3em (process-children-trim)) (make paragraph use: p-style space-before: *para-sep* first-line-start-indent: -1em (process-children-trim))))) (element DD (let ((compact (attribute-string "compact" (ancestor "DL")))) (if compact (sosofo-append (process-children-trim) (make paragraph-break)) (make paragraph use: p-style start-indent: (+ (inherited-start-indent) 2em) (process-children-trim))))) ;; ======================== INLINE ELEMENTS ========================== (define (BOLD-SEQUENCE) (make sequence font-posture: 'bold (process-children-trim))) (element B (BOLD-SEQUENCE)) (element EM (BOLD-SEQUENCE)) (element STRONG (BOLD-SEQUENCE)) (define (ITALIC-SEQUENCE) (make sequence font-posture: 'italic (process-children-trim))) (element I (ITALIC-SEQUENCE)) (element CITE (ITALIC-SEQUENCE)) (element VAR (ITALIC-SEQUENCE)) (define (MONO-SPACE-SEQUENCE) (make sequence font-family-name: *mono-font-family* font-size: *mf-size* (process-children-trim))) (element TT (MONO-SPACE-SEQUENCE)) (element CODE (MONO-SPACE-SEQUENCE)) (element KBD (MONO-SPACE-SEQUENCE)) (element SAMP (MONO-SPACE-SEQUENCE)) (element DFN (make sequence font-weight: 'bold font-posture: 'italic (process-children-trim))) (element STRIKE (make score type: 'through (process-children-trim))) (element U (make score type: 'after (process-children-trim))) (element SUP (make sequence font-size: (* (inherited-font-size) *ss-size-factor*) position-point-shift: (+ (* (inherited-font-size) *ss-shift-factor*)) (process-children-trim))) (element SUB (make sequence font-size: (* (inherited-font-size) *ss-size-factor*) position-point-shift: (- (* (inherited-font-size) *ss-shift-factor*)) (process-children-trim))) (element BIG (make sequence font-size: (/ (inherited-font-size) *ss-size-factor*) line-spacing: (/ (inherited-line-spacing) *ss-size-factor*))) (element SMALL (make sequence font-size: (* (inherited-font-size) *ss-size-factor*))) (element FONT (let ((fsize (attribute-string "SIZE"))) (make sequence font-size: (if fsize (PARSEDUNIT fsize) (inherited-font-size))))) ;; ============================= LINKS =============================== (element A (make sequence font-weight: 'bold font-posture: 'italic (process-children-trim))) ;; ============================ TABLES =============================== (element TABLE (make display-group content-map: '((caption #f)) (make table start-indent: *body-start-indent* (make sequence start-indent: 0pt)))) (element CAPTION (make paragraph label: 'caption use: p-style font-weight: 'bold space-before: *para-sep* space-after: (/ *para-sep* 2.0) start-indent: *body-start-indent* (literal (string-append "Table " (format-number (element-number) "1") ". ")) (process-children-trim))) (element TR (make table-row (process-children-trim))) (element TH (make table-cell (make paragraph font-weight: 'bold space-before: 0.25em space-after: 0.25em start-indent: 0.25em end-indent: 0.25em quadding: 'start (process-children-trim)))) (element TD (make table-cell (make paragraph space-before: 0.25em space-after: 0.25em start-indent: 0.25em end-indent: 0.25em quadding: 'start (process-children-trim)))) ;; ============================== RULES ================================ (element HR (let ((align (attribute-string "ALIGN")) (noshade (attribute-string "NOSHADE")) (size (attribute-string "SIZE")) (width (attribute-string "WIDTH"))) (make rule orientation: 'horizontal space-before: *block-sep* space-after: *block-sep* line-thickness: (if size (PARSEDUNIT size) 1pt) length: (if width (PARSEDUNIT width) *body-width*) display-alignment: (case align (("LEFT") 'start) (("CENTER") 'center) (("RIGHT") 'end) (else 'end))))) ;; ============================= GRAPHICS =============================== ;; Note that DSSSL does not support text flowed around an object, ;; so the action of the ALIGN attribute is merely to shift the image ;; to the left or right (element IMG (make external-graphic entity-system-id: (attribute-string "src") display?: #t space-before: 1em space-after: 1em display-alignment: (case (attribute-string "align") (("LEFT") 'start) (("RIGHT") 'end) (else 'center))))
Received on Friday, 19 July 1996 03:33:46 UTC