# 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 . 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 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;