source: trunk/gsdl/perllib/plugins/HTMLPlug.pm@ 6332

Last change on this file since 6332 was 6332, checked in by jmt12, 20 years ago

When -gli argument is provided to calling script these modules will now output gli specific, non-language nor verbosity specific, messages

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 26.5 KB
Line 
1###########################################################################
2#
3# HTMLPlug.pm -- basic html plugin
4#
5# A component of the Greenstone digital library software
6# from the New Zealand Digital Library Project at the
7# University of Waikato, New Zealand.
8#
9# Copyright (C) 1999 New Zealand Digital Library Project
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25###########################################################################
26
27#
28# Note that this plugin handles frames only in a very simple way
29# i.e. each frame is treated as a separate document. This means
30# search results will contain links to individual frames rather
31# than linking to the top level frameset.
32# There may also be some problems caused by the _parent target
33# (it's removed by this plugin)
34# To use frames properly you'll need to use the WebPlug plugin.
35#
36
37
38package HTMLPlug;
39
40use BasPlug;
41use ghtml;
42use unicode;
43use util;
44use parsargv;
45
46sub BEGIN {
47 @ISA = ('BasPlug');
48}
49
50my $arguments =
51 [ { 'name' => "process_exp",
52 'desc' => "{BasPlug.process_exp}",
53 'type' => "string",
54 'deft' => &get_default_process_exp() },
55 { 'name' => "block_exp",
56 'desc' => "{BasPlug.block_exp}",
57 'type' => 'string',
58 'deft' => &get_default_block_exp() },
59 { 'name' => "nolinks",
60 'desc' => "{HTMLPlug.nolinks}",
61 'type' => "flag" },
62 { 'name' => "keep_head",
63 'desc' => "{HTMLPlug.keep_head}",
64 'type' => "flag" },
65 { 'name' => "no_metadata",
66 'desc' => "{HTMLPlug.no_metadata}",
67 'type' => "flag" },
68 { 'name' => "metadata_fields",
69 'desc' => "{HTMLPlug.metadata_fields}",
70 'type' => "string",
71 'deft' => "Title" },
72 { 'name' => "hunt_creator_metadata",
73 'desc' => "{HTMLPlug.hunt_creator_metadata}",
74 'type' => "flag" },
75 { 'name' => "file_is_url",
76 'desc' => "{HTMLPlug.file_is_url}",
77 'type' => "flag" },
78 { 'name' => "assoc_files",
79 'desc' => "{HTMLPlug.assoc_files}",
80 'type' => "string",
81 'deft' => q^(?i)\.(jpe?g|gif|png|css)$^ },
82 { 'name' => "rename_assoc_files",
83 'desc' => "{HTMLPlug.rename_assoc_files}",
84 'type' => "flag" },
85 { 'name' => "title_sub",
86 'desc' => "{HTMLPlug.title_sub}",
87 'type' => "string",
88 'deft' => "" },
89 { 'name' => "description_tags",
90 'desc' => "{HTMLPlug.description_tags}",
91 'type' => "flag" } ];
92
93my $options = { 'name' => "HTMLPlug",
94 'desc' => "{HTMLPlug.desc}",
95 'inherits' => "yes",
96 'args' => $arguments };
97
98
99# sub print_usage {
100# print STDERR "\n usage: plugin HTMLPlug [options]\n\n";
101# print STDERR " options:\n";
102# print STDERR " -nolinks Don't make any attempt to trap links (setting this\n";
103# print STDERR " flag may improve speed of building/importing but\n";
104# print STDERR " any relative links within documents will be broken).\n";
105# print STDERR " -keep_head Don't remove headers from html files.\n";
106# print STDERR " -no_metadata Don't attempt to extract any metadata from files.\n";
107# print STDERR " -metadata_fields Comma separated list of metadata fields to attempt to
108# extract. Defaults to 'Title'.
109# Use 'tag<tagname>' to have the contents of the first
110# <tagname> pair put in a metadata element called
111# 'tagname'. Capitalise this as you want the metadata
112# capitalised in Greenstone, since the tag extraction
113# is case insensitive.\n";
114# print STDERR " -hunt_creator_metadata Find as much metadata as possible on authorship and
115# place it in the 'Creator' field. Requires the
116# -metadata_fields flag.\n";
117# print STDERR " -file_is_url Set if input filenames make up url of original source
118# documents e.g. if a web mirroring tool was used to
119# create the import directory structure\n";
120# print STDERR " -assoc_files Perl regular expression of file extensions to
121# associate with html documents.
122# Defaults to '(?i)\.(jpe?g|gif|png|css)\$'\n";
123# print STDERR " -rename_assoc_files Renames files associated with documents (e.g. images).
124# Also creates much shallower directory structure
125# (useful when creating collections to go on cd-rom).\n";
126# print STDERR " -title_sub Substitution expression to modify string stored as
127# Title. Used by, for example, PDFPlug to remove
128# \"Page 1\", etc from text used as the title.\n";
129# print STDERR " -description_tags Split document into sub-sections where <Section> tags
130# occur. Note that by setting this option you
131# implicitly set -no_metadata, as all metadata should
132# be included within the <Section> tags (this is only
133# true for documents that actually contain <Section> tags
134# however). Also, '-keep_head' will have no effect when
135# this option is set, regardless of whether a document
136# contains Section tags.\n";
137# }
138
139sub new {
140 my $class = shift (@_);
141 my $self = new BasPlug ($class, @_);
142 $self->{'plugin_type'} = "HTMLPlug";
143 # 14-05-02 To allow for proper inheritance of arguments - John Thompson
144 my $option_list = $self->{'option_list'};
145 push( @{$option_list}, $options );
146
147 if (!parsargv::parse(\@_,
148 q^nolinks^, \$self->{'nolinks'},
149 q^keep_head^, \$self->{'keep_head'},
150 q^no_metadata^, \$self->{'no_metadata'},
151 q^metadata_fields/.*/Title^, \$self->{'metadata_fields'},
152 q^hunt_creator_metadata^, \$self->{'hunt_creator_metadata'},
153 q^w3mir^, \$self->{'w3mir'},
154 q^file_is_url^, \$self->{'file_is_url'},
155 q^assoc_files/.*/(?i)\.(jpe?g|jpe|gif|png|css)$^, \$self->{'assoc_files'},
156 q^rename_assoc_files^, \$self->{'rename_assoc_files'},
157 q^title_sub/.*/^, \$self->{'title_sub'},
158 q^description_tags^, \$self->{'description_tags'},
159 "allow_extra_options")) {
160
161 print STDERR "\nIncorrect options passed to HTMLPlug, check your collect.cfg configuration file\n";
162 $self->print_txt_usage(""); # Use default resource bundle
163 die "\n";
164 }
165
166 # retain this for backward compatibility (w3mir option was replaced by
167 # file_is_url)
168 if ($self->{'w3mir'}) {
169 $self->{'file_is_url'} = 1;
170 }
171
172 $self->{'aux_files'} = {};
173 $self->{'dir_num'} = 0;
174 $self->{'file_num'} = 0;
175 return bless $self, $class;
176}
177
178
179sub get_default_block_exp {
180 my $self = shift (@_);
181
182 return q^(?i)\.(gif|jpe?g|jpe|png|css)$^;
183}
184
185sub get_default_process_exp {
186 my $self = shift (@_);
187
188 # the last option is an attempt to encode the concept of an html query ...
189 return q^(?i)(\.html?|\.shtml|\.shm|\.asp|\.php\d?|\.cgi|.+\?.+=.*)$^;
190}
191
192
193# do plugin specific processing of doc_obj
194sub process {
195 my $self = shift (@_);
196 my ($textref, $pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_;
197 my $outhandle = $self->{'outhandle'};
198
199 print STDERR "<Processing n='$file' p='HTMLPlug'>\n" if ($gli);
200
201 print $outhandle "HTMLPlug: processing $file\n"
202 if $self->{'verbosity'} > 1;
203
204 if ($ENV{'GSDLOS'} =~ /^windows/i) {
205 # this makes life so much easier... perl can cope with unix-style '/'s.
206 $base_dir =~ s@(\\)+@/@g;
207 $file =~ s@(\\)+@/@g;
208 }
209
210 # reset per-doc stuff...
211 $self->{'aux_files'} = {};
212 $self->{'dir_num'} = 0;
213 $self->{'file_num'} = 0;
214
215 my $cursection = $doc_obj->get_top_section();
216
217 $self->extract_metadata ($textref, $metadata, $doc_obj, $cursection)
218 unless $self->{'no_metadata'} || $self->{'description_tags'};
219
220 # Store URL for page as metadata - this can be used for an
221 # altavista style search interface. The URL won't be valid
222 # unless the file structure contains the domain name (i.e.
223 # like when w3mir is used to download a website).
224 my $web_url = "http://$file";
225 $doc_obj->add_metadata($cursection, "URL", $web_url);
226
227 if ($self->{'description_tags'}) {
228
229 # remove the html header - note that doing this here means any
230 # sections defined within the header will be lost (so all <Section>
231 # tags must appear within the body of the HTML)
232 $$textref =~ s/^.*?<body[^>]*>//is;
233 $$textref =~ s/(<\/body[^>]*>|<\/html[^>]*>)//isg;
234
235 my $opencom = '(?:<!--|&lt;!(?:&mdash;|&#151;|--))';
236 my $closecom = '(?:-->|(?:&mdash;|&#151;|--)&gt;)';
237 my $lt = '(?:<|&lt;)';
238 my $gt = '(?:>|&gt;)';
239 my $quot = '(?:"|&quot;|&rdquo;|&ldquo;)';
240
241 my $found_something = 0; my $top = 1;
242 while ($$textref =~ s/^(.*?)$opencom(.*?)$closecom//s) {
243 my $text = $1;
244 my $comment = $2;
245 if (defined $text) {
246 # text before a comment - note that getting to here
247 # doesn't necessarily mean there are Section tags in
248 # the document
249 $self->process_section(\$text, $base_dir, $file, $doc_obj, $cursection);
250 }
251 while ($comment =~ s/$lt(.*?)$gt//s) {
252
253 my $tag = $1;
254 if ($tag eq "Section") {
255 $found_something = 1;
256 $cursection = $doc_obj->insert_section($doc_obj->get_end_child($cursection)) unless $top;
257 $top = 0;
258 } elsif ($tag eq "/Section") {
259 $found_something = 1;
260 $cursection = $doc_obj->get_parent_section ($cursection);
261 } elsif ($tag =~ /^Metadata name=$quot(.*?)$quot/s) {
262 my $metaname = $1;
263 $comment =~ s/^(.*?)$lt\/Metadata$gt//s;
264 my $metavalue = $1;
265 $metavalue =~ s/^\s+//;
266 $metavalue =~ s/\s+$//;
267 # assume that no metadata value intentionally includes
268 # carriage returns or HTML tags (if they're there they
269 # were probably introduced when converting to HTML from
270 # some other format).
271 $metavalue =~ s/[\cJ\cM]/ /sg;
272 $metavalue =~ s/<[^>]+>//sg;
273 $metavalue =~ s/\s+/ /sg;
274 $doc_obj->set_utf8_metadata_element($cursection, $metaname, $metavalue);
275 } elsif ($tag eq "Description" || $tag eq "/Description") {
276 # do nothing with containing Description tags
277 } else {
278 # simple HTML tag (probably created by the conversion
279 # to HTML from some other format) - we'll ignore it and
280 # hope for the best ;-)
281 }
282 }
283 }
284 if ($cursection ne "") {
285 print $outhandle "HTMLPlug: WARNING: $file contains unmatched <Section></Section> tags\n";
286 }
287
288 $$textref =~ s/^.*?<body[^>]*>//is;
289 $$textref =~ s/(<\/body[^>]*>|<\/html[^>]*>)//isg;
290 if ($$textref =~ /\S/) {
291 if (!$found_something) {
292 print $outhandle "HTMLPlug: WARNING: $file appears to contain no Section tags so\n";
293 print $outhandle " will be processed as a single section document\n";
294
295 # go ahead and process single-section document
296 $self->process_section($textref, $base_dir, $file, $doc_obj, $cursection);
297
298 # if document contains no Section tags we'll go ahead
299 # and extract metadata (this won't have been done
300 # above as the -description_tags option prevents it)
301 $self->extract_metadata (\$doc_obj->get_text($cursection), $metadata, $doc_obj, $cursection)
302 unless $self->{'no_metadata'};
303
304 } else {
305 print $outhandle "HTMLPlug: WARNING: $file contains the following text outside\n";
306 print $outhandle " of the final closing </Section> tag. This text will\n";
307 print $outhandle " be ignored.";
308 my ($text);
309 if (length($$textref) > 30) {
310 $text = substr($$textref, 0, 30) . "...";
311 } else {
312 $text = $$textref;
313 }
314 $text =~ s/\n/ /isg;
315 print $outhandle " ($text)\n";
316 }
317 } elsif (!$found_something) {
318
319 # may get to here if document contained no valid Section
320 # tags but did contain some comments. The text will have
321 # been processed already but we should print the warning
322 # as above and extract metadata
323 print $outhandle "HTMLPlug: WARNING: $file appears to contain no Section tags so\n";
324 print $outhandle " will be processed as a single section document\n";
325
326 $self->extract_metadata (\$doc_obj->get_text($cursection), $metadata, $doc_obj, $cursection)
327 unless $self->{'no_metadata'};
328 }
329
330 } else {
331
332 # remove header and footer
333 if (!$self->{'keep_head'} || $self->{'description_tags'}) {
334 $$textref =~ s/^.*?<body[^>]*>//is;
335 $$textref =~ s/(<\/body[^>]*>|<\/html[^>]*>)//isg;
336 }
337
338 # single section document
339 $self->process_section($textref, $base_dir, $file, $doc_obj, $cursection);
340 }
341 return 1;
342}
343
344# note that process_section may be called multiple times for a single
345# section (relying on the fact that add_utf8_text appends the text to any
346# that may exist already).
347sub process_section {
348 my $self = shift (@_);
349 my ($textref, $base_dir, $file, $doc_obj, $cursection) = @_;
350 # trap links
351 if (!$self->{'nolinks'}) {
352
353 # usemap="./#index" not handled correctly => change to "#index"
354 $$textref =~ s/(<img[^>]*?usemap\s*=\s*\"?)([^\">\s]+)(\"?[^>]*>)/
355 $self->replace_usemap_links($1, $2, $3)/isge;
356
357 $$textref =~ s/(<(?:a|area|frame|link)\s+[^>]*?\s*(?:href|src)\s*=\s*\"?)([^\">\s]+)(\"?[^>]*>)/
358 $self->replace_href_links ($1, $2, $3, $base_dir, $file, $doc_obj, $cursection)/isge;
359 }
360
361 # trap images
362
363 # allow spaces if inside quotes - jrm21
364 $$textref =~ s/(<img[^>]*?src\s*=\s*)(\"[^\"]+\"|[^\s>]+)([^>]*>)/
365 $self->replace_images ($1, $2, $3, $base_dir, $file, $doc_obj, $cursection)/isge;
366
367 # add text to document object
368 # turn \ into \\ so that the rest of greenstone doesn't think there
369 # is an escape code following. (Macro parsing loses them...)
370 $$textref =~ s/\\/\\\\/go;
371 $doc_obj->add_utf8_text($cursection, $$textref);
372}
373
374sub replace_images {
375 my $self = shift (@_);
376 my ($front, $link, $back, $base_dir,
377 $file, $doc_obj, $section) = @_;
378 # remove quotes from link at start and end if necessary
379 if ($link=~/^\"/) {
380 $link=~s/^\"//;$link=~s/\"$//;
381 $front.='"';
382 $back="\"$back";
383 }
384
385 $link =~ s/\n/ /g;
386 my ($href, $hash_part, $rl) = $self->format_link ($link, $base_dir, $file);
387 return $front . $self->add_file ($href, $rl, $hash_part, $base_dir, $doc_obj, $section) . $back;
388}
389
390sub replace_href_links {
391 my $self = shift (@_);
392 my ($front, $link, $back, $base_dir, $file, $doc_obj, $section) = @_;
393
394 # attempt to sort out targets - frames are not handled
395 # well in this plugin and some cases will screw things
396 # up - e.g. the _parent target (so we'll just remove
397 # them all ;-)
398 $front =~ s/(target=\"?)_top(\"?)/$1_gsdltop_$2/is;
399 $back =~ s/(target=\"?)_top(\"?)/$1_gsdltop_$2/is;
400 $front =~ s/target=\"?_parent\"?//is;
401 $back =~ s/target=\"?_parent\"?//is;
402
403 return $front . $link . $back if $link =~ /^\#/s;
404 $link =~ s/\n/ /g;
405
406 my ($href, $hash_part, $rl) = $self->format_link ($link, $base_dir, $file);
407 # href may use '\'s where '/'s should be on Windows
408 $href =~ s/\\/\//g;
409
410 my ($filename) = $href =~ /^(?:.*?):(?:\/\/)?(.*)/;
411
412 ##### leave all these links alone (they won't be picked up by intermediate
413 ##### pages). I think that's safest when dealing with frames, targets etc.
414 ##### (at least until I think of a better way to do it). Problems occur with
415 ##### mailto links from within small frames, the intermediate page is displayed
416 ##### within that frame and can't be seen. There is still potential for this to
417 ##### happen even with html pages - the solution seems to be to somehow tell
418 ##### the browser from the server side to display the page being sent (i.e.
419 ##### the intermediate page) in the top level window - I'm not sure if that's
420 ##### possible - the following line should probably be deleted if that can be done
421 return $front . $link . $back if $href =~ /^(mailto|news|gopher|nntp|telnet|javascript):/is;
422
423
424 if (($rl == 0) || ($filename =~ /$self->{'process_exp'}/) ||
425 ($href =~ /\/$/) || ($href =~ /^(mailto|news|gopher|nntp|telnet|javascript):/i)) {
426 &ghtml::urlsafe ($href);
427 return $front . "_httpextlink_&rl=" . $rl . "&href=" . $href . $hash_part . $back;
428
429 } else {
430 # link is to some other type of file (eg image) so we'll
431 # need to associate that file
432 return $front . $self->add_file ($href, $rl, $hash_part, $base_dir, $doc_obj, $section) . $back;
433 }
434}
435
436sub add_file {
437 my $self = shift (@_);
438 my ($href, $rl, $hash_part, $base_dir, $doc_obj, $section) = @_;
439 my ($newname);
440
441 my $filename = $href;
442 $filename =~ s/^[^:]*:\/\///;
443 $filename = &util::filename_cat($base_dir, $filename);
444
445 # Replace %20's in URL with a space if required. Note that the filename
446 # may include the %20 in some situations
447 if ($filename =~ /\%20/) {
448 if (!-e $filename) {
449 $filename =~ s/\%20/ /g;
450 }
451 }
452
453 my ($ext) = $filename =~ /(\.[^\.]*)$/;
454
455 if ((!defined $ext) || ($ext !~ /$self->{'assoc_files'}/)) {
456 return "_httpextlink_&rl=" . $rl . "&href=" . $href . $hash_part;
457 }
458
459 if ($self->{'rename_assoc_files'}) {
460 if (defined $self->{'aux_files'}->{$href}) {
461 $newname = $self->{'aux_files'}->{$href}->{'dir_num'} . "/" .
462 $self->{'aux_files'}->{$href}->{'file_num'} . $ext;
463 } else {
464 $newname = $self->{'dir_num'} . "/" . $self->{'file_num'} . $ext;
465 $self->{'aux_files'}->{$href} = {'dir_num' => $self->{'dir_num'}, 'file_num' => $self->{'file_num'}};
466 $self->inc_filecount ();
467 }
468 $doc_obj->associate_file($filename, $newname, undef, $section);
469 return "_httpdocimg_/$newname";
470
471 } else {
472 ($newname) = $filename =~ /([^\/\\]*)$/;
473 $doc_obj->associate_file($filename, $newname, undef, $section);
474 return "_httpdocimg_/$newname";
475 }
476}
477
478
479sub format_link {
480 my $self = shift (@_);
481 my ($link, $base_dir, $file) = @_;
482
483 my ($before_hash, $hash_part) = $link =~ /^([^\#]*)(\#?.*)$/;
484
485 $hash_part = "" if !defined $hash_part;
486 if (!defined $before_hash || $before_hash !~ /[\w\.\/]/) {
487 my $outhandle = $self->{'outhandle'};
488 print $outhandle "HTMLPlug: ERROR - badly formatted tag ignored ($link)\n"
489 if $self->{'verbosity'};
490 return ($link, "", 0);
491 }
492
493 if ($before_hash =~ s@^((?:http|ftp|file)://)@@i) {
494 my $type = $1;
495
496 if ($link =~ /^(http|ftp):/i) {
497 # Turn url (using /) into file name (possibly using \ on windows)
498 my @http_dir_split = split('/', $before_hash);
499 $before_hash = &util::filename_cat(@http_dir_split);
500 }
501
502 $before_hash = $self->eval_dir_dots($before_hash);
503
504 my $linkfilename = &util::filename_cat ($base_dir, $before_hash);
505
506 my $rl = 0;
507 $rl = 1 if (-e $linkfilename);
508
509 # make sure there's a slash on the end if it's a directory
510 if ($before_hash !~ /\/$/) {
511 $before_hash .= "/" if (-d $linkfilename);
512 }
513
514 return ($type . $before_hash, $hash_part, $rl);
515
516 } elsif ($link !~ /^(mailto|news|gopher|nntp|telnet|javascript):/i) {
517 if ($before_hash =~ s@^/@@ || $before_hash =~ /\\/) {
518
519 # the first directory will be the domain name if file_is_url
520 # to generate archives, otherwise we'll assume all files are
521 # from the same site and base_dir is the root
522
523 if ($self->{'file_is_url'}) {
524 my @dirs = split /[\/\\]/, $file;
525 my $domname = shift (@dirs);
526 $before_hash = &util::filename_cat($domname, $before_hash);
527 $before_hash =~ s@\\@/@g; # for windows
528 }
529 else
530 {
531 # see if link shares directory with source document
532 # => turn into relative link if this is so!
533
534 if ($ENV{'GSDLOS'} =~ /^windows/i) {
535 # too difficult doing a pattern match with embedded '\'s...
536 my $win_before_hash=$before_hash;
537 $win_before_hash =~ s@(\\)+@/@g;
538 # $base_dir is already similarly "converted" on windows.
539 if ($win_before_hash =~ s@^$base_dir/@@o) {
540 # if this is true, we removed a prefix
541 $before_hash=$win_before_hash;
542 }
543
544 }
545 else {
546 $before_hash = &util::filename_cat("",$before_hash);
547 $before_hash =~ s@^$base_dir/@@;
548 }
549
550 }
551
552 } else {
553 # Turn relative file path into full path
554 my $dirname = &File::Basename::dirname($file);
555 $before_hash = &util::filename_cat($dirname, $before_hash);
556 $before_hash = $self->eval_dir_dots($before_hash);
557 }
558
559 my $linkfilename = &util::filename_cat ($base_dir, $before_hash);
560
561 # make sure there's a slash on the end if it's a directory
562 if ($before_hash !~ /\/$/) {
563 $before_hash .= "/" if (-d $linkfilename);
564 }
565
566 return ("http://" . $before_hash, $hash_part, 1);
567
568 } else {
569 # mailto, news, nntp, telnet, javascript or gopher link
570 return ($before_hash, "", 0);
571 }
572}
573
574sub extract_first_NNNN_characters {
575 my $self = shift (@_);
576 my ($textref, $doc_obj, $thissection) = @_;
577
578 foreach my $size (split /,/, $self->{'first'}) {
579 my $tmptext = $$textref;
580 # skip to the body
581 $tmptext =~ s/.*<body[^>]*>//i;
582 # remove javascript
583 $tmptext =~ s@<script.*?</script>@ @sig;
584 $tmptext =~ s/<[^>]*>/ /g;
585 $tmptext =~ s/&nbsp;/ /g;
586 $tmptext =~ s/^\s+//;
587 $tmptext =~ s/\s+$//;
588 $tmptext =~ s/\s+/ /gs;
589 $tmptext = substr ($tmptext, 0, $size);
590 $tmptext =~ s/\s\S*$/&#8230;/; # adds an ellipse (...)
591 $doc_obj->add_utf8_metadata ($thissection, "First$size", $tmptext);
592 }
593}
594
595sub extract_metadata {
596 my $self = shift (@_);
597 my ($textref, $metadata, $doc_obj, $section) = @_;
598 my $outhandle = $self->{'outhandle'};
599 # if we don't want metadata, we may as well not be here ...
600 return if (!defined $self->{'metadata_fields'});
601
602 # hunt for an author look in the metadata elements:
603 if (defined $self->{'hunt_creator_metadata'}) {
604 for my $name (split /,/, "AUTHOR,AUTHOR.EMAIL,CREATOR,DC.CREATOR,DC.CREATOR.CORPORATENAME") {
605 if ($$textref =~ /<meta(\s*?)(?:name|http-equiv)\s*=\s*\"?$name\"?([^>]*)/is) {
606 my $content = $1 . $2;
607 if ($content =~ /content\s*=\s*\"?(.*)\"?/is) {
608 if (defined $1) {
609 my $value = $1;
610 $value =~ s/\"$//;
611 $value =~ s/\s+/ /gs;
612 $doc_obj->add_utf8_metadata($section, "Creator", $value);
613 print $outhandle " extracted Creator metadata \"$value\"\n"
614 if ($self->{'verbosity'} > 2);
615 next;
616 }
617 }
618 }
619 }
620 }
621
622 foreach my $field (split /,/, $self->{'metadata_fields'}) {
623 my $found = 0;
624 # don't need to extract field if it was passed in from a previous
625 # (recursive) plugin
626 next if defined $metadata->{$field};
627
628 # see if there's a <meta> tag for this field
629 while ($$textref =~ /<meta(\s*?)(?:name|http-equiv)\s*=\s*\"?$field\"?([^>]*)/isg) {
630 my $content = $1 . $2;
631 if ($content =~ /content\s*=\s*\"?(.*)\"?/is) {
632 if (defined $1) {
633 my $value = $1;
634 $value =~ s/\"$//;
635 $value =~ s/\s+/ /gs;
636 $value =~ s/\".*//gs;
637 $doc_obj->add_utf8_metadata($section, $field, $value);
638 print $outhandle " extracted \"$field\" metadata \"$value\"\n"
639 if ($self->{'verbosity'} > 2);
640 $found = 1;
641 }
642 }
643 }
644 next if $found;
645 # TITLE: extract the document title
646
647 if ($field =~ /^title$/i) {
648
649 # see if there's a <title> tag
650 if ($$textref =~ /<title[^>]*>([^<]*)<\/title[^>]*>/is) {
651 if (defined $1) {
652 my $title = $1;
653 # Arg. This allows only ascii value characters in titles
654 if ($title =~ /\w/) {
655 $title =~ s/<[^>]*>/ /g;
656 $title =~ s/&nbsp;/ /g;
657 $title =~ s/\s+/ /gs;
658 $title =~ s/^\s+//;
659 $title =~ s/\s+$//;
660 $doc_obj->add_utf8_metadata ($section, $field, $title);
661 print $outhandle " extracted \"$field\" metadata \"$title\"\n"
662 if ($self->{'verbosity'} > 2);
663 next;
664 }
665 }
666 }
667
668 # if no title use first 100 characters
669 my $tmptext = $$textref;
670 $tmptext =~ s/<\/([^>]+)><\1>//g; # (eg) </b><b> - no space
671 $tmptext =~ s/<[^>]*>/ /g;
672 $tmptext =~ s/(?:&nbsp;|\xc2\xa0)/ /g; # utf-8 for nbsp...
673 $tmptext =~ s/^\s+//s;
674 $tmptext =~ s/\s+$//;
675 $tmptext =~ s/\s+/ /gs;
676 $tmptext =~ s/^$self->{'title_sub'}// if ($self->{'title_sub'});
677 $tmptext =~ s/^\s+//s; # in case title_sub introduced any...
678 $tmptext = substr ($tmptext, 0, 100);
679 $tmptext =~ s/\s\S*$/.../;
680 $doc_obj->add_utf8_metadata ($section, $field, $tmptext);
681 print $outhandle " extracted \"$field\" metadata \"$tmptext\"\n"
682 if ($self->{'verbosity'} > 2);
683 next;
684 }
685
686 # tag: extract the text between the first <H1> and </H1> tags
687 if ($field =~ /^tag[a-z0-9]+$/i) {
688
689 my $tag = $field;
690 $tag =~ s/^tag//i;
691 my $tmptext = $$textref;
692 $tmptext =~ s/\s+/ /gs;
693 if ($tmptext =~ /<$tag[^>]*>/i) {
694 foreach my $word ($tmptext =~ m/<$tag[^>]*>(.*?)<\/$tag[^>]*>/g) {
695 $word =~ s/&nbsp;/ /g;
696 $word =~ s/<[^>]*>/ /g;
697 $word =~ s/^\s+//;
698 $word =~ s/\s+$//;
699 $word =~ s/\s+/ /gs;
700 if ($word ne "") {
701 $doc_obj->add_utf8_metadata ($section, $tag, $word);
702 print $outhandle " extracted \"$tag\" metadata \"$word\"\n"
703 if ($self->{'verbosity'} > 2);
704 }
705 }
706 }
707 next;
708 }
709 }
710}
711
712
713# evaluate any "../" to next directory up
714# evaluate any "./" as here
715sub eval_dir_dots {
716 my $self = shift (@_);
717 my ($filename) = @_;
718
719 my $dirsep_os = &util::get_os_dirsep();
720 my @dirsep = split(/$dirsep_os/,$filename);
721
722 my @eval_dirs = ();
723 foreach my $d (@dirsep) {
724 if ($d eq "..") {
725 pop(@eval_dirs);
726
727 } elsif ($d eq ".") {
728 # do nothing!
729
730 } else {
731 push(@eval_dirs,$d);
732 }
733 }
734
735 return &util::filename_cat(@eval_dirs);
736}
737
738sub replace_usemap_links {
739 my $self = shift (@_);
740 my ($front, $link, $back) = @_;
741
742 $link =~ s/^\.\///;
743 return $front . $link . $back;
744}
745
746sub inc_filecount {
747 my $self = shift (@_);
748
749 if ($self->{'file_num'} == 1000) {
750 $self->{'dir_num'} ++;
751 $self->{'file_num'} = 0;
752 } else {
753 $self->{'file_num'} ++;
754 }
755}
756
757
758# Extend the BasPlug read_file so that strings like &eacute; are
759# converted to UTF8 internally.
760#
761# We don't convert &lt; or &gt; or &amp; or &quot; in case
762# they interfere with the GML files
763
764sub read_file {
765 my ($self, $filename, $encoding, $language, $textref) = @_;
766
767 &BasPlug::read_file($self, $filename, $encoding, $language, $textref);
768
769 # Convert entities to their UTF8 equivalents
770 $$textref =~ s/&(lt|gt|amp|quot|nbsp);/&z$1;/go;
771 $$textref =~ s/&([^;]+);/&ghtml::getcharequiv($1,1)/gseo;
772 $$textref =~ s/&z(lt|gt|amp|quot|nbsp);/&$1;/go;
773}
774
7751;
Note: See TracBrowser for help on using the repository browser.