diff options
author | P. J. McDermott <pjm@nac.net> | 2012-08-12 16:38:48 (EDT) |
---|---|---|
committer | P. J. McDermott <pjm@nac.net> | 2012-08-12 16:38:48 (EDT) |
commit | 8f4cc803a74728b07532a6c2ff2788b63b012d3c (patch) | |
tree | 2b7f018fead897b18a6ee94715519b942f71a5ef | |
parent | a5d1ce13172e8d91d24ede48f28cc269e3d1aa23 (diff) | |
parent | 37555e439fc99e0f14fa9d6c65c3a9db2de1b9da (diff) |
Merge branch 'markdown-book' of git://git.os.pehjota.net/users/pehjota/doc/policies
-rw-r--r-- | Makefile | 58 | ||||
-rw-r--r-- | include/document.tmpl | 69 | ||||
-rw-r--r-- | include/main.css | 105 | ||||
-rw-r--r-- | notes/markdown-vs-multimarkdown.txt | 23 | ||||
-rw-r--r-- | scripts/MarkdownBook/Book.pm | 264 | ||||
-rw-r--r-- | scripts/MarkdownBook/Document.pm | 245 | ||||
-rw-r--r-- | scripts/MarkdownBook/Document/html.pm | 153 | ||||
-rw-r--r-- | scripts/MarkdownBook/Document/txt.pm | 46 | ||||
-rw-r--r-- | scripts/MarkdownBook/HTMLTree.pm | 44 | ||||
-rw-r--r-- | scripts/MarkdownBook/Section.pm | 67 | ||||
-rwxr-xr-x | scripts/markdownbook.pl | 32 |
11 files changed, 1089 insertions, 17 deletions
@@ -1,23 +1,47 @@ -.SUFFIXES: -.SUFFIXES: .txt .html +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SRCS = -OBJS = $(SRCS:.txt=.html) +BOOKS = inclusion +FORMATS = html txt -.PHONY: all -all: $(OBJS) +# Default format in case a book target is called without FORMAT=* +FORMAT = html -$(OBJS): - @printf ' RENDER %s\n' '$@' - @title=$$(sed -n 's/^ Title: \(.*\)$$/\1/p' $*.txt | \ - head -n 1); \ - sed "s#@TITLE@#$$title#" include/header.html > $@ - @markdown $*.txt >> $@ - @cat include/footer.html >> $@ +all: $(FORMATS) + +books: $(BOOKS) + +$(FORMATS): + @make 'FORMAT=$@' books + +$(BOOKS): + @printf ' RENDER %s as %s\n' '$@' '$(FORMAT)' + @PERL5LIB=scripts scripts/markdownbook.pl '$(FORMAT)' 'policies/$@' + @[ '$(FORMAT)' = 'html' ] && \ + ln -sf '../../include/main.css' 'policies/$@/main.css' || true -.PHONY: clean clean: - @for obj in $(OBJS); do \ - printf ' RM %s\n' "$${obj}"; \ - rm -f $${obj}; \ + @for book in $(BOOKS); do \ + printf ' CLEAN %s\n' "$${book}"; \ + rm -f "policies/$${book}/"*.html; \ + rm -f "policies/$${book}/"*.txt; \ + rm -f "policies/$${book}/"main.css; \ done diff --git a/include/document.tmpl b/include/document.tmpl new file mode 100644 index 0000000..1f914cd --- /dev/null +++ b/include/document.tmpl @@ -0,0 +1,69 @@ +<!doctype html> + +<html> + <head> + <meta charset="utf-8"> +<TMPL_IF NAME="IS_INDEX"> + <title><TMPL_VAR NAME="BOOK_TITLE"></title> +<TMPL_ELSE> + <title><TMPL_VAR NAME="BOOK_TITLE"> - <TMPL_VAR NAME="TITLE"></title> +</TMPL_IF> + <link type="text/css" rel="stylesheet" href="main.css"> + <link rel="start" href="index.html"> +<TMPL_IF NAME="PREV_LINK"> + <link rel="prev" href="<TMPL_VAR NAME="PREV_LINK">"> +</TMPL_IF> +<TMPL_IF NAME="NEXT_LINK"> + <link rel="next" href="<TMPL_VAR NAME="NEXT_LINK">"> +</TMPL_IF> + </head> + <body> + <header> + <nav> + <ul> +<TMPL_IF NAME="PREV_LINK"> + <li class="prev"> + <a href="<TMPL_VAR NAME="PREV_LINK">">Prev</a> + </li> +</TMPL_IF> +<TMPL_IF NAME="NEXT_LINK"> + <li class="next"> + <a href="<TMPL_VAR NAME="NEXT_LINK">">Next</a> + </li> +</TMPL_IF> + <li class="start"> + <a href="index.html">Home</a> + </li> + </ul> + </nav> + <h1><TMPL_VAR NAME="BOOK_TITLE"></h1> +<TMPL_UNLESS NAME="IS_INDEX"> + <h2><TMPL_VAR NAME="CHAPT_TITLE"></h2> +</TMPL_UNLESS> + </header> + <section> +<TMPL_VAR NAME="BODY"> + </section> + <footer> + <nav> + <ul> +<TMPL_IF NAME="PREV_LINK"> + <li class="prev"> + <a href="<TMPL_VAR NAME="PREV_LINK">">Prev</a> + <TMPL_VAR NAME="PREV_TITLE"> + </li> +</TMPL_IF> +<TMPL_IF NAME="NEXT_LINK"> + <li class="next"> + <a href="<TMPL_VAR NAME="NEXT_LINK">">Next</a> + <TMPL_VAR NAME="NEXT_TITLE"> + </li> +</TMPL_IF> + <li class="start"> + <a href="index.html">Home</a> + </li> + </ul> + </nav> + </footer> + </body> +</html> diff --git a/include/main.css b/include/main.css new file mode 100644 index 0000000..6df6c2b --- /dev/null +++ b/include/main.css @@ -0,0 +1,105 @@ +* { + margin: 0px; + padding: 0px; + font-family: serif; + color: #000000; +} +body { + margin: 0px; + padding: 0px 12px; + background-color: #E0E0E0; +} +h1, h2 { + font-weight: bold; +} +a { + color: #0000FF; + text-decoration: underline; +} + +nav { + margin: 0px; + padding: 24px 12px; + height: 1%; + overflow: auto; +} +header nav { + border-bottom: 1px solid #666666; + margin-bottom: 24px +} +footer nav { + border-top: 1px solid #666666; + margin-top: 24px +} +nav ul { + display: inline; + list-style-type: none; +} +nav ul li.prev { + float: left; + width: 40%; + text-align: left; +} +nav ul li.next { + float: right; + width: 40%; + text-align: right; +} +nav ul li a { + display: block; +} +footer nav ul li a { + margin-bottom: 8px; +} +nav ul li.start { + text-align: center; + width: 20%; + margin: 0px auto; +} +header h1 { + font-size: 20pt; + font-weight: bold; + margin: 6px 12px; +} +header h2 { + font-size: 18pt; + font-weight: bold; + margin: 6px 12px; +} + +section { + margin: 0px; + margin: 12px 12px 24px 12px; +} + +section h1 { + font-size: 16pt; + font-weight: bold; + margin: 0px 0px 12px 0px; +} +section h2 { + font-size: 14pt; + font-weight: bold; + margin: 0px 0px 12px 0px; +} +section p { + font-size: 12pt; + margin: 0px 0px 12px 0px; +} +section li { + font-size: 12pt; +} +section code { + background-color: #EEEECC; + font-family: monospace; +} +section pre { + background-color: #EEEECC; + margin: 0px 0px 12px 16px; +} +section ul, section ol { + margin: 0px 0px 12px 24px; +} +section ul ul, section ol ol { + margin: 0px 0px 0px 24px; +} diff --git a/notes/markdown-vs-multimarkdown.txt b/notes/markdown-vs-multimarkdown.txt new file mode 100644 index 0000000..1d71d57 --- /dev/null +++ b/notes/markdown-vs-multimarkdown.txt @@ -0,0 +1,23 @@ +Markdown: <http://daringfireball.net/projects/markdown/> +MultiMarkdown: <http://fletcherpenney.net/multimarkdown/> + +MultiMarkdown seems to provide much of what we currently need and may want in +the future: cross references, footnotes, tables, and PDF rendering (though this +can be done with Markdown as well using Pandoc). Why don't we just use that to +do all our work for us? + +The cross references (hyperlinks to sections) only work within a single +document. We want policy manuals to have a chapter/appendix per document (e.g. +HTML file). MultiMarkdown doesn't really support hyperlinks to sections in +other documents. So while MMD might be nice for its other features, it looks +like we should still handle in-book hyperlinks with our own pre-processing. + +Then why don't we just use normal hyperlinks to sections in different chapters? +This assumes we're outputting documents only in HTML. We would have source text +like this: + + See [Library Packages](binpkgs.html#librarypackages) for more information. + +Rendering a plain text file that refers to other plain text files with a ".html" +extension makes no sense. Additionally, this fails to work in the case of PDF +rendering. diff --git a/scripts/MarkdownBook/Book.pm b/scripts/MarkdownBook/Book.pm new file mode 100644 index 0000000..4f30afb --- /dev/null +++ b/scripts/MarkdownBook/Book.pm @@ -0,0 +1,264 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use Carp; + +package MarkdownBook::Book; + +sub new +{ + my ($class, $format, $dir) = @_; + my $self; + my $control_fh; + + $class = ref($class) || $class; + $self = {}; + bless($self, $class); + + unless ($format =~ m/^[a-z]+/) { + Carp::croak('Invalid format "' . $format . '"'); + } + eval { + require 'MarkdownBook/Document/' . $format . '.pm'; + 1; + } or Carp::croak('Unsupported format "' . $format . '"'); + $self->{'format'} = $format; + + $self->{'dir'} = $dir; + $self->{'docs'} = []; + $self->{'sections'} = []; + $self->{'sections_by_id'} = {}; + + open($control_fh, '<', $dir . '/control'); + while (<$control_fh>) { + chomp($_); + if (m/^Title: /) { + s/^Title: (.*)$/$1/; + $self->{'title'} = $_; + } + } + close($control_fh); + + $self->create_documents(); + + return $self; +} + +sub dir +{ + my ($self, $dir) = @_; + my $old = $self->{'dir'}; + + $self->{'dir'} = $dir if defined($dir); + + return $old; +} + +sub title +{ + my ($self, $title) = @_; + my $old = $self->{'title'}; + + $self->{'title'} = $title if defined($title); + + return $old; +} + +sub documents +{ + my ($self, $documents) = @_; + my $old = $self->{'docs'}; + + $self->{'docs'} = $documents if defined($documents); + + return $old; +} + +sub _get_document_module +{ + my ($self) = @_; + + return 'MarkdownBook::Document::' . $self->{'format'}; +} + +sub create_documents +{ + my ($self) = @_; + my $series_fh; + my $file; + my $title; + my $i; + my $doc; + my $doc_prev; + my @letters; + + # Create index document. + $doc = $self->_get_document_module()->new($self, 'index', 'index', + undef, $self->{'title'}); + $doc_prev = $doc; + push(@{$self->{'docs'}}, $doc); + + # Create chapter documents. + $i = 0; + open($series_fh, '<', $self->{'dir'} . '/chapters') + or Carp::croak('Cannot open chapters file'); + while (<$series_fh>) { + chomp($_); + ($file, $title) = split(/[ \t]+/, $_, 2); + $doc = $self->_get_document_module()->new($self, 'chapter', $file, + ++$i, $title); + $doc->prev($doc_prev); + $doc_prev->next($doc) if defined $doc_prev; + $doc_prev = $doc; + push(@{$self->{'docs'}}, $doc); + } + close($series_fh); + + # Create appendix documents. + $i = -1; + @letters = ('A' .. 'Z'); + if (-e $self->{'dir'} . '/appendices') { + open($series_fh, '<', $self->{'dir'} . '/apendices') + or Carp::croak('Cannot open appendices file'); + while (<$series_fh>) { + chomp($_); + ($file, $title) = split(/[ \t]+/, $_, 2); + $doc = $self->_get_document_module()->new($self, 'appendix', $file, + $letters[++$i], $title); + $doc->prev($doc_prev); + $doc_prev->next($doc) if defined $doc_prev; + $doc_prev = $doc; + push(@{$self->{'docs'}}, $doc); + } + close($series_fh); + } +} + +sub add_section +{ + my ($self, $section) = @_; + + push(@{$self->{'sections'}}, $section); + + # Index sections (not documents) by ID. + if (ref($section) eq 'MarkdownBook::Section') { + $self->{'sections_by_id'}->{$section->id()} = $section; + } +} + +sub subst_macros +{ + my ($self, $text) = @_; + + # Substitute macros with arguments. + $text =~ s/ + \$ # Dollar sign + \[ # Left square bracket + ([^\]]+) # Macro + \] # Right square bracket + ( + \[ # Left square bracket + ([^\]]+) # Macro arguments + \] # Right square bracket + ) + /$self->_do_subst_macro($1, split(m@[ \t]+@, $3))/exg; + + # Substitute macros without arguments. + $text =~ s/ + \$ # Dollar sign + \[ # Left square bracket + ([^\]]+) # Macro + \] # Right square bracket + /$self->_do_subst_macro($1)/exg; + + return $text; +} + +sub _do_subst_macro +{ + my ($self, $macro, @args) = @_; + my $sec; + + if ($macro eq 'toc') { + return $self->_do_gen_toc(); + } elsif ($macro eq 'sectlink') { + if (@args != 1) { + Carp::carp('Invalid arguments to "sectlink" macro'); + } else { + $sec = $self->{'sections_by_id'}->{$args[0]}; + return '[ยง ' . $sec->number() . '][' . $sec->id() . ']'; + } + } else { + Carp::carp("Unrecognized macro \"$macro\""); + } +} + +sub _do_gen_toc +{ + my ($self) = @_; + my $section; + my $toc = ''; + + foreach $section (@{$self->{'sections'}}) { + $toc .= "\n" if $toc ne ''; + if (ref($section) =~ m/^MarkdownBook::Document/) { + next if $section->type() eq 'index'; + $toc .= ' * ['; + $toc .= $section->id(); + $toc .= ' '; + $toc .= $section->title(); + $toc .= ']['; + $toc .= $section->file(); + $toc .= ']'; + } elsif (ref($section) eq 'MarkdownBook::Section') { + $toc .= ' ' x $section->level(); + $toc .= ($section->level() == 1 ? ' - [' : ' * ['); + $toc .= $section->number(); + $toc .= ' '; + $toc .= $section->title(); + $toc .= ']['; + $toc .= $section->id(); + $toc .= ']'; + } + } + + return $toc; +} + +sub parse +{ + my ($self) = @_; + my $doc; + + foreach $doc (@{$self->{'docs'}}) { + $self->add_section($doc); + $doc->parse(); + } +} + +sub output +{ + my ($self) = @_; + my $doc; + + foreach $doc (@{$self->{'docs'}}) { + $doc->output(); + } +} + +1; diff --git a/scripts/MarkdownBook/Document.pm b/scripts/MarkdownBook/Document.pm new file mode 100644 index 0000000..2a30396 --- /dev/null +++ b/scripts/MarkdownBook/Document.pm @@ -0,0 +1,245 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use MarkdownBook::Section; +use Carp; + +package MarkdownBook::Document; + +sub new +{ + my ($class, $book, $type, $file, $id, $title) = @_; + my $self; + + $class = ref($class) || $class; + $self = {}; + bless($self, $class); + + $self->{'book'} = $book; + $self->{'type'} = $type; + $self->{'file'} = $file; + $self->{'id'} = $id; + $self->{'title'} = $title; + + return $self; +} + +sub book +{ + my ($self, $book) = @_; + my $old = $self->{'book'}; + + $self->{'book'} = $book if defined($book); + + return $old; +} + +sub type +{ + my ($self, $type) = @_; + my $old = $self->{'type'}; + + $self->{'type'} = $type if defined($type); + + return $old; +} + +sub file +{ + my ($self, $file) = @_; + my $old = $self->{'file'}; + + $self->{'file'} = $file if defined($file); + + return $old; +} + +sub file_path +{ + my ($self) = @_; + + return $self->{'book'}->dir() . '/' . $self->{'file'}; +} + +sub id +{ + my ($self) = @_; + + return $self->{'id'}; +} + +sub title +{ + my ($self, $title) = @_; + my $old = $self->{'title'}; + + $self->{'title'} = $title if defined($title); + + return $old; +} + +sub full_title +{ + my ($self) = @_; + + if ($self->{'type'} eq 'chapter') { + return sprintf('Chapter %d - %s', $self->{'id'}, $self->{'title'}); + } elsif ($self->{'type'} eq 'appendix') { + return sprintf('Appendix %s - %s', $self->{'id'}, $self->{'title'}); + } else { + return undef; + } +} + +sub prev +{ + my ($self, $other) = @_; + my $old = $self->{'prev'}; + + $self->{'prev'} = $other if defined($other); + + return $old; +} + +sub next +{ + my ($self, $other) = @_; + my $old = $self->{'next'}; + + $self->{'next'} = $other if defined($other); + + return $old; +} + +sub sections +{ + my ($self) = @_; + + # FIXME: Why is this necessary?! + foreach my $sec (@{$self->{'sections'}}) { + } + + return $self->{'sections'}; +} + +sub parse +{ + my ($self) = @_; + my $source_fh; + my $source_text; + + open($source_fh, '<', + $self->{'book'}->dir() . '/' . $self->{'file'} . '.mdwn') + or Carp::croak('Cannot open "' . $self->{'file'} . '" source document'); + $source_text = join('', <$source_fh>); + close($source_fh); + + # Parse headings of non-index documents. + if ($self->{'type'} ne 'index') { + $self->{'section_level_numbers'} = [0, 0]; + $self->{'section_level'} = -1; + $source_text =~ s/ + ^ + (.+) # Heading text + [ \t]* # Optional trailing whitespace + \n # Line break + (=+|-+) # Underline + [ \t]* # Optional trailing whitespace + $ + /$self->_do_heading($1, $2)/mexg; + } + + # Store parsed text. + $self->{'source_text'} = $source_text; +} + +sub _do_heading +{ + my ($self, $text, $underline) = @_; + my $level; + my $levels; + my $section_number; + my $section_title; + my $section_id; + my $section; + + # Shorten underline to one character. + $underline =~ s/^([=-]).*$/$1/; + + # Detect heading level. + if ($underline eq '=') { + $level = 1; + } else { + $level = 2; + } + + # Calculate section number. + $levels = $#{$self->{'section_level_numbers'}}; + if ($level != $self->{'section_level'}) { + foreach (@{$self->{'section_level_numbers'}}[$level .. $levels]) { + $_ = 0; + } + } + $self->{'section_level'} = $level; + ++${$self->{'section_level_numbers'}}[$level - 1]; + $section_number = join('.', @{$self->{'section_level_numbers'}}); + + # Add document ID to section number. + $section_number = $self->{'id'} . '.' . $section_number; + + # Trim off unused subsection parts. + $section_number =~ s/(?:\.0)*$//; + + # Parse out section title. + $section_title = $text; + $section_title =~ s/ + ^ + ([^\[]+) # Section title + [ \t]+ # Whitespace + \[ # Left square bracket + [^\]]+ # Section ID + \] # Right square bracket + $ + /$1/x; + + # Parse out section ID. + $section_id = $text; + $section_id =~ s/ + ^ + [^\[\]]+ # Section title + [ \t]+ # Whitespace + \[ # Left square bracket + ([^\]]+) # Section ID + \] # Right square bracket + $ + /$1/x; + + # Create and store section object. + $section = MarkdownBook::Section->new($self, + $level, $section_number, $section_id, $section_title); + push(@{$self->{'sections'}}, $section); + $self->{'book'}->add_section($section); + + # Prepend number to section title. + $text = $section_number . ' ' . $section_title; + + # Return underlined section title. + return $text . "\n" . $underline x length($text); +} + +1; diff --git a/scripts/MarkdownBook/Document/html.pm b/scripts/MarkdownBook/Document/html.pm new file mode 100644 index 0000000..60f22c7 --- /dev/null +++ b/scripts/MarkdownBook/Document/html.pm @@ -0,0 +1,153 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use MarkdownBook::Document; +use MarkdownBook::HTMLTree; +use Carp; +use Text::Markdown; +use HTML::TreeBuilder; +use HTML::Template; + +package MarkdownBook::Document::html; + +our @ISA = qw(MarkdownBook::Document); + +sub output +{ + my ($self) = @_; + my $text; + my $doc; + my $sec; + + # Substitute macros. + $text = $self->{'book'}->subst_macros( + $self->{'source_text'}); + + # Append link definitions. + $text .= "\n"; + foreach $doc (@{$self->{'book'}->documents()}) { + $text .= "\n"; + $text .= '['; + $text .= $doc->file(); + $text .= ']: '; + $text .= $doc->file(); + $text .= '.html'; + foreach $sec (@{$doc->sections()}) { + $text .= "\n"; + $text .= '['; + $text .= $sec->id(); + $text .= ']: '; + $text .= $doc->file(); + $text .= '.html#'; + $text .= $sec->id(); + } + } + + # Convert to HTML. + $text = Text::Markdown::Markdown($text); + + # Set "id" attributes of headings. + $self->_do_set_heading_id_attrs($text); + + # Output the templated HTML. + $self->_do_output_template(); + + # Clean up. + $self->{'tree'}->delete(); +} + +sub _do_set_heading_id_attrs +{ + my ($self, $text) = @_; + my @headings; + my $heading; + my $i = -1; + + # Parse HTML. + $self->{'tree'} = HTML::TreeBuilder->new(); + $self->{'tree'}->parse($text); + $self->{'tree'}->eof($text); + + # Find the "body" element. + @{$self->{'tree_body'}} = MarkdownBook::HTMLTree::find_elements_by_tag_names( + $self->{'tree'}, ('body')); + + # Don't modify headings of index documents. + return if $self->{'type'} eq 'index'; + + # Find all headings. + @headings = MarkdownBook::HTMLTree::find_elements_by_tag_names( + @{$self->{'tree_body'}}[0], ('h1', 'h2')); + + # Set "id" attributes. + foreach $heading (@headings) { + $heading->attr('id', ${$self->{'sections'}}[++$i]->id()); + } +} + +sub _do_output_template +{ + my ($self) = @_; + + my $doc_tmpl; + my %opt_end_tags; + my $elem; + my $body; + my $doc_fh; + + $doc_tmpl = HTML::Template->new(filename => 'include/document.tmpl'); + + # Don't omit any end tags. + %opt_end_tags = map([$_ => 0], %HTML::Element::optionalEndTag); + + # Get HTML text of all children of the "body" element. + foreach $elem (@{$self->{'tree_body'}}[0]->content_list()) { + # It's safe to assume (ref($elem) eq 'HTML::Element'). + $body .= $elem->as_HTML('<>&', '', \%opt_end_tags) . "\n"; + } + + $doc_tmpl->param(IS_INDEX => ($self->{'type'} eq 'index')); + + $doc_tmpl->param(BOOK_TITLE => $self->{'book'}->title()); + $doc_tmpl->param(TITLE => $self->{'title'}); + $doc_tmpl->param(CHAPT_TITLE => $self->full_title()); + + if (defined($self->{'prev'})) { + $doc_tmpl->param(PREV_LINK => $self->{'prev'}->file() . '.html'); + $doc_tmpl->param(PREV_TITLE => $self->{'prev'}->title()); + } else { + $doc_tmpl->param(PREV_LINK => undef); + $doc_tmpl->param(PREV_TITLE => undef); + } + if (defined($self->{'next'})) { + $doc_tmpl->param(NEXT_LINK => $self->{'next'}->file() . '.html'); + $doc_tmpl->param(NEXT_TITLE => $self->{'next'}->title()); + } else { + $doc_tmpl->param(NEXT_LINK => undef); + $doc_tmpl->param(NEXT_TITLE => undef); + } + + $doc_tmpl->param(BODY => $body); + + open($doc_fh, '>', $self->file_path() . '.html') + or Carp::croak('Cannot open "' . $self->{'file'} . '" destination document'); + $doc_tmpl->output(print_to => $doc_fh); + close($doc_fh); +} + +1; diff --git a/scripts/MarkdownBook/Document/txt.pm b/scripts/MarkdownBook/Document/txt.pm new file mode 100644 index 0000000..f150477 --- /dev/null +++ b/scripts/MarkdownBook/Document/txt.pm @@ -0,0 +1,46 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use MarkdownBook::Document; +use Carp; + +package MarkdownBook::Document::txt; + +our @ISA = qw(MarkdownBook::Document); + +sub output +{ + my ($self) = @_; + my $out_fh; + + open($out_fh, '>', + $self->{'book'}->dir() . '/' . $self->{'file'} . '.txt') + or Carp::croak('Cannot open "' . $self->{'file'} . + '" destination document'); + + # Print document title. + print($out_fh $self->{'title'} . "\n" . + '*' x length($self->{'title'}) . "\n\n\n"); + + # Print document text with macro substitutions. + print($out_fh $self->{'book'}->subst_macros($self->{'source_text'})); + + close($out_fh); +} + +1; diff --git a/scripts/MarkdownBook/HTMLTree.pm b/scripts/MarkdownBook/HTMLTree.pm new file mode 100644 index 0000000..e01bab0 --- /dev/null +++ b/scripts/MarkdownBook/HTMLTree.pm @@ -0,0 +1,44 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use HTML::Element; + +package MarkdownBook::HTMLTree; + +sub find_elements_by_tag_names +{ + my ($elem, @tagnames) = @_; + + my @list = $elem->content_list(); + my $tag; + my @retlist = (); + + foreach (@list) { + if (ref($_) ne 'HTML::Element') { + next; + } + $tag = $_->tag(); + if (grep($_ eq $tag, @tagnames)) { + push(@retlist, $_); + } + } + + return @retlist; +} + +1; diff --git a/scripts/MarkdownBook/Section.pm b/scripts/MarkdownBook/Section.pm new file mode 100644 index 0000000..82ff897 --- /dev/null +++ b/scripts/MarkdownBook/Section.pm @@ -0,0 +1,67 @@ +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +package MarkdownBook::Section; + +sub new +{ + my ($class, $doc, $lev, $num, $id, $title) = @_; + my $self; + + $class = ref($class) || $class; + $self = {}; + bless($self, $class); + + $self->{'document'} = $doc; + $self->{'level'} = $lev; + $self->{'number'} = $num; + $self->{'id'} = $id; + $self->{'title'} = $title; + + return $self; +} + +sub level +{ + my ($self) = @_; + + return $self->{'level'}; +} + +sub number +{ + my ($self) = @_; + + return $self->{'number'}; +} + +sub id +{ + my ($self) = @_; + + return $self->{'id'}; +} + +sub title +{ + my ($self) = @_; + + return $self->{'title'}; +} + +1; diff --git a/scripts/markdownbook.pl b/scripts/markdownbook.pl new file mode 100755 index 0000000..cceecdb --- /dev/null +++ b/scripts/markdownbook.pl @@ -0,0 +1,32 @@ +#! /usr/bin/perl +# +# Copyright (C) 2012 Patrick "P. J." McDermott +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +use MarkdownBook::Book; + +my $format; +my $doc_dir; +my $book; + +($format, $doc_dir) = @ARGV; + +$book = MarkdownBook::Book->new($format, $doc_dir); + +$book->parse(); +$book->output(); |