180 likes | 215 Views
DefDoc is an extensible, dynamic document creation system that leverages CLOS for structuring document elements with mix-ins for layout concerns. It features both positives and negatives inherited from TeX. CLOS comes to rescue for info organization. The system provides a powerful layout engine for document preparation and conversion.
E N D
DefDOC An extensible, dynamic document creation system http://common-lisp.net/project/defdoc/ By Rahul Jain (rjain@common-lisp.net)
TeX The Positives • Excellent breaking algorithm • Layerable macros • Simple data model Nested sequences of hboxes and vboxes • Lots of existing libraries
TeX The Negatives • Syntax requires a lot of confusing escaping and unescaping • Simple data model Info is lost quickly • All data is stored in fixed-sized arrays … size is fixed at compile time!
CLOS to the Rescue! • CONTENTS slot for tree structure • Other slots indicate properties of document elements • Mixins allow orthogonalizing concerns such as: • Wrapping • Flowing • Floating • Layout direction
Examples (defclasslogical-structure-element (vertical-element vertical-container flowing-container-mixin) ()) (defclassparagraph (logical-structure-element wrapping-container-mixin) () (:documentation "A standard paragraph of text."))
More CLOS! (defclasshtml-output (output-destination pretty-printable-mixin) ()) (defclassplain-text-output (output-destination fixed-width-layout) ((page-width :initform (make-length 72 'em)))) (defclasspdf-output (output-destination fixed-layout) ())
Layout Engine (defgenericprepare (input output) (:documentation "Breaks up the input into the basic elements to be laid out …")) (defgenericconvert (input output) (:documentation "Converts the input into the format specified by the output.")) Multiple Dispatch!
HTML Ouptut (defmacrodefine-html-conversion (type (element &optional (html-class 'html-output)) &body body) `(defmethodconvert ((,element ,type) (.output. ,html-class)) (flet ((convert (&optional (element (contents ,element))) (convert element .output.)) (convert-to-text (element) (let ((*suppress-html-tags* t)) (convert element .output.)))) ,@body)))
HTML Output (define-html-conversionparagraph (input) (with-html-block (:P) (convert))) (define-html-conversionitemized-list (input) (with-html-block (:UL) (dolist (element (contents input)) (with-html-block (:LI) (convert element))))) (define-html-conversionlink (input) (with-html-inline (:A :href (url input)) (convert))) (define-html-conversiondiscretionary-hyphen (input) (write-html-entity "shy"))
Project Website (defclassproject-website (document) ((title :accessor name :initarg name :initform (error "Name is required.")) (subtitle :initarg short-description))) (defclassdocumentation (section) ((title :allocation :class :initform "Documentation"))) (defclasscode (section) ((title :allocation :class :initform "Code"))) (defclasscommunication (section) ((title :allocation :class :initform "Communication"))) (defclassdependencies (section) ((title :allocation :class :initform "Dependencies")))
Project Website (defmethodconvert ((input dependencies) (output t)) (convert (make-instance 'itemized-list (contents input)) output)) (defgenericsection-before-p (o1 o2)) #.`(progn ,@ (let ((order '(documentation code dependencies communication))) (loop for (prior . following-list) on order nconc (loop for following in following-list collect `(defmethodsection-before-p ((o1 ,prior) (o2 ,following)) t) collect `(defmethodsection-before-p ((o2 ,following) (o1 ,prior)) nil))))) (defmethod prepare :before ((input project-website) output) (setf (contents input) (sort (contents input) 'section-before-p)))
The DefDoc Website (defpackage:defdoc.website (:use :common-lisp :defdoc :defdoc.elements :defdoc.layout :defdoc.frontends.basic :defdoc.contrib.project-website)) (in-package :defdoc.website) Starts off looking a lot like lisp code!
The DefDoc Website (defabbreviationDefDoc (doc italic () "Def") (doc small-caps () "Doc")) (defabbreviationLaTeX "LaTeX") Abstraction is a beautiful thing!
The DefDoc Website (defungen-src-link (target) (concatenate 'string "/cgi-bin/viewcvs.cgi/DefDoc/src" target "?cvsroot=defdoc" (unless (eql (aref target (1- (length target))) #\/) "&rev=HEAD&content-type=text/vnd.viewcvs-markup"))) (defungen-mailing-list-link (target) (concatenate 'string "/mailman/listinfo/defdoc-" target)) Defun, huh.
The DefDoc Website (defdocindex (project-website name (doc DefDoc) short-description "…" author "Rahul Jain") Note the class name and initargs
The DefDoc Website (documentation () (paragraph () "I have written up a document that is an overview“ "of the goals and rationale behind "(DefDoc)". " "It is available as " (link (url "overview.tex") (LaTeX))", " (link (url "overview.pdf") "PDF")", and " (link (url "overview.ps") "Postscript")".")) Note the usage of the abbreviations
The DefDoc Website (code () (paragraph () "The code is very incomplete, but what there is is " (link (url (gen-src-link "/")) "publically available")". " "There are definitions for various types of " (link (url (gen-src-link "/elements/")) "document elements")". " "There are also definitions for the abstract types of " (link (url (gen-src-link "/layout/")) "layout engines") " as well as the " (link (url (gen-src-link "/layout/html-engine.lisp")) " HTML layout engine")". " "Finally, there is a " (link (url (gen-src-link "/frontends/basic/")) "basic frontend")", " "essentially a few simple macros for creating "(DefDoc)" documents. " "A more fully-featured frontend is planned, which will be " "sensitive to the current context and allow texual content " "to be entered unquoted.") ; Ugh, that last sentence says it all… (paragraph () "This site is actually a "(DefDoc)" " (link (url (gen-src-link "/../website/website.lisp")) "document")"."))
The DefDoc Website (dependencies () (link (url "http://www.common-lisp.net/projects/rjain-utils/specified-types.html") "Specified types")) (communication () (paragraph () "There are 3 mailing lists for "(DefDoc)", " (link (url (gen-mailing-list-link "announce")) "defdoc-announce")", " (link (url (gen-mailing-list-link "devel")) "defdoc-devel")", and " (link (url (gen-mailing-list-link "cvs")) "defdoc-cvs")".")))