root/main/trunk/greenstone2/perllib/plugins/ConvertBinaryFile.pm @ 32205

Revision 32205, 20.0 KB (checked in by ak19, 2 years ago)

First set of commits to do with implementing the new 'paged_html' output option of PDFPlugin that uses using xpdftools' new pdftohtml. So far tested only on Linux (64 bit), but things work there so I'm optimistically committing the changes since they work. 2. Committing the pre-built Linux binaries of XPDFtools for both 32 and 64 bit built by the XPDF group. 2. To use the correct bitness variant of xpdftools, setup.bash now exports the BITNESS env var, consulted by gsConvert.pl. 3. All the perl code changes to do with using xpdf tools' pdftohtml to generate paged_html and feed it in the desired form into GS(3): gsConvert.pl, PDFPlugin.pm and its parent ConvertBinaryPFile.pm have been modified to make it all work. xpdftools' pdftohtml generates a folder containing an html file and a screenshot for each page in a PDF (as well as an index.html linking to each page's html). However, we want a single html file that contains each individual 'page' html's content in a div, and need to do some further HTML style, attribute and structure modifications to massage the xpdftool output to what we want for GS. In order to parse and manipulate the HTML 'DOM' to do this, we're using the Mojo::DOM package that Dr Bainbridge found and which he's compiled up. Mojo::DOM is therefore also committed in this revision. Some further changes and some display fixes are required, but need to check with the others about that.

  • Property svn:keywords set to Author Date Id Revision
Line 
1###########################################################################
2#
3# ConvertBinaryFile.pm -- plugin that facilitates conversion of binary files
4# through gsConvert.pl
5#
6# A component of the Greenstone digital library software
7# from the New Zealand Digital Library Project at the
8# University of Waikato, New Zealand.
9#
10# Copyright (C) 1999 New Zealand Digital Library Project
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25#
26###########################################################################
27
28# This plugin is inherited by such plugins as WordPlugin, PowerPointPlugin,
29# PostScriptPlugin,
30# RTFPlugin and PDFPlugin. It facilitates the conversion of these document types
31# to either HTML, Text or a series of images. It works by dynamically loading
32# an appropriate secondary plugin (HTMLPlug, StructuredHTMLPlug,
33# PagedImagePlugin or TextPlugin) based on the plugin argument 'convert_to'.
34
35package ConvertBinaryFile;
36
37use AutoExtractMetadata;
38use ghtml;
39use HTMLPlugin;
40use TextPlugin;
41use PagedImagePlugin;
42
43use strict;
44no strict 'refs'; # allow filehandles to be variables and viceversa
45no strict 'subs';
46use util;
47use FileUtils;
48
49
50sub BEGIN {
51    @ConvertBinaryFile::ISA = ('AutoExtractMetadata');
52}
53
54my $convert_to_list =
55    [ { 'name' => "auto",
56    'desc' => "{ConvertBinaryFile.convert_to.auto}" },
57      { 'name' => "html",
58    'desc' => "{ConvertBinaryFile.convert_to.html}" },
59      { 'name' => "text",
60    'desc' => "{ConvertBinaryFile.convert_to.text}" }
61      ];
62
63my $arguments =
64    [ { 'name' => "convert_to",
65    'desc' => "{ConvertBinaryFile.convert_to}",
66    'type' => "enum",
67    'reqd' => "yes",
68    'list' => $convert_to_list,
69    'deft' => "auto" },
70      { 'name' => "keep_original_filename",
71    'desc' => "{ConvertBinaryFile.keep_original_filename}",
72    'type' => "flag" },
73      { 'name' => "title_sub",
74    'desc' => "{HTMLPlugin.title_sub}",
75    'type' => "string",
76    #'type' => "regexp",
77    'deft' => "" },
78      { 'name' => "apply_fribidi",
79    'desc' => "{ConvertBinaryFile.apply_fribidi}",
80    'type' => "flag",
81    'reqd' => "no" },
82      { 'name' => "use_strings",
83    'desc' => "{ConvertBinaryFile.use_strings}",
84    'type' => "flag",
85    'reqd' => "no" },
86      ];
87
88my $options = { 'name'     => "ConvertBinaryFile",
89        'desc'     => "{ConvertBinaryFile.desc}",
90        'abstract' => "yes",
91        'inherits' => "yes",
92        'args'     => $arguments };
93
94
95sub load_secondary_plugins
96{
97    my $self = shift (@_);
98    my ($class,$input_args,$hashArgOptLists) = @_;
99
100    my @convert_to_list = split(",",$self->{'convert_to_plugin'});
101    my $secondary_plugins = {};
102    # find the plugin
103
104    foreach my $convert_to (@convert_to_list) {
105    # load in "convert_to" plugin package
106    my $plugin_class = $convert_to;
107    my $plugin_package = $plugin_class.".pm";
108
109    my $colplugname = undef;
110    if (defined $ENV{'GSDLCOLLECTDIR'}) {
111        $colplugname = &FileUtils::filenameConcatenate($ENV{'GSDLCOLLECTDIR'},
112                           "perllib","plugins",
113                           $plugin_package);
114    }
115
116    my $mainplugname = &FileUtils::filenameConcatenate($ENV{'GSDLHOME'},
117                           "perllib","plugins",
118                           $plugin_package);
119
120    if ((defined $colplugname) && (-e $colplugname)) { require $colplugname;}
121    elsif (-e $mainplugname) { require $mainplugname; }
122    else {
123        &gsprintf(STDERR, "{plugin.could_not_find_plugin}\n",
124              $plugin_class);
125        die "\n";
126    }
127
128    # call its constructor with extra options that we've worked out!
129    my $arglist = $input_args->{$plugin_class};
130
131    my ($secondary_plugin);
132    eval("\$secondary_plugin = new $plugin_class([],\$arglist)");
133    die "$@" if $@;
134    $secondary_plugins->{$plugin_class} = $secondary_plugin;
135    }
136    $self->{'secondary_plugins'} = $secondary_plugins;
137}
138
139sub new {
140    my ($class) = shift (@_);
141    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
142    push(@$pluginlist, $class);
143    my $classPluginName = (defined $pluginlist->[0]) ? $pluginlist->[0] : $class;
144    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
145    push(@{$hashArgOptLists->{"OptList"}},$options);
146
147    my $self = new AutoExtractMetadata($pluginlist, $inputargs, $hashArgOptLists);
148   
149    return bless $self, $class;
150}
151
152# should be called by subclasses after checking and setting
153# $self->{'convert_to'}
154sub set_standard_convert_settings {
155    my $self =shift (@_);
156   
157    my $convert_to = $self->{'convert_to'};
158    if ($convert_to eq "auto") {
159    $convert_to = "html";
160    $self->{'convert_to'} = "html";
161    }
162
163    if ($convert_to =~ /^html/ || $convert_to eq "paged_html") { # may be html or html_multi, or paged_html with the new Xpdf's own pdftohtml
164    $self->{'convert_to_plugin'} = "HTMLPlugin";
165    $self->{'convert_to_ext'} = "html";
166    } elsif ($convert_to eq "text") {
167    $self->{'convert_to_plugin'} = "TextPlugin";
168    $self->{'convert_to_ext'} = "txt";
169    } elsif ($convert_to eq "structuredhtml") {
170    $self->{'convert_to_plugin'} = "StructuredHTMLPlugin";
171    $self->{'convert_to_ext'} = "html";
172    } elsif ($convert_to =~ /^pagedimg/) {
173    $self->{'convert_to_plugin'} = "PagedImagePlugin";
174    my ($convert_to_ext) = $convert_to =~ /pagedimg\_(jpg|gif|png)/i;
175    $convert_to_ext = 'jpg' unless defined $convert_to_ext;
176    $self->{'convert_to_ext'} = $convert_to_ext;
177    }
178}
179sub init {
180    my $self = shift (@_);
181    my ($verbosity, $outhandle, $failhandle) = @_;
182
183    $self->SUPER::init($verbosity,$outhandle,$failhandle);
184
185    my $secondary_plugins =  $self->{'secondary_plugins'};
186
187    foreach my $plug_name (keys %$secondary_plugins) {
188    my $plugin = $secondary_plugins->{$plug_name};
189    $plugin->init($verbosity,$outhandle,$failhandle);
190    }
191}
192
193sub deinit {
194    # called only once, after all plugin passes have been done
195
196    my ($self) = @_;
197
198    my $secondary_plugins =  $self->{'secondary_plugins'};
199
200    foreach my $plug_name (keys %$secondary_plugins) {
201    my $plugin = $secondary_plugins->{$plug_name};
202    $plugin->deinit();
203    }
204}
205
206sub convert_post_process
207{
208    # by default do no post processing
209    return;
210}
211
212
213# Run conversion utility on the input file. 
214#
215# The conversion takes place in a collection specific 'tmp' directory so
216# that we don't accidentally damage the input.
217#
218# The desired output type is indicated by $output_ext.  This is usually
219# something like "html" or "word", but can be "best" (or the empty string)
220# to indicate that the conversion utility should do the best it can.
221sub tmp_area_convert_file {
222    my $self = shift (@_);
223    my ($output_ext, $input_filename, $textref) = @_;
224   
225    my $outhandle = $self->{'outhandle'};
226    my $convert_to = $self->{'convert_to'};
227    my $failhandle = $self->{'failhandle'};
228    my $convert_to_ext = $self->{'convert_to_ext'};
229   
230
231    my $upgraded_input_filename = &util::upgrade_if_dos_filename($input_filename);
232
233    # derive tmp filename from input filename
234    my ($tailname, $dirname, $suffix)
235    = &File::Basename::fileparse($upgraded_input_filename, "\\.[^\\.]+\$");
236
237    # softlink to collection tmp dir
238    my $tmp_dirname = &util::get_timestamped_tmp_folder();
239    if (defined $tmp_dirname) {
240    $self->{'tmp_dir'} = $tmp_dirname;
241    } else {
242    $tmp_dirname = $dirname;
243    }
244   
245#    # convert to utf-8 otherwise we have problems with the doc.xml file later on
246#    my $utf8_tailname = (&unicode::check_is_utf8($tailname)) ? $tailname : $self->filepath_to_utf8($tailname);
247
248    # make sure filename to be used can be stored OK in a UTF-8 compliant doc.xml file
249     my $utf8_tailname = &unicode::raw_filename_to_utf8_url_encoded($tailname);
250
251
252    # URLEncode this since htmls with images where the html filename is utf8 don't seem
253    # to work on Windows (IE or Firefox), as browsers are looking for filesystem-encoded
254    # files on the filesystem.
255    $utf8_tailname = &util::rename_file($utf8_tailname, $self->{'file_rename_method'}, "without_suffix");
256
257    my $lc_suffix = lc($suffix);
258    my $tmp_filename = &FileUtils::filenameConcatenate($tmp_dirname, "$utf8_tailname$lc_suffix");
259   
260    # If gsdl is remote, we're given relative path to input file, of the form import/utf8_tailname.suffix
261    # But we can't softlink to relative paths. Therefore, we need to ensure that
262    # the input_filename is the absolute path, see http://perldoc.perl.org/File/Spec.html
263    my $ensure_path_absolute = 1; # true
264    &FileUtils::softLink($input_filename, $tmp_filename, $ensure_path_absolute);
265
266    my $output_filename = $self->run_conversion_command($tmp_dirname, $tmp_filename,
267                            $utf8_tailname, $lc_suffix, $tailname, $suffix);
268
269    return $output_filename;
270}
271
272# The latter half of tmp_area_convert_file: runs the conversion command and returns the output file name
273# Split from tmp_area_convert_file because UnknownConverterPlugin can then inherit all of
274# tmp_area_convert_file and only needs to override this part:
275sub run_conversion_command {
276    my $self = shift (@_);
277    my ($tmp_dirname, $tmp_filename, $utf8_tailname, $lc_suffix, $tailname, $suffix) = @_;   
278
279    my $outhandle = $self->{'outhandle'};
280    my $convert_to = $self->{'convert_to'};
281    my $failhandle = $self->{'failhandle'};
282
283    my $verbosity = $self->{'verbosity'};
284    if ($verbosity > 0) {
285    print $outhandle "Converting $tailname$suffix to $convert_to format\n";
286    }
287
288    my $errlog = &FileUtils::filenameConcatenate($tmp_dirname, "err.log");
289   
290    # Execute the conversion command and get the type of the result,
291    # making sure the converter gives us the appropriate output type
292    my $output_type=$self->{'convert_to'};
293#    if ($convert_to =~ m/PagedImage/i) {
294#   $output_type = lc($convert_to)."_".lc($convert_to_ext);
295#    } else {
296#   $output_type = lc($convert_to);
297#    }
298
299    my $cmd = "\"".&util::get_perl_exec()."\" -S gsConvert.pl -verbose $verbosity ";
300    if (defined $self->{'convert_options'}) {
301    $cmd .= $self->{'convert_options'} . " ";
302    }
303    if ($self->{'use_strings'}) {
304      $cmd .= "-use_strings ";
305    }
306    $cmd .= "-errlog \"$errlog\" -output $output_type \"$tmp_filename\"";
307    print STDERR "calling cmd $cmd\n";
308    $output_type = `$cmd`;
309   
310    # remove symbolic link to original file
311    &FileUtils::removeFiles($tmp_filename);
312   
313    # Check STDERR here
314    chomp $output_type;
315    if ($output_type eq "fail") {
316    print $outhandle "Could not convert $tailname$suffix to $convert_to format\n";
317    print $failhandle "$tailname$suffix: " . ref($self) . " failed to convert to $convert_to\n";
318    # The following  meant that if a conversion failed, the document would be counted twice - do we need it for anything?
319    #$self->{'num_not_processed'} ++;
320    if (-s "$errlog") {
321        open(ERRLOG, "$errlog");
322        while (<ERRLOG>) {
323        print $outhandle "$_";
324        }
325        print $outhandle "\n";
326        close ERRLOG;
327    }
328    &FileUtils::removeFiles("$errlog") if (-e "$errlog");
329    return "";
330    }
331
332    # store the *actual* output type and return the output filename
333    # it's possible we requested conversion to html, but only to text succeeded
334    #$self->{'convert_to_ext'} = $output_type;
335    if ($output_type =~ /html/i) {
336    $self->{'converted_to'} = "HTML";
337    } elsif ($output_type =~ /te?xt/i) {
338    $self->{'converted_to'} = "Text";
339    } elsif ($output_type =~ /item/i){
340    $self->{'converted_to'} = "PagedImage";
341    }
342   
343    my $output_filename = $tmp_filename;
344    if ($output_type =~ /item/i) {
345    # running under windows
346    if ($ENV{'GSDLOS'} =~ /^windows$/i) {
347        $output_filename = $tmp_dirname . "\\$utf8_tailname\\" . $utf8_tailname . ".$output_type";
348    } else {
349        $output_filename = $tmp_dirname . "\/$utf8_tailname\/" . $utf8_tailname . ".$output_type";
350    }
351    } elsif ($output_type eq "paged_html") {
352    $output_filename =~ s/$lc_suffix$/.html/;
353    } else {
354    $output_filename =~ s/$lc_suffix$/.$output_type/;
355    }
356   
357    return $output_filename;
358}
359
360
361# Override BasPlug read_into_doc_obj - we need to call secondary plugin stuff
362sub read_into_doc_obj {
363    my $self = shift (@_);
364    my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
365
366    my $outhandle = $self->{'outhandle'};
367
368    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
369
370    my $output_ext = $self->{'convert_to_ext'};
371    my $conv_filename = "";
372    $conv_filename = $self->tmp_area_convert_file($output_ext, $filename_full_path);
373       
374    if ("$conv_filename" eq "") {return -1;} # had an error, will be passed down pipeline
375
376    # We used to return -1 here if $conv_filename didn't exist at this stage
377    # However, for "paged_html" convert_to mode, the converted HTML file $conv_filename
378    # will only be created from conversion products *after* convert_post_process() returns
379    my $output_type=$self->{'convert_to'};
380    if ($output_type ne "paged_html" && ! -e "$conv_filename") {return -1;} 
381    $self->{'conv_filename'} = $conv_filename;
382    $self->convert_post_process($conv_filename);
383    if ($output_type eq "paged_html" && ! -e "$conv_filename") {return -1;} 
384
385    # Run the "fribidi" (http://fribidi.org) Unicode Bidirectional Algorithm program over the converted file
386    # Added for fixing up Persian PDFs after being processed by pdftohtml, but may be useful in other cases too
387    if ($self->{'apply_fribidi'} && $self->{'converted_to'} =~ /(HTML|Text)/) {
388    my $fribidi_command = "fribidi \"$conv_filename\" >\"${conv_filename}.tmp\"";
389    if (system($fribidi_command) != 0) {
390        print STDERR "ERROR: Cannot run fribidi on \"$conv_filename\".\n";
391    }
392    else {
393        &FileUtils::moveFiles("${conv_filename}.tmp", $conv_filename);
394    }   
395    }
396   
397    my $secondary_plugins =  $self->{'secondary_plugins'};
398    my $num_secondary_plugins = scalar(keys %$secondary_plugins);
399
400    if ($num_secondary_plugins == 0) {
401    print $outhandle "Warning: No secondary plugin to use in conversion.  Skipping $file\n";
402    return 0; # effectively block it
403    }
404
405    my @plugin_names = keys %$secondary_plugins;
406    my $plugin_name = shift @plugin_names;
407   
408    if ($num_secondary_plugins > 1) {
409    print $outhandle "Warning: Multiple secondary plugins not supported yet!  Choosing $plugin_name\n.";
410    }
411   
412    my $secondary_plugin = $secondary_plugins->{$plugin_name};
413
414    # note: metadata is not carried on to the next level
415## **** I just replaced $metadata with {} in following
416    my ($rv,$doc_obj)
417    = $secondary_plugin->read_into_doc_obj ($pluginfo,"", $conv_filename, $block_hash, {}, $processor, $maxdocs, $total_count, $gli);
418
419    if ((!defined $rv) || ($rv<1)) {
420    # wasn't processed
421    return $rv;
422    }
423   
424    # Override previous gsdlsourcefilename set by secondary plugin
425    my $collect_file = &util::filename_within_collection($filename_full_path);
426    my $collect_conv_file = &util::filename_within_collection($conv_filename);
427    $doc_obj->set_source_filename ($collect_file, $self->{'file_rename_method'});
428    ## set_source_filename does not set the doc_obj source_path which is used in archives dbs for incremental
429    # build. so set it manually.
430    $doc_obj->set_source_path($filename_full_path);
431    $doc_obj->set_converted_filename($collect_conv_file);
432
433    my $plugin_filename_encoding = $self->{'filename_encoding'};
434    my $filename_encoding = $self->deduce_filename_encoding($file,$metadata,$plugin_filename_encoding);
435    $self->set_Source_metadata($doc_obj, $filename_full_path, $filename_encoding);
436       
437    $doc_obj->set_utf8_metadata_element($doc_obj->get_top_section(), "Plugin", "$self->{'plugin_type'}");
438    $doc_obj->set_utf8_metadata_element($doc_obj->get_top_section(), "FileSize", (-s $filename_full_path));
439
440    # ****
441    my ($tailname, $dirname, $suffix)
442    = &File::Basename::fileparse($filename_full_path, "\\.[^\\.]+\$");
443    $doc_obj->set_utf8_metadata_element($doc_obj->get_top_section(), "FilenameRoot", $tailname);
444
445    # do plugin specific processing of doc_obj
446    unless (defined ($self->process($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli))) {
447    print STDERR "<ProcessingError n='$file'>\n" if ($gli);
448    return -1;
449    }
450
451    my $topsection = $doc_obj->get_top_section();
452    $self->add_associated_files($doc_obj, $filename_full_path);
453
454    # extra_metadata is already called by sec plugin in process??
455    $self->extra_metadata($doc_obj, $topsection, $metadata); # do we need this here??
456    # do any automatic metadata extraction
457    $self->auto_extract_metadata ($doc_obj);
458
459    # have we found a Title??
460    $self->title_fallback($doc_obj,$topsection,$filename_no_path);
461
462    # force a new OID - this will use OIDtype option set for this plugin.
463    $self->add_OID($doc_obj, 1);
464
465    return (1, $doc_obj);
466
467}
468
469sub process {
470    my $self = shift (@_);
471    my ($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_;
472
473    return $self->process_type($base_dir, $file, $doc_obj);
474}
475
476# do plugin specific processing of doc_obj for doc_ext type
477sub process_type {
478    my $self = shift (@_);
479    my ($base_dir, $file, $doc_obj) = @_;
480   
481    # need to check that not empty
482    my ($doc_ext) = $file =~ /\.(\w+)$/;
483    $doc_ext = lc($doc_ext);
484    my $file_type = "unknown";
485    $file_type = $self->{'file_type'} if defined $self->{'file_type'};
486   
487    # associate original file with doc object
488    my $cursection = $doc_obj->get_top_section();
489    my $filename = &FileUtils::filenameConcatenate($base_dir, $file);
490    my $assocfilename = "doc.$doc_ext";
491    if ($self->{'keep_original_filename'} == 1) {
492    # this should be the same filename that was used for the Source and SourceFile metadata,
493    # as we will use SourceFile in the srclink (below)
494    $assocfilename = $doc_obj->get_assocfile_from_sourcefile();
495    }
496
497    $doc_obj->associate_file($filename, $assocfilename, undef, $cursection);
498
499    # We use set instead of add here because we only want one value
500    $doc_obj->set_utf8_metadata_element($cursection, "FileFormat", $file_type);
501    my $srclink_filename = "doc.$doc_ext";
502    if ($self->{'keep_original_filename'} == 1) {
503    $srclink_filename = $doc_obj->get_sourcefile();
504    }
505    # srclink_file is now deprecated because of the "_" in the metadataname. Use srclinkFile
506    $doc_obj->add_utf8_metadata ($cursection, "srcicon",  "_icon".$doc_ext."_");
507    $doc_obj->add_utf8_metadata ($cursection, "srclink_file", $srclink_filename);
508    $doc_obj->add_utf8_metadata ($cursection, "srclinkFile", $srclink_filename);
509    return 1;
510}
511
512sub clean_up_after_doc_obj_processing {
513     my $self = shift(@_);
514
515     my $tmp_dir = $self->{'tmp_dir'};
516     if (defined $tmp_dir && -d $tmp_dir) {
517     ##print STDERR "**** Suppressing clean up of tmp dir\n";
518     &FileUtils::removeFilesRecursive($tmp_dir);
519     $self->{'tmp_dir'} = undef;
520     }
521     
522
523}
524
525# This sub is shared across PowerPointPlugin and UnknownConverterPlugin,
526# so it's been copied into here from the former.
527sub generate_item_file {
528    my $self = shift(@_);
529    my ($input_filename) = @_;
530    my $outhandle = $self->{'outhandle'};
531    my ($tailname, $dirname, $suffix)
532    = &File::Basename::fileparse($input_filename, "\\.[^\\.]+\$");
533
534    my $plugin_name = $self->{'plugin_type'}; # inherited from BaseImporter
535
536    # find all the files in the directory
537    if (!opendir (DIR, $dirname)) {
538    print $outhandle "$plugin_name: Couldn't read directory $dirname\n";
539    return $input_filename;
540    }
541
542    my @dir = readdir (DIR);
543    closedir (DIR);
544
545    # start the item file
546    my $itemfile_name = &util::filename_cat($dirname, "$tailname.item");
547
548    # encoding specification????
549    if (!open (ITEMFILE, ">$itemfile_name")) {
550    print $outhandle "$plugin_name: Couldn't open $itemfile_name for writing\n";
551    }
552    print ITEMFILE "<GeneratedBy>$plugin_name\n";
553    # print the first page
554    my @sorted_dir = sort alphanum_sort @dir;
555    for (my $i = 0; $i < scalar(@sorted_dir); $i++) {
556    my $file = $sorted_dir[$i];
557    if ($file =~ /^img(\d+)\.jpg$/) {
558        my $num = $1;
559        $self->tidy_up_html(&util::filename_cat($dirname, "text$num.html"));
560        print ITEMFILE "$num:img$num.jpg:text$num.html:\n";
561    }
562    }
563    close ITEMFILE;
564    return $itemfile_name;
565
566}
567
5681;
569
570
571
572
573
574
575
Note: See TracBrowser for help on using the browser.