Codebase list imageindex / c9484c8
Imported Upstream version 1.1 Andreas Tille 8 years ago
10 changed file(s) with 5956 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 $Id: CHANGES,v 1.30 2007/04/04 19:55:51 edwinh Exp $
1 $Name: v1_1 $
2
3 Version 1.1:
4 - some cameras date stamp using / instead of : for the date fields. fixed.
5 - support for multi-image format files such as animated GIF. The first
6 image in the multi-image deck is used for thumbnails, etc.
7 - Major update providing support for video files. Support comes via
8 having mplayer installed on your system. The first frame of the video
9 file is used to create thumbnails/mediums. Small icons can be overlaid
10 on top of the thumb/medium images to give a visual cue that the object
11 being represented in the browser is actually a video file and not a
12 still image. Default for this behavior is on and can be turned off by
13 setting $do_video_thumbnail_icons to 0. A modern version of mplayer is
14 required for all video processing.
15 - Bug fixed where HTML links back to '../$indexhtml' files were not being
16 made in recursive mode.
17 - Take author page links off of default output (can use imageindex
18 link to find us)
19
20 Version 1.0.9:
21 - fix space in filenames/dirs problem
22
23 Version 1.0.8:
24 - $do_captions rc variable to turn on/off
25 - fix obsolete flag to montage
26
27 Version 1.0.7:
28 - autocaption uses Term::Readline::Gnu
29 - Add "-reverse" to flip the timestamp ordering of the images
30 Saved as metadata if not the default value
31 - Skip .thumbcache
32 - "-rotate" works even if you swap the file/direction spec
33 - misc tiny fixes from john
34
35 Version 1.0.6:
36 - Support for morphing ASCII "smilies" (i.e. :), ;), and :() embedded in
37 captions found within images over to small "emoticon" PNG
38 images. Enabled by default. Turn off setting $do_emoticons to 0.
39 - Date and Time formatting has been radically improved. New global
40 variables governing this feature are "printf()-like" strings. Codes
41 defined by POSIX strftime(3) are interpolated to allow the date/time
42 fields to completely user-defined.
43 - Support for adding in TITLE attributes onto anchors surrounding
44 thumbnail, montage, and slide images. These TITLE attributes are also
45 defined by "printf()-like" strings and are used to present more
46 information to a user after hovering the mouse over the image (requires
47 that the browser properly renders TITLE attributes as tooltips).
48 - Image captions have been sanitized when embedded in TITLE attributes or
49 within other HTML w.r.t. special characters which might cause parse
50 errors for some rendering engines.
51 - New METADATA attribute 'NumImages' stored in index.html files. This
52 provides a convenient mechanism for knowing the number of images
53 represented in the file without having to stoop to counting, etc.
54
55 Version 1.0.5:
56 - processing loops for medium/thumbnail views optimized for speed
57 - HTML output is now "prettier" to the eye as it is indented
58 - tweak details view with some  's instead of ' ' to render more
59 efficiently
60 - break out of frames if index link is hit with small javascript
61 snippet. lets us put index link back in slides and still validate
62 strict.
63 - tweak autocaption to allow newline to end comment
64
65 Version 1.0.4:
66 - added option -includeall which nullifies the "excluded file" list
67 that is saved in metadata (for full archiving purposes vs. external viewing)
68 - tweaked the HTML for comments that end with an ellipsis (...) so
69 they look better in tight spaces when rendered
70
71 Version 1.0.3:
72 - be able exclude some images from montages
73 - take index link out of slides until we find a better way of handling
74 both frame and non-frame views
75 - minor page compliance tweaks
76
77 Version 1.0.2:
78 - montages of large (> tile grid) numbers of images now "samples"
79 which pics to use rather than taking the first N images only
80 - fix saving meta / defaults / commandline semantics (only save if
81 different from defaults)
82 - fix :hover so it applies to a only, otherwise cuased mozilla 1.1
83 wierdness
84 - added quality option for thumbs & medium
85
86 Version 1.0.1:
87 - fix doctype; add version metatag/flag
88
89 Version 1.0:
90 - Initial public release
91
92 Version 0.9.5:
93 - fix up packaging a bit
94 - doctype fixes
95 - montage is working good enough for now
96
97 Version 0.9.4:
98 - slide caption location/date is changeable
99 - add -d 'var=value' override option
100 - tables, IMG borders use css
101 - start on montage changes
102 - dirs are timestamp sorted
103 - meta date tags stored at yyyymmdd.ss
104
105 Prior to that:
106 Hacked on and used for a year or more for digital photos.
107
108 Its fvwm2 background menu roots are much older
109
110
111 Local Variables:
112 mode: flyspell
113 end:
0 GNU GENERAL PUBLIC LICENSE
1 Version 2, June 1991
2
3 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
4 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The licenses for most software are designed to take away your
11 freedom to share and change it. By contrast, the GNU General Public
12 License is intended to guarantee your freedom to share and change free
13 software--to make sure the software is free for all its users. This
14 General Public License applies to most of the Free Software
15 Foundation's software and to any other program whose authors commit to
16 using it. (Some other Free Software Foundation software is covered by
17 the GNU Library General Public License instead.) You can apply it to
18 your programs, too.
19
20 When we speak of free software, we are referring to freedom, not
21 price. Our General Public Licenses are designed to make sure that you
22 have the freedom to distribute copies of free software (and charge for
23 this service if you wish), that you receive source code or can get it
24 if you want it, that you can change the software or use pieces of it
25 in new free programs; and that you know you can do these things.
26
27 To protect your rights, we need to make restrictions that forbid
28 anyone to deny you these rights or to ask you to surrender the rights.
29 These restrictions translate to certain responsibilities for you if you
30 distribute copies of the software, or if you modify it.
31
32 For example, if you distribute copies of such a program, whether
33 gratis or for a fee, you must give the recipients all the rights that
34 you have. You must make sure that they, too, receive or can get the
35 source code. And you must show them these terms so they know their
36 rights.
37
38 We protect your rights with two steps: (1) copyright the software, and
39 (2) offer you this license which gives you legal permission to copy,
40 distribute and/or modify the software.
41
42 Also, for each author's protection and ours, we want to make certain
43 that everyone understands that there is no warranty for this free
44 software. If the software is modified by someone else and passed on, we
45 want its recipients to know that what they have is not the original, so
46 that any problems introduced by others will not reflect on the original
47 authors' reputations.
48
49 Finally, any free program is threatened constantly by software
50 patents. We wish to avoid the danger that redistributors of a free
51 program will individually obtain patent licenses, in effect making the
52 program proprietary. To prevent this, we have made it clear that any
53 patent must be licensed for everyone's free use or not licensed at all.
54
55 The precise terms and conditions for copying, distribution and
56 modification follow.
57
58 GNU GENERAL PUBLIC LICENSE
59 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
60
61 0. This License applies to any program or other work which contains
62 a notice placed by the copyright holder saying it may be distributed
63 under the terms of this General Public License. The "Program", below,
64 refers to any such program or work, and a "work based on the Program"
65 means either the Program or any derivative work under copyright law:
66 that is to say, a work containing the Program or a portion of it,
67 either verbatim or with modifications and/or translated into another
68 language. (Hereinafter, translation is included without limitation in
69 the term "modification".) Each licensee is addressed as "you".
70
71 Activities other than copying, distribution and modification are not
72 covered by this License; they are outside its scope. The act of
73 running the Program is not restricted, and the output from the Program
74 is covered only if its contents constitute a work based on the
75 Program (independent of having been made by running the Program).
76 Whether that is true depends on what the Program does.
77
78 1. You may copy and distribute verbatim copies of the Program's
79 source code as you receive it, in any medium, provided that you
80 conspicuously and appropriately publish on each copy an appropriate
81 copyright notice and disclaimer of warranty; keep intact all the
82 notices that refer to this License and to the absence of any warranty;
83 and give any other recipients of the Program a copy of this License
84 along with the Program.
85
86 You may charge a fee for the physical act of transferring a copy, and
87 you may at your option offer warranty protection in exchange for a fee.
88
89 2. You may modify your copy or copies of the Program or any portion
90 of it, thus forming a work based on the Program, and copy and
91 distribute such modifications or work under the terms of Section 1
92 above, provided that you also meet all of these conditions:
93
94 a) You must cause the modified files to carry prominent notices
95 stating that you changed the files and the date of any change.
96
97 b) You must cause any work that you distribute or publish, that in
98 whole or in part contains or is derived from the Program or any
99 part thereof, to be licensed as a whole at no charge to all third
100 parties under the terms of this License.
101
102 c) If the modified program normally reads commands interactively
103 when run, you must cause it, when started running for such
104 interactive use in the most ordinary way, to print or display an
105 announcement including an appropriate copyright notice and a
106 notice that there is no warranty (or else, saying that you provide
107 a warranty) and that users may redistribute the program under
108 these conditions, and telling the user how to view a copy of this
109 License. (Exception: if the Program itself is interactive but
110 does not normally print such an announcement, your work based on
111 the Program is not required to print an announcement.)
112
113 These requirements apply to the modified work as a whole. If
114 identifiable sections of that work are not derived from the Program,
115 and can be reasonably considered independent and separate works in
116 themselves, then this License, and its terms, do not apply to those
117 sections when you distribute them as separate works. But when you
118 distribute the same sections as part of a whole which is a work based
119 on the Program, the distribution of the whole must be on the terms of
120 this License, whose permissions for other licensees extend to the
121 entire whole, and thus to each and every part regardless of who wrote it.
122
123 Thus, it is not the intent of this section to claim rights or contest
124 your rights to work written entirely by you; rather, the intent is to
125 exercise the right to control the distribution of derivative or
126 collective works based on the Program.
127
128 In addition, mere aggregation of another work not based on the Program
129 with the Program (or with a work based on the Program) on a volume of
130 a storage or distribution medium does not bring the other work under
131 the scope of this License.
132
133 3. You may copy and distribute the Program (or a work based on it,
134 under Section 2) in object code or executable form under the terms of
135 Sections 1 and 2 above provided that you also do one of the following:
136
137 a) Accompany it with the complete corresponding machine-readable
138 source code, which must be distributed under the terms of Sections
139 1 and 2 above on a medium customarily used for software interchange; or,
140
141 b) Accompany it with a written offer, valid for at least three
142 years, to give any third party, for a charge no more than your
143 cost of physically performing source distribution, a complete
144 machine-readable copy of the corresponding source code, to be
145 distributed under the terms of Sections 1 and 2 above on a medium
146 customarily used for software interchange; or,
147
148 c) Accompany it with the information you received as to the offer
149 to distribute corresponding source code. (This alternative is
150 allowed only for noncommercial distribution and only if you
151 received the program in object code or executable form with such
152 an offer, in accord with Subsection b above.)
153
154 The source code for a work means the preferred form of the work for
155 making modifications to it. For an executable work, complete source
156 code means all the source code for all modules it contains, plus any
157 associated interface definition files, plus the scripts used to
158 control compilation and installation of the executable. However, as a
159 special exception, the source code distributed need not include
160 anything that is normally distributed (in either source or binary
161 form) with the major components (compiler, kernel, and so on) of the
162 operating system on which the executable runs, unless that component
163 itself accompanies the executable.
164
165 If distribution of executable or object code is made by offering
166 access to copy from a designated place, then offering equivalent
167 access to copy the source code from the same place counts as
168 distribution of the source code, even though third parties are not
169 compelled to copy the source along with the object code.
170
171 4. You may not copy, modify, sublicense, or distribute the Program
172 except as expressly provided under this License. Any attempt
173 otherwise to copy, modify, sublicense or distribute the Program is
174 void, and will automatically terminate your rights under this License.
175 However, parties who have received copies, or rights, from you under
176 this License will not have their licenses terminated so long as such
177 parties remain in full compliance.
178
179 5. You are not required to accept this License, since you have not
180 signed it. However, nothing else grants you permission to modify or
181 distribute the Program or its derivative works. These actions are
182 prohibited by law if you do not accept this License. Therefore, by
183 modifying or distributing the Program (or any work based on the
184 Program), you indicate your acceptance of this License to do so, and
185 all its terms and conditions for copying, distributing or modifying
186 the Program or works based on it.
187
188 6. Each time you redistribute the Program (or any work based on the
189 Program), the recipient automatically receives a license from the
190 original licensor to copy, distribute or modify the Program subject to
191 these terms and conditions. You may not impose any further
192 restrictions on the recipients' exercise of the rights granted herein.
193 You are not responsible for enforcing compliance by third parties to
194 this License.
195
196 7. If, as a consequence of a court judgment or allegation of patent
197 infringement or for any other reason (not limited to patent issues),
198 conditions are imposed on you (whether by court order, agreement or
199 otherwise) that contradict the conditions of this License, they do not
200 excuse you from the conditions of this License. If you cannot
201 distribute so as to satisfy simultaneously your obligations under this
202 License and any other pertinent obligations, then as a consequence you
203 may not distribute the Program at all. For example, if a patent
204 license would not permit royalty-free redistribution of the Program by
205 all those who receive copies directly or indirectly through you, then
206 the only way you could satisfy both it and this License would be to
207 refrain entirely from distribution of the Program.
208
209 If any portion of this section is held invalid or unenforceable under
210 any particular circumstance, the balance of the section is intended to
211 apply and the section as a whole is intended to apply in other
212 circumstances.
213
214 It is not the purpose of this section to induce you to infringe any
215 patents or other property right claims or to contest validity of any
216 such claims; this section has the sole purpose of protecting the
217 integrity of the free software distribution system, which is
218 implemented by public license practices. Many people have made
219 generous contributions to the wide range of software distributed
220 through that system in reliance on consistent application of that
221 system; it is up to the author/donor to decide if he or she is willing
222 to distribute software through any other system and a licensee cannot
223 impose that choice.
224
225 This section is intended to make thoroughly clear what is believed to
226 be a consequence of the rest of this License.
227
228 8. If the distribution and/or use of the Program is restricted in
229 certain countries either by patents or by copyrighted interfaces, the
230 original copyright holder who places the Program under this License
231 may add an explicit geographical distribution limitation excluding
232 those countries, so that distribution is permitted only in or among
233 countries not thus excluded. In such case, this License incorporates
234 the limitation as if written in the body of this License.
235
236 9. The Free Software Foundation may publish revised and/or new versions
237 of the General Public License from time to time. Such new versions will
238 be similar in spirit to the present version, but may differ in detail to
239 address new problems or concerns.
240
241 Each version is given a distinguishing version number. If the Program
242 specifies a version number of this License which applies to it and "any
243 later version", you have the option of following the terms and conditions
244 either of that version or of any later version published by the Free
245 Software Foundation. If the Program does not specify a version number of
246 this License, you may choose any version ever published by the Free Software
247 Foundation.
248
249 10. If you wish to incorporate parts of the Program into other free
250 programs whose distribution conditions are different, write to the author
251 to ask for permission. For software which is copyrighted by the Free
252 Software Foundation, write to the Free Software Foundation; we sometimes
253 make exceptions for this. Our decision will be guided by the two goals
254 of preserving the free status of all derivatives of our free software and
255 of promoting the sharing and reuse of software generally.
256
257 NO WARRANTY
258
259 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
260 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
261 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
262 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
263 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
264 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
265 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
266 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
267 REPAIR OR CORRECTION.
268
269 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
270 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
271 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
272 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
273 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
274 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
275 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
276 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
277 POSSIBILITY OF SUCH DAMAGES.
278
279 END OF TERMS AND CONDITIONS
280
281 How to Apply These Terms to Your New Programs
282
283 If you develop a new program, and you want it to be of the greatest
284 possible use to the public, the best way to achieve this is to make it
285 free software which everyone can redistribute and change under these terms.
286
287 To do so, attach the following notices to the program. It is safest
288 to attach them to the start of each source file to most effectively
289 convey the exclusion of warranty; and each file should have at least
290 the "copyright" line and a pointer to where the full notice is found.
291
292 <one line to give the program's name and a brief idea of what it does.>
293 Copyright (C) <year> <name of author>
294
295 This program is free software; you can redistribute it and/or modify
296 it under the terms of the GNU General Public License as published by
297 the Free Software Foundation; either version 2 of the License, or
298 (at your option) any later version.
299
300 This program is distributed in the hope that it will be useful,
301 but WITHOUT ANY WARRANTY; without even the implied warranty of
302 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303 GNU General Public License for more details.
304
305 You should have received a copy of the GNU General Public License
306 along with this program; if not, write to the Free Software
307 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
308
309
310 Also add information on how to contact you by electronic and paper mail.
311
312 If the program is interactive, make it output a short notice like this
313 when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320 The hypothetical commands `show w' and `show c' should show the appropriate
321 parts of the General Public License. Of course, the commands you use may
322 be called something other than `show w' and `show c'; they could even be
323 mouse-clicks or menu items--whatever suits your program.
324
325 You should also get your employer (if you work as a programmer) or your
326 school, if any, to sign a "copyright disclaimer" for the program, if
327 necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335 This General Public License does not permit incorporating your program into
336 proprietary programs. If your program is a subroutine library, you may
337 consider it more useful to permit linking proprietary applications with the
338 library. If this is what you want to do, use the GNU Library General
339 Public License instead of this License.
0 ######################################################################
1 # $Id: Makefile.dist,v 1.3 2003/03/31 03:01:24 edwinh Exp $
2 # $Name: v1_1 $
3 ######################################################################
4
5 # Where the script binary should go
6 PREFIX ?= /usr/local
7
8 # Where perl lives
9 PERLPATH = /usr/bin/perl
10
11 ######################################################################
12
13 all: sed
14
15 install: all
16 install -m 0755 imageindex.sed $(PREFIX)/bin/imageindex
17 install -m 0755 autocaption.sed $(PREFIX)/bin/autocaption
18 install -m 0644 imageindex.1 $(PREFIX)/share/man/man1/imageindex.1
19
20 sed:
21 sed -e 's%/usr/bin/perl%$(PERLPATH)%g' imageindex > imageindex.sed
22 sed -e 's%/usr/bin/perl%$(PERLPATH)%g' autocaption > autocaption.sed
23
24 clean:
25 rm -f imageindex.sed
26 rm -f autocaption.sed
0 _ _ _
1 (_)_ __ ___ __ _ __ _ ___(_)_ __ __| | _____ __
2 | | '_ ` _ \ / _` |/ _` |/ _ \ | '_ \ / _` |/ _ \ \/ /
3 | | | | | | | (_| | (_| | __/ | | | | (_| | __/> <
4 |_|_| |_| |_|\__,_|\__, |\___|_|_| |_|\__,_|\___/_/\_\
5 |___/
6
7 ...a digital photo gallery tool
8
9 README for version 1.1
10
11
12 FEATURES
13
14 o Index, detail, slide, and frame views
15 o Simple, uncluttered output
16 o Static HTML output for ease of copying/archiving
17 o Uses captions from comments embedded in the image files
18 (utility provided). Captions will never be lost as long as you have
19 the image file itself.
20 o Keeps generated images up to date, removes stale files,
21 only generates needed thumbs, etc.
22 o Digital photo details extracted from EXIF data
23 o Can optionally recurse directory trees and make montage images of
24 directory contents
25 o Easily configurable, can use an rc file.
26 o CSS is used for fonts/styles.
27 o Can handle many image file formats
28 o NEW! Now supports video files (mplayer required for support)
29 o Pages pass W3C specs.
30
31
32 REQUIREMENTS
33
34 o perl 5.003_05 or newer
35 o ImageMagick and the Image::Magick perl module
36 o Optional; needed if you want to see EXIF data from the images
37 - Image::Info from CPAN
38 - which needs IO::String as well
39 o mplayer (at least 0.9.88 or newer) is required if you want support for
40 processing video files
41
42
43 QUICK USAGE SUMMARY
44
45 Please see the comments at the top of the file or run "imageindex -help" to
46 see all of the command line options
47
48 Examples:
49
50 imageindex
51 Index the images in the current directory
52
53 imageindex -noslide -x 200 -y 200 /path/to/images
54 Index the images in specified dir, without slideshow files, and
55 override the configured thumbnail size
56
57 imageindex -recurse -destdir /tmp/gallery
58 Index images, include subdirs, and export the results to different directory
59
60 imageindex -caption <file> "Some really cool picture"
61 Embed caption in a file and exit
62
63
64 CONFIGURATION
65
66 Edit any of the variables in configuration section at the top of the
67 script.
68
69 If you'd like, you can store any of these you'd like to override in a
70 ~/.imageindexrc file. (Use a '1;' as the last line of the file so it can
71 be require-d by the script.)
72
73
74 MISCELLANEOUS
75
76 The "autocaption" script provided is a quick hack to iterate over a
77 directory full of photos, show them to you, and interactively prompt to
78 apply embedded captions. (Captions use the JPEG comment field). This utility
79 will eventually be replaced with something more GUI-based.
80
81
82 ABOUT/CREDITS
83
84 Yes, there are other similar programs available. The ones we saw produced
85 cluttered or (IMHO) ugly output for the most part, relied on some sort of
86 server processing, and didn't handle image comments in a concise manner.
87 We did this just for our own purposes, but hopefully someone else can get
88 some use out of it.
89
90
91 Edwin Huffstutler
92 John Reynolds
93
94
95
96 Local Variables:
97 mode: flyspell
98 end:
0 $Id: TODO,v 1.43 2003/09/09 03:13:06 edwinh Exp $
1 $Name: v1_1 $
2
3 Bugs
4
5 - Fix embedded emoticons with unicode (pack/unpack is a problem)
6
7 Additions/Enhancements
8
9 - Flesh out .pod file.
10 Do pod2man for packaging, etc
11
12 - Medium-to-Large architecture change. Pre-compute what things need to be
13 done (which slide show .html files need to be created, which thumbs,
14 which mediums, etc.) and then execute these jobs through a for() loop
15 with fork()/exec() keeping N jobs "active" at a time where N is
16 configurable. This will help speed-up on dual CPU machines.
17
18 - Add more samples in sample directory
19
20 - Fill out lsm, send to ibiblio.org as part of release?
21 Upload to rh contrib?
22
23
24 Ideas (not sure)
25
26 - Use something like HTML::Stream rather than manually printf'ing tags.
27
28 - Add a way to dump default config variables into dotfile
29
30 - Make it use a ~/.imageindex.css file for the stylesheet customization
31 (so you don't have to muck with the perl variable) ??? If this is done
32 also add -writecss flag or something that creates a "default" copy of
33 this for you to edit later?
34
35 - Drop-shadow option for thumbnails?
36
37
38 Bugs/Cleanup
0 #!/usr/bin/perl -w
1 #
2 # This is a REAL QUICK HACK to add captions to JPG files. We'll replace it
3 # with something more Rico Suave(tm) as time goes one.
4 #
5 # Use --force to force the program to process all JPG files regardless of
6 # whether an existing comment exists or not.
7 #
8 # Use --oneline to get one line captions, ending when you hit enter.
9 # (won't wait for . at last line to end it)
10 #
11 # $Id: autocaption,v 1.7 2006/10/28 16:43:11 jjreynold Exp $
12 # $Name: v1_1 $
13 #
14 use Getopt::Long;
15 use Term::ReadLine;
16 use vars qw($opt_force);
17
18 $size = '500x400';
19 $geom = "${size}+5+5";
20
21 my $term = new Term::ReadLine 'imageindex autocaption';
22 my $OUT = $term->OUT || \*STDOUT;
23
24 &GetOptions('force','oneline') or die ("Invalid flag\n");
25
26 if (scalar (@ARGV)) {
27 @files = @ARGV;
28 }
29 else {
30 while ($name = <*.jpg *.JPG *.jpeg *.JPEG>) {
31 push (@files, $name);
32 }
33 }
34
35 foreach my $name (@files) {
36 printf $OUT ("Processing $name ...\n");
37 $qname = quotemeta ($name);
38 open (PIPE, "imageindex -caption $qname |") || die;
39 $existing_caption = '';
40 while (<PIPE>) {
41 if (/: "(.*)"\s*$/) {
42 $existing_caption = $1;
43 }
44 }
45 close (PIPE);
46 if ($existing_caption) {
47 printf $OUT ("Existing caption: \"$existing_caption\"\n");
48 if (! defined ($opt_force)) {
49 printf $OUT ("Skipping this file. Use -force to re-do captions.\n\n");
50 next;
51 }
52 }
53 else {
54 printf $OUT ("No existing caption found.\n");
55 }
56 if (!defined($opt_oneline)) {
57 printf $OUT ("Bringing up resized image. Enter a caption with \".\" last line:\n");
58 printf $OUT ("(entering \".\" will re-use any previous caption)\n");
59 } else {
60 printf $OUT ("Bringing up resized image. Enter a caption:\n");
61 printf $OUT ("(blank will re-use any previous caption)\n");
62 }
63 if ($pid = fork) {
64 my $caption = "";
65 my $prompt = "$name: ";
66 while (defined ($_ = $term->readline($prompt)) ) {
67 warn $@ if $@;
68 if (defined($opt_oneline)) {
69 $caption .= $_;
70 last;
71 } elsif ($_ =~ /^\.$/) {
72 last;
73 } else {
74 $caption .= " $_";
75 }
76 }
77 chomp($caption);
78 $term->addhistory($caption);
79 $caption = quotemeta ($caption);
80 if (($caption ne '') and ($caption ne '.')) {
81 printf $OUT ("Executing: imageindex -caption $qname $caption\n");
82 system ("imageindex -caption $qname $caption");
83 }
84
85 kill 15, $pid;
86
87 }
88 else {
89 exec 'display', '-geometry', $geom, '-resize', $size, $name;
90 }
91
92
93 }
0 #!/usr/bin/perl -w
1 #
2 # Edwin Huffstutler <edwinh@computer.org>
3 # John Reynolds <johnjen@reynoldsnet.org>
4 #
5 # perl script for: web page index/thumbnails of photos.
6 # orginally started life as a menu selector for fvwm2 backgrounds...
7 #
8 # USAGE:
9 #
10 # imageindex [options] <directory>
11 #
12 # <directory> is assumed to be "." if not given
13 #
14 # Options: (can be abbreviated if unique)
15 #
16 # -title <string> title for this page (saved for susbsequent runs)
17 # -destdir <dir> export image/html tree to dir (will be created if needed)
18 # -[no]recurse enable/disable recursion into subdirectories
19 # -[no]medium enable/disable medium size images/links
20 # -[no]slide enable/disable slideshow files
21 # -[no]detail enable/disable detail file
22 # -[no]dirs enable/disable directory entries
23 # -[no]montage enable/disable directory montages
24 # -forceregen force regeneration of thumbnails
25 # -columns <num> number of columns in html table (saved for susbsequent runs)
26 # -exclude <file> Exclude <file> from processing. Can be used multiple times
27 # -includeall Nullifies excluded file list (saved from previous run)
28 # -skipmont <file> Exclude <file> from being included in a directory montage.
29 # -reverse Order timestamps with newest first
30 # -x <num> override thumbnail x size
31 # -y <num> override thumbnail y size
32 # -help show this text
33 # -version show the current version
34 # -d 'var=val' force override of global variable
35 #
36 # See also the configuration section at the top of the program itself,
37 # or in ~/.imageindexrc
38 #
39 # (non-html-generating, utility options)
40 #
41 # -lowercase Lowercase all image files in a directory
42 # -caption <file> <string> Store comment in image
43 # -rotate <file> [cw|ccw] Rotate an image clockwise or counterclockwise
44 # -showexcluded Show which files were excluded in a prior run
45 #
46 ######################################################################
47
48 #
49 # Configuration options
50 #
51
52 # sizes / dirs
53 $thumbnail_dir = 'thumbnail';
54 $default_thumbnail_x = 200;
55 $default_thumbnail_y = 200;
56
57 # If both dimensions of the original are within this much of the thumb
58 # dimensions we will skip the thumbnail and just use the original
59 $thumbnail_threshold = 1.0;
60
61 # tesla
62
63 $med_x = 800;
64 $med_y = 600;
65 $med_dir = 'medium';
66
67 # If both dimensions of the original are within this much of the "medium"
68 # dimensions we will skip creating the medium-size format and just use the
69 # original
70 $med_threshold = 1.6;
71
72 # Enable/disable features, set default for various flags
73 $do_recurse = 0; # Recurse into subdirs?
74 $do_medium = 1; # Generate medium-format?
75 $do_slide = 1; # Generate slides/frame view?
76 $do_detail = 1; # Generate details page?
77 $do_dirs = 1; # Create directory entries?
78 $do_captions = 1; # Use caption info stored in images?
79 $do_montage = 1; # Create directory montages?
80 $do_emoticons = 1; # Replace ASCII smiley's with images?
81 $do_reverse = 0; # Sort timestamps in reverse order?
82 $do_video_files = 1; # process video files with mplayer?
83 $do_video_thumbnail_icons = 1; # Annotate movie icon onto thumbnails of video files
84
85 # video file options
86
87 # Brute force way of filtering out any "movie" files (mpg, mp4, avi,
88 # mov, etc.) which might lurk in a directory
89 #
90 $video_regexp = '(avi|mov|mpg|mpeg|mjpeg|m1v|m2v|wmv|fli|nuv|vob|ogm|vcd|svcd|mp4|qt)';
91
92 # Control which corner video icons are overlayed onto thumbnails of
93 # of video files. Values can be: SouthWest, NorthWest, NorthEast, SouthEast
94 $video_icon_gravity = 'SouthWest';
95
96 # Control which video "icon" is overlayed on top of thumbnails of video files
97 # 1 = yellow dot with "play" arrow
98 # 2 = purplish icon of video camera
99 $video_icon = 1;
100
101 # end video file options
102
103 $max_video_icons = 0; # do not adjust, this is just a global variable
104
105 # What the various image links point to - can be 'index', 'fullsize',
106 # 'medium', 'thumbnail', 'slide', or 'details'
107 $index_linkto = 'slide';
108 $details_linkto = 'index';
109 $slide_linkto = 'fullsize';
110
111 # Default number of columns to use
112 $default_columns = 3;
113
114 # Orientation of slide frame - 'horizontal' or 'vertical'
115 $frame_orient = 'vertical';
116
117 # Location of items in slide pages; 'top', 'bottom', or 'none'
118 $slide_caption = 'top';
119 $slide_date = 'bottom';
120
121 # Details index uses thumbs reduced by this amount
122 $detailshrink = 2;
123
124 # Quality for generated images
125 $thumb_quality = 50;
126 $med_quality = 80;
127
128 # Minimum and maximum number of tiles in directory montage images
129 $montage_min = 4;
130 $montage_max = 36;
131
132 # Space between montage images
133 $montage_whitespace = 2;
134
135 # What to do with leftover montage tiles; can be
136 # 'blank' or 'repeat'
137 $montage_fill = 'blank';
138
139 # Stylesheet specs
140 # Set element font, etc. properties here
141 $stylesheet = '
142 body { color: black; background: white; }
143
144 /* Fonts in the title */
145 h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif; font-size: 200%; font-weight: bold; text-align: center; }
146 h2.daterange { font-family: Arial,Helvetica,sans-serif; font-size: 125%; text-align: center; }
147 h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%; text-align: center; }
148
149 /* Photo captions & Directory titles */
150 div.caption { font-family: Arial,Helvetica,sans-serif; font-size: 100%; font-weight: bold; margin: 1em; }
151
152 /* Overall fonts on the index and details page */
153 div.index { font-family: Arial,Helvetica,sans-serif; font-size: 80%; }
154 div.detail { font-family: Arial,Helvetica,sans-serif; font-size: 80%; }
155 div.credits { font-family: Arial,Helvetica,sans-serif; font-size: 80%; text-align: right; margin: 10px }
156
157 /* Table attributes */
158 table.index { background: #ffffff; border: none; border-spacing: 8px; }
159 td.index { border: none; padding: 3px }
160 table.frame { background: #ffffff; border: none }
161 td.frame { border: none; padding: 0px }
162
163 /* Image attributes */
164 img.index { border: none; }
165 img.slide { border: none; }
166 img.frame { border: none; }
167
168 /* Link attributes */
169 a:link { color: blue; }
170 a:visited { color: green; }
171 a:hover { color: red; }
172 a:active { color: red; }
173
174 ';
175
176
177 # Text
178 $emptycell = "<I>empty</I>";
179 $updirtext = "up one directory";
180 $framelinktext = "slideshow view (frames)";
181 $detaillinktext = "details index";
182 $indexlinktext = "main index";
183 $default_titletext = "Image directory";
184
185 # These five variables control the TITLE attribute on anchor constructs in the
186 # index and frame views. When TITLE attributes are given they are usually
187 # rendered as "tooltip" bubbles that show text when a cursor hovers and stops
188 # over the active link. We use them here to give a visual cue about the image.
189 # These variables work much like printf(1) strings.
190 #
191 # %f => replaced with the filename of the image
192 # %d => replaced with the date/time of the image (or mtime of the file)
193 # %s => replaced with the size of the file (in Kb)
194 # %r => replaced with the resolution (XxY) of the original image
195 # %c => replaced with the image's caption (if stored with one)
196 # %% => replaced with a literal '%' character
197 #
198 # The following are used when directories are processed and a montage of
199 # that directory is used as the thumbnail of the dir.
200 #
201 # %n => replaced with number of images in a directory
202 # %b => replaced with the "begin" date from a directory of images
203 # %e => replaced with the "end" date from a directory of images
204 # %t => replaced with the "title" from a directory of images
205 #
206 # Other characters (including spaces) are literal. "undef" these in
207 # your ~/.imageindexrc file if you don't want them to show up. The "date/time"
208 # related constructs are interpolated using the date/time format variables
209 # defined below.
210 #
211 $framethumbtitle = "%f - %d";
212 $indexthumbtitle = "%f (%s)";
213 $slidethumbtitle = "%f (%s)";
214 $detailthumbtitle = "%c";
215 $montagetitle = "%n images %b through %e";
216
217 # Date/Time format strings. These strings are formatted much like the above
218 # variables and the definitions of the escape sequences come from the POSIX
219 # strftime(3) definitions. NOT ALL of strftime(3) are supported for obvious
220 # reasons.
221 #
222 # %S is replaced by the second as a decimal number (00-60).
223 # %M is replaced by the minute as a decimal number (00-59).
224 # %I is replaced by the hour (12-hour clock) as a decimal number (01-12).
225 # %H is replaced by the hour (24-hour clock) as a decimal number (00-23).
226 # %p is replaced by national representation of either "ante meridiem" or
227 # "post meridiem" as appropriate (currently only U.S. "am" or "pm")
228 # %R is equivalent to "%H:%M" (in *timeformat variables only).
229 # %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only).
230 #
231 # %Y is replaced by the year with century as a decimal number.
232 # %y is replaced by the year without century as a decimal number (00-99).
233 # %m is replaced by the month as a decimal number (01-12).
234 # %d is replaced by the day of the month as a decimal number (01-31).
235 # %F is equivalent to "%Y-%m-%d" (in *dateformat variables only).
236 # %D is equivalent to "%m/%d/%y" (in *dateformat variables only).
237 # %% is replaced by a literal "%".
238
239 $framedateformat = "%m/%d/%Y";
240 $frametimeformat = "%r";
241
242 $indexdateformat = "%m/%d/%Y";
243 $indextimeformat = "%r";
244
245 $slidedateformat = "%m/%d/%Y";
246 $slidetimeformat = "%r";
247
248 $detaildateformat = "%m/%d/%Y";
249 $detailtimeformat = "%I:%M %p";
250
251 # Pathnames
252 $indexfile = 'index.html';
253 $detailfile = 'details.html';
254 $framefile = 'frame.html';
255 $slidefile = 'slides.html';
256 $slide_dir = 'slides';
257 $stylefile = 'style.css';
258 $montagefile = 'montage.jpg';
259 $emoticonprefix = 'ii_';
260 $emoticonsmile = $emoticonprefix . 'smile.png';
261 $emoticonwink = $emoticonprefix . 'wink.png';
262 $emoticonfrown = $emoticonprefix . 'frown.png';
263
264 # File exclusion customization (regex)
265 # (Anything non-image and non-dir will be skipped automatically, this just
266 # makes it silent)
267 @exclude = qw(
268 ^CVS$
269 ^.nautilus-metafile.xml$
270 ^.thumbnails$
271 ^.nfs.*$
272 ^.xvpics$
273 ^.thumbcache$
274 ^ALBUM.OFA$
275 ^desktop.ini$
276 ^.*.txt$
277 );
278
279
280 # Metatags
281 $columnsmetatag = 'Columns';
282 $titlemetatag = 'Title';
283 $begindatemetatag = 'DateBegin';
284 $enddatemetatag = 'DateEnd';
285 $excludemetatag = 'ExcludedFiles';
286 $skipmetatag = 'SkipMontageFiles';
287 $numimagesmetatag = 'NumImages';
288 $reversemetatag = 'Reverse';
289 $thumbxmetatag = 'ThumbnailX';
290 $thumbymetatag = 'ThumbnailY';
291
292 # Any of the above can be overridden in an rc file in the user's home dir
293 $rcfile = "$ENV{'HOME'}/.imageindexrc";
294
295 ######################################################################
296 #
297 # $Id: imageindex,v 1.175 2007/04/04 19:55:51 edwinh Exp $
298 #
299 # imageindex is free software; you can redistribute it and/or modify
300 # it under the terms of the GNU General Public License as published by
301 # the Free Software Foundation; either version 2, or (at your option)
302 # any later version.
303 #
304 # imageindex is distributed in the hope that it will be useful,
305 # but WITHOUT ANY WARRANTY; without even the implied warranty of
306 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
307 # GNU General Public License for more details.
308 #
309 # You should have received a copy of the GNU General Public License
310 # along with imageindex; see the file COPYING.
311 #
312 ######################################################################
313
314 use Image::Magick; # comes with ImageMagick
315
316 # from CPAN - optional
317 eval('use Image::Info qw(image_info)');
318
319 # Shipped with perl
320 use POSIX;
321 use Getopt::Long;
322 use FileHandle;
323 use File::Basename;
324 use File::Copy;
325 use English;
326 use Carp;
327 require 'flush.pl';
328
329 # to shut up -w
330 use vars qw($opt_recurse);
331 use vars qw($opt_slide);
332 use vars qw($opt_dirs);
333 use vars qw($opt_detail);
334 use vars qw($opt_lowercase);
335 use vars qw($opt_help);
336 use vars qw($opt_debug);
337 use vars qw($opt_showexcluded);
338 use vars qw($opt_version);
339 use vars qw($opt_updirindexoverride);
340
341 &GetOptions(
342 'title=s',
343 'columns=i',
344 'x=i',
345 'y=i',
346 'forceregen',
347 'medium!',
348 'slide!',
349 'detail!',
350 'dirs!',
351 'montage!',
352 'recurse!',
353 'destdir=s',
354 'lowercase',
355 'caption=s',
356 'rotate=s',
357 'exclude=s@',
358 'skipmont=s@',
359 'showexcluded',
360 'includeall',
361 'version',
362 'help',
363 'debug',
364 'reverse!',
365 'd=s%',
366 'updirindexoverride'
367 ) or die ("Invalid flag\n");
368
369 # Find out which platform we're on so we don't give incorrect options to needed
370 # commands
371 #
372 $uname = `uname -s`;
373 chomp ($uname);
374
375 # Override config variables
376 foreach my $var (keys %opt_d) {
377 $value = $opt_d{$var};
378 print "(override) $var = $value\n";
379 eval("\$$var=\"$value\"");
380 }
381
382 &init_png_array();
383
384 # Read RC file
385 if (-e $rcfile) {
386 print "Using settings in $rcfile...\n" if ! defined ($opt_version);
387 require $rcfile;
388 }
389
390 # Rotate or caption image (then exit)
391 if (defined ($opt_rotate)) {
392 &rotate_image($opt_rotate,\@ARGV);
393 exit (0);
394 } elsif (defined ($opt_caption)) {
395 &caption_image($opt_caption,\@ARGV);
396 exit (0);
397 } elsif (defined ($opt_showexcluded)) {
398 &showexcluded($ARGV[0]);
399 exit (0);
400 } elsif (defined ($opt_version)) {
401 printf ("imageindex version: %s\n", &versionstring);
402 exit (0);
403 }
404
405 # The directory to search is the first argument
406 if (defined($ARGV[0])) {
407 $srcdir = $ARGV[0];
408 $srcdir =~ s:/$::;
409 } else {
410 $srcdir = ".";
411 }
412
413 # Give usage message
414 if (defined($opt_help)) {
415 &usage();
416 exit(0);
417 }
418
419 # Show backtrace if debug given
420 if (defined($opt_debug)) {
421 $SIG{__WARN__} = \&Carp::cluck;
422 }
423
424 # Where to generate files
425 $destdir = $srcdir;
426 if (defined($opt_destdir)) {
427 $destdir = $opt_destdir;
428 $destdir =~ s:/$::;
429 print "Exporting to $destdir\n";
430 unless (-d $destdir) {
431 printf ("Creating destination directory '$destdir'.\n");
432 mkdir ($destdir, 0755);
433 }
434 }
435
436 unless (-w $destdir) {
437 printf ("No write permission for $destdir\n");
438 exit (1);
439 }
440
441 if (defined($opt_medium)) {
442 $do_medium = $opt_medium
443 }
444
445 if (defined($opt_slide)) {
446 $do_slide = $opt_slide;
447 }
448
449 if (defined($opt_detail)) {
450 $do_detail = $opt_detail;
451 }
452
453 if (defined($opt_dirs)) {
454 $do_dirs = $opt_dirs;
455 }
456
457 if (defined($opt_montage)) {
458 $do_montage = $opt_montage;
459 }
460
461 if (defined($opt_recurse)) {
462 $do_recurse = $opt_recurse;
463 }
464
465 # no montages if we aren't doing dirs anyway
466 if ($do_dirs == 0) {
467 $do_montage = 0;
468 }
469
470 &initialize_current_vars();
471 &read_stored_meta_data();
472 &override_by_commandline();
473
474 if (!defined(&image_info)) {
475 print "Image::Info not found, not extracting EXIF data\n";
476 }
477
478 opendir(DIR, "$srcdir") || die "Can't open dir $srcdir: ($!)\n";
479 @files = readdir DIR;
480 closedir(DIR);
481 @files = grep (!/^\.?\.$/, @files);
482
483 # Skip the files/dirs we use or generate. Any other patterns go in the
484 # config section (@exclude) or in exclude file
485 my @generated_files = ($thumbnail_dir, $med_dir, $slide_dir,
486 $indexfile, $detailfile, $stylefile,
487 );
488
489 foreach my $pattern (@generated_files, @exclude) {
490 @files = grep (!/^$pattern$/, @files);
491 }
492
493 @files = &exclude_files(@files);
494
495 # Change all the names of image files to lowercase.
496 if (defined ($opt_lowercase)) {
497 &lower_case_files(@files);
498 exit (0);
499 }
500
501 # Keep track of which column to be in
502 my $col_counter = 1;
503
504 # Count how many files we create
505 my $object_counter = 0;
506 my $dir_counter = 0;
507 my $image_counter = 0;
508 my $thumbnail_counter = 0;
509 my $med_counter = 0;
510 my $slide_counter = 0;
511 my $modified_thumb = 0;
512
513 # Keep track of max thumb sizes to use for slide frame width
514 my $max_thumb_x = 0;
515 my $max_thumb_y = 0;
516
517 # Keep track of max thumb sizes to use for montage creation
518 my $max_mont_thumb_x = 0;
519 my $max_mont_thumb_y = 0;
520
521 my $mplayer_prog = &find_in_path ('mplayer');
522
523 # Extract info
524 print "Extracting image info";
525 flush (STDOUT);
526
527 foreach my $file (@files) {
528
529 # If directory, grab the timestamp
530 if (-d "$srcdir/$file") {
531
532 my $ts;
533
534 # Grab timestamp from meta tag
535 if (-e "$srcdir/$file/$indexfile") {
536
537 my $begin = &extract_meta_tag($begindatemetatag,"$srcdir/$file/$indexfile");
538 if (defined($begin)) {
539 if (!defined($firstdate) or ($begin < $firstdate)) {
540 $firstdate = $begin;
541 }
542 $ts = $begin;
543 }
544
545 my $end = &extract_meta_tag($enddatemetatag,"$srcdir/$file/$indexfile");
546 if (defined($end)) {
547 if (!defined($lastdate) or ($end > $lastdate)) {
548 $lastdate = $end;
549 }
550 $ts = $end if (!defined($ts));
551 }
552
553 }
554
555 # Fallback on dir mtime
556 if (!defined($ts)) {
557 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
558 $atime,$mtime,$ctime,$blksize,$blocks) = stat("$srcdir/$file");
559 $ts = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
560 }
561
562 push(@{$dir_timestamp{$ts}}, $file);
563
564 } else {
565
566 # Collect info from the image or video
567 &extract_file_info($file);
568
569 }
570 }
571 print "\n";
572
573
574 # Do dirs first
575 if ($do_dirs) {
576 foreach my $ts (sort bynumber keys %dir_timestamp) {
577 foreach my $dir (sort @{$dir_timestamp{$ts}}) {
578 &dir_entry($dir);
579 } # foreach dir that has this timestamp
580 } # foreach timestamp
581 }
582
583
584 # Bail if nothing here
585 if ($object_counter == 0) {
586 print "Nothing to do!\n";
587 unlink("$destdir/$indexfile") if (-e "$destdir/$indexfile");
588 unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile");
589 unlink("$destdir/$stylefile") if (-e "$destdir/$stylefile");
590 exit(0);
591 }
592
593 # Make thumb dirs if needed
594 foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) {
595 unless (-d "$destdir/$checkdir") {
596 mkdir("$destdir/$checkdir",0777);
597 }
598 }
599
600 # Nuke old thumbnails if original image gone
601 &nuke_out_of_date();
602
603 # Iterate over the files based on timestamp
604 # This is just to get back/forward links
605 undef $prev;
606 foreach (sort bynumber keys %timestamp) {
607
608 foreach my $pathname (sort @{$timestamp{$_}}) {
609
610 if (defined($prev)) {
611 my ($name,$path,$suffix);
612
613 ($name,$path,$suffix) = fileparse($prev,'\.\S+');
614 $back{$pathname} = "$name.html";
615
616 ($name,$path,$suffix) = fileparse($pathname,'\.\S+');
617 $forward{$prev} = "$name.html";
618 }
619 $prev = $pathname;
620
621 } # foreach image that has this timestamp
622
623 } # foreach timestamp
624
625 # Iterate over the files based on timestamp
626 # This will do the real work
627 foreach (sort bynumber keys %timestamp) {
628
629 foreach my $pathname (sort @{$timestamp{$_}}) {
630
631 my $filename = $info{$pathname}{'file'};
632 my $thumbnail = $info{$pathname}{'thumb'};
633 my $medium = $info{$pathname}{'medium'};
634 my $slide = $info{$pathname}{'slide'};
635
636 my $tmp_jpg_dir = "tmp_jpg_$$";
637
638 if (!defined($firstdate) or ($info{$pathname}{'date'} < $firstdate)) {
639 $firstdate = $info{$pathname}{'date'};
640 }
641
642 if (!defined($lastdate) or ($info{$pathname}{'date'} > $lastdate)) {
643 $lastdate = $info{$pathname}{'date'};
644 }
645
646 #
647 # First, deal with medium format of the image since we can save time shrinking
648 # the medium down to the thumbnail rather than fullsize->thumbnail
649 #
650 # If the file is a video, we must first extract the first frame of the video
651 # stream into JPEG form and we may or may not maniplate it from there.
652 #
653
654 # Skip if we want no medium images at all
655 #
656 if ($do_medium == 0) {
657 $skipmedium{$pathname} = 1;
658 unlink("$destdir/$medium") if (-e "$destdir/$medium");
659
660 } elsif (($info{$pathname}{'x'} <= ($med_x * $med_threshold)) and
661 ($info{$pathname}{'y'} <= ($med_y * $med_threshold))) {
662
663 my $image = new Image::Magick;
664 my $retval;
665
666 # Regardless of whether the "size" of a frame of a video is
667 # close to our medium threshold size, we need to create an
668 # image and have it there. If it is an image then the HTML will
669 # just link to the actual image rather than creating a 'medium'
670 # version. But for video files we need to make one regardless.
671 #
672 if ($info{$pathname}{'is_video'}) {
673 if ((! -e "$destdir/$medium") or
674 ( -M $pathname < -M "$destdir/$medium") or
675 defined($opt_forceregen)) {
676
677 my $icon = new Image::Magick;
678
679 print "Creating $destdir/$medium from first frame of $pathname\n";
680
681 my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog);
682 $retval = $image->Read(filename=>$tmpfile);
683 warn "$retval" if "$retval";
684 $retval = $image->Set(interlace=>Line);
685 warn "$retval" if "$retval";
686 # always want 100% here instead of $med_quality
687 $retval = $image->Set(quality=>100);
688 warn "$retval" if "$retval";
689
690 # If the user wants to, overlay a small icon on top of the
691 # thumbnail of the video file to give a visual cue that this
692 # file is a video and not a still photo
693 #
694 if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) {
695 my $iconfile;
696 if ($video_icon_gravity ne 'SouthWest' &&
697 $video_icon_gravity ne 'NorthWest' &&
698 $video_icon_gravity ne 'SouthEast' &&
699 $video_icon_gravity ne 'NorthEast') {
700 printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n");
701 $video_icon_gravity = 'SouthWest';
702 }
703 &write_video_icons ($tmp_jpg_dir);
704 if ($video_icon < 1 ||
705 $video_icon > $max_video_icons) {
706 printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n");
707 $video_icon = 1;
708 }
709 $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png';
710 $retval = $icon->Read(filename=>$iconfile);
711 warn "$retval" if "$retval";
712
713 $image->Composite(image=>$icon,gravity=>$video_icon_gravity);
714 }
715
716 $retval = $image->Write(filename=>"$destdir/$medium");
717 warn "$retval" if "$retval";
718
719 &delete_tmp_jpg_dir($tmp_jpg_dir);
720
721 } else {
722 # Up to date, existing medium
723 # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of
724 # the file here and the code below will set the thumb_x/y properties.
725 #
726 $retval = $image->Read("$destdir/$medium");
727 warn "$retval" if "$retval";
728
729 }
730
731 $info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize'));
732 $info{$pathname}{'med_x'} = $image->Get('width');
733 $info{$pathname}{'med_y'} = $image->Get('height');
734
735 $med_counter++;
736
737
738 } else {
739 # Skip if we are below the threshold size
740 $skipmedium{$pathname} = 1;
741 unlink("$destdir/$medium") if (-e "$destdir/$medium");
742 }
743
744 } else {
745 my $image = new Image::Magick;
746 my $retval;
747
748 # The size of the file was not within the "threshold" so we need to create a
749 # medium version
750
751 if ($info{$pathname}{'is_video'}) {
752
753 # Create a medium version out of the first frame of the video file and then
754 # resize it according to $med_x and $med_y
755 #
756 if ((! -e "$destdir/$medium") or
757 ( -M $pathname < -M "$destdir/$medium") or
758 defined($opt_forceregen)) {
759
760 my $icon = new Image::Magick;
761 my $newgeom = $med_x . "x" . $med_y;
762
763 print "Creating $destdir/$medium from first frame of $pathname\n";
764
765 my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog);
766 $retval = $image->Read(filename=>$tmpfile);
767 warn "$retval" if "$retval";
768 $retval = $image->Resize(geometry=>$newgeom);
769 warn "$retval" if "$retval";
770 $retval = $image->Set(interlace=>Line);
771 warn "$retval" if "$retval";
772 # always want 100% here instead of $med_quality
773 $retval = $image->Set(quality=>100);
774 warn "$retval" if "$retval";
775
776 # If the user wants to, overlay a small icon on top of the
777 # thumbnail of the video file to give a visual cue that this
778 # file is a video and not a still photo
779 #
780 if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) {
781 my $iconfile;
782 if ($video_icon_gravity ne 'SouthWest' &&
783 $video_icon_gravity ne 'NorthWest' &&
784 $video_icon_gravity ne 'SouthEast' &&
785 $video_icon_gravity ne 'NorthEast') {
786 printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n");
787 $video_icon_gravity = 'SouthWest';
788 }
789 &write_video_icons ($tmp_jpg_dir);
790 if ($video_icon < 1 ||
791 $video_icon > $max_video_icons) {
792 printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n");
793 $video_icon = 1;
794 }
795 $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png';
796 $retval = $icon->Read(filename=>$iconfile);
797 warn "$retval" if "$retval";
798
799 $image->Composite(image=>$icon,gravity=>$video_icon_gravity);
800 }
801
802 $retval = $image->Write(filename=>"$destdir/$medium");
803 warn "$retval" if "$retval";
804
805 &delete_tmp_jpg_dir($tmp_jpg_dir);
806
807 } else {
808 # Up to date, existing medium
809 # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of
810 # the file here and the code below will set the thumb_x/y properties.
811 #
812 $retval = $image->Read("$destdir/$medium");
813 warn "$retval" if "$retval";
814
815 }
816
817 $info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize'));
818 $info{$pathname}{'med_x'} = $image->Get('width');
819 $info{$pathname}{'med_y'} = $image->Get('height');
820
821 $med_counter++;
822
823 } else {
824 my $image = new Image::Magick;
825 my $retval;
826
827 # Create medium sized pic if it is not there,
828 # or is out of date with respect to original image
829 if ((! -e "$destdir/$medium") or
830 ( -M $pathname < -M "$destdir/$medium") or
831 defined($opt_forceregen)) {
832
833 my $newgeom = $med_x . "x" . $med_y;
834
835 print "Creating $destdir/$medium\n";
836
837 $retval = $image->Read(filename=>$pathname);
838 warn "$retval" if "$retval";
839 $retval = $image->Resize(geometry=>$newgeom);
840 warn "$retval" if "$retval";
841 $retval = $image->Set(interlace=>Line);
842 warn "$retval" if "$retval";
843 $retval = $image->Set(quality=>$med_quality);
844 warn "$retval" if "$retval";
845 if ($info{$pathname}{'is_multi_image_file'}) {
846 $retval = $image->[0]->Write(filename=>"$destdir/$medium");
847 warn "$retval" if "$retval";
848 } else {
849 $retval = $image->Write(filename=>"$destdir/$medium");
850 warn "$retval" if "$retval";
851 }
852
853 $image_cache{$pathname} = $image;
854
855 } else {
856
857 # Up to date, existing medium, grab dimensions
858 # Get the right hsize/vsize tags for the medium slides. Simply do a "Read" of
859 # the file here and the code below will set the med_x/y properties.
860 #
861 $retval = $image->Read("$destdir/$medium");
862 warn "$retval" if "$retval";
863 }
864
865 $info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize'));
866 $info{$pathname}{'med_x'} = $image->Get('width');
867 $info{$pathname}{'med_y'} = $image->Get('height');
868
869 $med_counter++;
870 }
871
872 }
873
874 #
875 # Next, deal with the thumbnail for this image. If we have just created a medium
876 # version of the image, then an open image "handle" will exist for it. We simply
877 # shrink that down to thumbnail size (if appropriate) rather than reading in the
878 # original file again just to shrink it (saves processing time).
879 #
880
881 # Skip thumb if we are below the threshold size
882 if (($info{$pathname}{'x'} <= ($current_thumbnail_x * $thumbnail_threshold)) and
883 ($info{$pathname}{'y'} <= ($current_thumbnail_y * $thumbnail_threshold))) {
884
885 my $image = new Image::Magick;
886 my $retval;
887
888 if ($info{$pathname}{'is_video'}) {
889 # If the file is a video but the X/Y is smaller than that of our
890 # thumbnail size we still need to make a JPG out of the first
891 # frame and store it in the thumbnail directory. This code is
892 # mostly like the code below for creating video thumbnails but
893 # we're just not resizing the first frame down to thumbnail size.
894 #
895 my $icon = new Image::Magick;
896
897 # We need to make a thumbnail from this video file. Get the first frame
898
899 print "Creating $destdir/$thumbnail from first frame of $pathname\n";
900
901 my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog);
902 $retval = $image->Read(filename=>$tmpfile);
903 warn "$retval" if "$retval";
904 $retval = $image->Set(interlace=>Line);
905 warn "$retval" if "$retval";
906 $retval = $image->Set(quality=>$thumb_quality);
907 warn "$retval" if "$retval";
908
909 # If the user wants to, overlay a small icon on top of the
910 # thumbnail of the video file to give a visual cue that this
911 # file is a video and not a still photo
912 #
913 if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) {
914 my $iconfile;
915 if ($video_icon_gravity ne 'SouthWest' &&
916 $video_icon_gravity ne 'NorthWest' &&
917 $video_icon_gravity ne 'SouthEast' &&
918 $video_icon_gravity ne 'NorthEast') {
919 printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n");
920 $video_icon_gravity = 'SouthWest';
921 }
922 &write_video_icons ($tmp_jpg_dir);
923 if ($video_icon < 1 ||
924 $video_icon > $max_video_icons) {
925 printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n");
926 $video_icon = 1;
927 }
928 $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png';
929 $retval = $icon->Read(filename=>$iconfile);
930 warn "$retval" if "$retval";
931
932 $image->Composite(image=>$icon,gravity=>$video_icon_gravity);
933 }
934
935
936 $retval = $image->Write(filename=>"$destdir/$thumbnail");
937 warn "$retval" if "$retval";
938
939 push(@montagefiles,"$destdir/$thumbnail");
940
941 $modified_thumb++;
942
943 $info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize'));
944 $info{$pathname}{'thumb_x'} = $image->Get('width');
945 $info{$pathname}{'thumb_y'} = $image->Get('height');
946
947 $thumbnail_counter++;
948
949 &delete_tmp_jpg_dir($tmp_jpg_dir);
950
951 } else {
952 # is NOT a video file
953 $info{$pathname}{'thumb_x'} = $info{$pathname}{'x'};
954 $info{$pathname}{'thumb_y'} = $info{$pathname}{'y'};
955
956 $skipthumb{$pathname} = 1;
957 if (-e "$destdir/$thumbnail") {
958 unlink("$destdir/$thumbnail");
959 $modified_thumb++;
960 }
961 push(@montagefiles,"$destdir/$filename");
962 }
963
964 } else {
965 my $image = new Image::Magick;
966 my $retval;
967
968 if ($info{$pathname}{'is_video'}) {
969
970 my $icon = new Image::Magick;
971
972 # We need to make a thumbnail from this video file. Get the first frame and
973 # resize it down to the appropriate size
974 if ((! -e "$destdir/$thumbnail") or
975 ( -M $pathname < -M "$destdir/$thumbnail") or
976 defined($opt_forceregen)) {
977
978 my $newgeom = $current_thumbnail_x . "x" . $current_thumbnail_y;
979
980 print "Creating $destdir/$thumbnail from first frame of $pathname\n";
981
982 my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog);
983 $retval = $image->Read(filename=>$tmpfile);
984 warn "$retval" if "$retval";
985 $retval = $image->Resize(geometry=>$newgeom);
986 warn "$retval" if "$retval";
987 $retval = $image->Set(interlace=>Line);
988 warn "$retval" if "$retval";
989 # Always want 100% here instead of $thumb_quality
990 $retval = $image->Set(quality=>100);
991 warn "$retval" if "$retval";
992
993 # If the user wants to, overlay a small icon on top of the thumbnail
994 # of the video file to give a visual cue that this file is a video and
995 # not a still photo
996 #
997 if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) {
998 my $iconfile;
999 if ($video_icon_gravity ne 'SouthWest' &&
1000 $video_icon_gravity ne 'NorthWest' &&
1001 $video_icon_gravity ne 'SouthEast' &&
1002 $video_icon_gravity ne 'NorthEast') {
1003 printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n");
1004 $video_icon_gravity = 'SouthWest';
1005 }
1006 &write_video_icons ($tmp_jpg_dir);
1007 if ($video_icon < 1 ||
1008 $video_icon > $max_video_icons) {
1009 printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n");
1010 $video_icon = 1;
1011 }
1012 $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png';
1013 $retval = $icon->Read(filename=>$iconfile);
1014 warn "$retval" if "$retval";
1015
1016 $image->Composite(image=>$icon,gravity=>$video_icon_gravity);
1017 }
1018
1019
1020 $retval = $image->Write(filename=>"$destdir/$thumbnail");
1021 warn "$retval" if "$retval";
1022
1023
1024 push(@montagefiles,"$destdir/$thumbnail");
1025
1026 $modified_thumb++;
1027
1028 &delete_tmp_jpg_dir($tmp_jpg_dir);
1029
1030 } else {
1031 # Up to date, existing thumb
1032 # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of
1033 # the file here and the code below will set the thumb_x/y properties.
1034 #
1035 $retval = $image->Read("$destdir/$thumbnail");
1036 warn "$retval" if "$retval";
1037
1038 push(@montagefiles,"$destdir/$thumbnail");
1039 }
1040
1041 $info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize'));
1042 $info{$pathname}{'thumb_x'} = $image->Get('width');
1043 $info{$pathname}{'thumb_y'} = $image->Get('height');
1044
1045 $thumbnail_counter++;
1046
1047
1048 } else {
1049 # Is NOT a video file
1050 my $image = new Image::Magick;
1051 my $retval;
1052
1053 # Create thumbnail if it is not there,
1054 # or is out of date with respect to original image
1055 if ((! -e "$destdir/$thumbnail") or
1056 ( -M $pathname < -M "$destdir/$thumbnail") or
1057 defined($opt_forceregen)) {
1058
1059 my $newgeom = $current_thumbnail_x . "x" . $current_thumbnail_y;
1060
1061 print "Creating $destdir/$thumbnail\n";
1062
1063 if (defined ($image_cache{$pathname})) {
1064 $image = $image_cache{$pathname};
1065
1066 $retval = $image->Resize(geometry=>$newgeom);
1067 warn "$retval" if "$retval";
1068 $retval = $image->Set(quality=>$thumb_quality);
1069 warn "$retval" if "$retval";
1070 if ($info{$pathname}{'is_multi_image_file'}) {
1071 $retval = $image->[0]->Write(filename=>"$destdir/$thumbnail");
1072 warn "$retval" if "$retval";
1073 } else {
1074 $retval = $image->Write(filename=>"$destdir/$thumbnail");
1075 warn "$retval" if "$retval";
1076 }
1077 }
1078 else {
1079 $retval = $image->Read(filename=>$pathname);
1080 warn "$retval" if "$retval";
1081 $retval = $image->Resize(geometry=>$newgeom);
1082 warn "$retval" if "$retval";
1083 $retval = $image->Set(interlace=>Line);
1084 warn "$retval" if "$retval";
1085 $retval = $image->Set(quality=>$thumb_quality);
1086 warn "$retval" if "$retval";
1087 if ($info{$pathname}{'is_multi_image_file'}) {
1088 $retval = $image->[0]->Write(filename=>"$destdir/$thumbnail");
1089 warn "$retval" if "$retval";
1090 } else {
1091 $retval = $image->Write(filename=>"$destdir/$thumbnail");
1092 warn "$retval" if "$retval";
1093 }
1094 }
1095 push(@montagefiles,"$destdir/$thumbnail");
1096
1097 $modified_thumb++;
1098
1099
1100 } else {
1101
1102 # Up to date, existing thumb
1103 # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of
1104 # the file here and the code below will set the thumb_x/y properties.
1105 #
1106 $retval = $image->Read("$destdir/$thumbnail");
1107 warn "$retval" if "$retval";
1108
1109 push(@montagefiles,"$destdir/$thumbnail");
1110
1111 }
1112
1113 $info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize'));
1114 $info{$pathname}{'thumb_x'} = $image->Get('width');
1115 $info{$pathname}{'thumb_y'} = $image->Get('height');
1116
1117 $thumbnail_counter++;
1118 }
1119 }
1120
1121 # Set the max thumb sizes, to be used for slide frame width
1122 if ($info{$pathname}{'thumb_x'} > $max_thumb_x) {
1123 $max_thumb_x = $info{$pathname}{'thumb_x'};
1124 }
1125 if ($info{$pathname}{'thumb_y'} > $max_thumb_y) {
1126 $max_thumb_y = $info{$pathname}{'thumb_y'};
1127 }
1128
1129
1130 # Set the max montage thumb sizes, to be used when creating montage images
1131 #
1132 $bn = basename ($thumbnail);
1133 unless (defined ($skipmont{$bn})) {
1134 if ($info{$pathname}{'thumb_x'} > $max_mont_thumb_x) {
1135 $max_mont_thumb_x = $info{$pathname}{'thumb_x'};
1136 }
1137 if ($info{$pathname}{'thumb_y'} > $max_mont_thumb_y) {
1138 $max_mont_thumb_y = $info{$pathname}{'thumb_y'};
1139 }
1140 }
1141
1142 #
1143 # Finally, create html for this image
1144 #
1145
1146 &image_entry($pathname);
1147
1148 } # foreach image that has this timestamp
1149
1150 } # foreach timestamp
1151
1152
1153 # Finish up the columns if needed
1154 if (($col_counter != 1) and
1155 ($col_counter <= $current_columns) and
1156 ($object_counter > $current_columns)) {
1157 foreach ($col_counter..$current_columns) {
1158 push(@index, " <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">$emptycell</TD>\n");
1159 push(@details, " <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">$emptycell</TD>\n");
1160 }
1161 push(@index, " </TR>\n");
1162 push(@details, " </TR>\n");
1163 }
1164
1165 # Nuke generated dirs if no contents
1166 system("rm -rf $destdir/$thumbnail_dir") if ($thumbnail_counter == 0);
1167 system("rm -rf $destdir/$slide_dir") if ($slide_counter == 0);
1168 system("rm -rf $destdir/$med_dir") if ($med_counter == 0);
1169
1170 # Create montage if we had more than just dir entries here
1171 if (($dir_counter != $object_counter)) {
1172 &create_montage(@montagefiles);
1173 }
1174
1175 # Create stylesheet
1176 &write_css();
1177
1178 # Write index web page
1179 open(INDEX,">$destdir/$indexfile") or die ("Can't open $destdir/$indexfile: $!\n");
1180 &page_header('index', $index_linkto);
1181 foreach (@index) {
1182 print INDEX;
1183 }
1184 &page_footer('index');
1185 close(INDEX);
1186
1187 # Write photo details file
1188 if ($do_detail == 1) {
1189 open(INDEX,">$destdir/$detailfile") or die ("Can't open $destdir/$indexfile: $!\n");
1190 &page_header('detail', $details_linkto);
1191 foreach (@details) {
1192 print INDEX;
1193 }
1194 &page_footer('detail');
1195 close(INDEX);
1196 } else {
1197 unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile");
1198 }
1199
1200 # Write slide/frame files
1201 if (($do_slide == 1) and ($slide_counter > 1)) {
1202 &write_frameset();
1203 } else {
1204 system("rm -rf $destdir/$slide_dir") if (-d "$destdir/$slide_dir");
1205 }
1206
1207 # Optionally export images somewhere else
1208 if ($opt_destdir) {
1209 printf ("Copying image files from '$srcdir' to '$destdir'.\n");
1210 foreach my $image (keys %info) {
1211 # BSD's default 'cp' cannot preserve links like GNU fileutils cp can
1212 #
1213 if ($uname =~ /BSD/) {
1214 system("cp -pv $image $destdir");
1215 }
1216 else {
1217 system("cp -dpuv $image $destdir");
1218 }
1219 }
1220 }
1221
1222 if (defined ($do_emoticons) && $do_emoticons) {
1223 foreach $icon ('wink', 'smile', 'frown') {
1224 if ($emoticon{$icon}) {
1225 &write_emoticon_png ($icon);
1226 } else {
1227 unlink ($destdir . '/' . $thumbnail_dir . "/$emoticonprefix${icon}.png");
1228 }
1229 }
1230 }
1231
1232 ######################################################################
1233 #
1234 # Write the various HTML parts for this image
1235 #
1236 ######################################################################
1237 sub image_entry {
1238
1239 my $pathname = shift(@_);
1240 my $filename = $info{$pathname}{'file'};
1241 my $link;
1242
1243 &index_html($pathname);
1244
1245 if ($do_detail == 1) {
1246 &details_html($pathname);
1247 }
1248
1249 if (($do_slide == 1) and ($image_counter > 1)) {
1250 &slide_html($pathname);
1251 } else {
1252 my $file = $info{$pathname}{slide};
1253 unlink($file) if (-e $file);
1254 }
1255
1256 # Increment for next time
1257 $col_counter++;
1258 $col_counter = 1 if ($col_counter > $current_columns);
1259
1260 }
1261
1262
1263 ###############################################################################
1264 #
1265 # Generate HTML for index page entry
1266 #
1267 ###############################################################################
1268
1269 sub index_html {
1270
1271 my $pathname = shift(@_);
1272 my $filename = $info{$pathname}{'file'};
1273 my $link;
1274 my $anchortext;
1275
1276 # At beginning of row?
1277 if ($col_counter == 1) {
1278 push(@index, " <TR>\n");
1279 }
1280
1281 # Image
1282 push(@index, " <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
1283 push(@index, " <DIV CLASS=\"index\">");
1284 push(@index, &format_date($info{$pathname}{'date'}, 'index'));
1285 push(@index,"</DIV>\n");
1286
1287 if (($index_linkto eq 'details') and ($do_detail == 1)) {
1288 $link = "$detailfile#$filename";
1289 } elsif (($index_linkto eq 'medium') and !defined($skipmedium{$pathname})) {
1290 $link = $info{$pathname}{'medium'};
1291 } elsif (($index_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) {
1292 $link = $info{$pathname}{'thumb'};
1293 } elsif (($index_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) {
1294 $link = $info{$pathname}{'slide'};
1295 } else {
1296 $link = $filename;
1297 }
1298
1299 $anchortext = " <A HREF=\"$link\" ";
1300
1301 if (defined ($indexthumbtitle) && $indexthumbtitle ne '') {
1302 my ($str);
1303 $str = &interpolate_title_string ($indexthumbtitle, $pathname, 'index');
1304 if ($str ne '') {
1305 $anchortext .= sprintf ("TITLE=\"%s\" ", $str);
1306 }
1307 }
1308
1309 $anchortext .= "NAME=\"$filename\">";
1310
1311 push(@index, $anchortext);
1312
1313 if (defined($skipthumb{$pathname})) {
1314 push(@index,"<IMG SRC=\"$filename\"");
1315 } else {
1316 push(@index,"<IMG SRC=\"$info{$pathname}{thumb}\"");
1317 }
1318 push(@index," WIDTH=\"$info{$pathname}{thumb_x}\" HEIGHT=\"$info{$pathname}{thumb_y}\"");
1319 push(@index," ALT=\" $filename \"");
1320 push(@index," CLASS=\"index\"");
1321 push(@index,"></A>\n");
1322
1323 push(@index, " <DIV CLASS=\"index\">");
1324
1325 # Full size link
1326 push(@index,"<A HREF=\"$filename\">full size</A>");
1327
1328 # Medium size link if within the threshold
1329 unless (defined($skipmedium{$pathname})) {
1330 push(@index,"&nbsp;|&nbsp;<A HREF=\"$info{$pathname}{medium}\">medium</A>");
1331 }
1332
1333 # Detail list link
1334 if ($do_detail == 1) {
1335 push(@index,"&nbsp;|&nbsp;<A HREF=\"$detailfile#$filename\">details</A>");
1336 }
1337
1338 push(@index,"</DIV>\n");
1339
1340 # Caption if any (jpeg comment field)
1341 if (($do_captions == 1) and defined($info{$pathname}{'comment'})) {
1342 my ($tmp);
1343 push(@index, " <DIV CLASS=\"caption\">");
1344 # Hack: if a comment has an ellipsis at the very end, make the HTML use a
1345 # non-breakable space before it so that the ellipsis doesn't "wrap" inside
1346 # the table field. It just looks better for those cases where the comment
1347 # is just long enough to wrap when rendered in the space given
1348 #
1349 $tmp = $info{$pathname}{'comment'};
1350 $tmp = &htmlize_caption ($tmp);
1351 if ($tmp =~ /(\s+)\.\.\.\s*$/) {
1352 $tmp =~ s/(\s+)\.\.\.\s*$/&nbsp;.../;
1353 }
1354 push(@index, $tmp);
1355 push(@index,"</DIV>\n");
1356 }
1357
1358 push(@index, " </TD>\n\n");
1359
1360 # At end of row?
1361 if ($col_counter == $current_columns) {
1362 push(@index, " </TR>\n");
1363 }
1364
1365 }
1366
1367
1368 ###############################################################################
1369 #
1370 # Generate HTML for slide/frame pages
1371 #
1372 ###############################################################################
1373 sub slide_html {
1374
1375 my $pathname = shift(@_);
1376 my $filename = $info{$pathname}{'file'};
1377 my $link;
1378 my $anchortext;
1379
1380 #
1381 # First the index frame info
1382 #
1383 if ($frame_orient eq 'horizontal') {
1384 push(@frame," <TD CLASS=\"frame\" ALIGN=\"center\" VALIGN=\"middle\">\n");
1385 } else {
1386 push(@frame," <TR>\n <TD CLASS=\"frame\" ALIGN=\"center\" VALIGN=\"middle\">\n");
1387 }
1388
1389 $anchortext = " <A HREF=\"../$info{$pathname}{slide}\" ";
1390 if (defined ($framethumbtitle) && $framethumbtitle ne '') {
1391 my ($str);
1392 $str = &interpolate_title_string ($framethumbtitle, $pathname, 'frame');
1393 if ($str ne '') {
1394 $anchortext .= sprintf ("TITLE=\"%s\" ", $str);
1395 }
1396 }
1397
1398 $anchortext .= "TARGET=\"view\">";
1399 push(@frame, $anchortext);
1400
1401 if (defined($skipthumb{$pathname})) {
1402 push(@frame,"<IMG SRC=\"../$filename\"");
1403 } else {
1404 push(@frame,"<IMG SRC=\"../$info{$pathname}{thumb}\"");
1405 }
1406 push(@frame," WIDTH=\"$info{$pathname}{thumb_x}\" HEIGHT=\"$info{$pathname}{thumb_y}\"");
1407 push(@frame," ALT=\" $filename \"");
1408 push(@frame," CLASS=\"frame\"");
1409 push(@frame,"></A>\n");
1410 if ($frame_orient eq 'horizontal') {
1411 push(@frame," </TD>");
1412 } else {
1413 push(@frame," </TD>\n </TR>");
1414 }
1415 push(@frame,"\n");
1416
1417 #
1418 # Then the individual slides
1419 #
1420 my $slide = new FileHandle "> $destdir/$info{$pathname}{slide}";
1421 if (!defined($slide)) {
1422 die("$destdir/$info{$pathname}{slide}: $!");
1423 }
1424
1425 select($slide);
1426 print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n";
1427 print "\"http://www.w3.org/TR/html401/strict.dtd\">\n";
1428 print "<HTML>\n";
1429 print "<HEAD>\n";
1430 $verstring = &versionstring();
1431 printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
1432 printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
1433 print "<TITLE>$current_titletext - $filename</TITLE>\n";
1434 print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">\n";
1435 print "</HEAD>\n<BODY>\n";
1436
1437 &next_prev_links($pathname);
1438
1439 # Caption if any
1440 if (($do_captions == 1) and ($slide_caption eq 'top') and defined($info{$pathname}{'comment'})) {
1441 print "<DIV CLASS=\"caption\">";
1442 my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide');
1443 print $tmp;
1444 print "</DIV>\n";
1445 }
1446
1447 # Date, filename
1448 if ($slide_date eq 'top') {
1449 print "<DIV CLASS=\"index\">";
1450 print &format_date($info{$pathname}{'date'}, 'slide');
1451 print " $filename";
1452 print "</DIV>\n";
1453 }
1454
1455 if ($slide_linkto eq 'index') {
1456 $link = "../$indexfile#$filename";
1457 } elsif (($slide_linkto eq 'details') and ($do_detail == 1)) {
1458 $link = "../$detailfile#$filename";
1459 } elsif (($slide_linkto eq 'medium') and !defined($skipmedium{$pathname})) {
1460 $link = "../$info{$pathname}{medium}";
1461 } elsif (($slide_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) {
1462 $link = "../$info{$pathname}{thumb}";
1463 } else {
1464 $link = "../$filename";
1465 }
1466
1467 print "\n<P>\n";
1468
1469 $anchortext = "<A HREF=\"$link\"";
1470 if (defined ($slidethumbtitle) && $slidethumbtitle ne '') {
1471 my ($str);
1472 $str = &interpolate_title_string ($slidethumbtitle, $pathname, 'slide');
1473 if ($str ne '') {
1474 $anchortext .= sprintf (" TITLE=\"%s\"", $str);
1475 }
1476 }
1477
1478 $anchortext .= ">";
1479 print $anchortext;
1480
1481 if ($info{$pathname}{'is_video'}) {
1482 print "<IMG SRC=\"../$info{$pathname}{medium}\"";
1483 print " WIDTH=\"$info{$pathname}{x}\" HEIGHT=\"$info{$pathname}{y}\"";
1484 } else {
1485 if (defined($skipmedium{$pathname})) {
1486 print "<IMG SRC=\"../$filename\"";
1487 print " WIDTH=\"$info{$pathname}{x}\" HEIGHT=\"$info{$pathname}{y}\"";
1488 } else {
1489 print "<IMG SRC=\"../$info{$pathname}{medium}\"";
1490 print " WIDTH=\"$info{$pathname}{med_x}\" HEIGHT=\"$info{$pathname}{med_y}\"";
1491 }
1492 }
1493
1494 print " ALT=\" $filename \"";
1495 print " CLASS=\"slide\">";
1496 print "</A>\n";
1497
1498 print "</P>\n";
1499
1500 # Caption if any
1501 if (($do_captions == 1) and ($slide_caption eq 'bottom') and defined($info{$pathname}{'comment'})) {
1502 print "<DIV CLASS=\"caption\">";
1503 my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide');
1504 print $tmp;
1505 print "</DIV>\n";
1506 }
1507
1508 # Date, filename
1509 if ($slide_date eq 'bottom') {
1510 print "<DIV CLASS=\"index\">";
1511 print &format_date($info{$pathname}{'date'}, 'slide');
1512 print " $filename";
1513 print "</DIV>\n";
1514 }
1515
1516 &next_prev_links($pathname);
1517 print "</BODY>\n</HTML>\n";
1518
1519 select(STDOUT);
1520 $slide->close();
1521 $slide_counter++;
1522
1523 unless(defined($first_slide)) {
1524 $first_slide = $info{$pathname}{'slide'};
1525 }
1526 }
1527
1528
1529 ###############################################################################
1530 #
1531 # Generate HTML for details page
1532 #
1533 ###############################################################################
1534 sub details_html {
1535
1536 my $pathname = shift(@_);
1537 my $filename = $info{$pathname}{'file'};
1538 my ($link, $anchortext);
1539
1540 # At beginning of row?
1541 if ($col_counter == 1) {
1542 push(@details, " <TR>\n");
1543 }
1544
1545
1546 if ($details_linkto eq 'index') {
1547 $link = "$indexfile#$filename";
1548 } elsif (($details_linkto eq 'medium') and !defined($skipmedium{$pathname})) {
1549 $link = "$info{$pathname}{medium}";
1550 } elsif (($details_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) {
1551 $link = "$info{$pathname}{thumb}";
1552 } elsif (($details_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) {
1553 $link = $info{$pathname}{'slide'};
1554 } else {
1555 $link = $filename;
1556 }
1557
1558 push(@details," <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
1559 push(@details," <TABLE BORDER=0 WIDTH=\"100%\">\n");
1560 push(@details," <TR>\n");
1561 push(@details," <TD VALIGN=\"middle\" ALIGN=\"center\">\n");
1562 push(@details," <DIV CLASS=\"detail\">\n");
1563 push(@details," <A NAME=\"$filename\">");
1564 push(@details, &format_date($info{$pathname}{'date'}, 'detail'));
1565 push(@details,"</A><BR>\n");
1566
1567 $anchortext = "<A HREF=\"$link\"";
1568 if (defined ($detailthumbtitle) && $detailthumbtitle ne '') {
1569 my ($str);
1570 $str = &interpolate_title_string ($detailthumbtitle, $pathname, 'detail');
1571 if ($str ne '') {
1572 $anchortext .= sprintf (" TITLE=\"%s\"", $str);
1573 }
1574 }
1575
1576 $anchortext .= ">";
1577 push(@details, $anchortext);
1578
1579 if (defined($skipthumb{$pathname})) {
1580 push(@details,"<IMG SRC=\"$filename\"");
1581 } else {
1582 push(@details,"<IMG SRC=\"$info{$pathname}{thumb}\"");
1583 }
1584 my $x = $info{$pathname}{'thumb_x'} / $detailshrink ;
1585 my $y = $info{$pathname}{'thumb_y'} / $detailshrink ;
1586 push(@details, sprintf(" WIDTH=\"%d\" HEIGHT=\"%d\"", $x, $y));
1587 push(@details," ALT=\" $filename \"");
1588 push(@details," CLASS=\"index\"");
1589 push(@details,"></A><BR>");
1590 push(@details,"$filename<BR>");
1591 push(@details,"</DIV>\n");
1592 push(@details," </TD>\n\n");
1593 push(@details," <TD VALIGN=\"middle\" ALIGN=\"left\">\n");
1594 push(@details," <DIV CLASS=\"detail\">");
1595 push(@details,"Original:&nbsp;<A HREF=\"$filename\">$info{$pathname}{geometry}</A>");
1596 push(@details,"&nbsp;($info{$pathname}{size})<BR>");
1597 unless (defined($skipmedium{$pathname})) {
1598 push(@details,"Medium:&nbsp;<A HREF=\"$info{$pathname}{medium}\">");
1599 push(@details,$info{$pathname}{'med_x'} . 'x' . $info{$pathname}{'med_y'} . "</A>");
1600 push(@details,"&nbsp;($info{$pathname}{med_size})<BR>");
1601 }
1602 unless (defined($skipthumb{$pathname})) {
1603 push(@details,"Thumbnail:&nbsp;<A HREF=\"$info{$pathname}{thumb}\">");
1604 push(@details,$info{$pathname}{'thumb_x'} . 'x' . $info{$pathname}{'thumb_y'} . "</A>");
1605 push(@details,"&nbsp;($info{$pathname}{thumb_size})<BR>");
1606 }
1607
1608 if (defined ($info{$pathname}{'is_multi_image_file'}) &&
1609 $info{$pathname}{'is_multi_image_file'} == 1) {
1610 push(@details,"Multi-image File:&nbsp;$info{$pathname}{scenes} scenes<BR>");
1611 }
1612
1613 # Video file stuff
1614 #
1615 if (defined ($info{$pathname}{'video_format'})) {
1616 push (@details, "Video Format:&nbsp;$info{$pathname}{'video_format'}<BR>");
1617 }
1618 if (defined ($info{$pathname}{'video_bitrate'})) {
1619 push (@details, "Video Bitrate:&nbsp;$info{$pathname}{'video_bitrate'}<BR>");
1620 }
1621 if (defined ($info{$pathname}{'video_fps'})) {
1622 push (@details, "Video Rate:&nbsp;$info{$pathname}{'video_fps'} f/s<BR>");
1623 }
1624 if (defined ($info{$pathname}{'audio_codec'})) {
1625 push (@details, "Audio Codec:&nbsp;$info{$pathname}{'audio_codec'}<BR>");
1626 }
1627 if (defined ($info{$pathname}{'audio_bitrate'})) {
1628 push (@details, "Audio Bitrate:&nbsp;$info{$pathname}{'audio_bitrate'} kbit/s<BR>");
1629 }
1630 if (defined ($info{$pathname}{'length'})) {
1631 push (@details, "Length (time):&nbsp;$info{$pathname}{'length'} sec<BR>");
1632 }
1633
1634 #
1635 # EXIF data
1636 #
1637 if (defined($info{$pathname}{'flash'})) {
1638 push(@details,"Flash:&nbsp;$info{$pathname}{flash}<BR>");
1639 }
1640 if (defined($info{$pathname}{'exposure_time'})) {
1641 push(@details,"Exposure time:&nbsp;$info{$pathname}{exposure_time}<BR>");
1642 }
1643 if (defined($info{$pathname}{'focus_dist'})) {
1644 push(@details,"Focus distance:&nbsp;$info{$pathname}{focus_dist}<BR>");
1645 }
1646 if (defined($info{$pathname}{'focal_length'})) {
1647 push(@details,"Focal length:&nbsp;$info{$pathname}{focal_length}<BR>");
1648 }
1649 if (defined($info{$pathname}{'aperture'})) {
1650 push(@details,"Aperture:&nbsp;$info{$pathname}{aperture}<BR>");
1651 }
1652
1653 push(@details,"\n");
1654 push(@details," </DIV>\n");
1655 push(@details," </TD>\n");
1656 push(@details," </TR>\n");
1657 push(@details," </TABLE>\n");
1658 push(@details," </TD>\n");
1659
1660 # At end of row?
1661 if ($col_counter == $current_columns) {
1662 push(@details, " </TR>\n");
1663 }
1664
1665
1666 }
1667
1668 sub delete_tmp_jpg_dir {
1669 my ($tmp_jpg_dir) = @_;
1670 while ($name = <$tmp_jpg_dir/*>) {
1671 unlink ($name);
1672 }
1673 rmdir $tmp_jpg_dir;
1674 }
1675
1676 sub extract_first_frame_jpg {
1677
1678 my ($filename, $tmp_jpg_dir, $mplayer_prog) = @_;
1679 my ($retval, $cmd);
1680
1681 # Need to give 2 frames here. On some MPG files mplayer produces no output when
1682 # you request 1 frame.
1683 $cmd = "$mplayer_prog $filename -noautosub -nosound -vo jpeg:outdir=${tmp_jpg_dir}:quality=100 -frames 2 > /dev/null 2>&1";
1684 print "About to execute: '$cmd'\n" if $opt_debug;
1685 $retval = system ($cmd);
1686 if ($retval) {
1687 printf ("warning: mplayer returned %d\n", $? >> 8);
1688 } else {
1689 return "${tmp_jpg_dir}/00000001.jpg";
1690 }
1691
1692 }
1693 ######################################################################
1694 #
1695 # Extract info from a given file
1696 #
1697 ######################################################################
1698 sub extract_file_info {
1699
1700 my $filename = shift (@_);
1701 my $pathname = "$srcdir/$filename";
1702 my $retval;
1703
1704 if ($filename =~ /\.${video_regexp}$/i) {
1705 $retval = &extract_movie_info ($filename);
1706 # mplayer told us that this wasn't a movie file so at least yell
1707 # at the user so that the video regexp might be adjusted
1708 if ($retval == -1) {
1709 print "\nwarning: $pathname identified by extension as video file but mplayer doesn't recognize it\n";
1710 flush (STDOUT);
1711 }
1712 } else {
1713 &extract_image_info ($filename);
1714 }
1715
1716 }
1717
1718 ######################################################################
1719 #
1720 # Extract info from movie file
1721 #
1722 ######################################################################
1723 sub extract_movie_info {
1724
1725 my $filename = shift (@_);
1726 my $pathname = "$srcdir/$filename";
1727 my ($retval, $cmd, $qt_found, $tmp);
1728 my $mplayer_prog = &find_in_path ('mplayer');
1729 my ($format, $bitrate, $x, $y, $fps, $aspect, $acodec, $abitrate);
1730 my ($arate, $anch, $length, $is_video);
1731
1732 print ".";
1733 flush (STDOUT);
1734
1735 if ($mplayer_prog eq '' || ($do_video_files == 0)) {
1736 if (($do_video_files != 0) && $mplayer_prog eq '') {
1737 print "\nwarning: Trying to process video files but cannot find mplayer in \$path!\n";
1738 flush (STDOUT);
1739 }
1740 print "\nSkipping $pathname";
1741 flush (STDOUT);
1742 return 0;
1743 } else {
1744 $object_counter++;
1745 $image_counter++;
1746 }
1747
1748
1749 $cmd = "$mplayer_prog -noautosub -vo null -ao null -frames 0 -identify $pathname |";
1750
1751 if (! open (PIPE, $cmd)) {
1752 printf (STDERR "Could not open pipe from mplayer! - $!\n");
1753 return 0;
1754 }
1755
1756 $is_video = 0;
1757
1758 $qt_found = 0;
1759
1760 while (<PIPE>) {
1761 if (/VIDEO:\s+(\S+)/) {
1762 $format = $1;
1763 $format =~ s/\[//g;
1764 $format =~ s/\]//g;
1765 }
1766 if (/QuickTime.*detected/) {
1767 $qt_found = 1;
1768 }
1769 if (/ID_VIDEO_FORMAT=(\S+)/) {
1770 $tmp = $1;
1771 if ($format eq 'jpeg' && $qt_found) {
1772 $tmp = 'QuickTime';
1773 }
1774 if ($tmp =~ /0x/) {
1775 $tmp = $format;
1776 }
1777 $format = $tmp;
1778 $is_video = 1;
1779 }
1780 if (/ID_VIDEO_BITRATE=(\S+)/) {
1781 $tmp = $1;
1782 if ($tmp == 0) {
1783 $tmp = 'VBR';
1784 }
1785 $bitrate = $tmp;
1786 }
1787 if (/ID_VIDEO_WIDTH=(\S+)/) {
1788 $x = $1;
1789 }
1790 if (/ID_VIDEO_HEIGHT=(\S+)/) {
1791 $y = $1;
1792 }
1793 if (/ID_VIDEO_FPS=(\S+)/) {
1794 $fps = $1;
1795 }
1796 if (/ID_VIDEO_ASPECT=(\S+)/) {
1797 $aspect = $1;
1798 }
1799 if (/ID_AUDIO_CODEC=(\S+)/) {
1800 $acodec = $1;
1801 }
1802 if (/ID_AUDIO_BITRATE=(\S+)/) {
1803 $tmp = $1;
1804 if ($tmp == 0) {
1805 $tmp = 'VBR';
1806 }
1807 $abitrate = $tmp;
1808 }
1809 if (/ID_AUDIO_RATE=(\S+)/) {
1810 $arate = $1;
1811 }
1812 if (/ID_AUDIO_NCH=(\S+)/) {
1813 $anch = $1;
1814 }
1815 if (/ID_LENGTH=(\S+)/) {
1816 $length = $1;
1817 }
1818
1819 }
1820
1821 close (PIPE);
1822
1823 if ($is_video) {
1824
1825 $info{$pathname}{'is_video'} = 1;
1826
1827 $info{$pathname}{'file'} = $filename;
1828 $info{$pathname}{'video_format'} = $format;
1829 $info{$pathname}{'video_bitrate'} = $bitrate;
1830 $info{$pathname}{'x'} = $x;
1831 $info{$pathname}{'y'} = $y;
1832 $info{$pathname}{'video_fps'} = $fps;
1833 $info{$pathname}{'video_aspect'} = $aspect;
1834 $info{$pathname}{'audio_codec'} = $acodec;
1835 $info{$pathname}{'audio_bitrate'} = $abitrate / 1000.0;
1836 $info{$pathname}{'audio_rate'} = $arate;
1837 $info{$pathname}{'audio_nch'} = $anch;
1838 $info{$pathname}{'length'} = $length;
1839
1840 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1841 $atime,$mtime,$ctime,$blksize,$blocks) = stat($pathname);
1842
1843 $info{$pathname}{'date'} = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
1844 $info{$pathname}{'size'} = &convert_to_kb($size);
1845
1846 $info{$pathname}{'geometry'} = $info{$pathname}{'x'} . "x" . $info{$pathname}{'y'};
1847
1848 $info{$pathname}{'format'} = $info{$pathname}{'video_format'};
1849
1850 my ($name,$path,$suffix) = fileparse($filename,'\.\S+');
1851
1852 if (-e "${name}.txt") {
1853 my $text;
1854 if (! open (IN, "${name}.txt")) {
1855 warn "Cannot open ${name}.txt for reading - $!\n";
1856 } else {
1857 $text = <IN>;
1858 $info{$pathname}{'comment'} = $text;
1859 close (IN);
1860 }
1861 }
1862
1863 $info{$pathname}{'thumb'} = "$thumbnail_dir/$name.jpg";
1864 $thumb_backref{"$thumbnail_dir/$name.jpg"} = $pathname;
1865
1866 $info{$pathname}{'medium'} = "$med_dir/$name.jpg";
1867 $med_backref{"$med_dir/$name.jpg"} = $pathname;
1868
1869 $info{$pathname}{'slide'} = "$slide_dir/$name.html";
1870 $slide_backref{"$slide_dir/$name.html"} = $pathname;
1871
1872 push(@{$timestamp{"$info{$pathname}{date}"}}, $pathname);
1873
1874 return (0);
1875 } else {
1876 return (1);
1877 }
1878
1879 }
1880
1881 ######################################################################
1882 #
1883 # Extract info from image
1884 #
1885 ######################################################################
1886 sub extract_image_info {
1887
1888 my $filename = shift (@_);
1889 my $pathname = "$srcdir/$filename";
1890 my $image = new Image::Magick;
1891 my $retval;
1892 my $i;
1893
1894 print ".";
1895 flush (STDOUT);
1896
1897 $retval = $image->Read($pathname);
1898
1899
1900 if ($retval ne "") {
1901 print "\nSkipping $pathname";
1902 flush (STDOUT);
1903 return;
1904 } else {
1905 $object_counter++;
1906 $image_counter++;
1907 }
1908
1909 # iterate over the number of scenes in this image (if the file format
1910 # supports it). "normal" files will only have $image->[0] defined. If there
1911 # is a better way to do this, I'm all ears--even the C code embedded within
1912 # ImageMagick itself iterates like this--there doesn't seem to be an
1913 # 'attribute' that can be easily fetched.
1914 #
1915 for ($i = 0; defined $image->[$i]; $i++) {
1916 # empty
1917 }
1918
1919 if ($i > 1) {
1920 $info{$pathname}{'is_multi_image_file'} = 1;
1921 $info{$pathname}{'scenes'} = $i;
1922 } else {
1923 $info{$pathname}{'is_multi_image_file'} = 0;
1924 $info{$pathname}{'scenes'} = 1;
1925 }
1926
1927 $info{$pathname}{'file'} = $filename;
1928
1929 # Use mtime as a fallback date in case we don't have exif data
1930 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1931 $atime,$mtime,$ctime,$blksize,$blocks) = stat($pathname);
1932
1933 $info{$pathname}{'date'} = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
1934 $info{$pathname}{'x'} = $image->Get('width');
1935 $info{$pathname}{'y'} = $image->Get('height');
1936 $info{$pathname}{'geometry'} = $info{$pathname}{'x'} . "x" . $info{$pathname}{'y'};
1937
1938 $info{$pathname}{'size'} = &convert_to_kb($image->Get('filesize'));
1939
1940 $info{$pathname}{'format'} = $image->Get('format');
1941
1942 $info{$pathname}{'comment'} = $image->Get('comment');
1943
1944 my ($name,$path,$suffix) = fileparse($filename,'\.\S+');
1945
1946
1947 if ($info{$pathname}{'format'} =~ /JFIF/i) {
1948
1949 $info{$pathname}{'thumb'} = "$thumbnail_dir/$filename";
1950 $thumb_backref{"$thumbnail_dir/$filename"} = $pathname;
1951
1952 $info{$pathname}{'medium'} = "$med_dir/$filename";
1953 $med_backref{"$med_dir/$filename"} = $pathname;
1954
1955 if (defined(&image_info)) {
1956
1957 my $exif = image_info("$pathname");
1958
1959 if (my $error = $exif->{error}) {
1960 warn "Can't parse image info: $error\n";
1961 }
1962
1963 if (defined($opt_debug)) {
1964 print "EXIF data for $pathname:\n";
1965 foreach (keys %$exif) {
1966 print " $_ = $exif->{$_}\n";
1967 }
1968 print "\n";
1969 }
1970
1971 if (defined($exif->{DateTimeOriginal})) {
1972 # some models use / instead of : in this field... (need to check spec)
1973 $exif->{DateTimeOriginal} =~ s/\//:/g;
1974 $exif->{DateTimeOriginal} =~ /\s*([\d:]+)\s+([\d:]+)/;
1975 my $dt = $1;
1976 my $tm = $2;
1977 $tm =~ s/://;
1978 $tm =~ s/:/\./;
1979 $dt =~ s/://g;
1980 $info{$pathname}{'date'} = $dt . $tm;
1981 }
1982
1983 if (defined($exif->{Flash})) {
1984 $info{$pathname}{'flash'} = $exif->{'Flash'};
1985 $info{$pathname}{'flash'} =~ s/0/no/;
1986 $info{$pathname}{'flash'} =~ s/1/yes/;
1987 }
1988
1989 if (defined($exif->{FocalLength})) {
1990 $info{$pathname}{'focal_length'} = sprintf("%4.1fmm", eval("$exif->{FocalLength}"));
1991 }
1992
1993 if (defined($exif->{SubjectDistance})) {
1994 $info{$pathname}{'focus_dist'} = sprintf("%4.1fm", eval("$exif->{SubjectDistance}"));
1995 }
1996
1997 if (defined($exif->{ExposureTime})) {
1998 $info{$pathname}{'exposure_time'} = $exif->{ExposureTime} . 's';
1999 }
2000
2001 if (defined($exif->{FNumber})) {
2002 $info{$pathname}{'aperture'} = "f/" . eval ("$exif->{FNumber}");
2003 }
2004
2005 }
2006
2007 } else {
2008
2009 $info{$pathname}{'thumb'} = "$thumbnail_dir/$name.jpg";
2010 $thumb_backref{"$thumbnail_dir/$name.jpg"} = $pathname;
2011
2012 $info{$pathname}{'medium'} = "$med_dir/$name.jpg";
2013 $med_backref{"$med_dir/$name.jpg"} = $pathname;
2014
2015 }
2016
2017 $info{$pathname}{'slide'} = "$slide_dir/$name.html";
2018 $slide_backref{"$slide_dir/$name.html"} = $pathname;
2019
2020 push(@{$timestamp{"$info{$pathname}{date}"}}, $pathname);
2021
2022 }
2023
2024
2025 ######################################################################
2026 #
2027 # Write HTML for directory entries
2028 #
2029 ######################################################################
2030 sub dir_entry {
2031
2032 my $dir = shift(@_);
2033 my $destdirname = "$destdir/$dir";
2034 my $srcdirname = "$srcdir/$dir";
2035 my $anchortext;
2036
2037 print "Processing directory $srcdirname\n";
2038
2039 # Recurse first
2040 if ($do_recurse == 1) {
2041 my $flags = "";
2042 $flags .= "-medium " if ($do_medium == 1);
2043 $flags .= "-nomedium " if ($do_medium == 0);
2044 $flags .= "-slide " if ($do_slide == 1);
2045 $flags .= "-noslide " if ($do_slide == 0);
2046 $flags .= "-dirs " if ($do_dirs == 1);
2047 $flags .= "-nodirs " if ($do_dirs == 0);
2048 $flags .= "-montage " if ($do_montage == 1);
2049 $flags .= "-nomontage " if ($do_montage == 0);
2050 $flags .= "-detail " if ($do_detail == 1);
2051 $flags .= "-nodetail " if ($do_detail == 0);
2052 $flags .= "-reverse " if ($do_reverse == 1);
2053 $flags .= "-noreverse " if ($do_reverse == 0);
2054 $flags .= "-forceregen " if (defined($opt_forceregen));
2055 $flags .= "-includeall " if (defined($opt_includeall));
2056 $flags .= "-columns $current_columns " if (defined($opt_columns));
2057 $flags .= "-x $opt_x " if (defined($opt_x));
2058 $flags .= "-y $opt_y " if (defined($opt_y));
2059 $flags .= "-destdir $destdirname " if ($destdir ne $srcdir);
2060 foreach my $var (keys %opt_d) {
2061 $flags .= " -d $var=$opt_d{$var}";
2062 }
2063 # If we're doing recursion and $updirtext is set either by default or
2064 # within a .imageindexrc file, then we're going to employ a terrible
2065 # hack here. We know that once this recursive call to ourselves returns
2066 # we're going to create an HTML page for the directory we're in now.
2067 # However, the recursive call will not "see" that file created and thus
2068 # the <a href> link isn't made back to "../$indexfile". We will tell
2069 # the recursive call to ourselves that it needs to think there's
2070 # a ../$indexfile file there regardless of whether it's there or not
2071 #
2072 if (defined $updirtext) {
2073 $flags .= "-updirindexoverride ";
2074 }
2075 system("cd \"$srcdirname\" ;$0 $flags -recurse");
2076
2077 }
2078
2079 my $dirtitle = "";
2080 my $first;
2081 my $last;
2082 my $montage;
2083 my $montage_x;
2084 my $montage_y;
2085
2086 # Only add entry if this dir has an index file
2087 if (-r "$destdirname/$indexfile") {
2088
2089 # Go fetch the title and dates from the HTML
2090 my $tmp1 = &extract_meta_tag ($titlemetatag,"$destdirname/$indexfile");
2091 my $tmp2 = &extract_meta_tag ($begindatemetatag,"$destdirname/$indexfile");
2092 my $tmp3 = &extract_meta_tag ($enddatemetatag,"$destdirname/$indexfile");
2093 if (defined($tmp1)) {
2094 $dirtitle = $tmp1;
2095 }
2096 if (defined($tmp2)) {
2097 $first = $tmp2;
2098 }
2099 if (defined($tmp3)) {
2100 $last = $tmp3;
2101 }
2102
2103 # If we found generated files in this dir, flag that we found something
2104 # valid to index
2105 $object_counter++;
2106 $dir_counter++;
2107
2108 # Set montage file if we found it
2109 if (($do_montage == 1) and ( -r "$destdirname/$thumbnail_dir/$montagefile")) {
2110
2111 print "Found montage in $destdirname\n" if defined($opt_debug);
2112 $montage = "$destdirname/$thumbnail_dir/$montagefile";
2113
2114 my $image = new Image::Magick;
2115 my $retval;
2116
2117 $retval = $image->Read(filename=>$montage);
2118 warn "$retval" if "$retval";
2119
2120 $montage_x = $image->Get('width');
2121 $montage_y = $image->Get('height');
2122
2123 }
2124
2125
2126 # At beginning of row?
2127 if ($col_counter == 1) {
2128 push(@index, "<TR>\n");
2129 push(@details, "<TR>\n");
2130 }
2131
2132 # Entry for this directory in main & details file
2133 push(@index, "<TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
2134 push(@details, "<TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
2135
2136 push(@details, "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n");
2137
2138 if (defined($montage)) {
2139 push(@details, "<TR><TD VALIGN=\"middle\" ALIGN=\"center\">\n");
2140 } else {
2141 push(@details, "<TR><TD COLSPAN=\"2\" VALIGN=\"middle\" ALIGN=\"center\">\n");
2142 }
2143
2144 if (defined($first)) {
2145
2146 my ($tmp_first, $tmp_last);
2147
2148 push(@index, "<DIV CLASS=\"index\">");
2149 push(@details, "<DIV CLASS=\"detail\">");
2150
2151 $tmp_first = &format_date ($first, 'index', 'dayonly');
2152 $tmp_last = &format_date ($last, 'index', 'dayonly');
2153
2154 if ($first ne $last) {
2155 push(@index, "$tmp_first - $tmp_last");
2156 } else {
2157 push(@index, "$tmp_first");
2158 }
2159
2160 $tmp_first = &format_date ($first, 'detail', 'dayonly');
2161 $tmp_last = &format_date ($last, 'detail', 'dayonly');
2162
2163 if ($first ne $last) {
2164 push(@details, "$tmp_first - $tmp_last");
2165 } else {
2166 push(@details, "$tmp_first");
2167 }
2168
2169 push(@index, "</DIV>\n");
2170 push(@details, "</DIV>\n");
2171 }
2172
2173
2174 if (defined($montage)) {
2175
2176 $anchortext = "<A HREF=\"$dir/$indexfile\"";
2177 if (defined ($montagetitle) && $montagetitle ne '') {
2178 my ($str);
2179 $str = &interpolate_title_string_dir ($montagetitle, $dir, 'index');
2180 if ($str ne '') {
2181 $anchortext .= sprintf (" TITLE=\"%s\"", $str);
2182 }
2183 }
2184 $anchortext .= ">";
2185 push(@index, $anchortext);
2186
2187 push(@index, "<IMG CLASS=\"index\" SRC=\"$dir/$thumbnail_dir/$montagefile\"");
2188 push(@index, " WIDTH=\"$montage_x\" HEIGHT=\"$montage_y\"");
2189 push(@index, " ALT=\"\"");
2190 push(@index, ">");
2191 push(@index, "</A>\n");
2192
2193 push(@index,"<DIV CLASS=\"index\">");
2194 push(@index, "<A HREF=\"$dir/$indexfile\">$dir</A>");
2195 push(@index,"</DIV>\n");
2196
2197 $anchortext = "<A HREF=\"$dir/$detailfile\"";
2198 if (defined ($montagetitle) && $montagetitle ne '') {
2199 my ($str);
2200 $str = &interpolate_title_string_dir ($montagetitle, $dir, 'index');
2201 if ($str ne '') {
2202 $anchortext .= sprintf (" TITLE=\"%s\"", $str);
2203 }
2204 }
2205 $anchortext .= ">";
2206 push(@details, $anchortext);
2207
2208 push(@details, "<IMG CLASS=\"index\" SRC=\"$dir/$thumbnail_dir/$montagefile\"");
2209 my $x = $montage_x / $detailshrink ;
2210 my $y = $montage_y / $detailshrink ;
2211 push(@details, sprintf(" WIDTH=\"%d\" HEIGHT=\"%d\"", $x, $y));
2212 push(@details, " ALT=\"\"");
2213 push(@details, ">");
2214 push(@details, "</A>");
2215
2216 push(@details, "</TD><TD VALIGN=\"middle\" ALIGN=\"left\">\n");
2217
2218 } else {
2219
2220 push(@index,"<DIV CLASS=\"index\">");
2221 push(@index, "<A HREF=\"$dir/$indexfile\">$dir</A>");
2222 push(@index,"</DIV>\n");
2223
2224 }
2225
2226 push(@index, "<DIV CLASS=\"caption\">");
2227 push(@details, "<DIV CLASS=\"detail\">");
2228
2229 if ($dirtitle ne "") {
2230 push(@index, "$dirtitle");
2231 push(@details, "$dirtitle");
2232 }
2233
2234 push(@details, "<BR><A HREF=\"$dir/$detailfile\">$dir</A>");
2235
2236 push(@index, "</DIV>\n");
2237 push(@details, "</DIV>\n");
2238
2239 push(@details,"</TD></TR></TABLE>\n");
2240
2241 push(@index, "</TD>\n");
2242 push(@details, "</TD>\n");
2243
2244 # At end of row?
2245 if ($col_counter == $current_columns) {
2246 push(@index, "</TR>\n");
2247 push(@details, "</TR>\n");
2248 }
2249
2250
2251 # Increment for next item
2252 $col_counter++;
2253 $col_counter = 1 if ($col_counter > $current_columns);
2254
2255 } # if dir had index file
2256
2257 }
2258
2259 ######################################################################
2260 #
2261 # Top of HTML index/detail files
2262 #
2263 ######################################################################
2264 sub page_header {
2265
2266 my $this = shift(@_);
2267 my $linkto = shift(@_);
2268 my $numlink = 0;
2269 my $verstring;
2270
2271 select(INDEX);
2272 print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n";
2273 print "\"http://www.w3.org/TR/html401/strict.dtd\">\n";
2274 print "<HTML>\n";
2275 print "<HEAD>\n";
2276 $verstring = &versionstring();
2277 printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
2278 printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
2279 if (defined ($write_meta_tag{$titlemetatag})) {
2280 print "<META NAME=\"$titlemetatag\" CONTENT=\"$current_titletext\">\n";
2281 }
2282 if (defined ($write_meta_tag{$columnsmetatag})) {
2283 print "<META NAME=\"$columnsmetatag\" CONTENT=\"$current_columns\">\n";
2284 }
2285 if (defined ($write_meta_tag{$thumbxmetatag})) {
2286 print "<META NAME=\"$thumbxmetatag\" CONTENT=\"$current_thumbnail_x\">\n";
2287 }
2288 if (defined ($write_meta_tag{$thumbymetatag})) {
2289 print "<META NAME=\"$thumbymetatag\" CONTENT=\"$current_thumbnail_y\">\n";
2290 }
2291 if (defined ($write_meta_tag{$reversemetatag})) {
2292 print "<META NAME=\"$reversemetatag\" CONTENT=\"$current_reverse\">\n";
2293 }
2294 if (defined($firstdate)) {
2295 print "<META NAME=\"$begindatemetatag\" CONTENT=\"$firstdate\">\n";
2296 }
2297 if (defined($lastdate)) {
2298 print "<META NAME=\"$enddatemetatag\" CONTENT=\"$lastdate\">\n";
2299 }
2300 if (!defined ($opt_includeall) && defined (@opt_exclude) && scalar (@opt_exclude)) {
2301 my $tmp = join (',', @opt_exclude);
2302 my $etmp;
2303
2304 # We need to "encode" this string in the HTML so that raw filenames
2305 # (that people should not try to access) are not exposed to the
2306 # outside world.
2307 #
2308 $etmp = &encodestring ($tmp);
2309 printf ("<META NAME=\"$excludemetatag\" CONTENT=\"%s\">\n", $etmp);
2310 }
2311 printf ("<META NAME=\"$numimagesmetatag\" CONTENT=\"%d\">\n", $image_counter);
2312
2313 if (defined (@opt_skipmont) && scalar (@opt_skipmont)) {
2314 my $tmp = join (',', @opt_skipmont);
2315 printf ("<META NAME=\"$skipmetatag\" CONTENT=\"%s\">\n", $tmp);
2316 }
2317 print "<TITLE>$current_titletext</TITLE>\n";
2318 print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"$stylefile\">\n";
2319 print "</HEAD>\n";
2320 print "<BODY>\n";
2321
2322 # Break out of frames
2323 print "<SCRIPT TYPE=\"textvascript\">\n";
2324 print "if (parent.frames.length > 0) {\n";
2325 print " parent.location.href = self.document.location\n";
2326 print "}\n";
2327 print "</SCRIPT>\n";
2328
2329 print "<H1 CLASS=\"title\">$current_titletext</H1>\n";
2330
2331 print "<H3>";
2332
2333 # On all these links, check to see if the variable is also defined. If
2334 # not (done in a .imageindexrc file perhaps) then skip the link
2335 # If the script-use-only flag updirindexoverride was given it means we've
2336 # come in from a recursive call and $updirtext was set--therefore there will
2337 # be $destdir/../$indexfile eventually ... link to it.
2338 #
2339 if ((defined ($opt_updirindexoverride) and ($do_dirs == 1)) or
2340 ((-e "$destdir/../$indexfile") and ($do_dirs == 1) and defined($updirtext))) {
2341 print "<A HREF=\"../$indexfile\">$updirtext</A>";
2342 $numlink++;
2343 }
2344
2345 if (($do_slide == 1) and ($slide_counter > 1) and
2346 defined($framelinktext)) {
2347 print "&nbsp;&nbsp;|&nbsp;&nbsp;" if ($numlink != 0);
2348 print "<A HREF=\"$slide_dir/$framefile\">$framelinktext</A>";
2349 $numlink++;
2350 }
2351
2352 if (($do_detail == 1) and ($this eq 'index') and defined($detaillinktext)) {
2353 print "&nbsp;&nbsp;|&nbsp;&nbsp;" if ($numlink != 0);
2354 print "<A HREF=\"$detailfile\">$detaillinktext</A>";
2355 $numlink++;
2356 }
2357
2358 if (($this eq 'detail') and defined($indexlinktext)) {
2359 print "&nbsp;&nbsp;|&nbsp;&nbsp;" if ($numlink != 0);
2360 print "<A HREF=\"$indexfile\">$indexlinktext</A>";
2361 $numlink++;
2362 }
2363
2364 print "\n<BR>\n" if ($numlink != 0);
2365
2366 print "</H3>\n";
2367
2368 if (defined($firstdate) and defined($lastdate)) {
2369
2370 my $tmp1 = &format_date($firstdate, $this, 'dayonly');
2371 my $tmp2 = &format_date($lastdate, $this, 'dayonly');
2372
2373 if ($tmp1 ne $tmp2) {
2374 if ($current_reverse == 0) {
2375 print "<H2 CLASS=\"daterange\">$tmp1 - $tmp2</H2>\n";
2376 } else {
2377 print "<H2 CLASS=\"daterange\">$tmp2 - $tmp1</H2>\n";
2378 }
2379 } else {
2380 print "<H2 CLASS=\"daterange\">$tmp1</H2>\n";
2381 }
2382 }
2383
2384 print "<TABLE WIDTH=\"100%\" CLASS=\"index\">\n";
2385
2386 select(STDOUT);
2387
2388 }
2389
2390
2391 ######################################################################
2392 #
2393 # Bottom of HTML file
2394 #
2395 ######################################################################
2396 sub page_footer {
2397
2398 my $time = localtime(time);
2399
2400 my $progurl = 'http://www.edwinh.org/imageindex/';
2401
2402 select(INDEX);
2403
2404 print "</TABLE>\n";
2405
2406 print "<DIV CLASS=\"credits\">";
2407 print "<I>page created on $time</I><BR>\n";
2408 print "by <A HREF=\"$progurl\">imageindex</A> ";
2409 print &versionstring();
2410 # print "<BR>\n";
2411 # print "<A HREF=\"http://www.edwinh.org/\">Edwin Huffstutler</A> <I>&lt;edwinh at computer dot org&gt;</I>";
2412 # print "<BR>\n";
2413 # print "<A HREF=\"http://www.reynoldsnet.org/\">John Reynolds</A> <I>&lt;johnjen at reynoldsnet dot org&gt;</I>";
2414 print "</DIV>\n";
2415
2416 print "</BODY>\n</HTML>\n";
2417
2418 select(STDOUT);
2419 }
2420
2421
2422 ######################################################################
2423 #
2424 # A "quickie" routine to show which files were excluded in a prior run
2425 #
2426 ######################################################################
2427
2428 sub showexcluded {
2429 my ($file) = @_;
2430 my ($rfile, $tmp, $utmp, @files, $str);
2431
2432 if (! defined ($file)) {
2433 if (-r $indexfile) {
2434 $rfile = $indexfile;
2435 }
2436 }
2437 else {
2438 $rfile = $file;
2439 }
2440 $tmp = &extract_meta_tag ($excludemetatag, $rfile);
2441 if (defined($tmp)) {
2442 # We need to "decode" this string as it has been encoded for storage
2443 # in the HTML so that raw filenames (that people should not try to
2444 # access) are not exposed to the outside world.
2445 #
2446 $utmp = &decodestring ($tmp);
2447 (@files) = split (/,/, $utmp);
2448 $str = join (',', @files);
2449 printf ("File '$rfile' shows the following record of excluded files:\n");
2450 printf ("%s\n", $str);
2451 }
2452 else {
2453 printf ("File '$rfile' shows no record of excluded files.\n");
2454 }
2455 return;
2456 }
2457
2458 ######################################################################
2459 #
2460 # Ignore certain files via META data stored in the index.html file
2461 #
2462 # Exports global variable %skipmont used later during montage
2463 # generation.
2464 #
2465 ######################################################################
2466 sub exclude_files {
2467
2468 my @files = @_;
2469 my (@filelist, $f, %exclude, $token, @tokens);
2470
2471 undef %exclude;
2472
2473 # -skipmont flags override any META data found. Else, look for the META tag
2474 # then process. Check to see if any of the -skipmont options were given as
2475 # strings of filenames concatenated with ',' characters. If so, support it.
2476 #
2477 if (defined (@opt_skipmont)) {
2478 foreach (@opt_skipmont) {
2479 (@tokens) = split (/,/, $_);
2480 foreach $token (@tokens) {
2481 $skipmont{$token}++;
2482 }
2483 }
2484 }
2485 elsif (-r "$destdir/$indexfile") {
2486 my $tmp = &extract_meta_tag ($skipmetatag, "$destdir/$indexfile");
2487 if (defined($tmp)) {
2488 (@opt_skipmont) = split (/,/, $tmp);
2489 my $str = join (',', @opt_skipmont);
2490 printf ("Using saved skip-montage files: %s\n", $str);
2491 foreach (@opt_skipmont) {
2492 $skipmont{$_}++;
2493 }
2494 }
2495 }
2496
2497 # -exclude flags override any META data found. Else, look for the META tag
2498 # then process. Check to see if any of the -exclude options were given as
2499 # strings of filenames concatenated with ',' characters. If so, support it.
2500 #
2501 if (defined (@opt_exclude)) {
2502 # -includeall takes priority over -exclude on the commandline if they are
2503 # used together (wierd, but ...)
2504 #
2505 unless (defined ($opt_includeall)) {
2506 foreach (@opt_exclude) {
2507 (@tokens) = split (/,/, $_);
2508 foreach $token (@tokens) {
2509 $exclude{$token}++;
2510 }
2511 }
2512 }
2513 }
2514 elsif (-r "$destdir/$indexfile") {
2515 my $tmp = &extract_meta_tag ($excludemetatag, "$destdir/$indexfile");
2516 my $utmp;
2517 if (defined($tmp) && !defined ($opt_includeall)) {
2518 # We need to "decode" this string as it has been encoded for storage
2519 # in the HTML so that raw filenames (that people should not try to
2520 # access) are not exposed to the outside world.
2521 #
2522 $utmp = &decodestring ($tmp);
2523 (@opt_exclude) = split (/,/, $utmp);
2524 my $str = join (',', @opt_exclude);
2525 printf ("Using saved excluded files: %s\n", $str);
2526 foreach (@opt_exclude) {
2527 $exclude{$_}++;
2528 }
2529 }
2530 }
2531
2532 foreach $f (@files) {
2533 if (! $exclude{$f}) {
2534 push (@filelist, $f);
2535 } else {
2536 print "Excluding '$f'\n";
2537 if (-d $f) {
2538 chmod (0700, $f);
2539 }
2540 else {
2541 chmod (0600, $f);
2542 }
2543 }
2544 }
2545 return (@filelist);
2546 }
2547
2548
2549 ######################################################################
2550 #
2551 # Nuke generated files if original image gone
2552 #
2553 ######################################################################
2554 sub nuke_out_of_date {
2555 foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) {
2556 opendir(THUMBS,"$destdir/$checkdir") || die "Can't open dir $checkdir: ($!)\n";
2557 foreach (readdir(THUMBS)) {
2558 next if (m/^\.?\.$/);
2559 next if (m/$framefile/);
2560 next if (m/$slidefile/);
2561 next if (m/$montagefile/);
2562 next if (m/$emoticonsmile/);
2563 next if (m/$emoticonwink/);
2564 next if (m/$emoticonfrown/);
2565 if (!defined($thumb_backref{"$checkdir/$_"}) and
2566 !defined($slide_backref{"$checkdir/$_"}) and
2567 !defined($med_backref{"$checkdir/$_"})) {
2568 print "Removing stale $destdir/$checkdir/$_\n";
2569 unlink("$destdir/$checkdir/$_") || warn "Can't unlink $destdir/$checkdir/$_: ($!)\n";
2570 $modified_thumb++;
2571
2572 }
2573 }
2574 closedir(THUMBS);
2575 }
2576
2577 }
2578
2579 ######################################################################
2580 #
2581 # Convert bytes to kb string
2582 #
2583 ######################################################################
2584 sub convert_to_kb {
2585
2586 my $bytes = shift(@_);
2587 if ($bytes > (1024 * 1024)) {
2588 $bytes = sprintf("%.1fM", $bytes / (1024.0 * 1024.0));
2589 } else {
2590 $bytes = sprintf("%dk", $bytes / 1024);
2591 }
2592 return($bytes);
2593 }
2594
2595 ######################################################################
2596 #
2597 # Sortq by integer date stamp
2598 #
2599 ######################################################################
2600 sub bynumber {
2601 if ($current_reverse == 0) {
2602 $a <=> $b;
2603 } else {
2604 $b <=> $a;
2605 }
2606 }
2607
2608
2609 ######################################################################
2610 #
2611 # Write frameset file for slideshows
2612 #
2613 ######################################################################
2614 sub write_frameset {
2615
2616 # This is impossible to get rid of
2617 my $framefudge = 35;
2618 my $verstring;
2619
2620 open(FRAME,">$destdir/$slide_dir/$framefile") or die ("Can't open $destdir/$slide_dir/$framefile: $!\n");
2621
2622 select(FRAME);
2623
2624 print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\"\n";
2625 print "\"http://www.w3.org/TR/html401/frameset.dtd\">\n";
2626 print "<HTML>\n";
2627 print "<HEAD>\n";
2628 $verstring = &versionstring();
2629 printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
2630 printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
2631 print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">";
2632 print "<TITLE>$current_titletext</TITLE>\n";
2633 print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">\n";
2634 print "</HEAD>\n";
2635 if ($frame_orient eq 'horizontal') {
2636 printf("<FRAMESET ROWS=\"%d, *\">\n", $max_thumb_y + $framefudge);
2637 } else {
2638 printf("<FRAMESET COLS=\"%d, *\">\n", $max_thumb_x + $framefudge);
2639 }
2640 print "<FRAME NAME=\"thumb\" SRC=\"$slidefile\">\n";
2641 print "<FRAME NAME=\"view\" SRC=\"../$first_slide\">\n";
2642 print "<NOFRAMES>No frames in this browser...go back</NOFRAMES>\n";
2643 print "</FRAMESET>\n";
2644 print "</HTML>\n";
2645
2646 select(STDOUT);
2647 close (FRAME);
2648
2649
2650 open(FRAME,">$destdir/$slide_dir/$slidefile") or die ("Can't open $destdir/$slide_dir/$slidefile: $!\n");
2651 select(FRAME);
2652
2653
2654 print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n";
2655 print "\"http://www.w3.org/TR/html401/strict.dtd\">\n";
2656 print "<HTML>\n";
2657 print "<HEAD>\n";
2658 $verstring = &versionstring();
2659 printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
2660 printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
2661 print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">\n";
2662 print "<TITLE>$current_titletext</TITLE>\n";
2663 print "</HEAD>\n<BODY>\n";
2664 print "<TABLE CLASS=\"frame\"\n";
2665 print " <TR>\n" if ($frame_orient eq 'horizontal');
2666 foreach (@frame) {
2667 print;
2668 }
2669 print " </TR>\n" if ($frame_orient eq 'horizontal');
2670 print "</TABLE>\n";
2671 print "</BODY>\n</HTML>\n";
2672
2673 select(STDOUT);
2674 close(FRAME);
2675
2676 }
2677
2678
2679 ######################################################################
2680 #
2681 # Do next/index/prev links on slide pages
2682 #
2683 ######################################################################
2684 sub next_prev_links {
2685
2686 my $pathname = shift(@_);
2687
2688 print "<DIV CLASS=\"index\">";
2689
2690 if (defined($back{$pathname})) {
2691 print "<A HREF=\"$back{$pathname}\">&lt;&nbsp;previous</A>&nbsp;|&nbsp;";
2692 } else {
2693 print "&lt;&nbsp;previous&nbsp;|&nbsp;";
2694 }
2695 print "<A HREF=\"../$indexfile\">index</A>";
2696 if (defined($forward{$pathname})) {
2697 print "&nbsp;|&nbsp;<A HREF=\"$forward{$pathname}\">next&nbsp;&gt;</A>";
2698 } else {
2699 print "&nbsp;|&nbsp;next&nbsp;&gt;";
2700 }
2701
2702 print "</DIV>\n";
2703
2704 }
2705
2706
2707 ######################################################################
2708 #
2709 # Lower-case all the filenames. I hate the uppercase filenames that come
2710 # from my camera's default software (and Windud software). Plus I didn't
2711 # want this "utility" in another script, so just place it here.
2712 #
2713 ######################################################################
2714 sub lower_case_files {
2715 my (@files) = @_;
2716 my ($newfile, $lowername);
2717
2718 foreach $name (@files) {
2719 ($lowername = $name) =~ tr/A-Z/a-z/;
2720 if ($name =~ /[A-Z]/) {
2721 print "Moving '$name' to '$lowername'\n";
2722 move("$name","$lowername");
2723 }
2724 }
2725 }
2726
2727
2728 ######################################################################
2729 #
2730 # extract the NAME tag from an HTML file
2731 #
2732 ######################################################################
2733 sub extract_meta_tag {
2734 my ($tag, $filename) = @_;
2735 my ($name, $content, $retval);
2736
2737 if (! (open (FILE, $filename))) {
2738 print STDERR "Cannot open '$filename' for reading - $!\n";
2739 return (0);
2740 }
2741 # <META NAME="Columns" CONTENT="3">
2742 #
2743 while (<FILE>) {
2744 if (/<META\s+NAME=\"(.*?)\"\s+CONTENT=\"(.*)\">/) {
2745 $name = $1;
2746 $content = $2;
2747 if ($name eq $tag) {
2748 $retval = $content;
2749 last;
2750 }
2751 }
2752 }
2753 close (FILE);
2754 return ($retval);
2755 }
2756
2757
2758 ###############################################################################
2759 #
2760 # Rotate given image 90 degrees
2761 #
2762 ###############################################################################
2763 sub rotate_image {
2764
2765 my $file = shift(@_);
2766 my $argv = shift(@_);
2767
2768 if ($file =~ m/^(cw|ccw)$/) {
2769 # If file is cw or ccw,
2770 # assume the args were given backwards
2771 my $tmp = $file;
2772 $file = $$argv[0];
2773 $$argv[0] = $tmp;
2774 }
2775
2776 -r "$file" || die("$file: ", $!);
2777 -w "$file" || die("$file: ", $!);
2778
2779
2780 # grab the mtime of the file so we can reset it after we update it
2781 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
2782 $ctime,$blksize,$blocks) = stat($file);
2783 my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
2784
2785 my ($name,$path,$suffix) = fileparse($file,'\.\S+');
2786 my $thumb;
2787 my $medium;
2788
2789 my $image = new Image::Magick;
2790
2791 my $retval = $image->Read("$file");
2792 warn "$retval" if "$retval";
2793
2794 if (!defined($$argv[0]) or
2795 ($$argv[0] !~ m/^cc?w$/i)) {
2796 print "Need 'cw' or 'ccw' argument to rotate image clockwise/counterclockwise\n";
2797 exit(1);
2798 }
2799
2800 if ($$argv[0] =~ /^ccw$/i) {
2801 $deg = -90;
2802 } else {
2803 $deg = 90;
2804 }
2805
2806 print "Rotating $file $deg degrees\n";
2807 $retval = $image->Rotate($deg);
2808 warn "$retval" if "$retval";
2809
2810 $retval = $image->Write(filename=>"$file");
2811 warn "$retval" if "$retval";
2812
2813 system ("touch -t $posix_mtime $file");
2814
2815 # Nuke the generated images if they exist
2816 # (touching the timestamp above breaks automatic regeneration logic)
2817 if ($image->Get('format') =~ /JFIF/i) {
2818 $thumb = $path . "$thumbnail_dir/$name" . $suffix;
2819 $medium = $path . "$med_dir/$name" . $suffix;
2820 } else {
2821 $thumb = $path . "$thumbnail_dir/$name.jpg";
2822 $medium = $path . "$med_dir/$name.jpg";
2823 }
2824 unlink($thumb) if (-e "$thumb");
2825 unlink($medium) if (-e "$medium");
2826
2827
2828
2829 }
2830
2831 ###############################################################################
2832 #
2833 # Set or display caption for a particular image
2834 #
2835 ###############################################################################
2836 sub caption_image {
2837
2838 my $file = shift(@_);
2839 my $argv = shift(@_);
2840 my ($esc_comment, $tmpfile);
2841
2842 -r "$file" || die("$file: ", $!);
2843
2844 # grab the mtime of the file so we can reset it after we update it
2845 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
2846 $ctime,$blksize,$blocks) = stat($file);
2847 my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
2848
2849 my $image = new Image::Magick;
2850
2851 my $retval = $image->Read("$file");
2852 warn "$retval" if "$retval";
2853
2854 my $format = $image->Get('format');
2855 warn "$retval" if "$retval";
2856
2857 # Set caption if another arg is present, or just display it
2858 if (defined($$argv[0])) {
2859
2860 -w "$file" || die("$file: ", $!);
2861
2862 # Try to find wrjpgcom so we can use it for adding captions to JPG images
2863 my $wrjpgcom_prog = &find_in_path ('wrjpgcom');
2864 my $quote_file = quotemeta ($file);
2865
2866 # If a jpeg file and we found a wrjpgcom program in our path, use
2867 # it! It simply puts the comment in the JPEG header without reading
2868 # (uncompressing) and writing (re-compressing) the file out so
2869 # there is no chance for data loss.
2870 #
2871 if (($format =~ /JFIF/i) and defined($wrjpgcom_prog)) {
2872
2873 $tmpfile = "$file.$$";
2874 my $tmpfile_quote = "$quote_file.$$";
2875 $esc_comment = quotemeta ($$argv[0]);
2876 # FIXME
2877 # check to see how '?' and other punctuation is escaped and fix
2878 # it seems things are not correct.
2879 system ("$wrjpgcom_prog -replace -comment $esc_comment $quote_file > $tmpfile_quote");
2880 if (($? >> 8) != 0) {
2881 printf(STDERR "Error in creating JPEG comment with 'wrjpgcom'. Leaving existing file intact.\n");
2882 } else {
2883 move($tmpfile, $file);
2884 }
2885
2886 } else {
2887 # Fall back to PerlMagick's routines.
2888 #
2889 $retval = $image->Comment("$$argv[0]");
2890 warn "$retval" if "$retval";
2891
2892 $retval = $image->Write(filename=>"$file", quality=>"95",
2893 sampling_factor=>"1x1");
2894 warn "$retval" if "$retval";
2895 }
2896
2897 system ("touch -t $posix_mtime $quote_file");
2898
2899 } else {
2900
2901 my $text = $image->Get('comment');
2902
2903 if (defined($text)) {
2904 print "$file: \"$text\"\n";
2905 } else {
2906 print "$file: (no caption)\n";
2907 }
2908
2909 }
2910
2911 }
2912
2913
2914 ###############################################################################
2915 #
2916 # Print usage info from top of file
2917 #
2918 ###############################################################################
2919 sub usage {
2920
2921 open(FILE,"$0") or die "Can't open $0: $OS_ERROR";
2922 while(<FILE>) {
2923 last if (m/^\#\s+USAGE:/);
2924 }
2925 while(<FILE>) {
2926 last if (m/^\#\#\#\#\#\#\#/);
2927 s/^\# ?//;
2928 print;
2929 }
2930 close(FILE);
2931
2932 }
2933
2934 ######################################################################
2935 #
2936 # Format timestamp for HTML pages. This routine assumes that the date
2937 # given to it is in YYYYMMDDHHMM.SS format that we've created from the
2938 # EXIF/mtime date using strftime().
2939 #
2940 ######################################################################
2941 sub format_date {
2942 my ($date, $context, $dayonly) = @_;
2943 my ($timeformat, $dateformat);
2944
2945 if ($context eq 'frame') {
2946 $timeformat = $frametimeformat;
2947 $dateformat = $framedateformat;
2948 }
2949 elsif ($context eq 'index') {
2950 $timeformat = $indextimeformat;
2951 $dateformat = $indexdateformat;
2952 }
2953 elsif ($context eq 'slide') {
2954 $timeformat = $slidetimeformat;
2955 $dateformat = $slidedateformat;
2956 }
2957 else {
2958 $timeformat = $detailtimeformat;
2959 $dateformat = $detaildateformat;
2960 }
2961
2962 # Replace "macro" patterns in the format string first
2963 #
2964 $timeformat =~ s/\%R/\%H:\%M/g;
2965 $timeformat =~ s/\%r/\%I:\%M:\%S \%p/g;
2966 $dateformat =~ s/\%F/\%Y-\%m-\%d/g;
2967 $dateformat =~ s/\%D/\%m\/\%d\/\%y/g;
2968
2969 $date =~ /(\d\d\d\d)(\d\d)(\d\d)(\d\d)?(\d\d)?\.?(\d\d)?/;
2970 my $year = $1;
2971 my $month = $2;
2972 my $day = $3;
2973 my $hour = $4;
2974 my $min = $5;
2975 my $sec = $6;
2976 my ($ampm, $two_digit_year, $twelve_hour);
2977
2978 if ($year =~ /^\d\d(\d\d)$/) {
2979 $two_digit_year = $1;
2980 }
2981 else {
2982 $two_digit_year = '??'; # shouldn't ever been seen
2983 }
2984
2985 # If we're told to, only format a date with no time
2986 #
2987 if (defined ($dayonly)) {
2988 $dateformat =~ s/\%Y/$year/g;
2989 $dateformat =~ s/\%y/$two_digit_year/g;
2990 $dateformat =~ s/\%m/$month/g;
2991 $dateformat =~ s/\%d/$day/g;
2992 $dateformat =~ s/\%\%/\%/g;
2993 return ($dateformat);
2994 }
2995 else {
2996 if (defined($hour)) {
2997 $twelve_hour = $hour;
2998 $ampm = 'AM';
2999 if ($hour >= 12) {
3000 $ampm = 'PM';
3001 }
3002 if ($hour > 12) {
3003 $twelve_hour -= 12;
3004 }
3005 }
3006 else {
3007 $hour = '??';
3008 $twelve_hour = '??';
3009 $ampm = '??'; #again, should never be seen
3010 }
3011 if (! defined ($min)) {
3012 $min = '??';
3013 }
3014 if (! defined ($sec)) {
3015 $sec = '??';
3016 }
3017
3018 $dateformat =~ s/\%Y/$year/g;
3019 $dateformat =~ s/\%y/$two_digit_year/g;
3020 $dateformat =~ s/\%m/$month/g;
3021 $dateformat =~ s/\%d/$day/g;
3022 $dateformat =~ s/\%\%/\%/g;
3023
3024 $timeformat =~ s/\%S/$sec/g;
3025 $timeformat =~ s/\%M/$min/g;
3026 $timeformat =~ s/\%I/$twelve_hour/g;
3027 $timeformat =~ s/\%H/$hour/g;
3028 $timeformat =~ s/\%p/$ampm/g;
3029 $timeformat =~ s/\%\%/\%/g;
3030
3031 return("$dateformat $timeformat");
3032 }
3033
3034 }
3035
3036 ######################################################################
3037 #
3038 # Return version string from CVS tag
3039 #
3040 ######################################################################
3041 sub versionstring {
3042
3043 my $ver = ' $Name: v1_1 $ ';
3044 $ver =~ s/Name//g;
3045 $ver =~ s/[:\$]//g;
3046 $ver =~ s/\s+//g;
3047 $ver =~ s/^v//g;
3048 $ver =~ s/_/\./g;
3049 if ($ver eq '') {
3050 $ver = "cvs devel - " . '$Revision: 1.175 $ ';
3051 # Nuke the $ signs -- what if somebody is keeping pages under RCS
3052 # or CVS control?
3053 $ver =~ s/\$//g;
3054 $ver =~ s/\s*$//;
3055 }
3056 return($ver);
3057
3058 }
3059
3060 ###############################################################################
3061 #
3062 # Create CSS file that is shared among the HTML pages
3063 #
3064 ###############################################################################
3065 sub write_css {
3066
3067
3068 open(CSS,">$destdir/$stylefile") or die ("Can't open $destdir/$stylefile: $!\n");
3069 select(CSS);
3070
3071 print $stylesheet;
3072
3073 select(STDOUT);
3074 close(CSS);
3075
3076
3077 }
3078
3079 ###############################################################################
3080 #
3081 # "Interpolate" %? escapes found in our printf-like strings defined for the
3082 # TITLE attributes. See the beginning of this file for their definition
3083 #
3084 ###############################################################################
3085
3086 sub interpolate_title_string {
3087 my ($formatstring, $pathname, $context) = @_;
3088 my ($filename, $date, $size, $resolution, $caption);
3089 my ($tmp);
3090
3091 $filename = $info{$pathname}{'file'};
3092 $date = &format_date ($info{$pathname}{'date'}, $context);
3093 $size = $info{$pathname}{'size'};
3094 $resolution = $info{$pathname}{'geometry'};
3095 $caption = $info{$pathname}{'comment'};
3096 if (! defined ($caption)) {
3097 $caption = '';
3098 }
3099 $tmp = $formatstring;
3100
3101 $tmp =~ s/\%f/$filename/g if $filename;
3102 $tmp =~ s/\%d/$date/g if $date;
3103 $tmp =~ s/\%s/$size/g if $size;
3104 $tmp =~ s/\%r/$resolution/g if $resolution;
3105 $tmp =~ s/\%c/$caption/g;
3106 $tmp =~ s/\%\%/%/g;
3107
3108 # In case the format string has " marks in it, change all those to '.
3109 # The " marks are needed to mark the argument to the TITLE attribute.
3110 #
3111 $tmp =~ s/\"/\'/g;
3112 return ($tmp);
3113
3114 }
3115
3116 ###############################################################################
3117 #
3118 # "Interpolate" %? escapes found in our printf-like strings defined for the
3119 # TITLE attributes. However, the %? escapes for this function are based on what
3120 # you could conceivably need when processing a directory.
3121 #
3122 # See the beginning of this file for their definition
3123 #
3124 ###############################################################################
3125
3126 sub interpolate_title_string_dir {
3127 my ($formatstring, $dir, $context) = @_;
3128 my ($tmp, $num, $date, $metadate, $metatitle);
3129
3130 $tmp = $formatstring;
3131 $num = &extract_meta_tag($numimagesmetatag, "$srcdir/$dir/$indexfile");
3132
3133 # If we plucked out the number of images from the metadata of a directory's
3134 # index.html file, replace it. Else, give a warning if we didn't find it but
3135 # somebody still used %n
3136 #
3137 if (defined ($num)) {
3138 $tmp =~ s/\%n/$num/g if $num;
3139 }
3140 else {
3141 if ($tmp =~ /\%n/) {
3142 if (!defined ($remember_warning{$dir})) {
3143 printf (STDERR "Warning: %%n escape used in format string and %s META tag not found in %s. Re-run imageindex in '$dir'.\n", $numimagesmetatag, "$srcdir/$dir/$indexfile");
3144 $remember_warning{$dir}++;
3145 }
3146 }
3147 }
3148
3149 $metadate = &extract_meta_tag($begindatemetatag, "$srcdir/$dir/$indexfile");
3150 $date = &format_date ($metadate, $context, 'dayonly');
3151 $tmp =~ s/\%b/$date/g if $date;
3152
3153 $metadate = &extract_meta_tag($enddatemetatag, "$srcdir/$dir/$indexfile");
3154 $date = &format_date ($metadate, $context, 'dayonly');
3155 $tmp =~ s/\%e/$date/g if $date;
3156
3157 $metatitle = &extract_meta_tag($titlemetatag, "$srcdir/$dir/$indexfile");
3158 $tmp =~ s/\%t/$metatitle/g if $metatitle;
3159
3160 # In case the format string has " marks in it, change all those to '.
3161 # The " marks are needed to mark the argument to the TITLE attribute.
3162 #
3163 $tmp =~ s/\"/\'/g;
3164 return ($tmp);
3165
3166 }
3167
3168 ###############################################################################
3169 #
3170 # Look for external programs we depend on in the $PATH. It just finds the first
3171 # occurence of $prog in $PATH.
3172 #
3173 ###############################################################################
3174 sub find_in_path {
3175 my ($prog) = @_;
3176 my ($retval);
3177
3178 undef $retval;
3179 foreach $dir (split (/:/, $ENV{'PATH'})) {
3180 if (-r "$dir/$prog" && -x "$dir/$prog") {
3181 $retval = "$dir/$prog";
3182 }
3183 }
3184 return ($retval);
3185 }
3186
3187
3188 ###############################################################################
3189 #
3190 # Encode/decode routines for exclude filenames when stuffed in a meta tag
3191 #
3192 ###############################################################################
3193 sub encodestring {
3194 my ($tmp) = @_;
3195 my $etmp;
3196 $etmp = pack ("u*", $tmp);
3197 # Hack the string to get rid of \n chars so we can store it on 1 line
3198 $etmp =~ s/\n/..1xn!_ltr../g;
3199
3200 # Get rid of ampersands
3201 $etmp =~ s/\&/..xn!_ltr1../g;
3202
3203 # Get rid of double-quotes
3204 $etmp =~ s/\"/..sb!_lho1../g;
3205 return ($etmp);
3206 }
3207
3208 sub decodestring {
3209 my ($tmp) = @_;
3210 my $utmp;
3211
3212 # Unhack the string to bring back & characters
3213 $tmp =~ s/\.\.sb\!_lho1\.\./\"/g;
3214 $tmp =~ s/\.\.xn\!_ltr1\.\./\&/g;
3215
3216 # Unhack the string to bring back & characters
3217 $tmp =~ s/\.\.xn\!_ltr1\.\./\&/g;
3218
3219 # Unhack the string to bring back \n characters
3220 $tmp =~ s/\.\.1xn\!_ltr\.\./\n/g;
3221 $utmp = unpack ("u*", $tmp);
3222 return ($utmp);
3223 }
3224
3225 #############################################################################
3226 #
3227 # This routine samples linearly (as possible) across the available files in
3228 # a directory. The first pass at sampling is a simple modulo function based
3229 # upon the ratio of files to the number of tiles we can use in the montage.
3230 # If that first pass sample did not produce enough files, then we go back
3231 # iteratively through the list and as evenly-as-possible select unused
3232 # files from those left in the pool.
3233 #
3234 #############################################################################
3235 sub sample_files_for_montage {
3236 my (@files) = @_;
3237 my ($numdiv, $numchosen, $chunksize, $numfiles, $numleft);
3238 my ($i, $index, $f, @ret);
3239
3240 $numfiles = scalar (@files);
3241 $numdiv = sprintf ("%d", $numfiles / $montage_max);
3242 $numdiv++;
3243
3244 for ($i = 0; $i < $numfiles; $i++) {
3245 if (($i % $numdiv) == 0) {
3246 $chosen{$files[$i]}++;
3247 }
3248 }
3249
3250 $numchosen = scalar (keys %chosen);
3251
3252 $numleft = $montage_max - $numchosen;
3253
3254 if ($numleft) {
3255 $chunksize = sprintf ("%d", $numfiles / $numleft);
3256 $index = 0;
3257 for ($i = 0; $i < $numleft; $i++) {
3258 &mark_next_file_for_montage ($index + 1, $numfiles, @files);
3259 $index = $index + $chunksize;
3260 }
3261 }
3262
3263 foreach $f (@files) {
3264 if ($chosen{$f}) {
3265 push (@ret, $f);
3266 }
3267 }
3268
3269 return (@ret);
3270 }
3271
3272 #############################################################################
3273 #
3274 # cycle through the given list of files. If the list[$index] is already marked
3275 # (via the global hash %chosen) then move onto the next one, etc.
3276 #
3277 #############################################################################
3278 sub mark_next_file_for_montage {
3279 my ($index, $numfiles, @files) = @_;
3280 my ($i);
3281
3282 for ($i = $index; $i < $numfiles; $i++) {
3283 if (! $chosen{$files[$i]}) {
3284 $chosen{$files[$i]}++;
3285 last;
3286 }
3287 }
3288 }
3289
3290 ###############################################################################
3291 #
3292 # Exclude certain filenames from the list of thumbnails to be used in the
3293 # montage image.
3294 #
3295 ###############################################################################
3296 sub exclude_montage_files {
3297 my (@files) = @_;
3298 my (@tmp, $file);
3299
3300 foreach (@files) {
3301 $file = basename ($_);
3302 unless (defined ($skipmont{$file})) {
3303 push (@tmp, $_);
3304 }
3305 }
3306 return (@tmp);
3307 }
3308
3309 ###############################################################################
3310 #
3311 # "html-ize" a caption found in an image. Just in case there are certain
3312 # characters used which we want to "escape."
3313 #
3314 ###############################################################################
3315 sub htmlize_caption {
3316 my ($caption, $slide) = @_;
3317
3318 $caption =~ s/\&/\&amp;/g;
3319 $caption =~ s/\</\&lt;/g;
3320 $caption =~ s/\>/\&gt;/g;
3321 $caption =~ s/\"/\&quot;/g;
3322
3323 # Help smiley's render in a "mo-better" way when they are at the end of a
3324 # caption and enclosed in parens
3325 #
3326 if ($caption =~ /(:\-?[\(\)])\s*\)\s*$/) {
3327 my $tmp = $1;
3328 $caption =~ s/:\-?[\(\)]\s*\)\s*$/$tmp\&nbsp;\)/;
3329 }
3330
3331 $caption = &emoticonify ($caption, $slide);
3332
3333 return ($caption);
3334
3335 }
3336
3337 ###############################################################################
3338 #
3339 # Translate ASCII smiley's embedded into image captions into emoticons
3340 #
3341 ###############################################################################
3342 sub emoticonify {
3343 my ($caption, $slide) = @_;
3344 my ($thumbdir, $attr);
3345
3346 return ($caption) if (! defined ($do_emoticons) || $do_emoticons == 0);
3347
3348 # This is a hack, please ignore and move on ... nothing to see here.
3349 #
3350 $caption =~ s/\&nbsp;/NoNBrEaKaBleSpacE/g;
3351
3352 $thumbdir = $thumbnail_dir;
3353 if ($slide) {
3354 $thumbdir = '../' . $thumbdir;
3355 }
3356 $attr = 'STYLE="vertical-align: middle;" WIDTH="19" HEIGHT="19"';
3357
3358 if ($caption =~ s/:\-?\)/\<IMG SRC=\"$thumbdir\/$emoticonsmile\" $attr ALT=\" \[smiley icon\] \"\>/g) {
3359 $emoticon{'smile'}++;
3360 }
3361 if ($caption =~ s/;\-?\)/\<IMG SRC=\"$thumbdir\/$emoticonwink\" $attr ALT=\" \[smiley icon\] \"\>/g) {
3362 $emoticon{'wink'}++;
3363 }
3364 if ($caption =~ s/:\-?\(/\<IMG SRC=\"$thumbdir\/$emoticonfrown\" $attr ALT=\" \[frown icon\] \"\>/g) {
3365 $emoticon{'frown'}++;
3366 }
3367
3368 # Undo the hack
3369 #
3370 $caption =~ s/NoNBrEaKaBleSpacE/\&nbsp;/g;
3371 return ($caption);
3372
3373 }
3374
3375 ###############################################################################
3376 #
3377 # Write out PNG files representing the emoticons
3378 #
3379 ###############################################################################
3380 sub write_emoticon_png {
3381 my ($type) = @_;
3382 my ($img);
3383
3384 if (! open (IMG, '>' . $destdir . '/' . $thumbnail_dir . "/$emoticonprefix" . $icon . ".png")) {
3385 printf (STDERR "Could not open emoticon file for '$icon' for writing - $!\n");
3386 return;
3387 }
3388 # UUDecode the small PNG files that represent the emoticons and dump them to
3389 # the appropriate files in $thumbnail_dir
3390 #
3391 $img = unpack ("u*", $png{$type});
3392 print IMG $img;
3393 close (IMG);
3394 }
3395
3396 ###############################################################################
3397 #
3398 # Write out PNG files representing the emoticons
3399 #
3400 ###############################################################################
3401 sub write_video_icons {
3402 my ($tmp_jpg_dir) = @_;
3403 my ($img);
3404
3405 foreach $key (keys %png) {
3406 next if $key !~ /video/;
3407 if (! open (IMG, '>' . $tmp_jpg_dir . '/' . $key . ".png")) {
3408 printf (STDERR "Could not open video icon file for '$key' for writing - $!\n");
3409 return;
3410 }
3411 # UUDecode the small PNG files that represent the emoticons and dump them to
3412 # the appropriate files in $thumbnail_dir
3413 #
3414 $img = unpack ("u*", $png{$key});
3415 print IMG $img;
3416 $max_video_icons++;
3417 close (IMG);
3418 }
3419 }
3420
3421 ###############################################################################
3422 #
3423 # Create a montage of images in the current directory. This image will be
3424 # pointed to by the parent directory's index.html file to show a sort of
3425 # "thumbnail preview" of the contents of this directory.
3426 #
3427 ###############################################################################
3428
3429 sub create_montage {
3430
3431 my @files = @_;
3432 my (@modfiles);
3433
3434 @files = &exclude_montage_files (@files);
3435
3436 foreach (@files) {
3437 push (@modfiles, quotemeta ($_));
3438 }
3439
3440 # If we have defined that a lesser number of "tiles" can be used in the
3441 # montage vs. the # of files in this directory, then we'll "sample" the
3442 # files as evenly as possible to avoid clustering of shots that might be
3443 # similar to each other.
3444 #
3445 if (scalar (@modfiles) > $montage_max) {
3446 @modfiles = &sample_files_for_montage (@modfiles);
3447 }
3448
3449 if ($do_montage == 1) {
3450
3451 if (($modified_thumb != 0) or (! -e "$destdir/$thumbnail_dir/$montagefile")) {
3452
3453 my $number = $#modfiles + 1;
3454 my $tile_x = 1;;
3455 my $tile_y = 1;
3456
3457 # FIXME these both blindly expand x before expanding y
3458 # Should this depend on some aspect ratio?
3459 while(($tile_x * $tile_y) < $montage_min) {
3460 $tile_x++;
3461 $tile_y++ if (($tile_x * $tile_y) < $montage_min);
3462 }
3463 while(($tile_x * $tile_y) < $number) {
3464 $tile_x++;
3465 $tile_y++ if (($tile_x * $tile_y) < $number);
3466 }
3467
3468 my $index = 0;
3469 while (($#modfiles + 1) < ($tile_x * $tile_y)) {
3470 if ($montage_fill eq 'blank') {
3471 push(@modfiles, "NULL:");
3472 } else {
3473 push(@modfiles, $modfiles[$index]);
3474 $index = ($index+1) % $number;
3475
3476 }
3477 }
3478
3479 my $tile = sprintf("%dx%d", $tile_x, $tile_y);
3480 my $geom = sprintf("%dx%d", $max_mont_thumb_x, $max_mont_thumb_y);
3481 my $newgeom = sprintf("%dx%d", $current_thumbnail_x, $current_thumbnail_y);
3482
3483 print "Picked $tile array of $geom for montage\n" if ($opt_debug);
3484
3485 print "Creating $destdir/$thumbnail_dir/$montagefile\n";
3486
3487 system("montage -quality $thumb_quality -bordercolor white -transparent white -border $montage_whitespace -geometry $geom -tile $tile @modfiles \"$destdir/$thumbnail_dir/$montagefile\"");
3488 if (($? >> 8) != 0) {
3489 printf(STDERR "Error in creating montage file\n");
3490 return(-1);
3491 }
3492
3493 # Resize to std. thumbnail
3494 my $image = new Image::Magick;
3495 my $retval;
3496
3497 $retval = $image->Read(filename=>"$destdir/$thumbnail_dir/$montagefile");
3498 warn "$retval" if "$retval";
3499 $retval = $image->Resize(geometry=>$newgeom);
3500 warn "$retval" if "$retval";
3501 $retval = $image->Set(interlace=>Line);
3502 warn "$retval" if "$retval";
3503 $retval = $image->Write(filename=>"$destdir/$thumbnail_dir/$montagefile");
3504 warn "$retval" if "$retval";
3505
3506 }
3507
3508 } else {
3509
3510 unlink("$destdir/$thumbnail_dir/$montagefile")
3511 if (-e "$destdir/$thumbnail_dir/$montagefile");
3512
3513 }
3514
3515 }
3516
3517 sub read_stored_meta_data {
3518 my ($tmp);
3519
3520 if (-r "$destdir/$indexfile") {
3521 $tmp = &extract_meta_tag ($columnsmetatag, "$destdir/$indexfile");
3522 # If we found data, check it against program defaults
3523 if (defined ($tmp)) {
3524 $current_columns = $tmp;
3525 print "Using saved number of columns: $current_columns\n" if ! defined ($opt_columns);
3526 }
3527
3528 $tmp = &extract_meta_tag ($titlemetatag, "$destdir/$indexfile");
3529 # If we found data, check it against program defaults
3530 if (defined ($tmp)) {
3531 $current_titletext = $tmp;
3532 print "Using saved title: $current_titletext\n" if ! defined ($opt_title);
3533 }
3534
3535 $tmp = &extract_meta_tag ($thumbxmetatag, "$destdir/$indexfile");
3536 # If we found data, check it against program defaults
3537 if (defined ($tmp)) {
3538 $current_thumbnail_x = $tmp;
3539 print "Using saved thumbnail X size: $current_thumbnail_x\n" if ! defined ($opt_x);
3540 }
3541
3542 $tmp = &extract_meta_tag ($thumbymetatag, "$destdir/$indexfile");
3543 # If we found data, check it against program defaults
3544 if (defined ($tmp)) {
3545 $current_thumbnail_y = $tmp;
3546 print "Using saved thumbnail Y size: $current_thumbnail_y\n" if ! defined ($opt_y);
3547 }
3548
3549 $tmp = &extract_meta_tag ($reversemetatag, "$destdir/$indexfile");
3550 # If we found data, check it against program defaults
3551 if (defined ($tmp)) {
3552 $current_reverse = $tmp;
3553 print "Using saved reverse: $current_reverse\n" if ! defined ($opt_reverse);
3554 }
3555
3556 &decide_which_md_to_store();
3557 }
3558 }
3559
3560 sub override_by_commandline {
3561 if (defined($opt_columns)) {
3562 $current_columns = $opt_columns;
3563 }
3564 if (defined($opt_title)) {
3565 $current_titletext = $opt_title;
3566 }
3567 if (defined($opt_reverse)) {
3568 $current_reverse = $opt_reverse;
3569 }
3570 if (defined($opt_x)) {
3571 $current_thumbnail_x = $opt_x;
3572 if ($current_thumbnail_x != $default_thumbnail_x) {
3573 $opt_forceregen = 1;
3574 }
3575 }
3576 if (defined($opt_y)) {
3577 $current_thumbnail_y = $opt_y;
3578 if ($current_thumbnail_y != $default_thumbnail_y) {
3579 $opt_forceregen = 1;
3580 }
3581 }
3582 &decide_which_md_to_store();
3583 }
3584
3585 sub decide_which_md_to_store {
3586 if ($current_columns != $default_columns) {
3587 $write_meta_tag{$columnsmetatag}++;
3588 }
3589 else {
3590 undef $write_meta_tag{$columnsmetatag};
3591 }
3592
3593 if ($current_thumbnail_x != $default_thumbnail_x) {
3594 $write_meta_tag{$thumbxmetatag}++;
3595 }
3596 else {
3597 undef $write_meta_tag{$thumbxmetatag};
3598 }
3599
3600 if ($current_thumbnail_y != $default_thumbnail_y) {
3601 $write_meta_tag{$thumbymetatag}++;
3602 }
3603 else {
3604 undef $write_meta_tag{$thumbymetatag};
3605 }
3606
3607 if ($current_titletext ne $default_titletext) {
3608 $write_meta_tag{$titlemetatag}++;
3609 }
3610 else {
3611 undef $write_meta_tag{$titlemetatag};
3612 }
3613
3614 if ($current_reverse ne $do_reverse) {
3615 $write_meta_tag{$reversemetatag}++;
3616 }
3617 else {
3618 undef $write_meta_tag{$reversemetatag};
3619 }
3620 }
3621
3622 sub initialize_current_vars {
3623 $current_columns = $default_columns;
3624 $current_titletext = $default_titletext;
3625 $current_thumbnail_x = $default_thumbnail_x;
3626 $current_thumbnail_y = $default_thumbnail_y;
3627 $current_reverse = $do_reverse;
3628 }
3629
3630 ##############################################################################
3631 #
3632 # Just initialize the 'png' array with UUENCODED PNG files for emoticons. This
3633 # was placed down here so as not to clutter up the top of the file where the
3634 # other globals are initialized.
3635 #
3636 ##############################################################################
3637 sub init_png_array {
3638
3639 $png{'wink'} = <<'EOF';
3640 MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7____,
3641 MS``S,P#___\```#__P!/FRMM`````7123E,`0.;89@````%B2T=$!?AOZ<<`
3642 M``!F241!5'C:;8_1#8`P"$3O@Q'L!CJ!#M`FQP`FL/\J%FJ-)O+U<H&7`P!(
3643 M5N2PN"_)TJESH'I.G6'&XFNB`<UV=5,_*]0.->:R.N?=+?A#36P6)H9!(F)Z
3644 CI1BYC1+=-K2?9J^^SQ<7J48K([9O4:P`````245.1*Y"8((`
3645 `
3646 EOF
3647
3648 $png{'smile'} = <<'EOF';
3649 MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7__P#,
3650 MS`!F9@#_,P````#___]]YKD%````!G123E/______P"SOZ2_`````6)+1T0%
3651 M^&_IQP```&U)1$%4>-I-C\$-P"`,`UV)`?I@@#ZZ03M`*L&?!]E_E=H$(5`>
3652 MEP39#MR]E%+=&T@GD*NPD\A"PWBU@4,VADQ$*J,:OHF'<14(BX]14R#0%J1+
3653 M>!.I(&,I=(!Q3+IT2\\+N2D#A\JP)]ORKBM^`[0;1*VK3]P`````245.1*Y"
3654 "8((`
3655 `
3656 EOF
3657
3658 $png{'frown'} = <<'EOF';
3659 MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````'E!,5$7____,
3660 MS`"9F0!F9@`S,P#,S#/___\S,S,```#__P`[/ZS;`````7123E,`0.;89@``
3661 M``%B2T=$"?'9I>P```"$241!5'C:78^Q#<,P#`19:@1I`WN!`%G`@`<(8"T0
3662 M>`.GE-R8*EV%OVU(24YA@L7A^>"31$3,G*@6!\!7=D$@\(8%!=K)Q&/#]U-4
3663 M=AC>\;&.0I0A:YQVG$FM)\<E`X9X`F/'5A4UK304G0Z&&3->$;-N<-TJEM;0
3664 @UQOZ@DOVMWO_7_P`Y9]*.M\PG><`````245.1*Y"8((`
3665 `
3666 EOF
3667
3668 $png{'video_icon1'} = <<'EOF';
3669 MB5!.1PT*&@H````-24A$4@```!D````="`8```!?>,=U````!F)+1T0`_P#_
3670 M`/^@O:>3````"7!(67,```!(````2`!&R6L^```&$4E$051(Q[V6WXM=5Q7'
3671 M/VN?<^Z],YUDIDF3W#-ITJ9B6S'X$D-2"A6*6,6*A3XI2'WJ@P\JH@511'T3
3672 M6Z$//O<_L(J"H'UH06B)4%O0MEIIDR;IF>F0=I*Y]_S8>Z^U?+CYJ0WVJ0L.
3673 M'-B'\]G[N]9>WP4?0\C-%IJF"4%R>6!Z*-[LF\V-MPOW$4Y1@&M=U_J1($W3
3674 M%"+*='J[7KQP*A2RLRK$PV[YB.`'7*0$B;@)C.>#'3B5;>UM\Y'6];K_7TC3
3675 M-!(D2A6V9!3>.UF$X>LAV,D0..R:UR"7IHH@!!',3;.-SB7;]TSO=SWI++5U
3676 M/?6;0IJF*8)DGY2;HU$X^SVQV7=%9%\(!1`0"4A1D',F6,1T`(]8;HG#I!VJ
3677 MX[_4\N!3$+JZKO/UD'#=NU72KDR*K1^/*O^)%*-]A0@A5(1J%RX37$%01")N
3678 M$=,(>4;I&\M5?.7[(;WS+3R'IFGD?R!-TT@(.8RJ^0]$_`E7QH(A18$4RTA1
3679 M(H5@&.**NN$`GB&4B!A%>N.6T>R/3X3AGU_!;\Q_:)IF%9`BQ).C:O9X**U2
3680 M5Y``Q0KJCND<TQ[7'DT]EGK0%E*'QP%WHPA"9<V>:O:G'X5XYIX;("+2!;&J
3681 ME$N/"Q?WNW:X)0("8@@]ECLDS_$\PZT#[2%U6.S)W4#NY^34HJE%VC>/%A>?
3682 M^^'&V==6KD(<ST7H/S>6\U_U?`DL$4+`0TG.$4T=%CLTS7";(]8M`*G%8H?8
3683 M'-(<B3NX1K"N"MV;CTA\]]@52!D8O.#]SZ/;JV:1HEHF%`'5B%NBL!ZS@:Z;
3684 ML;5U@;5E86ELF$;$$V8#9CUB/9X'4$6XL+N<O7@<>`$@%'IQ=QE/'\6V(8`1
3685 M4#7$$J06U1:1R(6M;7[WAU=X_?6SI*''+:':X3:`#5B."QES#WF.Q#.W7JLN
3686 MB:LBLUU8)LB8`(L=>L1=<<U@F1P3Y\YO\]:9#QC:1.$9<2-X1BX_F.)F&(*;
3687 M7KTKI3C1DT?%801!$L&&Q8YT6%00"6S@7'.)O[YZEB(D'CRQE^6Q(GG`?0#-
3688 MBT)(<5'2Y?QJXDLW-7S04`[@<RP)KOU"AM1CJ<-SQ&//,"1>_OL63S_S-PK[
3689 M)`\>7V,RRGA.>!S(<8Z;XJ($'>RJ7.:63/.`*N0.T@X^[$!L\=PM@-+A/D=0
3690 M<G;^??H2+[VRP<YLAN>.U,[(PPYN+05ST!VRSO6:7*%TBHD).V"ZD"=WN.=%
3691 M8C52A8CF#I',WK62SWYJA2^>V,WN,I/:B.>>@A[U#O6$FR`N[56(AKJ-(S]3
3692 MV$L4WN(ZH+''B8A%+/=HR*A&QB/G@6-K?//A*<?NGE!(AWL"&5`;\)S`#*DF
3693 MJ9CL>^VJ7-/U0Q%9_JVF8F9#0LB$TA#/6$ZX&:Z9(,:MNRKN/KS,X6E%56;`
3694 M,$NX)=PSN(,K4N[]P/=\^5\;[[XC`.7FYOD0Y(.79;3VG+7;7W*-8[,!3P-F
3695 M'=B`6V2ERGSAQ"IW[@O<MLM(<5%5KG&12^_!#9$2&:^_;)-/O.5A<CGQ)NS=
3696 M?W0[RCU/(K)IOFB`P3K$+O>HU+,RCIRXM^+0;8[X@%G$M87<@J7%*2BP<$N.
3697 MY<%GIT=.SNKI?@<(=;UN3;,A:I,WLE:G0RA!,FH=GGI(`];/\7[.2!)B<UQG
3698 M8#N0YPL`!I0@`5DZ\&+8__"S'V):CA6K[Z>E^WYJH6X(%:X#:(M=Z:ZTF'>H
3699 MS?&T#:E=_)OKO$.J"TR._&S]WD>WFHWWY`9(7==>KQ]V'=WU0C]YX!=6'MK&
3700 MP..<'&>8]:BVN,WQ',$$/%QV;P,?`(VL?.8IF3[V/,`5J?[;?JGK@V;5H5_G
3701 MI?L?\]&=KW+9%8M@BSODF87K^>($KB`E,KG]8EB][^<<^O;3TWL>T:;9N,%^
3702 M;SIWO??VG^\NNK]\Q[O37]-+S:T^>PW/LVNPXA8H]T26CIP*Z]_X5;%\Q^_S
3703 MZ+!.U^^X^;1R?31-(W5=^^;Y?Y1B_8/>;SX4WO_-'IV_5;@R1ML9XX/;['_H
3704 M%,N??KX^<O_FYL9&,'?JNK:/"KF2*VN:S0)R*?D"KMU"*5<(8V&T%\*2X![K
3705 =>OJAT^/'%O\!>',9BLN`3D\`````245.1*Y"8((`
3706 `
3707 EOF
3708
3709 $png{'video_icon2'} = <<'EOF';
3710 MB5!.1PT*&@H````-24A$4@```"0````D"`,```#6WFBJ```!LU!,5$4````<
3711 M&!(F#0\L$@PP(AA((QY6+BAD.C9S0C8L&DLN'5TL(4,[)%@F*FP])VX[,VE(
3712 M,VE(.71G1D)T5DUX6UAQ9EE:27:$@701,I`Y)H4W,X0_,I4<.*8</;0G.JDQ
3713 M.:P[.;5'*H5+-(M%,951.YI*+J%&.*M2-J9)-[E7/+T>1:LL0:L\4:PH1[,U
3714 M2+<]6;Y&08M318U#2Y)=49%B68QS:I1%1JM41ZM;6J9'1;)72+5#4[A75+Q=
3715 M:+)J:*A@:K)./<M9/L17/=DL4<1$2L=:1L984L)<1M=<4M=A2<YG5LEA2]EG
3716 M5-M'8\5;<LA.;-5===5E:\-E><ET>\=A8-1L>=1W=]!>1NIC3>IF5N=K5/%B
3717 M9>A^;.]]?N!L:?MN?/5]B+1H@<YYA<1IA-)VA]9VEMI?I-Y^J-]F@>EXF.%X
3718 MK^Y_ONQ\M_%_P>^&D(F%HI2&LJ*'P:^!E,>$F-F4DMB&MLR(H]R"E>N"JN:#
3719 MMN>%J_.&MO*TM.2&RL.&Q]2(U]J,XMZ6XMR&Q>R(U.R6WN.%R/:'V/><V/.@
3720 MUOR(Y?:.\O:5_/RHY_*D_?G,V_`,`@3___]7$.SF````D7123E/_________
3721 M____________________________________________________________
3722 M____________________________________________________________
3723 M____________________________________________________________
3724 M__\`,OEAFP````%B2T=$D'@*C@P````)<$A9<P```&(```!B`(^5@48````)
3725 M=G!!9P```"0````D`'@'&X\```.E241!5#C+;93]0]I&&,?3JM6YRD3F"A,0
3726 M8AF$F=IUFQ:*N@1S`1+95FI!W!Q4,EHP>Z$;PV80&LC!G6SP)^\"UFK7[]T/
3727 ME\LGS_-<GGL>:O2.#A,)&1Q<WZ.N/HBR*,OYG'P$B-X/$4+*BWE+V40";('_
3728 M0X?R43[.NYUN,D/.]40^]]88=>DIG]KSNCV;GG669=U.IX_/9:/U:Y`L9Z/.
3729 ML'/CZ5]U,DJ[,=;C9K+9"Y?4A1W9%V;CB@9[:##H=HSZMW'6[4MD)RZI"9-@
3730 MV4>[+V%O,.CW!_TN[IT]VU]?#8D36Q8DR9(0WDB7.SV(^H0;=/L]:);V-\(^
3731 M,0]*$T@^W/1&]A7#T"'A.JB+=60:Y4P:A'EQ+SJ&4H\%[T:ZV#!,J'<[`X@@
3732 MTO66H2G%@[B7$:-_6%!2C+#IDYIA:%#'&`]>(]Q%J&,:RDEZW2?D`(%D272S
3733 MWREG!H00X=\<<TM$CC_UEEE6BK%O1%'\FQK]&/5Z]ZOEEJ%"%6''G5:SV?S=
3734 M\6L+FF9%V0Z)N<VGU.@).?[/U;$AC)MSO[2;3;UYYV-=[QCE<C'"\+DM:I3R
3735 M>NZ5E"K46_B\.CTS-1P.;PYGII9T9+RJO-A>%<5C*I?XW+-?,LVV8UAHW[AQ
3736 M<WBAZ=D>JIXVTF$N;T%>;Z*BJ>WEH6-Y[I(A%&[!BO(\XN,!)?[@]MX[M4(>
3737 M7B$LZ#6$6OED+R`!2CADW0+)+)X;7M?T.83FV>.(B^.IT?>`!B].$5X:%IJW
3738 M9L9ACS751K!5.5@00D?4Z!'P?Z:8$!=F_SEWW+(.9V$SLP6$H/D*^),,H$9Q
3739 ML+9RH))\M+':_O`RKF6$$=2>/0PP)/"1$%U;>0)5%:MDMT-22P91FW@SM>)*
3740 MB.>.K=S=]3-UDWQH8>0-20_&*B)+M?)P-<B`?\DMD`3Z;LF`"!%C*E)-DAYB
3741 MU^*?[ZZYN)QL714YN44_*)#<C2EB":F3:61H6RB5/Y[<3"'H_[*F01*8BDTK
3742 M.,N9:I8_672EI..+0DA&Z4_O*V?DYXT'A%6R-&K^11?'_/2F6F0A22_8=TH5
3743 M0S,TTX2&J6J-K^TV.R<PX++NY*1`VQ?H[5JCKFF52KE1VZ%M-COCXQ)7*E@2
3744 MA.!'MVV+][_*9#([.P]<MVWV`",P\K5>($O1+^A%OVW^`TOS\RY[@).V4N]T
3745 M%0#D:##@\MML?I?=%0PP63[YIJU<Z4\`2#$NRC'!32Z4E*38R_=W.@'$>%)A
3746 =$L^#&,B\W?\/+ROZRK9):IT`````245.1*Y"8((`
3747 `
3748 EOF
3749
3750 }
0 .\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
1 .\"
2 .\" Standard preamble:
3 .\" ========================================================================
4 .de Sh \" Subsection heading
5 .br
6 .if t .Sp
7 .ne 5
8 .PP
9 \fB\\$1\fR
10 .PP
11 ..
12 .de Sp \" Vertical space (when we can't use .PP)
13 .if t .sp .5v
14 .if n .sp
15 ..
16 .de Vb \" Begin verbatim text
17 .ft CW
18 .nf
19 .ne \\$1
20 ..
21 .de Ve \" End verbatim text
22 .ft R
23 .fi
24 ..
25 .\" Set up some character translations and predefined strings. \*(-- will
26 .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
27 .\" double quote, and \*(R" will give a right double quote. | will give a
28 .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
29 .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
30 .\" expand to `' in nroff, nothing in troff, for use with C<>.
31 .tr \(*W-|\(bv\*(Tr
32 .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
33 .ie n \{\
34 . ds -- \(*W-
35 . ds PI pi
36 . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
37 . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
38 . ds L" ""
39 . ds R" ""
40 . ds C` ""
41 . ds C' ""
42 'br\}
43 .el\{\
44 . ds -- \|\(em\|
45 . ds PI \(*p
46 . ds L" ``
47 . ds R" ''
48 'br\}
49 .\"
50 .\" If the F register is turned on, we'll generate index entries on stderr for
51 .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
52 .\" entries marked with X<> in POD. Of course, you'll have to process the
53 .\" output yourself in some meaningful fashion.
54 .if \nF \{\
55 . de IX
56 . tm Index:\\$1\t\\n%\t"\\$2"
57 ..
58 . nr % 0
59 . rr F
60 .\}
61 .\"
62 .\" For nroff, turn off justification. Always turn off hyphenation; it makes
63 .\" way too many mistakes in technical documents.
64 .hy 0
65 .if n .na
66 .\"
67 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
68 .\" Fear. Run. Save yourself. No user-serviceable parts.
69 . \" fudge factors for nroff and troff
70 .if n \{\
71 . ds #H 0
72 . ds #V .8m
73 . ds #F .3m
74 . ds #[ \f1
75 . ds #] \fP
76 .\}
77 .if t \{\
78 . ds #H ((1u-(\\\\n(.fu%2u))*.13m)
79 . ds #V .6m
80 . ds #F 0
81 . ds #[ \&
82 . ds #] \&
83 .\}
84 . \" simple accents for nroff and troff
85 .if n \{\
86 . ds ' \&
87 . ds ` \&
88 . ds ^ \&
89 . ds , \&
90 . ds ~ ~
91 . ds /
92 .\}
93 .if t \{\
94 . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
95 . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
96 . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
97 . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
98 . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
99 . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
100 .\}
101 . \" troff and (daisy-wheel) nroff accents
102 .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
103 .ds 8 \h'\*(#H'\(*b\h'-\*(#H'
104 .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
105 .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
106 .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
107 .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
108 .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
109 .ds ae a\h'-(\w'a'u*4/10)'e
110 .ds Ae A\h'-(\w'A'u*4/10)'E
111 . \" corrections for vroff
112 .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
113 .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
114 . \" for low resolution devices (crt and lpr)
115 .if \n(.H>23 .if \n(.V>19 \
116 \{\
117 . ds : e
118 . ds 8 ss
119 . ds o a
120 . ds d- d\h'-1'\(ga
121 . ds D- D\h'-1'\(hy
122 . ds th \o'bp'
123 . ds Th \o'LP'
124 . ds ae ae
125 . ds Ae AE
126 .\}
127 .rm #[ #] #H #V #F C
128 .\" ========================================================================
129 .\"
130 .IX Title "IMAGEINDEX 1"
131 .TH IMAGEINDEX 1 "2007-03-30" "perl v5.8.8" "User Contributed Perl Documentation"
132 .SH "NAME"
133 imageindex \- a digital photo gallery tool
134 .SH "SYNOPSIS"
135 .IX Header "SYNOPSIS"
136 Imageindex is a digital picture gallery program. It provides automatic
137 generation of thumbnails and other size views of the images and video files,
138 and W3C compliant \s-1HTML\s0 to allow viewing of the thumbnails and images or videos.
139 It also creates montages of all images in a given directory to be used in
140 directory entries within the \s-1HTML\s0.
141 .SH "DESCRIPTION"
142 .IX Header "DESCRIPTION"
143 Imageindex has evolved from a simple thumbnail-generation program into a
144 full-blown gallery application. With it you can create static thumbnails and
145 \&\*(L"medium\*(R" views (good for dial-up web viewers) along with static \s-1HTML\s0 which
146 presents the images in chronological order (based upon the date in the Exif
147 header or date stamp of the file itself). Support has now been added for
148 animated GIFs and video files (we support whatever file formats your
149 installation of mplayer supports).
150 .PP
151 Imageindex creates static rather than dynamic content for many reasons. First
152 and foremost it is much easier to archive pictures onto CD-ROMs on a periodic
153 basis when the content is just \*(L"there\*(R" rather than \*(L"trapped\*(R" in a database of
154 some sort. You create your own \*(L"database\*(R" with your own directories and let
155 imageindex handle the presentation of the images to the world. Imageindex will
156 use \*(L"montages\*(R" (or thumbnails of all your thumbnails crammed together) of
157 subdirectories if they occur alongside images (see the the sample pages on the
158 imageindex website \- http://www.edwinh.org/imageindex/sample.html).
159 .PP
160 There are four basic \*(L"views\*(R" that imageindex creates (enabled by default). The
161 \&\*(L"index\*(R" view shows thumbnails of all images in a directory in a basic table
162 format (defaults to 3 columns). Within each cell of the table basic information
163 such as the date/time of the picture are given as well as any comment (or
164 caption) present in the image. Links to the various sizes of image and other
165 views for the image are also presented by default.
166 .PP
167 The second view presented is the \*(L"slide show\*(R" view. In this view the \*(L"medium\*(R"
168 sized image is presented along with information such as date/time, any caption
169 embedded in the image, etc. Currently for video files, the 'medium' slides only
170 point to the actual video file processed. Future versions will allow for direct
171 playback inside the browser (much like YouTube). There are \*(L"previous\*(R" and
172 \&\*(L"next\*(R" links on each page which let the viewer quickly cycle through each
173 \&\*(L"medium\*(R" image without having to constantly invoke the browser's \*(L"back\*(R" button.
174 .PP
175 Captions for video files can be created by creating a file with the same
176 basename as the original video file but having \*(L".txt\*(R" as the extension. The
177 contents of this file will be used just like captions embedded in formats like
178 \&\s-1JPG\s0 when processing the resultant \s-1HTML\s0.
179 .PP
180 In conjunction with the \*(L"slide show\*(R" view there is a \*(L"frame view\*(R". When enabled
181 a link to the frame view appears at the top of the index view's page. When the
182 frame view is visited, the browser's pane splits into two portions. On the left
183 all the thumbnails are lined up close together. On the right hand portion the
184 same \*(L"slide show\*(R" pages are loaded. As the user clicks on a thumbnail on the
185 left, it's \*(L"slide\*(R" view (including the \*(L"medium\*(R" image) is displayed in the
186 right had side of the frame. This creates a very convenient mechanism for
187 browsing through many images.
188 .PP
189 Finally a \*(L"details\*(R" view exists. This details view is much like the index view
190 as the thumbnails (reduced in size further) are presented in a table format,
191 but much more information is presented in each cell. This is very useful for
192 images that come from digital cameras where Exif headers have filled with lots
193 of neat information.
194 .PP
195 The \s-1HTML\s0 output of imageindex can be customized by creating a
196 \&\*(L".imageindexrc\*(R" in your \f(CW$HOME\fR directory and placing certain variables (see
197 \&\s-1VARIABLES\s0 section) in that file and editing to your taste. One of the variables
198 controls the output of a cascading style sheet which ultimately directs your
199 browser how to render the \s-1HTML\s0. All color, font, indenting, etc. changes you
200 wish to make can be done in this style sheet variable.
201 .SH "VARIABLES"
202 .IX Header "VARIABLES"
203 When you create your \*(L".imageindexrc\*(R" file in your \f(CW$HOME\fR directory, you can put
204 any or all of the following variables in there and tweak as needed. This is
205 Perl code itself and is subsequently \*(L"included\*(R" into imageindex as it runs.
206 .PP
207 You \fBmust\fR end the file by putting a \*(L"1;\*(R" at the end of it. It's a Perl thing!
208 .PP
209 The values you see in these examples are the program defaults. If you like the
210 default value of a particular variable you don't need to include it in your
211 \&\*(L".imageindexrc\*(R" file.
212 .PP
213 Name of the directory that holds thumbnail images
214 .PP
215 .Vb 1
216 \& $thumbnail_dir = 'thumbnail';
217 .Ve
218 .PP
219 Size of the thumbnail images in the \*(L"x\*(R" direction (pixels). Note that
220 imageindex preserves the aspect ratio of an image when it is reduced from its
221 original size to form a thumbnail image. So, if the \*(L"x\*(R" dimension is smaller
222 than the \*(L"y\*(R" dimension, a thumbnail might have an \*(L"x\*(R" size smaller than
223 \&\f(CW$default_thumbnail_x\fR.
224 .PP
225 .Vb 1
226 \& $default_thumbnail_x = 200;
227 .Ve
228 .PP
229 Size of the thumbnail images in the \*(L"y\*(R" direction (pixels). Again, note that
230 imageindex preserves the aspect ratio of an image when it is reduced from its
231 original size to form a thumbnail image. So, if the \*(L"y\*(R" dimension is smaller
232 than the \*(L"x\*(R" dimension, a thumbnail might have an \*(L"y\*(R" size smaller than
233 \&\f(CW$default_thumbnail_y\fR.
234 .PP
235 .Vb 1
236 \& $default_thumbnail_y = 200;
237 .Ve
238 .PP
239 If both dimensions of the original are within this much of the thumbnail
240 dimensions we will skip the thumbnail and just use the original as the
241 \&\*(L"thumbnail.\*(R"
242 .PP
243 .Vb 1
244 \& $thumbnail_threshold = 1.0;
245 .Ve
246 .PP
247 Size of the \*(L"medium\*(R" images in the \*(L"x\*(R" direction (pixels). Note that
248 imageindex preserves the aspect ratio of an image when it is reduced from its
249 original size to form a \*(L"medium\*(R" image. So, if the \*(L"x\*(R" dimension is smaller
250 than the \*(L"y\*(R" dimension, a \*(L"medium\*(R" image might have an \*(L"x\*(R" size smaller than
251 \&\f(CW$med_x\fR.
252 .PP
253 .Vb 1
254 \& $med_x = 800;
255 .Ve
256 .PP
257 Size of the \*(L"medium\*(R" images in the \*(L"y\*(R" direction (pixels). Note that
258 imageindex preserves the aspect ratio of an image when it is reduced from its
259 original size to form a \*(L"medium\*(R" image. So, if the \*(L"y\*(R" dimension is smaller
260 than the \*(L"x\*(R" dimension, a \*(L"medium\*(R" image might have an \*(L"y\*(R" size smaller than
261 \&\f(CW$med_y\fR.
262 .PP
263 .Vb 1
264 \& $med_y = 600;
265 .Ve
266 .PP
267 Name of the directory that holds \*(L"medium\*(R" images
268 .PP
269 .Vb 1
270 \& $med_dir = 'medium';
271 .Ve
272 .PP
273 If both dimensions of the original are within this much of the \*(L"medium\*(R"
274 dimensions we will skip creating the medium-size format and just use the
275 original. This saves needless creating a \*(L"medium\*(R" image if it's close in size
276 to the original already.
277 .PP
278 .Vb 1
279 \& $med_threshold = 1.6;
280 .Ve
281 .PP
282 Automatically recurse into subdirectories? Set to 1 to enable.
283 .PP
284 .Vb 1
285 \& $do_recurse = 0;
286 .Ve
287 .PP
288 Generate \*(L"medium\*(R" images at all? Set to 1 to enable.
289 .PP
290 .Vb 1
291 \& $do_medium = 1;
292 .Ve
293 .PP
294 Generate the \*(L"slide show\*(R" and frame view? Set to 1 to enable.
295 .PP
296 .Vb 1
297 \& $do_slide = 1;
298 .Ve
299 .PP
300 Generate the \*(L"details\*(R" view? Set to 1 to enable.
301 .PP
302 .Vb 1
303 \& $do_captions = 1;
304 .Ve
305 .PP
306 Use/display caption info stored in images? Set to 1 to enable.
307 .PP
308 .Vb 1
309 \& $do_detail = 1;
310 .Ve
311 .PP
312 Process subdirectories as entries in the normal \*(L"index\*(R" and \*(L"details\*(R" views?
313 Set to 1 to enable. If an entire directory hierarchy has been processed with
314 \&\f(CW$do_montage\fR set to 1 (see below), the montage file for a given directory will
315 be used as the \*(L"thumbnail\*(R" for a subdirectory.
316 .PP
317 .Vb 1
318 \& $do_dirs = 1;
319 .Ve
320 .PP
321 Create a montage of all the images? When enabled all of the images that are
322 processed are turned into an NxM montage of very small thumbnails in a tiled
323 pattern. The resulting image is shrunk to the \f(CW$default_thumbnail_x\fR x
324 \&\f(CW$default_thumbnail_y\fR dimensions and stored in the \f(CW$thumbnail_dir\fR directory. The
325 size of the tiles grows as the number of images in a directory increase, but
326 can be bounded by variables outlined below. Set to 1 to enable.
327 .PP
328 .Vb 1
329 \& $do_montage = 1;
330 .Ve
331 .PP
332 Map \s-1ASCII\s0 \*(L"smiley\*(R" patterns embedded within an image's comment into real
333 \&\*(L"emoticon\*(R" images? When enabled the \s-1ASCII\s0 smiley faces such as :) and :\-), the
334 winks ;) and ;\-), and the frowns :( and :\-( are mapped to small \s-1PNG\s0 images that
335 display the emotion conveyed. Set to 1 to enable.
336 .PP
337 .Vb 1
338 \& $do_emoticons = 1;
339 .Ve
340 .PP
341 Sort timestamps in reverse order.
342 .PP
343 .Vb 1
344 \& $do_reverse = 1;
345 .Ve
346 .PP
347 Process video files. This relies on a fairly recent version of mplayer being
348 installed on your system. We've tested with up to 0.99.8. The kind of video
349 files that are supported are up to the codecs that are compiled and used with
350 mplayer installation on your system.
351 .PP
352 .Vb 1
353 \& $do_video_files = 1;
354 .Ve
355 .PP
356 Overlay a small icon into one of the corners of the thumbnail and medium views
357 when processing the first frame of a video file. This gives a \*(L"visual cue\*(R" that
358 the file being represented in your browser is a video file and not a still
359 image.
360 .PP
361 .Vb 1
362 \& $do_video_thumbnail_icons = 1;
363 .Ve
364 .PP
365 Use the following as a regular expression to identify video files by their
366 extension. For certain technical reasons it was more feasible to rely upon this
367 quick and effective method. If files from your camera (or whatever) end in a
368 different extension, just put that extension here too.
369 .PP
370 .Vb 1
371 \& $video_regexp = '(avi|mov|mpg|mpeg|mjpeg|m1v|m2v|wmv|fli|nuv|vob|ogm|vcd|svcd|mp4|qt)';
372 .Ve
373 .PP
374 If you enable the \*(L"visual cue\*(R" icons for video files mentioned above, the
375 following variable determines which corner it is placed. Acceptable values are:
376 SouthWest, NorthWest, NorthEast, SouthEast (case sensitive!).
377 .PP
378 .Vb 1
379 \& $video_icon_gravity = 'SouthWest';
380 .Ve
381 .PP
382 If you enable the \*(L"visual cue\*(R" icons for video files mentioned above, there are
383 two to pick from (currently). Set to 1 (default) for a yellow dot with a 'play'
384 arrow. Set to 2 for a purplish icon of a video camera. More of these will be
385 created in further releases.
386 .PP
387 .Vb 1
388 \& $video_icon = 1;
389 .Ve
390 .PP
391 The following three variables control what hyperlinks in the \s-1HTML\s0 output
392 \&\*(L"point\*(R" to. They can be set to the following:
393 .PP
394 .Vb 7
395 \& index : points to the name reference for an image in the index view
396 \& fullsize : points to the actual image itself
397 \& medium : points to the "medium" version of an image
398 \& thumbnail : points to the thumbnail version of an image
399 \& slide : points to the "slide show" HTML page written for an image
400 \& details : points to the name reference for an image in the details
401 \& view
402 .Ve
403 .PP
404 The folling variable controls what the hyperlink for the thumbnail image in the
405 index view points to:
406 .PP
407 .Vb 1
408 \& $index_linkto = 'slide';
409 .Ve
410 .PP
411 The folling variable controls what the hyperlink for the thumbnail image in the
412 details view points to:
413 .PP
414 .Vb 1
415 \& $details_linkto = 'index';
416 .Ve
417 .PP
418 The folling variable controls what the hyperlink for the \*(L"medium\*(R" image in the
419 slide view points to:
420 .PP
421 .Vb 1
422 \& $slide_linkto = 'fullsize';
423 .Ve
424 .PP
425 Default number of columns to use in the index and detail views
426 .PP
427 .Vb 1
428 \& $default_columns = 3;
429 .Ve
430 .PP
431 Set the orientation of slide frame \- 'horizontal' or 'vertical'. When 'vertical'
432 the browser pane will split vertically with all the thumbnails towards the
433 left. When 'horizontal' the browser pane splits horizontally with the
434 thumbnails arranged in the upper portion
435 .PP
436 .Vb 1
437 \& $frame_orient = 'vertical';
438 .Ve
439 .PP
440 The following two variables can be set to any of the following three values:
441 .PP
442 .Vb 4
443 \& top : put the item in question at the top of the page when rendered
444 \& bottom : put the item in question at the bottom of the page when
445 \& rendered
446 \& none : omit the item from the HTML output
447 .Ve
448 .PP
449 Controls if an image caption (or comment) embedded in the image will be
450 retrieved and written into the \s-1HTML\s0 output. By default it is written above the
451 \&\*(L"medium\*(R" image presented in the \*(L"slide\*(R" view.
452 .PP
453 .Vb 1
454 \& $slide_caption = 'top';
455 .Ve
456 .PP
457 Controls if the date/time of an image is written into the \s-1HTML\s0 output. By
458 default it is written below the \*(L"medium\*(R" image presented in the \*(L"slide\*(R" view.
459 .PP
460 .Vb 1
461 \& $slide_date = 'bottom';
462 .Ve
463 .PP
464 In the \*(L"detail\*(R" view, the thumbnail images are shrunk to a size smaller than
465 the normal thumbnails (to conserve valueable space). This sets the number of
466 times they are shrunk. By default it is shrunk by a factor of 2.
467 .PP
468 .Vb 1
469 \& $detailshrink = 2;
470 .Ve
471 .PP
472 The thumbnail and \*(L"medium\*(R" images are written out as \s-1JPEG\s0 files (even if the
473 original images were not \s-1JPEG\s0's). The following two variables control the
474 \&\*(L"quality\*(R" for generated images. The value can range from 0 to 100 where 100
475 means \*(L"don't lose any quality in favor of file size.\*(R"
476 .PP
477 Adjust the quality of the thumbnails being written out
478 .PP
479 .Vb 1
480 \& $thumb_quality = 50;
481 .Ve
482 .PP
483 Adjust the quality of the \*(L"medium\*(R" images written out
484 .PP
485 .Vb 1
486 \& $med_quality = 80;
487 .Ve
488 .PP
489 Adjust the minimum number of tiles that will be found in a montage image. If
490 the number of images in a directory is lower than this value, images will
491 either be repeated or blanks will be inserted (see \f(CW$montage_fill\fR).
492 .PP
493 .Vb 1
494 \& $montage_min = 4;
495 .Ve
496 .PP
497 Adjust the maximum number of tiles that will be found in a montage. If the
498 number of images in a directory is higher than this number, the montage will be
499 made by \*(L"evenly picking\*(R" \f(CW$montage_max\fR images in the directory and only using
500 those.
501 .PP
502 .Vb 1
503 \& $montage_max = 36;
504 .Ve
505 .PP
506 Adjust the space between montage images (pixels).
507 .PP
508 .Vb 1
509 \& $montage_whitespace = 2;
510 .Ve
511 .PP
512 When there is not a \*(L"even\*(R" number of images in a directory and a complete NxM
513 tile montage cannot be formed, images can be used again or empty space can be
514 used. Set to 'repeat' to re-use images and 'blank' to use empty space.
515 .PP
516 .Vb 1
517 \& $montage_fill = 'blank';
518 .Ve
519 .PP
520 The following variable controls all aspects of how the \s-1HTML\s0 output is rendered
521 in standards compliant browsers. The contents of this variable will be written
522 out into a cascading style sheet and the properties found within it will govern
523 how the \s-1HTML\s0 is rendered. All color, font, size, alignment, etc. property
524 changes can take place. This might require some knowledge of cascading style
525 sheets. A good primer can be found here:
526 .PP
527 .Vb 1
528 \& http://www.w3schools.com/css/css_reference.asp
529 .Ve
530 .PP
531 .Vb 2
532 \& $stylesheet = '
533 \& body { color: black; background: white; }
534 .Ve
535 .PP
536 .Vb 7
537 \& /* Fonts in the title */
538 \& h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif;
539 \& font-size: 200%; font-weight: bold; text-align: center; }
540 \& h2.daterange { font-family: Arial,Helvetica,sans-serif;
541 \& font-size: 125%; text-align: center; }
542 \& h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%;
543 \& text-align: center; }
544 .Ve
545 .PP
546 .Vb 3
547 \& /* Photo captions & Directory titles */
548 \& div.caption { font-family: Arial,Helvetica,sans-serif;
549 \& font-size: 100%; font-weight: bold; margin: 1em; }
550 .Ve
551 .PP
552 .Vb 7
553 \& /* Overall fonts on the index and details page */
554 \& div.index { font-family: Arial,Helvetica,sans-serif;
555 \& font-size: 80%; }
556 \& div.detail { font-family: Arial,Helvetica,sans-serif;
557 \& font-size: 80%; }
558 \& div.credits { font-family: Arial,Helvetica,sans-serif;
559 \& font-size: 80%; text-align: right; margin: 10px }
560 .Ve
561 .PP
562 .Vb 6
563 \& /* Table attributes */
564 \& table.index { background: #ffffff; border: none;
565 \& border-spacing: 8px; }
566 \& td.index { border: none; padding: 3px }
567 \& table.frame { background: #ffffff; border: none }
568 \& td.frame { border: none; padding: 0px }
569 .Ve
570 .PP
571 .Vb 4
572 \& /* Image attributes */
573 \& img.index { border: none; }
574 \& img.slide { border: none; }
575 \& img.frame { border: none; }
576 .Ve
577 .PP
578 .Vb 5
579 \& /* Link attributes */
580 \& a:link { color: blue; }
581 \& a:visited { color: green; }
582 \& a:hover { color: red; }
583 \& a:active { color: red; }
584 .Ve
585 .PP
586 .Vb 1
587 \& ';
588 .Ve
589 .PP
590 Adjust what is presented in \*(L"empty\*(R" table cells when there are not an \*(L"even\*(R"
591 number of images in a directory.
592 .PP
593 .Vb 1
594 \& $emptycell = "<I>empty</I>";
595 .Ve
596 .PP
597 Control the text of a hyperlink to a parent directory. If you do not desire
598 that this link be present in the index and \*(L"details\*(R" views \*(L"undef\*(R" the variable
599 (undef \f(CW$updirtext\fR;)
600 .PP
601 .Vb 1
602 \& $updirtext = "up one directory";
603 .Ve
604 .PP
605 Control the text of a hyperlink to the frame view. If you do not desire
606 that this link be present in the index and \*(L"details\*(R" views \*(L"undef\*(R" the variable
607 (undef \f(CW$framelinktext\fR;)
608 .PP
609 .Vb 1
610 \& $framelinktext = "slideshow view (frames)";
611 .Ve
612 .PP
613 Control the text of a hyperlink to the detail view. If you do not desire
614 that this link be present in the index view \*(L"undef\*(R" the variable
615 (undef \f(CW$detaillinktext\fR;)
616 .PP
617 .Vb 1
618 \& $detaillinktext = "details index";
619 .Ve
620 .PP
621 Control the text of a hyperlink to the index view. If you do not desire
622 that this link be present in the detail view \*(L"undef\*(R" the variable
623 (undef \f(CW$indexlinktext\fR;)
624 .PP
625 .Vb 1
626 \& $indexlinktext = "main index";
627 .Ve
628 .PP
629 Control the default \s-1TITLE\s0 string written out in the \s-1HTML\s0 for a given
630 directory. This is most usually given on a per-directory basis via the command
631 line and \*(L"remembered\*(R" within \s-1META\s0 data inside the index \s-1HTML\s0 file itself.
632 .PP
633 .Vb 1
634 \& $default_titletext = "Image directory";
635 .Ve
636 .PP
637 The following five variables control the \s-1TITLE\s0 attribute on anchor constructs
638 in the index and frame views. When \s-1TITLE\s0 attributes are given they are usually
639 rendered as \*(L"tooltip\*(R" bubbles that show text when a cursor hovers and stops
640 over the active link. We use them here to give a visual cue about the image.
641 These variables work much like \fIprintf\fR\|(1) strings. The values that can be
642 interpolated for a given image are:
643 .PP
644 .Vb 6
645 \& %f => replaced with the filename of the image
646 \& %d => replaced with the date/time of the image (or mtime of the file)
647 \& %s => replaced with the size of the file (in Kb)
648 \& %r => replaced with the resolution (XxY) of the original image
649 \& %c => replaced with the image's caption (if stored with one)
650 \& %% => replaced with a literal '%' character
651 .Ve
652 .PP
653 The following codes are interpolated when directories are processed and a
654 montage of that directory is used as the thumbnail of the subdirectory.
655 .PP
656 .Vb 4
657 \& %n => replaced with number of images in a directory
658 \& %b => replaced with the "begin" date from a directory of images
659 \& %e => replaced with the "end" date from a directory of images
660 \& %t => replaced with the "title" from a directory of images
661 .Ve
662 .PP
663 Other characters (including spaces) are literal. \*(L"undef\*(R" these in your
664 \&\*(L".imageindexrc\*(R" file if you don't want the \s-1TITLE\s0 attributes to be written into
665 the \s-1HTML\s0. The \*(L"date/time\*(R" related constructs are interpolated using the
666 date/time format variables defined below.
667 .PP
668 Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the
669 frame view. The default is \*(L"<filename> \- <date>\*(R" for an image
670 .PP
671 .Vb 1
672 \& $framethumbtitle = "%f - %d";
673 .Ve
674 .PP
675 Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the
676 index view. The default is \*(L"<filename> (<size>)\*(R" for an image
677 .PP
678 .Vb 1
679 \& $indexthumbtitle = "%f (%s)";
680 .Ve
681 .PP
682 Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the
683 slide view. The default is \*(L"<filename> (<size>)\*(R" for an image
684 .PP
685 .Vb 1
686 \& $slidethumbtitle = "%f (%s)";
687 .Ve
688 .PP
689 Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the
690 detail view. The default is caption (or comment) of an image if one was
691 embedded within it.
692 .PP
693 .Vb 1
694 \& $detailthumbtitle = "%c";
695 .Ve
696 .PP
697 Control the \s-1TITLE\s0 attributes for hyperlinks to montage images within the
698 index view when a subdirectory is being presented. The default is to show how
699 many images the subdirectory had and the date range that is spanned.
700 .PP
701 .Vb 1
702 \& $montagetitle = "%n images %b through %e";
703 .Ve
704 .PP
705 The following eight variables control how dates and times are formatted when
706 written into the \s-1HTML\s0. Again we're using \fIprintf\fR\|(1)\-like variables where codes
707 are interpolated according to a user's taste.
708 .PP
709 The definitions of the escape sequences come from the \s-1POSIX\s0 \fIstrftime\fR\|(3)
710 definitions. \s-1NOT\s0 \s-1ALL\s0 of \fIstrftime\fR\|(3) are supported for obvious reasons.
711 .PP
712 .Vb 8
713 \& %S is replaced by the second as a decimal number (00-60).
714 \& %M is replaced by the minute as a decimal number (00-59).
715 \& %I is replaced by the hour (12-hour clock) as a decimal number (01-12).
716 \& %H is replaced by the hour (24-hour clock) as a decimal number (00-23).
717 \& %p is replaced by national representation of either "ante meridiem" or
718 \& "post meridiem" as appropriate (currently only U.S. "am" or "pm")
719 \& %R is equivalent to "%H:%M" (in *timeformat variables only).
720 \& %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only).
721 .Ve
722 .PP
723 .Vb 7
724 \& %Y is replaced by the year with century as a decimal number.
725 \& %y is replaced by the year without century as a decimal number (00-99).
726 \& %m is replaced by the month as a decimal number (01-12).
727 \& %d is replaced by the day of the month as a decimal number (01-31).
728 \& %F is equivalent to "%Y-%m-%d" (in *dateformat variables only).
729 \& %D is equivalent to "%m/%d/%y" (in *dateformat variables only).
730 \& %% is replaced by a literal "%".
731 .Ve
732 .PP
733 Control the way the date is formed in the frame view
734 .PP
735 .Vb 1
736 \& $framedateformat = "%m/%d/%Y";
737 .Ve
738 .PP
739 Control the way the time is formed in the frame view
740 .PP
741 .Vb 1
742 \& $frametimeformat = "%r";
743 .Ve
744 .PP
745 Control the way the date is formed in the index view
746 .PP
747 .Vb 1
748 \& $indexdateformat = "%m/%d/%Y";
749 .Ve
750 .PP
751 Control the way the time is formed in the index view
752 .PP
753 .Vb 1
754 \& $indextimeformat = "%r";
755 .Ve
756 .PP
757 Control the way the date is formed in the slide view
758 .PP
759 .Vb 1
760 \& $slidedateformat = "%m/%d/%Y";
761 .Ve
762 .PP
763 Control the way the time is formed in the slide view
764 .PP
765 .Vb 1
766 \& $slidetimeformat = "%r";
767 .Ve
768 .PP
769 Control the way the date is formed in the detail view
770 .PP
771 .Vb 1
772 \& $detaildateformat = "%m/%d/%Y";
773 .Ve
774 .PP
775 Control the way the date is formed in the detail view
776 .PP
777 .Vb 1
778 \& $detailtimeformat = "%I:%M %p";
779 .Ve
780 .PP
781 Control what the index view's \s-1HTML\s0 filename will be
782 .PP
783 .Vb 1
784 \& $indexfile = 'index.html';
785 .Ve
786 .PP
787 Control what the detail view's \s-1HTML\s0 filename will be
788 .PP
789 .Vb 1
790 \& $detailfile = 'details.html';
791 .Ve
792 .PP
793 Control what the frame view's \s-1HTML\s0 filename will be
794 .PP
795 .Vb 1
796 \& $framefile = 'frame.html';
797 .Ve
798 .PP
799 Control what the slide view's \s-1HTML\s0 filename will be
800 .PP
801 .Vb 1
802 \& $slidefile = 'slides.html';
803 .Ve
804 .PP
805 Control the name of the directory where all the \*(L"slide view\*(R" \s-1HTML\s0 files will be
806 deposited (one per image)
807 .PP
808 .Vb 1
809 \& $slide_dir = 'slides';
810 .Ve
811 .PP
812 Control the name of the cascading style sheet written out in each directory
813 .PP
814 .Vb 1
815 \& $stylefile = 'style.css';
816 .Ve
817 .PP
818 Control the name of the montage image if enabled
819 .PP
820 .Vb 1
821 \& $montagefile = 'montage.jpg';
822 .Ve
823 .PP
824 Control the prefix of the emoticon \s-1PNG\s0 image filenames
825 .PP
826 .Vb 1
827 \& $emoticonprefix = 'ii_';
828 .Ve
829 .SH "EXAMPLES"
830 .IX Header "EXAMPLES"
831 As an example, suppose you just want to change some date/time format strings. A
832 complete \*(L".imageindexrc\*(R" file in this case would be:
833 .PP
834 .Vb 2
835 \& $framedateformat = "%F";
836 \& $frametimeformat = "%R";
837 .Ve
838 .PP
839 .Vb 2
840 \& $indexdateformat = "%F";
841 \& $indextimeformat = "%R";
842 .Ve
843 .PP
844 .Vb 2
845 \& $slidedateformat = "%F";
846 \& $slidetimeformat = "%R";
847 .Ve
848 .PP
849 .Vb 1
850 \& $detaildateformat = "%m/%y";
851 .Ve
852 .PP
853 .Vb 1
854 \& 1; # don't for get this as the last line in the file!
855 .Ve
856 .SH "ACKNOWLEDGMENTS"
857 .IX Header "ACKNOWLEDGMENTS"
858 We would like to thank Larry Wall, creator of Perl for his \*(L"swiss army
859 chainsaw\*(R" of a scripting language (as well as all those who have hacked on Perl
860 throughout the years). We would also like to thank all who have contributed to
861 ImageMagick and its companion module PerlMagick. Without PerlMagick this
862 software would be exceedingly less robust. Additionally we would like to thank
863 the creators of mplayer (and all contributors). Without mplayer the support
864 introduced for video files would never have come about.
865 .SH "AUTHORS"
866 .IX Header "AUTHORS"
867 .Vb 2
868 \& Edwin Huffstutler <edwinh at edwinh dot org>
869 \& John Reynolds <johnjen at reynoldsnet dot org>
870 .Ve
0 =head1 NAME
1
2 imageindex - a digital photo gallery tool
3
4 =head1 SYNOPSIS
5
6 Imageindex is a digital picture gallery program. It provides automatic
7 generation of thumbnails and other size views of the images and video files,
8 and W3C compliant HTML to allow viewing of the thumbnails and images or videos.
9 It also creates montages of all images in a given directory to be used in
10 directory entries within the HTML.
11
12 =head1 DESCRIPTION
13
14 Imageindex has evolved from a simple thumbnail-generation program into a
15 full-blown gallery application. With it you can create static thumbnails and
16 "medium" views (good for dial-up web viewers) along with static HTML which
17 presents the images in chronological order (based upon the date in the Exif
18 header or date stamp of the file itself). Support has now been added for
19 animated GIFs and video files (we support whatever file formats your
20 installation of mplayer supports).
21
22 Imageindex creates static rather than dynamic content for many reasons. First
23 and foremost it is much easier to archive pictures onto CD-ROMs on a periodic
24 basis when the content is just "there" rather than "trapped" in a database of
25 some sort. You create your own "database" with your own directories and let
26 imageindex handle the presentation of the images to the world. Imageindex will
27 use "montages" (or thumbnails of all your thumbnails crammed together) of
28 subdirectories if they occur alongside images (see the the sample pages on the
29 imageindex website - http://www.edwinh.org/imageindex/sample.html).
30
31 There are four basic "views" that imageindex creates (enabled by default). The
32 "index" view shows thumbnails of all images in a directory in a basic table
33 format (defaults to 3 columns). Within each cell of the table basic information
34 such as the date/time of the picture are given as well as any comment (or
35 caption) present in the image. Links to the various sizes of image and other
36 views for the image are also presented by default.
37
38 The second view presented is the "slide show" view. In this view the "medium"
39 sized image is presented along with information such as date/time, any caption
40 embedded in the image, etc. Currently for video files, the 'medium' slides only
41 point to the actual video file processed. Future versions will allow for direct
42 playback inside the browser (much like YouTube). There are "previous" and
43 "next" links on each page which let the viewer quickly cycle through each
44 "medium" image without having to constantly invoke the browser's "back" button.
45
46 Captions for video files can be created by creating a file with the same
47 basename as the original video file but having ".txt" as the extension. The
48 contents of this file will be used just like captions embedded in formats like
49 JPG when processing the resultant HTML.
50
51 In conjunction with the "slide show" view there is a "frame view". When enabled
52 a link to the frame view appears at the top of the index view's page. When the
53 frame view is visited, the browser's pane splits into two portions. On the left
54 all the thumbnails are lined up close together. On the right hand portion the
55 same "slide show" pages are loaded. As the user clicks on a thumbnail on the
56 left, it's "slide" view (including the "medium" image) is displayed in the
57 right had side of the frame. This creates a very convenient mechanism for
58 browsing through many images.
59
60 Finally a "details" view exists. This details view is much like the index view
61 as the thumbnails (reduced in size further) are presented in a table format,
62 but much more information is presented in each cell. This is very useful for
63 images that come from digital cameras where Exif headers have filled with lots
64 of neat information.
65
66 The HTML output of imageindex can be customized by creating a
67 ".imageindexrc" in your $HOME directory and placing certain variables (see
68 VARIABLES section) in that file and editing to your taste. One of the variables
69 controls the output of a cascading style sheet which ultimately directs your
70 browser how to render the HTML. All color, font, indenting, etc. changes you
71 wish to make can be done in this style sheet variable.
72
73 =head1 VARIABLES
74
75 When you create your ".imageindexrc" file in your $HOME directory, you can put
76 any or all of the following variables in there and tweak as needed. This is
77 Perl code itself and is subsequently "included" into imageindex as it runs.
78
79 You B<must> end the file by putting a "1;" at the end of it. It's a Perl thing!
80
81 The values you see in these examples are the program defaults. If you like the
82 default value of a particular variable you don't need to include it in your
83 ".imageindexrc" file.
84
85 Name of the directory that holds thumbnail images
86
87 $thumbnail_dir = 'thumbnail';
88
89 Size of the thumbnail images in the "x" direction (pixels). Note that
90 imageindex preserves the aspect ratio of an image when it is reduced from its
91 original size to form a thumbnail image. So, if the "x" dimension is smaller
92 than the "y" dimension, a thumbnail might have an "x" size smaller than
93 $default_thumbnail_x.
94
95 $default_thumbnail_x = 200;
96
97 Size of the thumbnail images in the "y" direction (pixels). Again, note that
98 imageindex preserves the aspect ratio of an image when it is reduced from its
99 original size to form a thumbnail image. So, if the "y" dimension is smaller
100 than the "x" dimension, a thumbnail might have an "y" size smaller than
101 $default_thumbnail_y.
102
103 $default_thumbnail_y = 200;
104
105 If both dimensions of the original are within this much of the thumbnail
106 dimensions we will skip the thumbnail and just use the original as the
107 "thumbnail."
108
109 $thumbnail_threshold = 1.0;
110
111 Size of the "medium" images in the "x" direction (pixels). Note that
112 imageindex preserves the aspect ratio of an image when it is reduced from its
113 original size to form a "medium" image. So, if the "x" dimension is smaller
114 than the "y" dimension, a "medium" image might have an "x" size smaller than
115 $med_x.
116
117 $med_x = 800;
118
119 Size of the "medium" images in the "y" direction (pixels). Note that
120 imageindex preserves the aspect ratio of an image when it is reduced from its
121 original size to form a "medium" image. So, if the "y" dimension is smaller
122 than the "x" dimension, a "medium" image might have an "y" size smaller than
123 $med_y.
124
125 $med_y = 600;
126
127 Name of the directory that holds "medium" images
128
129 $med_dir = 'medium';
130
131 If both dimensions of the original are within this much of the "medium"
132 dimensions we will skip creating the medium-size format and just use the
133 original. This saves needless creating a "medium" image if it's close in size
134 to the original already.
135
136 $med_threshold = 1.6;
137
138 Automatically recurse into subdirectories? Set to 1 to enable.
139
140 $do_recurse = 0;
141
142 Generate "medium" images at all? Set to 1 to enable.
143
144 $do_medium = 1;
145
146 Generate the "slide show" and frame view? Set to 1 to enable.
147
148 $do_slide = 1;
149
150 Generate the "details" view? Set to 1 to enable.
151
152 $do_captions = 1;
153
154 Use/display caption info stored in images? Set to 1 to enable.
155
156 $do_detail = 1;
157
158 Process subdirectories as entries in the normal "index" and "details" views?
159 Set to 1 to enable. If an entire directory hierarchy has been processed with
160 $do_montage set to 1 (see below), the montage file for a given directory will
161 be used as the "thumbnail" for a subdirectory.
162
163 $do_dirs = 1;
164
165 Create a montage of all the images? When enabled all of the images that are
166 processed are turned into an NxM montage of very small thumbnails in a tiled
167 pattern. The resulting image is shrunk to the $default_thumbnail_x x
168 $default_thumbnail_y dimensions and stored in the $thumbnail_dir directory. The
169 size of the tiles grows as the number of images in a directory increase, but
170 can be bounded by variables outlined below. Set to 1 to enable.
171
172 $do_montage = 1;
173
174 Map ASCII "smiley" patterns embedded within an image's comment into real
175 "emoticon" images? When enabled the ASCII smiley faces such as :) and :-), the
176 winks ;) and ;-), and the frowns :( and :-( are mapped to small PNG images that
177 display the emotion conveyed. Set to 1 to enable.
178
179 $do_emoticons = 1;
180
181 Sort timestamps in reverse order.
182
183 $do_reverse = 1;
184
185 Process video files. This relies on a fairly recent version of mplayer being
186 installed on your system. We've tested with up to 0.99.8. The kind of video
187 files that are supported are up to the codecs that are compiled and used with
188 mplayer installation on your system.
189
190 $do_video_files = 1;
191
192 Overlay a small icon into one of the corners of the thumbnail and medium views
193 when processing the first frame of a video file. This gives a "visual cue" that
194 the file being represented in your browser is a video file and not a still
195 image.
196
197 $do_video_thumbnail_icons = 1;
198
199 Use the following as a regular expression to identify video files by their
200 extension. For certain technical reasons it was more feasible to rely upon this
201 quick and effective method. If files from your camera (or whatever) end in a
202 different extension, just put that extension here too.
203
204 $video_regexp = '(avi|mov|mpg|mpeg|mjpeg|m1v|m2v|wmv|fli|nuv|vob|ogm|vcd|svcd|mp4|qt)';
205
206 If you enable the "visual cue" icons for video files mentioned above, the
207 following variable determines which corner it is placed. Acceptable values are:
208 SouthWest, NorthWest, NorthEast, SouthEast (case sensitive!).
209
210 $video_icon_gravity = 'SouthWest';
211
212 If you enable the "visual cue" icons for video files mentioned above, there are
213 two to pick from (currently). Set to 1 (default) for a yellow dot with a 'play'
214 arrow. Set to 2 for a purplish icon of a video camera. More of these will be
215 created in further releases.
216
217 $video_icon = 1;
218
219 The following three variables control what hyperlinks in the HTML output
220 "point" to. They can be set to the following:
221
222 index : points to the name reference for an image in the index view
223 fullsize : points to the actual image itself
224 medium : points to the "medium" version of an image
225 thumbnail : points to the thumbnail version of an image
226 slide : points to the "slide show" HTML page written for an image
227 details : points to the name reference for an image in the details
228 view
229
230 The folling variable controls what the hyperlink for the thumbnail image in the
231 index view points to:
232
233 $index_linkto = 'slide';
234
235 The folling variable controls what the hyperlink for the thumbnail image in the
236 details view points to:
237
238 $details_linkto = 'index';
239
240 The folling variable controls what the hyperlink for the "medium" image in the
241 slide view points to:
242
243 $slide_linkto = 'fullsize';
244
245 Default number of columns to use in the index and detail views
246
247 $default_columns = 3;
248
249 Set the orientation of slide frame - 'horizontal' or 'vertical'. When 'vertical'
250 the browser pane will split vertically with all the thumbnails towards the
251 left. When 'horizontal' the browser pane splits horizontally with the
252 thumbnails arranged in the upper portion
253
254 $frame_orient = 'vertical';
255
256 The following two variables can be set to any of the following three values:
257
258 top : put the item in question at the top of the page when rendered
259 bottom : put the item in question at the bottom of the page when
260 rendered
261 none : omit the item from the HTML output
262
263 Controls if an image caption (or comment) embedded in the image will be
264 retrieved and written into the HTML output. By default it is written above the
265 "medium" image presented in the "slide" view.
266
267 $slide_caption = 'top';
268
269 Controls if the date/time of an image is written into the HTML output. By
270 default it is written below the "medium" image presented in the "slide" view.
271
272 $slide_date = 'bottom';
273
274 In the "detail" view, the thumbnail images are shrunk to a size smaller than
275 the normal thumbnails (to conserve valueable space). This sets the number of
276 times they are shrunk. By default it is shrunk by a factor of 2.
277
278 $detailshrink = 2;
279
280 The thumbnail and "medium" images are written out as JPEG files (even if the
281 original images were not JPEG's). The following two variables control the
282 "quality" for generated images. The value can range from 0 to 100 where 100
283 means "don't lose any quality in favor of file size."
284
285 Adjust the quality of the thumbnails being written out
286
287 $thumb_quality = 50;
288
289 Adjust the quality of the "medium" images written out
290
291 $med_quality = 80;
292
293 Adjust the minimum number of tiles that will be found in a montage image. If
294 the number of images in a directory is lower than this value, images will
295 either be repeated or blanks will be inserted (see $montage_fill).
296
297 $montage_min = 4;
298
299 Adjust the maximum number of tiles that will be found in a montage. If the
300 number of images in a directory is higher than this number, the montage will be
301 made by "evenly picking" $montage_max images in the directory and only using
302 those.
303
304 $montage_max = 36;
305
306 Adjust the space between montage images (pixels).
307
308 $montage_whitespace = 2;
309
310 When there is not a "even" number of images in a directory and a complete NxM
311 tile montage cannot be formed, images can be used again or empty space can be
312 used. Set to 'repeat' to re-use images and 'blank' to use empty space.
313
314 $montage_fill = 'blank';
315
316 The following variable controls all aspects of how the HTML output is rendered
317 in standards compliant browsers. The contents of this variable will be written
318 out into a cascading style sheet and the properties found within it will govern
319 how the HTML is rendered. All color, font, size, alignment, etc. property
320 changes can take place. This might require some knowledge of cascading style
321 sheets. A good primer can be found here:
322
323 http://www.w3schools.com/css/css_reference.asp
324
325 $stylesheet = '
326 body { color: black; background: white; }
327
328 /* Fonts in the title */
329 h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif;
330 font-size: 200%; font-weight: bold; text-align: center; }
331 h2.daterange { font-family: Arial,Helvetica,sans-serif;
332 font-size: 125%; text-align: center; }
333 h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%;
334 text-align: center; }
335
336 /* Photo captions & Directory titles */
337 div.caption { font-family: Arial,Helvetica,sans-serif;
338 font-size: 100%; font-weight: bold; margin: 1em; }
339
340 /* Overall fonts on the index and details page */
341 div.index { font-family: Arial,Helvetica,sans-serif;
342 font-size: 80%; }
343 div.detail { font-family: Arial,Helvetica,sans-serif;
344 font-size: 80%; }
345 div.credits { font-family: Arial,Helvetica,sans-serif;
346 font-size: 80%; text-align: right; margin: 10px }
347
348 /* Table attributes */
349 table.index { background: #ffffff; border: none;
350 border-spacing: 8px; }
351 td.index { border: none; padding: 3px }
352 table.frame { background: #ffffff; border: none }
353 td.frame { border: none; padding: 0px }
354
355 /* Image attributes */
356 img.index { border: none; }
357 img.slide { border: none; }
358 img.frame { border: none; }
359
360 /* Link attributes */
361 a:link { color: blue; }
362 a:visited { color: green; }
363 a:hover { color: red; }
364 a:active { color: red; }
365
366 ';
367
368
369 Adjust what is presented in "empty" table cells when there are not an "even"
370 number of images in a directory.
371
372 $emptycell = "<I>empty</I>";
373
374 Control the text of a hyperlink to a parent directory. If you do not desire
375 that this link be present in the index and "details" views "undef" the variable
376 (undef $updirtext;)
377
378 $updirtext = "up one directory";
379
380 Control the text of a hyperlink to the frame view. If you do not desire
381 that this link be present in the index and "details" views "undef" the variable
382 (undef $framelinktext;)
383
384 $framelinktext = "slideshow view (frames)";
385
386 Control the text of a hyperlink to the detail view. If you do not desire
387 that this link be present in the index view "undef" the variable
388 (undef $detaillinktext;)
389
390 $detaillinktext = "details index";
391
392 Control the text of a hyperlink to the index view. If you do not desire
393 that this link be present in the detail view "undef" the variable
394 (undef $indexlinktext;)
395
396 $indexlinktext = "main index";
397
398 Control the default TITLE string written out in the HTML for a given
399 directory. This is most usually given on a per-directory basis via the command
400 line and "remembered" within META data inside the index HTML file itself.
401
402 $default_titletext = "Image directory";
403
404 The following five variables control the TITLE attribute on anchor constructs
405 in the index and frame views. When TITLE attributes are given they are usually
406 rendered as "tooltip" bubbles that show text when a cursor hovers and stops
407 over the active link. We use them here to give a visual cue about the image.
408 These variables work much like printf(1) strings. The values that can be
409 interpolated for a given image are:
410
411 %f => replaced with the filename of the image
412 %d => replaced with the date/time of the image (or mtime of the file)
413 %s => replaced with the size of the file (in Kb)
414 %r => replaced with the resolution (XxY) of the original image
415 %c => replaced with the image's caption (if stored with one)
416 %% => replaced with a literal '%' character
417
418 The following codes are interpolated when directories are processed and a
419 montage of that directory is used as the thumbnail of the subdirectory.
420
421 %n => replaced with number of images in a directory
422 %b => replaced with the "begin" date from a directory of images
423 %e => replaced with the "end" date from a directory of images
424 %t => replaced with the "title" from a directory of images
425
426 Other characters (including spaces) are literal. "undef" these in your
427 ".imageindexrc" file if you don't want the TITLE attributes to be written into
428 the HTML. The "date/time" related constructs are interpolated using the
429 date/time format variables defined below.
430
431 Control the TITLE attributes for hyperlinks to thumbnail images within the
432 frame view. The default is "<filename> - <date>" for an image
433
434 $framethumbtitle = "%f - %d";
435
436 Control the TITLE attributes for hyperlinks to thumbnail images within the
437 index view. The default is "<filename> (<size>)" for an image
438
439 $indexthumbtitle = "%f (%s)";
440
441 Control the TITLE attributes for hyperlinks to thumbnail images within the
442 slide view. The default is "<filename> (<size>)" for an image
443
444 $slidethumbtitle = "%f (%s)";
445
446 Control the TITLE attributes for hyperlinks to thumbnail images within the
447 detail view. The default is caption (or comment) of an image if one was
448 embedded within it.
449
450 $detailthumbtitle = "%c";
451
452 Control the TITLE attributes for hyperlinks to montage images within the
453 index view when a subdirectory is being presented. The default is to show how
454 many images the subdirectory had and the date range that is spanned.
455
456 $montagetitle = "%n images %b through %e";
457
458 The following eight variables control how dates and times are formatted when
459 written into the HTML. Again we're using printf(1)-like variables where codes
460 are interpolated according to a user's taste.
461
462 The definitions of the escape sequences come from the POSIX strftime(3)
463 definitions. NOT ALL of strftime(3) are supported for obvious reasons.
464
465 %S is replaced by the second as a decimal number (00-60).
466 %M is replaced by the minute as a decimal number (00-59).
467 %I is replaced by the hour (12-hour clock) as a decimal number (01-12).
468 %H is replaced by the hour (24-hour clock) as a decimal number (00-23).
469 %p is replaced by national representation of either "ante meridiem" or
470 "post meridiem" as appropriate (currently only U.S. "am" or "pm")
471 %R is equivalent to "%H:%M" (in *timeformat variables only).
472 %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only).
473
474 %Y is replaced by the year with century as a decimal number.
475 %y is replaced by the year without century as a decimal number (00-99).
476 %m is replaced by the month as a decimal number (01-12).
477 %d is replaced by the day of the month as a decimal number (01-31).
478 %F is equivalent to "%Y-%m-%d" (in *dateformat variables only).
479 %D is equivalent to "%m/%d/%y" (in *dateformat variables only).
480 %% is replaced by a literal "%".
481
482 Control the way the date is formed in the frame view
483
484 $framedateformat = "%m/%d/%Y";
485
486 Control the way the time is formed in the frame view
487
488 $frametimeformat = "%r";
489
490 Control the way the date is formed in the index view
491
492 $indexdateformat = "%m/%d/%Y";
493
494 Control the way the time is formed in the index view
495
496 $indextimeformat = "%r";
497
498 Control the way the date is formed in the slide view
499
500 $slidedateformat = "%m/%d/%Y";
501
502 Control the way the time is formed in the slide view
503
504 $slidetimeformat = "%r";
505
506 Control the way the date is formed in the detail view
507
508 $detaildateformat = "%m/%d/%Y";
509
510 Control the way the date is formed in the detail view
511
512 $detailtimeformat = "%I:%M %p";
513
514 Control what the index view's HTML filename will be
515
516 $indexfile = 'index.html';
517
518 Control what the detail view's HTML filename will be
519
520 $detailfile = 'details.html';
521
522 Control what the frame view's HTML filename will be
523
524 $framefile = 'frame.html';
525
526 Control what the slide view's HTML filename will be
527
528 $slidefile = 'slides.html';
529
530 Control the name of the directory where all the "slide view" HTML files will be
531 deposited (one per image)
532
533 $slide_dir = 'slides';
534
535 Control the name of the cascading style sheet written out in each directory
536
537 $stylefile = 'style.css';
538
539 Control the name of the montage image if enabled
540
541 $montagefile = 'montage.jpg';
542
543 Control the prefix of the emoticon PNG image filenames
544
545 $emoticonprefix = 'ii_';
546
547 =head1 EXAMPLES
548
549 As an example, suppose you just want to change some date/time format strings. A
550 complete ".imageindexrc" file in this case would be:
551
552 $framedateformat = "%F";
553 $frametimeformat = "%R";
554
555 $indexdateformat = "%F";
556 $indextimeformat = "%R";
557
558 $slidedateformat = "%F";
559 $slidetimeformat = "%R";
560
561 $detaildateformat = "%m/%y";
562
563 1; # don't for get this as the last line in the file!
564
565 =head1 ACKNOWLEDGMENTS
566
567 We would like to thank Larry Wall, creator of Perl for his "swiss army
568 chainsaw" of a scripting language (as well as all those who have hacked on Perl
569 throughout the years). We would also like to thank all who have contributed to
570 ImageMagick and its companion module PerlMagick. Without PerlMagick this
571 software would be exceedingly less robust. Additionally we would like to thank
572 the creators of mplayer (and all contributors). Without mplayer the support
573 introduced for video files would never have come about.
574
575 =head1 AUTHORS
576
577 Edwin Huffstutler <edwinh at edwinh dot org>
578 John Reynolds <johnjen at reynoldsnet dot org>
579
580
581
0 Summary: Photo gallery creation tool
1 Name: imageindex
2 %define version 1.1
3 Version: %{version}
4 Release: 1
5 Packager: Edwin Huffstutler <edwinh@computer.org>
6 Source: %{name}-%{version}.tar.gz
7 Url: http://www.edwinh.org/imageindex/
8 License: GPL
9 Group: Applications/Graphics
10 BuildRoot: /var/tmp/%{name}-root
11 BuildArch: noarch
12 Requires: perl-Image-Info
13 Requires: perl-IO-String
14 Requires: ImageMagick-perl
15
16 %description
17 Photo gallery creation tool
18
19 %prep
20 %setup -q
21
22 %build
23 make all PREFIX=$RPM_BUILD_ROOT/usr
24
25 %install
26 rm -rf $RPM_BUILD_ROOT
27 mkdir -p $RPM_BUILD_ROOT/usr/bin
28 mkdir -p $RPM_BUILD_ROOT/usr/share/man/man1
29 make install PREFIX=$RPM_BUILD_ROOT/usr
30
31 %clean
32 rm -rf $RPM_BUILD_ROOT
33
34 %files
35 %defattr(-,root,root)
36 %{_bindir}/*
37 %doc %{_mandir}/man1/*
38 %doc CHANGES README COPYING TODO