|
| 1 | +;;; Generate a RTFD document with different text styles |
| 2 | +;;; |
| 3 | +;;; This is a demo of library (lispkit styled-text). It explains how to |
| 4 | +;;; programmatically generate a complex text document, composing it out of |
| 5 | +;;; several different paragraph styles, text styles, and text block styles. |
| 6 | +;;; The example also shows how to create styled lists and styled text tables |
| 7 | +;;; and explains how to nest them. |
| 8 | +;;; |
| 9 | +;;; Author: Matthias Zenger |
| 10 | +;;; Copyright © 2022 Matthias Zenger. All rights reserved. |
| 11 | +;;; |
| 12 | +;;; Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 13 | +;;; use this file except in compliance with the License. You may obtain a copy |
| 14 | +;;; of the License at |
| 15 | +;;; |
| 16 | +;;; http://www.apache.org/licenses/LICENSE-2.0 |
| 17 | +;;; |
| 18 | +;;; Unless required by applicable law or agreed to in writing, software |
| 19 | +;;; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 20 | +;;; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 21 | +;;; License for the specific language governing permissions and limitations |
| 22 | +;;; under the License. |
| 23 | + |
| 24 | +; Import required libraries |
| 25 | + |
| 26 | +(import (lispkit base) |
| 27 | + (lispkit draw) |
| 28 | + (lispkit styled-text)) |
| 29 | + |
| 30 | +; Load all the fonts we need upfront |
| 31 | + |
| 32 | +(define title-font (font "Times" 20 bold)) |
| 33 | +(define header-font (font "Times" 17 bold)) |
| 34 | +(define body-font (font "Times" 15 normal)) |
| 35 | +(define bold-font (font "Times" 15 bold)) |
| 36 | +(define italic-font (font "Times" 15 normal italic)) |
| 37 | +(define small-font (font "Times" 13 bold)) |
| 38 | + |
| 39 | +; Define common paragraph styles |
| 40 | + |
| 41 | +(define title-style |
| 42 | + (make-paragraph-style |
| 43 | + 'alignment: 'center |
| 44 | + 'paragraph-spacing-after: 8 |
| 45 | + 'paragraph-spacing-before: 14)) |
| 46 | + |
| 47 | +(define header-style |
| 48 | + (make-paragraph-style |
| 49 | + 'alignment: 'left |
| 50 | + 'paragraph-spacing-after: 9 |
| 51 | + 'paragraph-spacing-before: 14)) |
| 52 | + |
| 53 | +(define body-style |
| 54 | + (make-paragraph-style |
| 55 | + 'alignment: 'justified |
| 56 | + 'paragraph-spacing-after: 6)) |
| 57 | + |
| 58 | +(define body-style-left (copy-paragraph-style body-style)) |
| 59 | +(paragraph-style-set! body-style-left 'alignment 'left) |
| 60 | + |
| 61 | +(define indented-style |
| 62 | + (make-paragraph-style |
| 63 | + 'alignment: 'left |
| 64 | + 'paragraph-spacing-after: 8 |
| 65 | + 'paragraph-spacing-before: 8 |
| 66 | + 'first-head-indent: 20 |
| 67 | + 'head-indent: 20 |
| 68 | + 'tail-indent: 500)) |
| 69 | + |
| 70 | +; Start document with a title that is centered and shown in blue. Subsequent elements of |
| 71 | +; the document are appended to styled text `document` |
| 72 | + |
| 73 | +(define document |
| 74 | + (styled-text "Generated Sample Document\n" title-font blue title-style)) |
| 75 | + |
| 76 | +; First section |
| 77 | + |
| 78 | +; Add the section title |
| 79 | +(styled-text-append! document |
| 80 | + (styled-text "Overview of the approach\n" header-font black header-style)) |
| 81 | +; Start with regular text |
| 82 | +(styled-text-append! document |
| 83 | + (styled-text "Lorem ipsum dolor sit amet, " body-font black body-style)) |
| 84 | +; Inject some text in italics |
| 85 | +(styled-text-append! document |
| 86 | + (styled-text "consectetur adipiscing elit" italic-font black body-style)) |
| 87 | +(styled-text-append! document |
| 88 | + (styled-text ". Maecenas lectus orci, accumsan sed arcu ornare, " body-font black body-style)) |
| 89 | +; Inject some text in bold |
| 90 | +(styled-text-append! document |
| 91 | + (styled-text "egestas mattis diam" bold-font black body-style)) |
| 92 | +; And now two paragraphs are added to showcase the paragraph spacing |
| 93 | +(styled-text-append! document |
| 94 | + (styled-text |
| 95 | + (string-append |
| 96 | + ". Vestibulum ut mauris quis nulla laoreet varius. Etiam luctus ac diam tincidunt " |
| 97 | + "pharetra. Nulla a mi sed nunc sagittis venenatis. Curabitur quis lorem et ipsum " |
| 98 | + "aliquam maximus. Duis eget magna mauris. Sed malesuada est rhoncus eros hendrerit " |
| 99 | + "porttitor.\n" |
| 100 | + "Aenean sagittis ac mi vitae vehicula. Nam consequat eros aliquam sem feugiat " |
| 101 | + "aliquet. Cras ultrices pretium eros ut euismod. Aliquam nec turpis sed urna " |
| 102 | + "condimentum ullamcorper a tristique augue.\n") |
| 103 | + body-font black body-style)) |
| 104 | + |
| 105 | +; Second section |
| 106 | + |
| 107 | +; We start with a section title and some normal text first |
| 108 | +(styled-text-append! document |
| 109 | + (styled-text "Detailed exploration\n" header-font black header-style)) |
| 110 | +(styled-text-append! document |
| 111 | + (styled-text |
| 112 | + (string-append |
| 113 | + "Donec rhoncus, neque a consequat ultrices, massa erat tempor nulla, et sodales " |
| 114 | + "nulla lectus facilisis enim. Pellentesque habitant morbi tristique senectus et " |
| 115 | + "netus et malesuada fames ac turpis egestas. Nam quis vestibulum augue.\n") |
| 116 | + body-font black body-style-left)) |
| 117 | +; The following paragraph gets indented and its width gets capped; text is shown |
| 118 | +; in italics. |
| 119 | +(styled-text-append! document |
| 120 | + (make-styled-text |
| 121 | + (string-append |
| 122 | + "Mauris scelerisque massa erat, maximus luctus purus laoreet nec. In et massa eu " |
| 123 | + "eros porttitor sollicitudin ut nec tortor. Suspendisse gravida magna placerat, " |
| 124 | + "volutpat enim ut, accumsan elit.\n") |
| 125 | + 'font: italic-font |
| 126 | + 'foreground-color: (color 0.3 0.3 0.3) |
| 127 | + 'paragraph-style: indented-style)) |
| 128 | + |
| 129 | +; Third section |
| 130 | + |
| 131 | +; The next section shows a table; we define a few text block styles ahead which will be |
| 132 | +; used to set up the table. Tables are not available on iOS, so the following section |
| 133 | +; is added only on macOS. |
| 134 | + |
| 135 | +(cond-expand (macos |
| 136 | + |
| 137 | +; Headers get centered |
| 138 | +(define table-header-style (make-paragraph-style 'alignment: 'center)) |
| 139 | + |
| 140 | +; The header row's first column takes 20% of the overall width, has a bold border at the |
| 141 | +; bottom and its background is light gray |
| 142 | +(define fst-head-style |
| 143 | + (make-text-block-style |
| 144 | + 'width: (percent 20) |
| 145 | + 'height: 20 |
| 146 | + 'border: '(1 1 1 2) |
| 147 | + 'padding: '(2 2 4 4) |
| 148 | + 'margin: 2 |
| 149 | + 'border-color: gray |
| 150 | + 'background-color: (color 0.9 0.9 0.9) |
| 151 | + 'vertical-alignment: 'middle)) |
| 152 | + |
| 153 | +; The header row's second column takes 80% of the overall width, it spans two regular |
| 154 | +; columns (we define this later). |
| 155 | +(define snd-head-style (copy-text-block-style fst-head-style)) |
| 156 | +(text-block-style-set! snd-head-style 'width (percent 80)) |
| 157 | + |
| 158 | +; The tables first column has again gray borders, takes up 20%, and has border, padding, |
| 159 | +; and margin spaces. |
| 160 | +(define fst-col-style |
| 161 | + (make-text-block-style |
| 162 | + 'width: (percent 20) |
| 163 | + 'border: 1 |
| 164 | + 'padding: 8 |
| 165 | + 'margin: 2 |
| 166 | + 'border-color: gray)) |
| 167 | + |
| 168 | +; The other two columns each take up 40% and are otherwise identical to the first column |
| 169 | +(define col-style (copy-text-block-style fst-col-style)) |
| 170 | +(text-block-style-set! col-style 'width (percent 40)) |
| 171 | + |
| 172 | +; We also predefine a simple nested table below (embedded in some text): |
| 173 | +(define nested-tbl |
| 174 | + (styled-text "Nullam efficitur tellus non molestie:\n" body-font black body-style-left)) |
| 175 | +(define table-start (string-length (styled-text-string nested-tbl))) |
| 176 | +(styled-text-append! nested-tbl |
| 177 | + (make-styled-text-table 2 |
| 178 | + (list |
| 179 | + (list "Lorem" "Dolor") |
| 180 | + (list "Ipsum" "Sit")) |
| 181 | + (make-text-block-style |
| 182 | + 'width: (percent 100) |
| 183 | + 'border: 1 |
| 184 | + 'padding: 2 |
| 185 | + 'margin: 2 |
| 186 | + 'border-color: (color 0.4 0.4 0.9) |
| 187 | + 'background-color: (color 0.93 0.93 1.0)) |
| 188 | + (make-paragraph-style |
| 189 | + 'paragraph-spacing-after: 8 |
| 190 | + 'paragraph-spacing-before: 2) |
| 191 | + #t)) |
| 192 | +(define table-end (string-length (styled-text-string nested-tbl))) |
| 193 | +(styled-text-append! nested-tbl |
| 194 | + (styled-text "Mauris egestas dolor mauris, id mollis urna ultrices feugiat." |
| 195 | + body-font black |
| 196 | + body-style-left)) |
| 197 | +; The following lines set all fonts in the nested table to `small-font` and the |
| 198 | +; foreground color of the text is set to dark blue. Patching attributes is another |
| 199 | +; possibility to change the style of a large section of text quickly. |
| 200 | +(styled-text-add! nested-tbl table-start table-end 'font small-font) |
| 201 | +(styled-text-add! nested-tbl table-start table-end 'foreground-color (color 0 0 0.6)) |
| 202 | + |
| 203 | +; Now we start adding the section title |
| 204 | +(styled-text-append! document |
| 205 | + (styled-text "Tabular data\n" header-font black header-style)) |
| 206 | +; ...followed by the table: |
| 207 | +(styled-text-append! document |
| 208 | + (make-styled-text-table 3 |
| 209 | + (list |
| 210 | + (list (list (make-styled-text "First column" |
| 211 | + 'font: (font "Helvetica-Bold" 12) |
| 212 | + 'paragraph-style: table-header-style) 1 fst-head-style) |
| 213 | + (list (make-styled-text "Second column\n(experimental)" |
| 214 | + 'font: (font "Helvetica-Bold" 12) |
| 215 | + 'paragraph-style: table-header-style) 2 snd-head-style)) |
| 216 | + (list (list (styled-text "Maecenas eget nibh felis. Etiam sagittis sit." |
| 217 | + body-font black body-style-left) 1 fst-col-style) |
| 218 | + (list (styled-text (string-append |
| 219 | + "Nunc pretium, ante porta varius eleifend, nulla quam tristique nisl, " |
| 220 | + "id vulputate felis est sed quam.") body-font black body-style-left) 1 col-style) |
| 221 | + (list (styled-text (string-append |
| 222 | + "Nam ac accumsan elit. Pellentesque habitant morbi tristique senectus " |
| 223 | + "et netus et malesuada fames ac turpis egestas.") body-font black) 1 col-style)) |
| 224 | + (list (list |
| 225 | + (styled-text (string-append |
| 226 | + "Maecenas non dui scelerisque, tincidunt lacus in, suscipit felis. " |
| 227 | + "Suspendisse pharetra.") body-font black body-style-left) 1 fst-col-style) |
| 228 | + (list nested-tbl 1 col-style) |
| 229 | + (styled-text (string-append |
| 230 | + "Aliquam velit est, congue sed sollicitudin in, luctus non enim. Nulla " |
| 231 | + "facilisi. Aliquam diam urna, ultrices facilisis diam quis, tempor " |
| 232 | + "pharetra felis. Nullam nec condimentum sapien. Nullam at dolor nec " |
| 233 | + "tortor.") body-font black body-style-left) |
| 234 | + )) |
| 235 | + col-style |
| 236 | + body-style-left |
| 237 | + #t)) |
| 238 | +)) |
| 239 | + |
| 240 | +; Fourth section |
| 241 | + |
| 242 | +; The last section showcases lists. We define the list style upfront here: |
| 243 | +(define list-style |
| 244 | + (make-paragraph-style |
| 245 | + 'alignment: 'left |
| 246 | + 'first-head-indent: 0.0 |
| 247 | + 'head-indent: 40.0 |
| 248 | + 'line-spacing: -1.0 |
| 249 | + 'paragraph-spacing-before: 4.0 |
| 250 | + 'paragraph-spacing-after: 4.0)) |
| 251 | +; Tab stops are used to format the table. All predefined tab stops are removed first and |
| 252 | +; then two tab stops are added. The first is for the number (it's right aligned), the |
| 253 | +; second is for the list content (it's left aligned). |
| 254 | +(paragraph-style-tabstops-clear! list-style) |
| 255 | +(paragraph-style-tabstop-add! list-style 30 'right) |
| 256 | +(paragraph-style-tabstop-add! list-style 40 'left) |
| 257 | + |
| 258 | +; Again, first the section header is added |
| 259 | +(styled-text-append! document |
| 260 | + (styled-text "Summary\n" header-font black header-style)) |
| 261 | +; ...followed by some text |
| 262 | +(styled-text-append! document |
| 263 | + (styled-text |
| 264 | + (string-append |
| 265 | + "Donec vestibulum tortor at est venenatis malesuada. Aliquam erat volutpat. " |
| 266 | + "Sed consectetur quam quis odio varius, eget facilisis justo pharetra. " |
| 267 | + "Nulla maximus vestibulum laoreet. Interdum et malesuada fames ac ante " |
| 268 | + "ipsum primis in faucibus.\n") |
| 269 | + body-font black body-style)) |
| 270 | +; ...followed by the enumerated list. |
| 271 | +(styled-text-append! document |
| 272 | + (styled-text |
| 273 | + (string-append |
| 274 | + "\t1.\tLorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas nec " |
| 275 | + "rhoncus erat, in ullamcorper diam. Pellentesque tristique pulvinar sagittis.\n" |
| 276 | + "\t2.\tAliquam id tortor ac nulla tempus auctor a ut sem. Nulla eget aliquam " |
| 277 | + "sem, a consequat nibh. Sed ullamcorper semper semper. Quisque mattis.\n" |
| 278 | + "\t3.\tVulputate accumsan. Morbi euismod dolor turpis, at dignissim augue " |
| 279 | + "maximus sed. Etiam neque tortor, venenatis non eros quis, dignissim rhoncus " |
| 280 | + "lectus. Duis accumsan diam eget neque rutrum bibendum. Suspendisse laoreet, " |
| 281 | + "turpis in tempus congue, sapien dui vulputate elit, vitae porttitor metus " |
| 282 | + "lorem vitae ante. Proin efficitur lorem vel dui finibus pretium. Donec " |
| 283 | + "pretium erat nec turpis laoreet sagittis.\n") |
| 284 | + body-font |
| 285 | + black |
| 286 | + list-style)) |
| 287 | + |
| 288 | +; Saving and opening the document |
| 289 | + |
| 290 | +; Path to the generated RTFD file |
| 291 | +(define rtfd-file-path |
| 292 | + (path (car (system-directory 'documents)) "sample-doc.rtfd")) |
| 293 | + |
| 294 | +; Save the document |
| 295 | +(save-styled-text rtfd-file-path document 'rtfd) |
| 296 | + |
| 297 | +; Open the document in a system-specific fashion |
| 298 | +(open-file rtfd-file-path) |
0 commit comments