comparison bins_edit @ 0:a84c32f131df 1.1.29

Import vendor version
author Peter Gervai <grin@grin.hu>
date Wed, 15 Oct 2008 23:28:56 +0200
parents
children 3021deef1599
comparison
equal deleted inserted replaced
-1:000000000000 0:a84c32f131df
1 #!/usr/bin/perl -w
2
3 # bins_edit for BINS Photo Album version 1.1.29
4 # Copyright (C) 2001-2004 Jérôme Sautret (Jerome@Sautret.org)
5 #
6 # $Id: bins_edit,v 1.21 2004/10/24 13:19:16 jerome Exp $
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; see the file COPYING. If not, write to
20 # the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 # Boston, MA 02111-1307, USA.
22
23 # Type "bins_edit -h" on command line for usage information.
24
25 use strict;
26
27 use Getopt::Long;
28 use IO::File;
29 use UNIVERSAL qw(isa);
30
31 # XML parsing & writing
32 use XML::Grove;
33 use XML::Grove::Builder;
34 use XML::Grove::Path;
35 use XML::Grove::PerlSAX;
36 use XML::Parser::PerlSAX;
37 #use XML::Handler::XMLWriter;
38 use XML::Handler::YAWriter;
39 use Text::Iconv;
40 use HTML::Entities;
41
42 my $verbose = 1;
43 my $html=0;
44
45 my $localEncoding;
46 $localEncoding = `locale charmap`;
47 if ($? != 0 ) {
48 $localEncoding = "LATIN1";
49 }
50 else {
51 if (! $localEncoding or ($localEncoding eq "ANSI_X3.4-1968")) {
52 chop($localEncoding);
53 # ANSI is unspeakably primitive, promote it.
54 $localEncoding = "LATIN1";
55 print "Forcing encoding to $localEncoding\n" if ($verbose >=2);
56 }
57 }
58 my $converter = Text::Iconv->new($localEncoding, "UTF-8");
59
60 # decode HTML entites which doesn't exist in XML
61 sub decodeEntites{
62 my $s = shift;
63
64 my %entities = (
65 AElig => 'Æ', # capital AE diphthong (ligature)
66 Aacute => 'Á', # capital A, acute accent
67 Acirc => 'Â', # capital A, circumflex accent
68 Agrave => 'À', # capital A, grave accent
69 Aring => 'Å', # capital A, ring
70 Atilde => 'Ã', # capital A, tilde
71 Auml => 'Ä', # capital A, dieresis or umlaut mark
72 Ccedil => 'Ç', # capital C, cedilla
73 ETH => 'Ð', # capital Eth, Icelandic
74 Eacute => 'É', # capital E, acute accent
75 Ecirc => 'Ê', # capital E, circumflex accent
76 Egrave => 'È', # capital E, grave accent
77 Euml => 'Ë', # capital E, dieresis or umlaut mark
78 Iacute => 'Í', # capital I, acute accent
79 Icirc => 'Î', # capital I, circumflex accent
80 Igrave => 'Ì', # capital I, grave accent
81 Iuml => 'Ï', # capital I, dieresis or umlaut mark
82 Ntilde => 'Ñ', # capital N, tilde
83 Oacute => 'Ó', # capital O, acute accent
84 Ocirc => 'Ô', # capital O, circumflex accent
85 Ograve => 'Ò', # capital O, grave accent
86 Oslash => 'Ø', # capital O, slash
87 Otilde => 'Õ', # capital O, tilde
88 Ouml => 'Ö', # capital O, dieresis or umlaut mark
89 THORN => 'Þ', # capital THORN, Icelandic
90 Uacute => 'Ú', # capital U, acute accent
91 Ucirc => 'Û', # capital U, circumflex accent
92 Ugrave => 'Ù', # capital U, grave accent
93 Uuml => 'Ü', # capital U, dieresis or umlaut mark
94 Yacute => 'Ý', # capital Y, acute accent
95 aacute => 'á', # small a, acute accent
96 acirc => 'â', # small a, circumflex accent
97 aelig => 'æ', # small ae diphthong (ligature)
98 agrave => 'à', # small a, grave accent
99 aring => 'å', # small a, ring
100 atilde => 'ã', # small a, tilde
101 auml => 'ä', # small a, dieresis or umlaut mark
102 ccedil => 'ç', # small c, cedilla
103 eacute => 'é', # small e, acute accent
104 ecirc => 'ê', # small e, circumflex accent
105 egrave => 'è', # small e, grave accent
106 eth => 'ð', # small eth, Icelandic
107 euml => 'ë', # small e, dieresis or umlaut mark
108 iacute => 'í', # small i, acute accent
109 icirc => 'î', # small i, circumflex accent
110 igrave => 'ì', # small i, grave accent
111 iuml => 'ï', # small i, dieresis or umlaut mark
112 ntilde => 'ñ', # small n, tilde
113 oacute => 'ó', # small o, acute accent
114 ocirc => 'ô', # small o, circumflex accent
115 ograve => 'ò', # small o, grave accent
116 oslash => 'ø', # small o, slash
117 otilde => 'õ', # small o, tilde
118 ouml => 'ö', # small o, dieresis or umlaut mark
119 szlig => 'ß', # small sharp s, German (sz ligature)
120 thorn => 'þ', # small thorn, Icelandic
121 uacute => 'ú', # small u, acute accent
122 ucirc => 'û', # small u, circumflex accent
123 ugrave => 'ù', # small u, grave accent
124 uuml => 'ü', # small u, dieresis or umlaut mark
125 yacute => 'ý', # small y, acute accent
126 yuml => 'ÿ', # small y, dieresis or umlaut mark
127
128 # Some extra Latin 1 chars that are listed in the HTML3.2 draft (21-May-96)
129 copy => '©', # copyright sign
130 reg => '®', # registered sign
131 nbsp => "\240", # non breaking space
132
133 # Additional ISO-8859/1 entities listed in rfc1866 (section 14)
134 iexcl => '¡',
135 cent => '¢',
136 pound => '£',
137 curren => '¤',
138 yen => '¥',
139 brvbar => '¦',
140 sect => '§',
141 uml => '¨',
142 ordf => 'ª',
143 laquo => '«',
144 'not' => '¬', # not is a keyword in perl
145 shy => '­',
146 macr => '¯',
147 deg => '°',
148 plusmn => '±',
149 sup1 => '¹',
150 sup2 => '²',
151 sup3 => '³',
152 acute => '´',
153 micro => 'µ',
154 para => '¶',
155 middot => '·',
156 cedil => '¸',
157 ordm => 'º',
158 raquo => '»',
159 frac14 => '¼',
160 frac12 => '½',
161 frac34 => '¾',
162 iquest => '¿',
163 'times' => '×', # times is a keyword in perl
164 divide => '÷',
165 );
166
167 while (my($entity, $char) = each(%entities)) {
168 $s =~ s/\&$entity\;/$char/g;
169 }
170 return $s;
171 }
172
173 sub charac_indent{
174 my $n = shift(@_);
175 my $s="\n";
176 for (1..$n){
177 $s .= " ";
178 }
179 return XML::Grove::Characters->new ( Data => $s );
180 }
181
182 sub setField{
183 my $field = shift(@_); # field to add or modify
184 my $value = shift(@_); # value to set to field
185 my $fileType = shift(@_); # type of file (iamge or album)
186 my $document = shift(@_); # XML document as a Grove
187
188 if (! $html) {
189 $value = encode_entities($value, '\00-\31<&"');
190 }
191
192 my $characters =
193 XML::Grove::Characters->new( Data =>
194 decodeEntites($value));
195 #my $characters = XML::Grove::Characters->new ( Data => $value );
196
197 my $fieldName;
198 my $fieldValue;
199 foreach my $element
200 (@{$document->at_path('/'.$fileType.'/description')->{Contents}}) {
201 if (isa($element, 'XML::Grove::Element') && $element->{Name} eq "field") {
202 $fieldName = $element->{Attributes}{'name'};
203 $fieldValue = "";
204 if ($fieldName eq $field) {
205 print " Modifying field '$fieldName' to '$value'... "
206 if ($verbose >= 3);
207 @{$element->{Contents}} = ( charac_indent(3),
208 $characters,
209 charac_indent(2));
210 print "OK.\n" if ($verbose >= 3);
211 return;
212 }
213 }
214 }
215
216 print " Adding field '$field' with value '$value'... " if ($verbose >= 2);
217 my $element = XML::Grove::Element->new ( Name => 'field',
218 Contents => [charac_indent(3),
219 $characters,
220 charac_indent(2)],
221 Attributes => {"name" => $field});
222 push @{$document->at_path('/'.$fileType.'/description')->{Contents}},
223 (charac_indent(2), $element, charac_indent(1));
224
225 print "OK.\n" if ($verbose >= 2);
226 }
227
228 sub setFields{
229 my $file = shift(@_);
230 my $fields = shift(@_);
231 my $album = shift(@_); # type of file (0 if image or 1 if album)
232 my $document;
233
234 my $fileType;
235 if ($album) {
236 $fileType = "album";
237 } else{
238 $fileType = "image";
239 }
240
241 if (-e $file) {
242 # Get XML document as a Grove
243 print " Reading file '$file'... " if ($verbose >= 2);
244 my $grove_builder = XML::Grove::Builder->new;
245 my $parser = XML::Parser::PerlSAX->new ( Handler => $grove_builder );
246 $document = $parser->parse ( Source => { SystemId => $file } );
247 print "OK.\n" if ($verbose >= 2);
248 } else {
249 print " Creating file '$file'... " if ($verbose >= 2);
250 my @elements;
251 push @elements, (charac_indent(1),
252 XML::Grove::Element->new ( Name => 'description',
253 Contents => [charac_indent(1)]),
254 charac_indent(1),
255 XML::Grove::Element->new ( Name => 'bins',
256 Contents => [charac_indent(1)]),
257 );
258 if (!$album) {
259 push @elements, ( charac_indent(1),
260 XML::Grove::Element->new ( Name => 'exif',
261 Contents =>
262 [charac_indent(1)]),
263 );
264 }
265 push @elements, charac_indent(0);
266 my $element =
267 XML::Grove::Element->new ( Name => $fileType,
268 Contents => \@elements);
269 $document = XML::Grove::Document->new ( Contents => [ $element ] );
270 print "OK.\n" if ($verbose >= 3);
271 }
272
273 my $fieldName;
274 my $fieldValue;
275 while ( ($fieldName, $fieldValue) = each(%$fields) ) {
276 if (defined $fieldValue) {
277 setField($fieldName, $fieldValue, $fileType, $document);
278 }
279 }
280
281 print " Writing file '$file'... " if ($verbose >= 2);
282 # Write the Grove to the desc file
283 my $fileHandler = new IO::File;
284 open($fileHandler, '>', $file)
285 or die("Cannot open file $file to write Exif tag ($!)");
286 binmode($fileHandler, ":utf8") if $^V ge v5.8.0;
287
288 my $my_handler = new XML::Handler::YAWriter( 'Output' => $fileHandler,
289 # 'Escape' => {
290 # '--' => '&mdash;',
291 #'&' => '&amp;',
292 # },
293 'Encoding' => "UTF-8",
294 );
295 # my $my_handler = XML::Handler::XMLWriter->new( Output => $fileHandler,
296 # Newlines => 0);
297 $document->parse(DocumentHandler => $my_handler);
298 close ($fileHandler) || bail ("can't close $file ($!)");
299 print "OK.\n" if ($verbose >= 2);
300 }
301
302 sub copyleft{
303 print "\nbins_edit for BINS Photo Album 1.1.29 (http://bins.sautret.org/)\n";
304 print "Copyright © 2001-2004 Jérôme Sautret (Jerome\@Sautret.org)\n";
305 print "This is free software with ABSOLUTELY NO WARRANTY.\n";
306 print "See COPYING file for details.\n\n";
307 }
308
309 sub usage{
310 my $exit=shift; # should we exit after usage information ?
311 copyleft();
312
313 print <<EoF ;
314 bins_edit is a script to set fields in XML pictures description files for BINS.
315
316 usage:
317 bins_edit [-a|--album] [-m|--html]
318 [-t|--title title] [-e|--event event] [-l|--location location]
319 [-p|--people people] [-y|--date date] [-d|--description description]
320 [--longdesc longDescription] [--shortdesc shortDesription]
321 [--sample pictureFileName]
322 [-g|--generic tag=value]
323 [-h|--help] [-v|--verbose] [-q|--quiet] file [files...]
324
325 EoF
326
327 if ($exit){
328 print "Type bins_edit --help for complete help.\n";
329 exit ($exit);
330 }
331 }
332
333
334 sub help{
335 usage(0);
336 print <<EoF ;
337 Options:
338 -t, --title, -e, --event event, -l, --location,
339 -p, --people, -y, --date, -d, --description :
340 these switchs are used to set a value to a picture
341 desciption field.
342 -t, --title, --longdesc, --shortdesc, --sample :
343 these switchs are used to set a value to an album
344 desciption field (with --album option)
345 -a, --album : edit album description.
346 (default is editing image description)
347 In this case, the file parameter must be the
348 source directory of the album.
349 Only the --title, --longdesc, --shortdesc and --sample
350 switchs have sense with this option.
351 -m, --html : input value will be considering as HTML code, thus,
352 no HTML encoding will be done.
353 -v, --verbose : this switch can appear several times to increase
354 verbosity level.
355 -q, --quiet : suppress output
356
357 If filenames have no .xml suffix, it is added, so you can directly give
358 picture names on the command line.
359 Spaces and other special characters (even newlines) can be used in values
360 given as parameters as long as they are enclosed between quotes.
361
362 Examples:
363 Set the title of the Image.jpg file to "My picture":
364 bins_edit -t "My picture" Image.jpg
365
366 Set the title and location of all JPEG pictures in the directory:
367 bins_edit --title Holiday --location Paris *.jpg
368
369 Use of HTML values:
370 bins_edit --html --description '<b>BINS</b> is cool' file.jpg
371
372 Set the title short description and sample image of the album
373 in the current directory (note the dot as final parameter):
374 bins_edit -a -t "My Album" --sample image.jpg --shortdesc "This is my album" .
375
376 EoF
377
378 exit 1;
379 }
380
381
382 sub main{
383 my %values;
384 my $album = 0; # 1 if it a album description file
385
386
387 # process args
388 Getopt::Long::Configure("bundling");
389 GetOptions('t|title:s' => \$values{title},
390 'e|event:s' => \$values{event},
391 'l|location:s' => \$values{location},
392 'p|people:s' => \$values{people},
393 'y|date:s' => \$values{date},
394 'd|description:s' => \$values{description},
395 'longdesc:s' => \$values{longdesc},
396 'shortdesc:s' => \$values{shortdesc},
397 'sample:s' => \$values{sampleimage},
398 'g|generic=s%' => \%values,
399 'm|html' => \$html,
400 'a|album' => \$album,
401 'v|verbose+' => \$verbose,
402 'q|quiet' => sub { $verbose = 0 },
403 'h|help' => sub { help() },
404 'copyright' => sub { copyleft() },
405 )
406 or usage(1);
407
408 my @files;
409 if ($#ARGV < 0) {
410 if ($album) {
411 @files = (".");
412 } else {
413 print "No files specified.\n";
414 usage(1)
415 }
416 } else {
417 @files = @ARGV;
418 }
419
420 copyleft() if ($verbose >=2);
421
422 foreach my $file (@files) {
423 if ($album) {
424 $file .= "/album.xml";
425 }
426 if ($file !~ m/.xml$/) {
427 $file .= ".xml";
428 }
429 print "Processing file '$file'... " if ($verbose >= 1);
430 print "\n" if ($verbose >= 2);
431 setFields($file, \%values, $album);
432 print "OK.\n" if ($verbose == 1);
433 }
434 }
435
436 main();