-#!/usr/bin/perl
+#!/usr/bin/python
#
# imgsizer -- correct image sizes in WWW pages
# by Eric S. Raymond <esr@thyrsus.com>
# a decent file(1) and rdjpgcom(1). To fix up sizes for non-local images
# it requires the httpget(1) script.
#
-# Works with either Perl4 or Perl5, Perl4 is much faster.
+# Works with Python 1.5.2
#
# Copy, use, and redistribute freely, but don't take my name off it and
# clearly mark an altered version. Fixes and enhancements cheerfully
# the line number an error occurred due to the input record separator
# being set to ">".
#
+# Rewritten in Python by Eric S. Raymond <esr@thyrsus.com> 11 July 2001
+#
+# Time to get rid of the dependency on httpget. The -l option is cone, too;
+# instead, we deduce the right case by looking at the leading tag. -q
+# is gone; we always emit without quotes. -m is gone too, instead we
+# try commands in least to most expensive order, and notice when a command
+# returns not to try it again.
+#
+import sys, os, getopt, filecmp, string, re, urllib, commands
-$version = "1.9, 23 Feb 2001";
+version = "2.0, 11 July 2001";
-$splash = <<EOT;
-imgsizer version $version, Eric S. Raymond <esr\@thyrsus.com>
-See my home page at <http://www.tuxedo.org/~esr> for updates.
-EOT
+splash = """imgsizer version %s, Eric S. Raymond <esr@thyrsus.com>
+See <http://www.tuxedo.org/~esr/software.html> for updates."""
-$usage = <<EOT;
-Usage: imgsizer [OPTIONS] [HTML File]
+usage = """Usage: imgsizer [OPTIONS] [HTML File]
Options:
- -v, --version
+ -V, --version
Display version information and exit.
Display usage information.
- -m, --no-magick
-
- Use file(1) and rdjpgcom(1) to determine the image size, instead
- of identify(1) from the ImageMagick suite, which is the default.
-
-d <directory>, --document-root <directory>
- Directory where aboslute image filenames (i.e, ones which contain
+ Directory where absolute image filenames (i.e, ones which contain
a leading "/") may be found.
- -l, --lowercase
+ -n, --no-overwrite
- Output HTML tag attributes imgsizer generates in lowercase.
+ Don't overwrite existing width and heigbt tags if both are present.
- -q, --no-quotes
+"""
- Omit the quotes in HTML tag attributes imgsizer generates.
-EOT
+def attrformat(xc, dim):
+ if lower:
+ res = " " + dim
+ else:
+ res = " " + string.upper(dim)
+ res = res + '=' + str(xc)
+ return res
-# Anything that can fetch a URL defined on the command line
-# and dump it to stdout will do here. We try a number of
-# alternatives and crap out if we can't find any of them
-%fetchers = {"curl":"curl", "httpget":"httpget", "lynx":"lynx -source"}
-$fetcher = ""
-for $trythis in keys(fetchers) {
- if (system(`whereis $trythis >/dev/null`) == 0) {
- $fetcher = $fetchers{trythis}
- }
-unless ($fetcher)
- die("Can't find a remote URL fetcher.\n")
+def sizefix(infp, outfp):
+# Apply attrfix to the attributes in each image tag
+ global lower
+ while 1:
+ ch = infp.read(1)
+ if ch == '':
+ return
+ outfp.write(ch)
+ if ch == '<':
+ # within an HTML tag
+ lead = infp.read(3)
+ if ch == '':
+ return
+ outfp.write(lead)
+ if lead.find('>') > -1:
+ continue
+ if not lead in ("img", "IMG"):
+ continue
+ # within an image tag
+ lower = (lead == 'img')
+ state = suppress = 0
+ attributes = ""
+ while 1:
+ ch = infp.read(1)
+ if ch == '':
+ return
+ if ch == '>':
+ break
+ attributes = attributes + ch
+ outfp.write(transform(attributes) + ">")
+
+x_match = re.compile (r" ([0-9]+) *x *([0-9]+) ")
+rdjpg_match = re.compile (r" ([0-9]+)w *\* *([0-9]+)h ")
+
+def imgsize(src):
+ "Return the image size in pixels for a given image source."
+ global magick, rdjpgcom
+ try:
+ (filename, headers) = urllib.urlretrieve(src)
+ except IOError:
+ return None
+ # Now let's see if we can get a size for the retrieved image.
+ # Try file(1) first -- cheapest, as it doesn't read the whole image
+ (status, output) = commands.getstatusoutput("file " + filename)
+ if status == 0:
+ # file(1) works for every common image format other than JPEG
+ if string.find(output, "JPEG") == -1:
+ sizes = x_match.search(output)
+ if sizes:
+ return (sizes.group(1), sizes.group(2))
+ elif rdjpgcom:
+ # Use rdjpcom(1) to handle JPEGs
+ (status, output) = commands.getstatusoutput("rdjpcom " + filename)
+ sizes = rdjpg_match.search(output)
+ if sizes:
+ return (sizes.group(1), sizes.group(2))
+ elif status == 127:
+ rdjpgcom = 0
+ # Next try identify(1), more expensive nut bulletproof
+ if magick:
+ (status, output) = commands.getstatusoutput("identify " + filename)
+ if status == 0:
+ sizes = x_match.search(output)
+ if sizes:
+ return (sizes.group(1), sizes.group(2))
+ elif status == 127:
+ magick = 0
+ # All attempts failed
+ sys.stderr.write("imgsizer: couldn't analyze " + src)
+
+source = re.compile('SRC="?([^" \t\n]*)"?', re.I)
+awidth = re.compile(' *WIDTH="?[0-9]*"?', re.I)
+aheight = re.compile(' *HEIGHT="?[0-9]*"?', re.I)
+pwidth = re.compile('WIDTH="?[0-9]*%"?', re.I)
+pheight = re.compile('HEIGHT="?[0-9]*%"?', re.I)
+
+def transform(attr):
+ src = source.search(attr)
+ # Must have a source part and no percents in existing width or height
+ if not src or pwidth.search(attr) or pheight.search(attr):
+ return attr
+ if no_overwrite and awidth.search(attr) and aheight.search(attr):
+ return attr
+ # Correct the url for documentation root, if present
+ url = src.group(1)
+ if url[0] == '/' and root:
+ url = os.path.join(root, url)
+ # OK, get the size tuple if possible
+ dimensions = imgsize(url)
+ if not dimensions:
+ return attr
+ else:
+ # Nuke any old size attr
+ if not no_overwrite:
+ attr = re.sub(awidth, "", attr)
+ attr = re.sub(aheight, "", attr)
+ # Compute image dimensions
+ (xc, yc) = dimensions
+ # Plug in the new attr
+ return attr + attrformat(xc, "width") + attrformat(yc, "height")
# Output uppercase tags, surrounded by quotes, by default.
-$lower = 0;
-$quotes = 1;
+lower = 0
+quotes = 1
# Set the default DocumentRoot to the current working directory.
-$root = ".";
-
-$out = "imgsizer-out$$";
-$dir = "."; # NOTE: if you are doing <yourfile , make sure that pwd is correct!
-$magick = 1; # using ImageMagick by default
-$/='>'; # define INPUT_RECORD_SEPARATOR to split on HTML marker boundaries
-
-# Look in IMGSIZER environmental variable for additional command line options
-unshift @ARGV, split /\s+/, $ENV{IMGSIZER}
- if ($ENV{IMGSIZER});
-
-while ($i = shift @ARGV)
-{
- last if ($i eq "--");
-
- unless ($i =~ /^-/)
- {
- unshift @ARGV, $i;
- last;
- }
-
- if ($i =~ /^-(\w{2,})$/i)
- {
- # Hack to parse "-foo" as "-f -o -o"
- unshift @ARGV, map { "-$_" } split //, $1;
- next;
- }
-
- if ($i =~ /^(--ver(sion)?|-v)$/i)
- {
- die "\n$splash\n";
- }
-
- if ($i =~ /^(--help|--usage|-h)$/i)
- {
- die "\n$splash\n$usage\n";
- }
-
- if ($i =~ /^(--no-?magick|-m)$/i)
- {
- $magick = 0;
- next;
- }
-
- if ($i =~ /^(--document-?root(=.*)?|-d)$/i)
- {
- if ($2 =~ /^=(.*)/)
- {
- $root = $1;
- }
-
- else
- {
- die "Error: --document-root requires a directory name\n"
- unless (@ARGV);
-
- $root = shift @ARGV;
- }
-
- die "Error: --document-root, \"$root\", is not a directory ",
- "or does not exist\n"
- unless (-d $root);
- next;
- }
-
- if ($i =~ /^(--lower-?(case)?|-l)$/i)
- {
- $lower = 1;
- next;
- }
-
- if ($i =~ /^(--no-?quotes?|-q)$/i)
- {
- $quotes = 0;
- next;
- }
-
- die "Error: Unknown option: $i\n";
-}
-
-if (@ARGV) {
- foreach $x (@ARGV) {
- $file = $x;
- $dir = &dirname($x);
- open(TEMP, ">$out")
- || die("imgsizer: couldn't open tempfile $out \n");
- close(STDIN);
- if (!open(STDIN, "$file")) {
- print STDERR "imgsize: couldn't open $x for input\n";
- } else {
- select(TEMP);
- &sizefix;
- close(TEMP);
- if (`cmp $out $file` && system("cp $out $file")) {
- print STDERR "imgsize: couldn't replace ${file}\n}";
- }
- }
- }
- unlink($out);
-}
-else {
- $file = "stdin";
- &sizefix;
-}
-
-sub dirname
-# return filename with the basename part stripped away
-{
- local($file) = @_;
- if ($file =~ /\//) {
- $file =~ s:/[^/]*$::;
- } else {
- $file = ".";
- }
- return($file);
-}
-
-sub sizefix
-# Apply attrfix to the attributes in each image tag
-{
- local $line = 1;
-
- while (<STDIN>) {
-
- $line += scalar @{[ /[\n\r]/g ]};
-
- if (/(<IMG\s+)(.*)>/is) {
- print $`; print $1;
- $tag = $2;
- print &attrfix($tag);
- print ">";
- }
- else {
- print $_;
- }
- }
-}
-
-sub error
-{
- print STDERR "\"$file\", line $line: $_[0]\n";
-}
-
-sub attrfix
-# fix up a space-separated sequence of image attributes
-{
- $attribs = $_[0];
-
- unless ( ($attribs =~ /SRC=([^\s]+)/is)
- || ($attribs =~ /SRC\s*=\s*\"([^"]*)\"/is) )
- {
- &error("image \"${attribs}\"' has no SRC attribute");
- return $attribs;
- }
-
- $url = $1;
-
- # fetch the image
- if ($url =~ /^http:/) {
- $image = "imgsizer-tmp$$";
- $status = system("$fetcher ${url} >${image}");
- if ($status) {
- &error("can't fetch $1");
- unlink("imgsizer-tmp$$");
- return $attribs;
- }
- }
- elsif ($url =~ m!^"?/!) {
- $image = $root . $url; # This catches local files that are in
- # virtual root, assuming we're called
- # from /var/www or equivalent.
- }
- elsif (!($url =~ /^[a-z]+:/)) {
- $image = "$dir/$url"; # This catches local files
- }
-
- # Don't try to fix up percent sizes
- if ($attribs =~ /[0-9]%/s) {
- return $attribs;
- }
-
- if ($magick) {
- ($name, $size) = split(' ', `identify ${image}`);
- unlink("imgsizer-tmp$$");
- if ($name =~ /:/) {
- &error("can't analyze included image ${image}");
- return $attribs;
- }
- if (!($size=~ /([0-9]+)x([0-9]+)/)) {
- &error("size field ${size} is ill-formed");
- return $attribs;
- }
- $xc = $1;
- $yc = $2;
- }
-
- else {
- $result = `file ${image}`;
- if ($result =~ /JPEG/) {
- $result = `rdjpgcom -verbose ${image}`;
- unlink("imgsizer-tmp$$");
- if ($result =~ /rdjpgcom:/) {
- &error("can't analyze included image ${image}");
- return $attribs;
- }
- else {
- $result =~ /(\d+)w \* (\d+)h/;
- $xc = $1;
- $yc = $2;
- }
- }
- else {
- unlink("imgsizer-tmp$$");
- if ($result !~ /(\d+) x (\d+)/) {
- &error("can't analyze included image ${image}");
- return $attribs;
- }
- else {
- $xc = $1;
- $yc = $2;
- }
- }
- }
-
- if ($attribs =~ /WIDTH\s*=\s*["0123456789]+/is) {
- $attribs = $` . &widthstring . $';
- } else {
- $attribs .= " " . &widthstring;
- }
-
- if ($attribs =~ /HEIGHT\s*=\s*["0123456789]+/is) {
- $attribs = $` . &heightstring . $';
- } else {
- $attribs .= " " . &heightstring;
- }
-
- return $attribs;
-}
-
-sub widthstring
-{
- return ($lower ? "width=" : "WIDTH=")
- . ($quotes ? '"' : '') . $xc . ($quotes ? '"' : '');
-}
-
-sub heightstring
-{
- return ($lower ? "height=" : "HEIGHT=")
- . ($quotes ? '"' : '') . $yc . ($quotes ? '"' : '');
-}
+root = "."
+
+out = "imgsizer-out$$"
+dir = "." # NOTE: if you are doing <yourfile , make sure that pwd is correct!
+
+# Optimization latches -- if an attempt to invoke a command returns 127
+# "not found" there will turn off and that command won't be tried again.
+magick = 1 # using ImageMagick by default
+rdjpgcom = 1 # using rdjpcom by default
+
+# Collect options from the environment first, then the command line
+options = os.environ.get("IMGSIZER")
+if options:
+ options = string.split(options)
+else:
+ options = []
+options = options + sys.argv[1:]
+
+# Process options
+(options, arguments) = getopt.getopt(options, "Vhd:n",
+ ('version', 'help', 'usage', 'document=', 'no-overwrite'))
+no_overwrite = 0
+for (switch, val) in options:
+ if switch in ('-V', '--version'):
+ print splash % version
+ raise SystemExit
+ elif switch in ('-h', '--help', '--usage'):
+ print splash + "\n\n" + usage
+ raise SystemExit
+ elif switch in ('-d', '--document'):
+ root = val
+ if not os.path.isdir(root):
+ print "Document root isn't a directory"
+ raise SystemExit, 1
+ elif switch in ('-n', '--no-overwrite'):
+ no_overwrite = 1
+
+if not arguments:
+ sizefix(sys.stdin, sys.stdout)
+else:
+ for file in arguments:
+ base = os.path.basename(file)
+ dir = os.path.dirname(file)
+ try:
+ infp = open(file)
+ except:
+ print "imgsizer: can't open input file", file
+ raise SystemExit, 1
+ tempfile = file + ".~imgsizer-%d~" % os.getpid()
+ try:
+ outfp = open(tempfile)
+ except OSError:
+ print "imgsizer: can't open tempfile"
+ raise SystemExit, 1
+ sizefix(infp, outfp)
+ if filecmp.cmp(file, tempfile):
+ os.remove(tempfile)
+ else:
+ try:
+ os.rename(tempfile, file)
+ except OSError:
+ sys.stderr.write("imgsize: couldn't replace " + file)
+ os.remove(tempfile)
+
+# End
.PP
.SH OPTIONS
.TP
-.B \-v, \-\-version
+.B \-V, \-\-version
Display version information and exit.
.TP
.B \-h, \-\-help
Display usage information.
.TP
-.B \-m, \-\-no\-magick
-Use file(1) and rdjpgcom(1) to determine the image size, instead
-of identify(1) from the ImageMagick suite, which is the default.
-.TP
.B \-d <directory>, \-\-document\-root <directory>
Directory where aboslute image filenames (i.e, ones which contain
a leading "/") may be found.
.TP
-.B \-l, \-\-lowercase
-Output HTML tag attributes imgsizer generates in lowercase.
+.B \-n, \-\-no-overwwrite
.TP
-.B \-q, \-\-no\-quotes
-Omit the quotes in HTML tag attributes imgsizer generates.
.SH DESCRIPTION
The
.B imgsizer
argument file is not actually modified until the script completes a
successful conversion pass.
.PP
-The \-q option suppresses generation of string quotes in generated
-attributes. The \-l option generates attributes in lower case.
The \-d <directory> option sets the DocumentRoot, which images with an
absolute filename (i.e., ones which contain a leading "/") may be found.
If none is specified, the DocumentRoot defaults to the current working
directory.
.PP
+The \-n (no-overwrite) opion prevents the program from overwriting
+existing width and height tags if both are present.
+.PP
Additional options may also be specified in the environmental variable
-"IMGSIZER". For example, to avoid typing "imgsizer -options ..." each
+"IMGSIZER". For example, to avoid typing "imgsizer -d /var/www/docs" each
time imgsizer is invoked, you might tell sh (or one of its descendants):
.PP
.RS
-IMGSIZER="\-options"; export IMGSIZER
+IMGSIZER="\-d /var/www/docs"; export IMGSIZER
.RE
.PP
or, if you use csh:
.PP
.RS
-setenv IMGSIZER "\-options"
+setenv IMGSIZER "\-d /var/www/docs"
.RE
.PP
-This script is written in Perl, and thus requires a Perl interpreter
+This script is written in Python, and thus requires a Python interpreter
on the host system. It also requires either the
.IR identify (1)
-utility distributed in the freeware ImageMagick suite of image-display
+utility distributed in the open-source ImageMagick suite of image-display
and manipulation tools, or, with the \-\-no\-magick option, a modern
version of
.IR file (1)
and
-.IR rdjpgcom (1)
-(the --no-magick mode is faster). These
-utilities are used to extract sizes from the images;
+.IR rdjpgcom (1).
+These utilities are used to extract sizes from the images;
.B imgsizer
itself has no knowledge of graphics formats. The script will handle any
image format known to
including PNG, GIF, JPEG, XBM, XPM, PostScript, BMP, TIFF, and anything else
even remotely likely to show up as an inline image.
.PP
-In order for
-.B imgsizer
-to correctly interpret remote URLs, the freeware script
-.IR httpget (1)
-must be executable and present in your execution path;
-.B imgsizer
-will complain and fail if either the required size-reporting tools or
-.IR httpget (1)
-are needed and not present.
+.SH NOTE
+The -q, -l, and -m options of the 1.0 versions are gone. What they
+used to do has been made unnecessary by smarter logic.
.SH AUTHOR
Originally created by Eric\ S.\ Raymond\ <esr@thyrsus.com>. Additional code
contributed by Erik\ Rossen and Michael\ C.\ Toren\ <michael@toren.net>.
-.SH AVAILABILITY
For updates, see <http://www.tuxedo.org/~esr>
.SH SEE ALSO
.IR identify (1),
-.IR httpget (1).
+.IR file (1),
+.IR rdjpgcom(1).