Add rule regarding safekeeping of special objects in Kai Monastery. Harmonise translation
[project-aon.git] / common / scripts / create-epub-cover.pl
1 #!/usr/bin/perl
2 #
3 # Generate covers for books to use as cover in ePUBs
4
5 # This program is copyright 2012, 2017 by Javier Fernandez-Sanguino <jfs@computer.org>
6 #
7 #    This program is free software; you can redistribute it and/or modify
8 #    it under the terms of the GNU General Public License as published by
9 #    the Free Software Foundation; either version 2 of the License, or
10 #    (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #    You should have received a copy of the GNU General Public License
18 #    along with this program; if not, write to the Free Software
19 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 #
21 # For more information please see
22 #  http://www.gnu.org/licenses/licenses.html#GPL
23
24
25 use strict;
26 use warnings;
27
28 use File::Path qw(mkpath);
29
30 my $PROGRAM_NAME    = 'gbtoepub';
31 my $USAGE           = "$PROGRAM_NAME [options] book-code\n\t--meta=[metadata file]\n\t--xml=[book XML]\n\t--language=[language area of input data (output determined by meta file)]\n\t--font-files=[font-files]\n\t--no-validate\n\t--verbose\n";
32
33 my $FILENAME_SEPARATOR = '/';
34
35 my $CONVERT    = qx{which convert};
36 my $CP         = qx{which cp};
37 my $MV         = qx{which mv};
38 my $RM         = qx{which rm};
39 my $CHMOD      = qx{which chmod};
40
41 chomp $CONVERT;
42 chomp $CP;
43 chomp $MV;
44 chomp $RM;
45 chomp $CHMOD;
46
47 # Check that all the binaries are were want them
48
49 my @BINARIES;
50 push @BINARIES, ($CONVERT, $CP, $MV, $RM, $CHMOD);
51
52 foreach (@BINARIES) {
53     if ( ! -e $_ ) {
54         die "$PROGRAM_NAME: Cannot find binary '".$_."'. Please install it.\n";
55     }
56 }
57
58 ###
59
60 my $OEBPS_DIR      = 'OEBPS';
61 my $META_INF_DIR   = 'META-INF';
62
63
64 ###
65
66 my $bookCode     = '';
67 my $metaFile     = '';
68 my $bookXML      = '';
69 my $fontFiles    = "common/fonts";
70 my $language     = 'en';
71
72 my $verbose = 0;
73 my $noValidate = 0;
74
75 ### read command line options
76
77 while( $#ARGV > -1 ) {
78     my $cmdLineItem = shift @ARGV;
79     if( $cmdLineItem =~ /^--meta=(.+)$/ ) {
80         $metaFile = $1;
81     }
82     elsif( $cmdLineItem =~ /^--xml=(.+)$/ ) {
83         $bookXML = $1;
84     }
85     elsif( $cmdLineItem =~ /^--language=(.+)$/ ) {
86         $language = $1;
87     }
88     elsif( $cmdLineItem =~ /^--verbose/ ) {
89         $verbose = 1;
90     }
91     elsif( $cmdLineItem =~ /^--font-files=(.+)$/ ) {
92         $fontFiles = $1;
93     }
94     else { 
95         $bookCode = $cmdLineItem;
96     }
97 }
98
99 if( $bookCode eq '' ) { 
100     die "$PROGRAM_NAME: Unspecified book code\n$USAGE";
101 }
102 if( $metaFile eq '' ) { $metaFile = "$language/.publisher/rules/epub"; }
103 if( $bookXML eq '' ) { $bookXML = "$language/xml/$bookCode.xml"; }
104
105
106 ### read in metadata file
107
108 unless( -e $metaFile && -f $metaFile && -r $metaFile ) {
109     die qq{$PROGRAM_NAME: Improper metadata file "$metaFile"\n};
110 }
111
112 open( META, '<', $metaFile ) or 
113     die qq{$PROGRAM_NAME: Unable to open metadata file "$metaFile": $!\n};
114
115 my $meta = '';
116 while( my $line = <META> ) {
117     $meta .= $line if $line !~ /^[[:space:]]*#/;
118 }
119 close META;
120
121 ### interpret rules from metadata
122 my $rulesString = '';
123 if( $meta =~ /^[[:space:]]*$bookCode[[:space:]]*{([^}]*)}/sm ) {
124     $rulesString = $1;
125 }
126 else {
127     die "$PROGRAM_NAME: Book code ($bookCode) not found in metadata file or invalid file syntax\n";
128 }
129
130 my @rules = split( /[[:space:]\n]*;[[:space:]\n]*/, $rulesString );
131 my %rulesHash;
132 foreach my $rule (@rules) {
133     if( $rule =~ /[[:space:]]*([^:]+)[[:space:]]*:[[:space:]]*(.+)$/s ) {
134         $rulesHash{ $1 } = $2;
135     }
136     else {
137         die "$PROGRAM_NAME: Unrecognized rule syntax:\n$rule\n";
138     }
139 }
140
141 unless( defined $rulesHash{'book-series'} ) {
142     die "$PROGRAM_NAME: no book series set\n";
143 }
144
145 my $SERIES = get_series($rulesHash{'book-series'}) ;
146 my $SERIES_NUMBER = get_series_number($bookCode);
147
148
149 ### create output directories
150
151 my %outPath;
152 $outPath{'top'} = $rulesHash{'language'} . $FILENAME_SEPARATOR .
153                      'epub' . $FILENAME_SEPARATOR .
154                      $rulesHash{'book-series'} . $FILENAME_SEPARATOR .
155                      $bookCode;
156
157 $outPath{'meta-inf'} = $outPath{'top'} . $FILENAME_SEPARATOR . $META_INF_DIR;
158 $outPath{'oebps'} = $outPath{'top'} . $FILENAME_SEPARATOR . $OEBPS_DIR;
159
160 foreach my $directory (keys(%outPath)) {
161     unless( -e $outPath{$directory} && -d $outPath{$directory} ) {
162         mkpath $outPath{$directory}
163             or die "$PROGRAM_NAME: Unknown error creating output directory " .
164                    "\"$outPath{$directory}\"\n";
165     }
166 }
167
168 ### create content files
169
170
171 ## write coverpage 
172
173 # Generate the cover image. This can be done in two ways:
174 # 1.- A file is available under the directory of JPEG files for the book
175 # 2.- A file is generated using imagemagick
176
177 # Cover filename
178 my $coverImage = $outPath{'oebps'} . $FILENAME_SEPARATOR . "cover.jpg"; 
179 # Cover filename generated by Project Aon
180 my $pa_coverImage = $rulesHash{'language'} . $FILENAME_SEPARATOR . "jpeg" . $FILENAME_SEPARATOR .$rulesHash{'book-series'}. $FILENAME_SEPARATOR .$bookCode . $FILENAME_SEPARATOR . "cover.jpg";
181
182 if ( -e  "$pa_coverImage") {
183     # Copy the file here
184     print STDERR "DEBUG: Using cover from $pa_coverImage\n" if $verbose;
185     system "cp $pa_coverImage $coverImage";
186 } else {
187
188 # Use Imagemagick to generate the cover page
189
190     print STDERR "DEBUG: Will generate cover with ImageMagick\n" if $verbose;
191     my $TITLE = quote_shell(find_title($bookXML));
192     my $AUTHOR = quote_shell(find_author($bookXML));
193     my $ILLUSTRATOR = quote_shell(find_illustrator($bookXML));
194     my $convert_cmd = "";
195
196     if ( -e "$fontFiles/SouvenirStd-Demi.otf" && -e "$fontFiles/SouvenirStd-Light.otf" ) { 
197         $convert_cmd="$CONVERT -size 600x800 -background white  -font $fontFiles/SouvenirStd-Demi.otf -pointsize 32 -fill '#006633' -gravity north caption:\"\" -annotate +0+218 \"$TITLE\"  -font $fontFiles/SouvenirStd-Light.otf -pointsize 22 -fill black -annotate +0+304 '$AUTHOR' -annotate +0+333 '$ILLUSTRATOR' $coverImage"
198     } else {
199         print STDERR "WARN: Fontfiles not found, using standard font\n";
200         $convert_cmd="$CONVERT -size 600x800 -background white -pointsize 32 -fill '#006633' -gravity north caption:\"\" -annotate +0+218 \"$TITLE\"  -pointsize 22 -fill black -annotate +0+304 '$AUTHOR' -annotate +0+333 '$ILLUSTRATOR' $coverImage";
201     }
202
203     print STDERR "DEBUG: Will run '$convert_cmd'\n" if $verbose;
204     system $convert_cmd;
205 }
206
207
208 exit 0;
209
210 ################################################################################
211 # Subroutines
212 ################################################################################
213
214
215 # Determine series long name by the series acronym
216 sub get_series {
217     my ($series) = @_;
218     my $series_name = "";
219     if ($series eq "lw" ) {
220         $series_name = "Lone Wolf";
221     } elsif ($series eq "ls" ) {
222         $series_name = "Lobo Solitario";
223     } elsif ($series eq "gs" ) {
224         $series_name = "Grey Star the Wizard";
225     } elsif ($series eq "fw" ) {
226         $series_name = "Freeway Warrior";
227     } else {
228         print STDERR "WARN: Undefined series. Short name given: '$series'\n";
229         $series_name = "[undefined]";
230     }
231     return $series_name;
232 }
233
234 # Determine the series number based on book code
235 sub get_series_number {
236     my ($bookCode) = @_;
237     my $series_number = "";
238     if ( $bookCode =~ /^(\d\d)/ ) {
239         $series_number = $1;
240     } else {
241         print STDERR "WARN: Undefined series number. Book code is '$bookCode'.\n";
242         $series_number = "xx";
243     }
244     return $series_number;
245 }
246
247 # Determine the book title by reading the book meta information
248 sub find_title {
249     my ($book) = @_;
250     my $title = ""; my $line = "";
251     open (BOOK, "head -100 $book | ") || die ("Could not read $book: $!");
252     while ($title eq "" && ( $line = <BOOK> ) ) {
253         chomp $line;
254         if ( $line =~ /<title>(.*?)<\/title>/ ) {
255             $title = $1;
256         }
257     }
258     close BOOK;
259
260     if ( $title eq "" ) {
261         print STDERR "WARN: Cannot find title for book '$book'\n";
262         $title = "[Undefined]";
263     }
264
265     return convert_entities($title);
266 }
267
268 # Determine the book author by reading the book meta information
269 sub find_author {
270     my ($book) = @_;
271     my $author = ""; 
272     my $line = "";
273     open (BOOK, "head -100 $book |") || die ("Could not read $book: $!");
274
275     my $find_line = 0;
276     while ($author eq "" && ( $line = <BOOK> ) ) {
277         chomp $line;
278         if ( $find_line == 1 && $line =~ /<line>(.*?)<\/line>/ ) {
279             $author = $1;
280         }
281         $find_line = 1 if ( $line =~ /<creator class="medium">/ );
282         $find_line = 0 if ( $line =~ /<\/creator>/ );
283         if ( $line =~ /<creator class="author">(.*?)<\/title>/ ) {
284             $author = $1;
285         }
286     }
287     close BOOK;
288
289     if ( $author eq "" ) {
290         print STDERR "WARN: Cannot find author for book '$book'\n";
291         $author = "[Undefined]";
292     }
293
294
295     return $author;
296 }
297
298 # Determine the book illustrator by reading the book meta information
299 sub find_illustrator {
300     my ($book) = @_;
301     my $illustrator = "";
302     my $line = "";
303     open (BOOK, "head -100 $book | ") || die ("Could not read $book: $!");
304
305     my $find_line = 0;
306     while ($illustrator eq "" && ( $line = <BOOK> ) ) {
307         chomp $line;
308         if ( $find_line == 1 && $line =~ /<line>Illustrated by (.*?)<\/line>/ ) {
309             $illustrator = $1;
310         }
311         if ( $find_line == 1 && $line =~ /<line>Ilustrado por (.*?)<\/line>/ ) {
312             $illustrator = $1;
313         }
314         $find_line = 1 if ( $line =~ /<creator class="medium">/ );
315         $find_line = 0 if ( $line =~ /<\/creator>/ );
316         if ( $line =~ /<creator class="illustrator">(.*?)<\/title>/ ) {
317             $illustrator = $1;
318         }
319     }
320     close BOOK;
321
322     if ( $illustrator eq "" ) {
323         print STDERR "WARN: Cannot find illustrator for book '$book'\n";
324         if ( $language eq "en" ) {
325             $illustrator = "[Unknown]";
326         } elsif ( $language eq "es" ) {
327             $illustrator = "[Desconocido]";
328         }
329     }
330     if ( $language eq "en" ) {
331         $illustrator = "Illustrated by ".$illustrator;
332     } elsif ( $language eq "es" ) {
333         $illustrator = "Illustrado por ".$illustrator;
334     }
335
336     return $illustrator;
337 }
338
339 sub convert_entities {
340 # Convert character entities to their correspondent values
341     my ($text) = @_;
342
343     $text =~ s/\<ch.apos\/\>/'/g; 
344     $text =~ s/\<ch.nbsp\/\>/ /g;
345     $text =~ s/\<ch.plusmn\/\>/+-/g;
346     $text =~ s/\<ch.aacute\/\>/á/g;
347     $text =~ s/\<ch.eacute\/\>/é/g;
348     $text =~ s/\<ch.iacute\/\>/í/g;
349     $text =~ s/\<ch.oacute\/\>/ó/g;
350     $text =~ s/\<ch.uacute\/\>/ú/g;
351     $text =~ s/\<ch.ntilde\/\>/ñ/g;
352     $text =~ s/\<ch.Aacute\/\>/Á/g;
353     $text =~ s/\<ch.Eacute\/\>/É/g;
354     $text =~ s/\<ch.Iacute\/\>/Í/g;
355     $text =~ s/\<ch.Oacute\/\>/Ó/g;
356     $text =~ s/\<ch.Uacute\/\>/Ú/g;
357     $text =~ s/\<ch.auml\/\>/ä/g;
358     $text =~ s/\<ch.euml\/\>/ë/g;
359     $text =~ s/\<ch.iuml\/\>/ï/g;
360     $text =~ s/\<ch.ouml\/\>/ö/g;
361     $text =~ s/\<ch.uuml\/\>/ü/g;
362     $text =~ s/\<ch.Ntilde\/\>/Ñ/g;
363     $text =~ s/\<ch.acute\/\>/´/g;
364     $text =~ s/\<ch.iexcl\/\>/¡/g;
365     $text =~ s/\<ch.iquest\/\>/¿/g;
366     $text =~ s/\<ch.laquo\/\>/«/g;
367     $text =~ s/\<ch.raquo\/\>/»/g;
368     $text =~ s/\<ch.ampersand\/\>/&/g;
369
370     return $text;
371 }
372
373 # Quote metacaracters for shell use
374 sub quote_shell {
375     my ($text) = @_;
376     $text =~ s/'/\\'/g; 
377     return $text;
378 }