mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
520 lines
17 KiB
C++
520 lines
17 KiB
C++
// Filename: indexify.cxx
|
|
// Created by: drose (03Apr02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://www.panda3d.org/license.txt .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d@yahoogroups.com .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "indexify.h"
|
|
#include "rollDirectory.h"
|
|
#include "notify.h"
|
|
#include "pnmTextMaker.h"
|
|
#include "default_font.h"
|
|
#include "default_index_icons.h"
|
|
#include "indexParameters.h"
|
|
#include "string_utils.h"
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Indexify::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
Indexify::
|
|
Indexify() {
|
|
clear_runlines();
|
|
add_runline("[opts] roll1-dir roll2-dir [roll3-dir ...]");
|
|
|
|
set_program_description
|
|
("This program reads a collection of directories containing photo "
|
|
"archives (typically JPEG files), and will generate a number of "
|
|
"thumbnail images and a series of HTML pages to browse them. It is "
|
|
"especially useful in preparation for burning the photo archives to "
|
|
"CD.\n\n"
|
|
|
|
"A number of directories is named on the command line; each "
|
|
"directory must contain a number of image files, and all directories "
|
|
"should be within the same parent directory. Each directory is "
|
|
"considered a \"roll\", which may or may not correspond to a physical "
|
|
"roll of film, and the photos within each directory are grouped "
|
|
"correspondingly on the generated HTML pages.\n\n"
|
|
|
|
"If a file exists by the same name as an image file but with the "
|
|
"extension \"cm\", that file is taken to be a HTML comment about that "
|
|
"particular image and is inserted the HTML page for that image. "
|
|
"Similarly, if there is a file within a roll directory with the same "
|
|
"name as the directory itself (but with the extension \"cm\"), that file "
|
|
"is inserted into the front page to introduce that particular roll.\n\n"
|
|
|
|
"Normally, all image files with the specified extension (normally "
|
|
"\"jpg\") within a roll directory are included in the index, and sorted "
|
|
"into alphabetical (or numeric) order. If you wish to specify a "
|
|
"different order, or use only a subset of the images in a directory, "
|
|
"create a file in the roll directory with the same name as the "
|
|
"directory itself, and the extension \"ls\". This file should "
|
|
"simply list the filenames (with or without extension) within the "
|
|
"roll directory in the order they should be listed. If the ls "
|
|
"file exists but is empty, it indicates that the files should be "
|
|
"listed in reverse order, as from a camera that loads its film "
|
|
"upside-down.");
|
|
|
|
add_option
|
|
("t", "title", 0,
|
|
"Specifies the title to give to the front HTML page.",
|
|
&Indexify::dispatch_string, NULL, &_front_title);
|
|
|
|
add_option
|
|
("a", "archive-dir", 0,
|
|
"Write the generated files to the indicated directory, instead of "
|
|
"the directory above roll1-dir.",
|
|
&Indexify::dispatch_filename, NULL, &_archive_dir);
|
|
|
|
add_option
|
|
("r", "relative-dir", 0,
|
|
"When -a is specifies to place the generate html files in a directory "
|
|
"other than the one above the actual roll directories, you may need "
|
|
"to specify how the html files will address the roll directories. This "
|
|
"parameter specifies the relative path to the directory above the roll "
|
|
"directories, from the directory named by -a.",
|
|
&Indexify::dispatch_filename, NULL, &_roll_dir_root);
|
|
|
|
add_option
|
|
("f", "", 0,
|
|
"Forces the regeneration of all reduced and thumbnail images, even if "
|
|
"image files already exist that seem to be newer than the source "
|
|
"image files.",
|
|
&Indexify::dispatch_none, &force_regenerate);
|
|
|
|
add_option
|
|
("r", "", 0,
|
|
"Specifies that roll directory names are encoded using the Rose "
|
|
"convention of six digits: mmyyss, where mm and yy are the month and "
|
|
"year, and ss is a sequence number of the roll within the month. This "
|
|
"name will be reformatted to m-yy/s for output.",
|
|
&Indexify::dispatch_none, &format_rose);
|
|
|
|
add_option
|
|
("d", "", 0,
|
|
"Run in \"dummy\" mode; don't load any images, but instead just "
|
|
"draw an empty box indicating where the thumbnails will be.",
|
|
&Indexify::dispatch_none, &dummy_mode);
|
|
|
|
add_option
|
|
("slide", "", 0,
|
|
"Draw a frame, like a slide mount, around each thumbnail image.",
|
|
&Indexify::dispatch_none, &draw_frames);
|
|
|
|
add_option
|
|
("e", "extension", 0,
|
|
"Specifies the filename extension (without a leading dot) to identify "
|
|
"photo files within the roll directories. This is normally jpg.",
|
|
&Indexify::dispatch_string, NULL, &_photo_extension);
|
|
|
|
add_option
|
|
("i", "", 0,
|
|
"Indicates that default navigation icon images should be generated "
|
|
"into a directory called \"icons\" which will be created within the "
|
|
"directory named by -a. This is meaningful only if -prev, -next, and "
|
|
"-up are not explicitly specified.",
|
|
&Indexify::dispatch_none, &_generate_icons);
|
|
|
|
add_option
|
|
("omit-rh", "", 0,
|
|
"Omits roll headers introducing each roll directory, including any "
|
|
"headers defined in roll.cm files.",
|
|
&Indexify::dispatch_none, &omit_roll_headers);
|
|
|
|
add_option
|
|
("cmdir", "director", 0,
|
|
"Searches in the named directory for .cm files before searching within "
|
|
"the source archive. This option may be repeated.",
|
|
&Indexify::dispatch_search_path, NULL, &cm_search);
|
|
|
|
add_option
|
|
("omit-full", "", 0,
|
|
"Omits links to the full-size images.",
|
|
&Indexify::dispatch_none, &omit_full_links);
|
|
|
|
add_option
|
|
("caption", "size[,spacing]", 0,
|
|
"Specifies the font size in pixels of the thumbnail captions. If the "
|
|
"optional spacing parameter is included, it is the number of pixels "
|
|
"below each thumbnail that the caption should be placed. Specify "
|
|
"-caption 0 to disable thumbnail captions.",
|
|
&Indexify::dispatch_caption, NULL);
|
|
|
|
add_option
|
|
("fnum", "", 0,
|
|
"Writes the frame number of each thumbnail image into the caption "
|
|
"on the index page, instead of the image filename. This only works "
|
|
"if the photo image filenames consist of the roll directory name "
|
|
"concatenated with a frame number.",
|
|
&Indexify::dispatch_none, &caption_frame_numbers);
|
|
|
|
add_option
|
|
("font", "fontname", 0,
|
|
"Specifies the filename of the font to use to generate the thumbnail "
|
|
"captions.",
|
|
&Indexify::dispatch_filename, NULL, &_font_filename);
|
|
|
|
add_option
|
|
("fontaa", "factor", 0,
|
|
"Specifies a scale factor to apply to the fonts used for captioning "
|
|
"when generating text for the purpose of antialiasing the fonts a "
|
|
"little better than FreeType can do by itself. The letters are "
|
|
"generated large and then scaled to their proper size. Normally this "
|
|
"should be a number in the range 3 to 4 for best effect.",
|
|
&Indexify::dispatch_double, NULL, &_font_aa_factor);
|
|
|
|
add_option
|
|
("thumb", "x,y", 0,
|
|
"Specifies the size in pixels of the thumbnail images.",
|
|
&Indexify::dispatch_int_pair, NULL, &thumb_width);
|
|
|
|
add_option
|
|
("reduced", "x,y", 0,
|
|
"Specifies the size in pixels of reduced images (images presented after "
|
|
"the first click on a thumbnail).",
|
|
&Indexify::dispatch_int_pair, NULL, &reduced_width);
|
|
|
|
add_option
|
|
("space", "x,y", 0,
|
|
"Specifies the x,y spacing between thumbnail images, in pixels.",
|
|
&Indexify::dispatch_int_pair, NULL, &thumb_x_space);
|
|
|
|
add_option
|
|
("index", "x,y", 0,
|
|
"Specifies the size in pixels of the index images (the images that "
|
|
"contain an index of thumbnails).",
|
|
&Indexify::dispatch_int_pair, NULL, &max_index_width);
|
|
|
|
add_option
|
|
("prev", "filename", 0,
|
|
"Specifies the relative pathname from the archive directory (or "
|
|
"absolute pathname) to the \"previous\" photo icon.",
|
|
&Indexify::dispatch_filename, NULL, &prev_icon);
|
|
|
|
add_option
|
|
("next", "filename", 0,
|
|
"Specifies the relative pathname from the archive directory (or "
|
|
"absolute pathname) to the \"next\" photo icon.",
|
|
&Indexify::dispatch_filename, NULL, &next_icon);
|
|
|
|
add_option
|
|
("up", "filename", 0,
|
|
"Specifies the relative pathname from the archive directory (or "
|
|
"absolute pathname) to the \"up\" photo icon.",
|
|
&Indexify::dispatch_filename, NULL, &up_icon);
|
|
|
|
_photo_extension = "jpg";
|
|
_text_maker = (PNMTextMaker *)NULL;
|
|
_font_aa_factor = 4.0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Indexify::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
Indexify::
|
|
~Indexify() {
|
|
RollDirs::iterator di;
|
|
for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
|
|
RollDirectory *roll_dir = (*di);
|
|
delete roll_dir;
|
|
}
|
|
|
|
if (_text_maker != (PNMTextMaker *)NULL) {
|
|
delete _text_maker;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Indexify::handle_args
|
|
// Access: Protected, Virtual
|
|
// Description: Does something with the additional arguments on the
|
|
// command line (after all the -options have been
|
|
// parsed). Returns true if the arguments are good,
|
|
// false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Indexify::
|
|
handle_args(ProgramBase::Args &args) {
|
|
if (args.empty()) {
|
|
nout << "You must specify the roll directories containing archive photos on the command line.\n";
|
|
return false;
|
|
}
|
|
|
|
RollDirectory *prev_roll_dir = (RollDirectory *)NULL;
|
|
Args::const_iterator ai;
|
|
for (ai = args.begin(); ai != args.end(); ++ai) {
|
|
Filename filename = Filename::from_os_specific(*ai);
|
|
filename.standardize();
|
|
if (filename.is_directory()) {
|
|
string basename = filename.get_basename();
|
|
if (basename == "icons" || basename == "html" || basename == "reduced") {
|
|
nout << "Ignoring " << filename << "; indexify-generated directory.\n";
|
|
|
|
} else {
|
|
RollDirectory *roll_dir = new RollDirectory(filename);
|
|
if (prev_roll_dir != (RollDirectory *)NULL) {
|
|
roll_dir->_prev = prev_roll_dir;
|
|
prev_roll_dir->_next = roll_dir;
|
|
}
|
|
|
|
_roll_dirs.push_back(roll_dir);
|
|
prev_roll_dir = roll_dir;
|
|
}
|
|
|
|
} else if (filename.exists()) {
|
|
nout << "Ignoring " << filename << "; not a directory.\n";
|
|
|
|
} else {
|
|
nout << filename << " does not exist.\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Indexify::post_command_line
|
|
// Access: Protected, Virtual
|
|
// Description: This is called after the command line has been
|
|
// completely processed, and it gives the program a
|
|
// chance to do some last-minute processing and
|
|
// validation of the options and arguments. It should
|
|
// return true if everything is fine, false if there is
|
|
// an error.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Indexify::
|
|
post_command_line() {
|
|
if (_roll_dirs.empty()) {
|
|
nout << "No roll directories.\n";
|
|
return false;
|
|
}
|
|
|
|
if (_archive_dir.empty()) {
|
|
// Choose a default archive directory, above the first roll directory.
|
|
_archive_dir = _roll_dirs.front()->get_dir().get_dirname();
|
|
if (_archive_dir.empty()) {
|
|
_archive_dir = ".";
|
|
}
|
|
}
|
|
_archive_dir.standardize();
|
|
|
|
if (!_roll_dir_root.empty()) {
|
|
_roll_dir_root.standardize();
|
|
}
|
|
|
|
if (_front_title.empty()) {
|
|
// Supply a default title.
|
|
if (_roll_dirs.size() == 1) {
|
|
_front_title = _roll_dirs.front()->get_name();
|
|
} else {
|
|
_front_title = _roll_dirs.front()->get_name() + " to " + _roll_dirs.back()->get_name();
|
|
}
|
|
}
|
|
|
|
if (caption_font_size != 0) {
|
|
if (!_font_filename.empty()) {
|
|
_text_maker = new PNMTextMaker(_font_filename, 0);
|
|
if (!_text_maker->is_valid()) {
|
|
delete _text_maker;
|
|
_text_maker = (PNMTextMaker *)NULL;
|
|
}
|
|
}
|
|
|
|
if (_text_maker == (PNMTextMaker *)NULL) {
|
|
_text_maker = new PNMTextMaker(default_font, default_font_size, 0);
|
|
if (!_text_maker->is_valid()) {
|
|
nout << "Unable to open default font.\n";
|
|
delete _text_maker;
|
|
_text_maker = (PNMTextMaker *)NULL;
|
|
}
|
|
}
|
|
|
|
if (_text_maker != (PNMTextMaker *)NULL) {
|
|
_text_maker->set_pixel_size(caption_font_size, _font_aa_factor);
|
|
_text_maker->set_align(PNMTextMaker::A_center);
|
|
}
|
|
}
|
|
|
|
if (_generate_icons) {
|
|
if (prev_icon.empty()) {
|
|
prev_icon = Filename("icons", default_left_icon_filename);
|
|
Filename icon_filename(_archive_dir, prev_icon);
|
|
|
|
if (force_regenerate || !icon_filename.exists()) {
|
|
nout << "Generating " << icon_filename << "\n";
|
|
icon_filename.make_dir();
|
|
icon_filename.set_binary();
|
|
|
|
ofstream output;
|
|
if (!icon_filename.open_write(output)) {
|
|
nout << "Unable to write to " << icon_filename << "\n";
|
|
exit(1);
|
|
}
|
|
output.write(default_left_icon, default_left_icon_size);
|
|
}
|
|
}
|
|
if (next_icon.empty()) {
|
|
next_icon = Filename("icons", default_right_icon_filename);
|
|
Filename icon_filename(_archive_dir, next_icon);
|
|
if (force_regenerate || !icon_filename.exists()) {
|
|
nout << "Generating " << icon_filename << "\n";
|
|
icon_filename.make_dir();
|
|
icon_filename.set_binary();
|
|
|
|
ofstream output;
|
|
if (!icon_filename.open_write(output)) {
|
|
nout << "Unable to write to " << icon_filename << "\n";
|
|
exit(1);
|
|
}
|
|
output.write(default_right_icon, default_right_icon_size);
|
|
}
|
|
}
|
|
if (up_icon.empty()) {
|
|
up_icon = Filename("icons", default_up_icon_filename);
|
|
Filename icon_filename(_archive_dir, up_icon);
|
|
if (force_regenerate || !icon_filename.exists()) {
|
|
nout << "Generating " << icon_filename << "\n";
|
|
icon_filename.make_dir();
|
|
icon_filename.set_binary();
|
|
|
|
ofstream output;
|
|
if (!icon_filename.open_write(output)) {
|
|
nout << "Unable to write to " << icon_filename << "\n";
|
|
exit(1);
|
|
}
|
|
output.write(default_up_icon, default_up_icon_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
finalize_parameters();
|
|
|
|
|
|
return ProgramBase::post_command_line();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Indexify::dispatch_caption
|
|
// Access: Protected, Static
|
|
// Description: Dispatch function for the -caption parameter, which
|
|
// takes either one or two numbers separated by a comma,
|
|
// representing the caption font size and the optional
|
|
// pixel spacing of the caption under the image.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Indexify::
|
|
dispatch_caption(const string &opt, const string &arg, void *) {
|
|
vector_string words;
|
|
tokenize(arg, words, ",");
|
|
|
|
int caption_spacing = 0;
|
|
|
|
bool okflag = false;
|
|
if (words.size() == 1) {
|
|
okflag =
|
|
string_to_int(words[0], caption_font_size);
|
|
|
|
} else if (words.size() == 2) {
|
|
okflag =
|
|
string_to_int(words[0], caption_font_size) &&
|
|
string_to_int(words[1], caption_spacing);
|
|
}
|
|
|
|
if (!okflag) {
|
|
nout << "-" << opt
|
|
<< " requires one or two integers separated by a comma.\n";
|
|
return false;
|
|
}
|
|
|
|
thumb_caption_height = caption_font_size + caption_spacing;
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Indexify::run
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void Indexify::
|
|
run() {
|
|
bool all_ok = true;
|
|
|
|
RollDirs::iterator di;
|
|
for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
|
|
RollDirectory *roll_dir = (*di);
|
|
if (!roll_dir->scan(_photo_extension)) {
|
|
nout << "Unable to read " << *roll_dir << "\n";
|
|
all_ok = false;
|
|
}
|
|
roll_dir->collect_index_images();
|
|
}
|
|
|
|
if (!all_ok) {
|
|
exit(1);
|
|
}
|
|
|
|
// First, generate all the images.
|
|
for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
|
|
RollDirectory *roll_dir = (*di);
|
|
if (!roll_dir->generate_images(_archive_dir, _text_maker)) {
|
|
nout << "Failure.\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// Then go back and generate the HTML.
|
|
|
|
Filename html_filename(_archive_dir, "index.htm");
|
|
nout << "Generating " << html_filename << "\n";
|
|
html_filename.set_text();
|
|
ofstream root_html;
|
|
if (!html_filename.open_write(root_html)) {
|
|
nout << "Unable to write to " << html_filename << "\n";
|
|
exit(1);
|
|
}
|
|
|
|
root_html
|
|
<< "<html>\n"
|
|
<< "<head>\n"
|
|
<< "<title>" << _front_title << "</title>\n"
|
|
<< "</head>\n"
|
|
<< "<body>\n"
|
|
<< "<h1>" << _front_title << "</h1>\n";
|
|
|
|
for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
|
|
RollDirectory *roll_dir = (*di);
|
|
if (!roll_dir->generate_html(root_html, _archive_dir, _roll_dir_root)) {
|
|
nout << "Failure.\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
root_html
|
|
<< "</body>\n"
|
|
<< "</html>\n";
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
Indexify prog;
|
|
prog.parse_command_line(argc, argv);
|
|
prog.run();
|
|
return 0;
|
|
}
|