]> www.wagner.pp.ru Git - fiction/Kate-the-Empress.git/blob - Tex2fb2
Kate-the-Empress.fb2 validates
[fiction/Kate-the-Empress.git] / Tex2fb2
1 #!/usr/bin/perl -CDS
2 use utf8;
3 # char-level modes
4 my $poetry = 0;
5 my $verbatim = 0;
6 my @sections;
7 my $buffer;
8 my $idseq = 0; # sequentual number of footnotes
9 my $footnotes="";
10 #
11 # TODO italic paragaphs
12 # footnotes
13 # epigraphs
14 #
15 # print fictionbook header
16 print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
17 print "<FictionBook xmlns=\"http://www.gribuser.ru/xml/fictionbook/2.0\"
18 xmlns:l=\"http://www.w3.org/1999/xlink\">\n";
19 my $metadata = shift @ARGV;
20 open F,"<",$metadata;
21 while (<F>) {
22         print $_;
23 }
24 close F;
25 print "<body>\n";
26 my $header =1;
27 LINE:
28 while (<>) {
29 $environ = undef;
30 if (/\\(begin|end){(\w+)}/) {
31         $environ=$2;
32         $begin=$1 eq "begin";
33         if ($environ eq 'verbatim') {
34                 $verbatim=$begin;
35         } elsif ($environ eq 'verse') {
36                 if ($begin) {
37                 pushsection("poem",undef);
38                 } else {
39                         add_to_section(tag($buffer,'stanza')."\n") if $buffer;
40                         $buffer="";
41                         flushsection('poem');
42                 }
43                 $poetry = $begin;
44         } elsif($environ eq 'document' && $begin) {
45                 $header=0;
46         }
47 }
48 next LINE if $header;
49 if ((/^$/ || $environ) && $buffer) {
50 #output on empty line (p or stanza) depending on poetry mode
51         add_to_section(tag(flushbuffer($buffer),$poetry?"stanza":"p")."\n");
52         $buffer="";
53 }
54 next LINE if $environ;
55 # Section headings
56 if (/\\(part|chapter|section|subsection|subsubsection)\*?{(.*)}/) {
57         if ($buffer) {
58                 add_to_section(tag(flushbuffer($buffer),$poetry?"stanza":"p"));
59                 $buffer="";
60         }
61         pushsection($1,tag($2,"p"));
62         next LINE;
63 }
64 if (/\\vspace{/) {
65         add_to_section("<empty-line />");
66         next LINE;
67 }
68 next LINE if /\\pagebreak\b/;
69 #normal mode: 
70 if (!$verbatim) {
71 #strip TeX comments 
72 s/([^\\])%.*$/$1/;
73 s/^%.*$//;
74 # strip \sloppy
75 s/\\sloppy\s+//g;
76 s/\\sloppy{}//g;
77 s/\\sloppy([^\w])/$1/g;
78 # strip extra space
79 s/^\s+//;
80 s/\s+$//;
81 s/(\s)\s+/$1/g;
82 #replace TeX ligatures ~ --- << >> \% with appropriate unicode symbols
83 s/~/\xA0/g;
84 s/---/—/g;
85 s/<</«/g;
86 s/>>/»/g;
87 s/\\%/%/g;
88 s/\\dots/\x{2026}/g;
89 }
90 #replace ' and " with entities
91 s/&/&amp;/g;
92 s/'/&apos;/g;
93 s/"/&quot;/g;
94 s/</&lt;/g;
95 s/>/&gt;/g;
96
97 if ($poetry) {
98         chomp;
99         s/\s*\\\\$//;
100   $buffer.=tag($_,'v')."\n";
101 } elsif ($verbatim) {
102         add_to_section(tag(tag($_,"code"),"p"));
103 } else {
104   $buffer.=" ".$_;
105 }
106 }
107 if ($buffer) {
108         add_to_section(tag(flushbuffer($buffer),"p"));
109         $buffer="";
110 }
111
112 while (@sections) {
113         flushsection();
114 }
115 print "</body>\n";
116 ## print footnotes
117 print "<body>\n$footnotes\n</body>" if $footnotes;
118 print "</FictionBook>";
119
120 sub add_to_section {
121         my $data = shift;
122         return if ($#sections<0) ;
123         if ($data =~ /^\s*<section>/ && $sections[$#sections]->{data} !~ /^\s*<section>/) {
124                 $sections[$#sections]->{data} = tag($sections[$#sections]->{data},"section")."\n";
125         }       
126         $sections[$#sections]->{data}.=$data;
127 }
128
129 sub flushsection {
130         my $tag= shift || 'section';
131         my $str = pop @sections;
132         my $content="";
133         if ($str->{title}) {
134                 $content = tag($str->{title},"title");
135         }
136         if ($str->{data} =~ /^\s*$/s) {
137                 $content .= "<p>\n</p>";
138         } else {
139                 $content .=  $str->{data};
140         }
141         if ($#sections >=0) {
142                 add_to_section(tag($content,$tag)."\n");
143         } else {
144                 print tag($content,$tag);
145         }
146 }
147
148 sub pushsection {
149         my ($level,$title)=@_;
150         # Find section of $level in the current stack
151         my $found=scalar(@sections);
152         LEVEL:
153         for (my $i=0;$i<=$#sections;$i++) {
154                 if ($sections[$i]->{level} eq $level) {
155                         $found=$i;
156                         last LEVEL;
157                 }
158         }
159         # if found, flush everything below
160         while (scalar(@sections) > $found) {
161                 flushsection;
162         }
163         push @sections,{level=>$level,title=>$title,data=>""};
164 }
165
166 sub tag {
167         my ($content,$name) = @_;
168         $content =~s/^\s+//s;
169         return "" unless $content;
170         return "<$name>$content</$name>";
171 }
172
173 sub flushbuffer {
174         local $_ = shift;
175         s/{\\(em|it|bf)(?:\s+|{})([^{}]+)}/<emphasis>$2<\/emphasis>/g;
176         s/\\(emph|textit|textbf){([^{}]+)}/<emphasis>$2<\/emphasis>/g;
177         s/\\footnote{(.*)}/push_footnote($1)/e;
178         s/[{}]//g;
179         return $_;
180 }
181
182
183 sub push_footnote {
184         my $id = "note_".(++$idseq);
185         $footnotes.="<section id=\"$id\">".tag(shift,'p')."</section>\n";
186         return "<a l:href=\"#$id\" type=\"note\">$idseq</a>";
187 }