[OpenMadrigal-developers] script to convert postscript plots to png and jpeg

Christian Haggstrom c00chm at cs.umu.se
Thu Apr 15 07:07:36 EDT 2004


Hi Madrigal people,

The plots and images found among the experiments is often in Postscript
format, but it's easier to display them as PNG or JPG in a web browser.
Therefore I have made a python script to convert them on-the-fly using
ghostscript.

Maybe other are interested in that too, so I decided to send it onto this list.
It's not written in the same style as the other python cgi scripts,
but I can rewrite it cleaner you want it included in the CVS.

You can see what it looks like here:

http://www.eiscat.se/madrigal/cgi-bin/madExperiment.cgi?exp=2003%2feis%2f18dec03&displayLevel=0&expTitle=tau8_60

if you come from a trusted IP, you'll see links to the postscript source file
as well.

Attached you will find a patch that can be applied onto $MADROOT.
Suggestions and comments is of course welcome.
-- 
Christian Häggström  | web maintainer at www.eiscat.com
-------------- next part --------------
Index: MANIFEST
===================================================================
RCS file: /opt/local/MillstoneCVS/OpenMadrigal/madroot/MANIFEST,v
retrieving revision 1.114
diff -u -r1.114 MANIFEST
--- MANIFEST	15 Mar 2004 19:56:25 -0000	1.114
+++ MANIFEST	15 Apr 2004 10:46:46 -0000
@@ -524,6 +524,7 @@
 source/madpy/scripts/cgi/getInstrumentsService.py
 source/madpy/scripts/cgi/getParametersService.py
 source/madpy/scripts/cgi/madCalculatorService.py
+source/madpy/scripts/cgi/getMadplot.py
 source/madpy/scripts/bin/rebuildInstParmTab.py
 source/madpy/scripts/bin/rebuildInstKindatTab.py
 source/madpy/scripts/bin/checkMetadata.py
Index: source/madtcl/cgi/madExperiment.cgi
===================================================================
RCS file: /opt/local/MillstoneCVS/OpenMadrigal/madroot/source/madtcl/cgi/madExperiment.cgi,v
retrieving revision 1.26
diff -u -r1.26 madExperiment.cgi
--- source/madtcl/cgi/madExperiment.cgi	5 Jun 2003 19:24:28 -0000	1.26
+++ source/madtcl/cgi/madExperiment.cgi	15 Apr 2004 10:46:46 -0000
@@ -342,55 +342,23 @@
             }
  
             # List graphics files
-            set gifList {}; set jpegList {}; set psList {}; set numberColumns 0
-            catch { [set gifList [lsort [glob $expPath/*.gif]]] }
-            catch { [set jpegList [lsort [glob $expPath/*.jpeg]]] }
-            catch { [lappend jpegList [lsort [glob $expPath/*.jpg]]] }
-            catch { [set psList [lsort [glob $expPath/*.ps.gz]]] }
-            catch { [lappend psList [lsort [glob $expPath/*.ps]]] }
-            if { [ llength $gifList ] } { incr numberColumns }
-            if { [ llength $jpegList ] } { incr numberColumns }            
-            if { [ llength $psList ] && $remoteAddress == "internal" } { incr numberColumns }
-            if { $numberColumns > 0 } {
+
+			if { $remoteAddress == "internal" } {
+				set listSources 1
+			} else {
+				set listSources 0
+			}
+			catch { exec ./getMadplot.py h $expPath $listSources } cmdOutput
+            if { "$cmdOutput" != "" } {
                 cgi_table border align=center {
                     cgi_table_row {
-                        th colspan=$numberColumns align=center "IMAGES"
-                    }
-                    cgi_table_row {            
-                        if { [ llength $gifList ] } { th align=center "GIF files" }          
-                        if { [ llength $jpegList ] } { th align=center "JPEG files" } 
-                        if { [ llength $psList ] && $remoteAddress == "internal" } { th align=center "PostScript files" }                       
-                    }           
-                    if { [ llength $gifList ] } { 
-                        cgi_table_data { 
-                            cgi_bullet_list {
-                                foreach fileEntry $gifList { 
-                                    put [cgi_url [ file tail $fileEntry ] $linkPath/[ file tail $fileEntry ] ]  
-                                    cgi_br
-                                }
-                            }
-                        }
-                    }
-                    if { [ llength $jpegList ] } { 
-                        cgi_table_data { 
-                            cgi_bullet_list {
-                                foreach fileEntry $jpegList { 
-                                    put [cgi_url [ file tail $fileEntry ] $linkPath/[ file tail $fileEntry ] ]  
-                                    cgi_br
-                                }
-                            }
-                        }
-                    }
-                    if { [ llength $psList ] && $remoteAddress == "internal" } { 
-                        cgi_table_data { 
-                            cgi_bullet_list {
-                                foreach fileEntry $psList {
-                                    put [cgi_url [ file tail $fileEntry ] $linkPath/[ file tail $fileEntry ] ] 
-                                    cgi_br
-                                }
-                            }
-                        }
+                        th "IMAGES"
                     }
+					foreach s [ split $cmdOutput "\n" ] {
+                    	cgi_table_row {            
+							td "$s"
+                    	}           
+					}
                 }
             }
  
@@ -398,6 +366,8 @@
                 # List original datasets
                 set dataList {}
                 catch { [set dataList [glob $expPath/*sigma.gz]] }
+                catch { [lappend dataList [glob $expPath/*.tar.gz]] }
+                catch { [lappend dataList [glob $expPath/*.tgz]] }
                 if { [ llength $dataList ] } {
                     cgi_p [cgi_bold "Original fitted data files"]
                     cgi_bullet_list {         
--- /dev/null	Thu Apr 15 12:45:06 2004
+++ source/madpy/scripts/cgi/getMadplot.py	Thu Apr 15 12:36:52 2004
@@ -1,0 +1,272 @@
+#!PYTHONEXE
+# list and convert the plots available from madrigal
+#
+# This file should be put in the cgi-bin directory, and the script that
+# displays experiment information should run, for example:
+#  ./getMadplot.py h 2003/eis/16dec03
+# HTML links are generated for all images found belonging to that experiment.
+# The links points back to this script, which converts from postscript the
+# the requested fileformat on-the-fly.
+#
+# External programs required: gs, gzip
+
+import sys, os, time
+
+# Import the Madrigal classes
+# check if pythonlibpath env variable exists
+# written 'PYTHON' + 'LIBPATH' to stop automatic replacement during setup
+temp = os.environ.get('PYTHON' + 'LIBPATH')
+if temp: sys.path.append(temp)
+sys.path.append('MADROOT/lib/python')
+
+import madrigal.metadata
+# realpath expands all symbolic links in MADROOT/experiments
+exp_path = os.path.realpath(madrigal.metadata.MadrigalDB().getExperimentDir())
+
+import madrigal.ui.web
+madrigalweb = madrigal.ui.web.MadrigalWeb()
+
+# These seems to be neccesary on www.eiscat.com... ugly
+os.environ['GS_FONTPATH'] = os.environ.get('GS_FONTPATH', '.')+':/usr/openwin/lib/X11/fonts/Type1/outline'
+os.environ['GS_LIB'] = os.environ.get('GS_LIB', '.')+':/usr/openwin/lib/X11/fonts/Type1:/usr/openwin/lib/X11/fonts/Type3:/usr/local/share/ghostscript/8.00/lib/fonts'
+
+# Here is the table of all known filetypes. It should be easy to add more.
+# Each filetype have a 5-tuple;
+#   extension
+#   command to convert from postscript data on stdin
+#   likewise for compressed postscript. Leave as None to use gzip in combination with above command.
+#   Short desctiprion. None here omits it when the filetypes are listed.
+#   whether the original file is used. Only IP:s listed in trustedIPs.txt may download these.
+#      Set this to False for lossy conversions.
+
+gs_base = "gs -dNOPAUSE -q -sDEVICE=%s -sOutputFile=- -_"
+formats = (
+	# (ext, ps-command, ps.gz-command, desc, orig)
+	(".ps.gz", 'gzip -c', 'cat', 'Gzipped PostScript', True),
+	(".eps.gz", 'gzip -c', 'cat', None, True),
+	(".ps", 'cat', 'gunzip -c', 'PostScript', True),
+	(".eps", 'cat', 'gunzip -c', None, True),
+	(".png", gs_base%'png256 -g580x820',None,'PNG image', False),
+	(".jpg", gs_base%'jpeg -g580x820',None,'JPEG image', False),
+	(".pdf", gs_base%'pdfwrite -sPAPERSIZE=a4',None,'Adobe PDF', False),
+	(".gif", None, None, 'GIF image', False),
+)
+
+script_name = os.path.basename(sys.argv[0])
+
+class error(Exception):
+	"Base class for errors generated in this file"
+	pass
+
+def exist_file(base, ext):
+	path = base+ext
+	if os.path.exists(path):
+		return path
+def exist_ps(base, ext = ''):
+	for ext0 in ('.ps', '.eps'):
+		path = exist_file(base+ext0,ext)
+		if path: return path
+
+def cgi_file():
+	"Get the requested file from the CGI environment"
+	file = os.environ.get('QUERY_STRING') or os.environ['PATH_INFO']
+	if file[0] == '/': file = file[1:]
+	return file
+
+def validate_path(path):
+	"Check the path for evil things like /../ and return a tuple (full_path, last_path)"
+	file = os.path.realpath(os.path.join(exp_path,path))
+	if not file.startswith(exp_path):
+		raise error, "Illegal path"
+	return file, file[len(exp_path)+1:]
+
+english_weekdays = "Mon Tue Wed Thu Fri Sat Sun".split()
+english_months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split()
+
+def rfc1123(date):
+	"Convert a date to RFC1123 standard, to be used in HTTP headers"
+	date = time.gmtime(date)
+	return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (english_weekdays[date[6]], date[2], english_months[date[1] - 1], date[0], date[3], date[4], date[5])
+
+def process(file):
+	file, _ = validate_path(file)
+	req_file = os.path.basename(file)
+	for ext, cmd, cmd_gz, desc, source in formats:
+		if file.endswith(ext):
+			break
+	else:
+		raise error, "Extension not supported"
+	if not isTrusted and source:
+		raise error, "You are not allowed to download source files"
+	if os.path.exists(file):
+		cmd = "cat"
+		src = file
+	else:
+		file = file[:-len(ext)]
+		src = exist_ps(file)
+		if not src:
+			if not cmd:
+				raise error, "File not found"
+			src = exist_ps(file, '.gz')
+			if src:
+				cmd = cmd_gz or 'gunzip -c | '+cmd
+	if not src:
+		raise error, "Source file not found"
+
+	import mimetypes
+	mimetype, encoding = mimetypes.guess_type(req_file, strict=0)
+
+	print "Content-Type:",mimetype
+	if encoding:
+		print "Content-Encoding:", encoding
+	print "Last-Modified:", rfc1123(os.path.getmtime(src))
+	print
+	cmd = cmd.split(' | ')
+	cmd[0] += ' < '+src
+	cmd = ' | '.join(cmd)
+	return cmd
+
+def list_formats():
+	for ext, cmd, cmd_gz, desc, _ in formats:
+		print ext
+
+def list_files(base):
+	path, base = validate_path(base)
+	dict = {}
+	for ext, _, _, desc, source in formats:
+		if not os.path.exists(path+ext): continue
+		if not source:
+			if desc:
+				dict[ext] = None
+			continue
+		for ext, cmd, _, desc, source in formats:
+			if cmd and desc and (isTrusted or not source):
+				dict[ext] = None
+	if not dict:
+		raise error, "File not found"
+	return dict.keys()
+
+def list_directory(dir):
+	realdir, dir = validate_path(dir)
+	if not os.path.isdir(realdir): raise error, "Not a directory"
+	files = {}
+	for file in os.listdir(realdir):
+		for ext, _, _, desc, source in formats:
+			if file.endswith(ext): break
+		else: continue
+		base = file[:-len(ext)]
+		dict = files.setdefault(base, {})
+		if not source:
+			if desc:
+				dict[ext] = None
+			continue
+		for ext, cmd, _, desc, source in formats:
+			if cmd and desc and (isTrusted or not source):
+				dict[ext] = None
+	for base in files.keys():
+		files[base] = files[base].keys()
+	return files
+
+class Style:
+	def run_cmd(self, cmd):
+		sys.stdout.flush()
+		os.execl('/bin/sh', 'sh', '-c', cmd)
+
+	def process_query(self, file):
+		try:
+			_, file = validate_path(file)
+		except error, mess:
+			self.die(mess)
+		try:
+			cmd = process(file)
+		except error, mess0:
+			try:
+				files = list_directory(file)
+			except error, mess1:
+				try:
+					files = list_files(file)
+				except error, mess2:
+					self.die('\n'.join(map(str, (mess0, mess1, mess2))))
+				else:
+					self.output_alternatives(file, files)
+			else:
+				self.output_directory(file, files)
+		else:
+			self.run_cmd(cmd)
+
+	def output_directory(self, dir, files):
+		for base, exts in files.items():
+			self.output_alternatives(dir+'/'+base, exts)
+
+	def output_alternatives(self, base, exts):
+		print os.path.basename(base),
+		for ext in exts:
+			print '<a href="%s/%s%s">%s</a>'%(script_name, base, ext, ext),
+		print
+
+	def die(self, mess):
+		print mess
+		sys.exit()
+
+class CGIStyle(Style):
+	def output_directory(self, dir, files):
+		print "Content-type: text/html\n"
+		for base, exts in files.items():
+			Style.output_alternatives(self, dir+'/'+base, exts)
+			print '<br>'
+	def output_alternatives(self, base, exts):
+		print "Content-type: text/html\n"
+		Style.output_alternatives(self, base, exts)
+	def die(self, mess):
+		print "Content-type: text/plain\n"
+		Style.die(self, mess)
+
+class HtmlStyle(Style):
+	def process_query(self, file):
+		try:
+			_, file = validate_path(file)
+		except error, mess:
+			self.die(mess)
+		try:
+			files = list_directory(file)
+		except error, mess1:
+			try:
+				files = list_files(file)
+			except error, mess2:
+				self.die('\n'.join(map(str, (mess1, mess2))))
+			else:
+				self.output_alternatives(file, files)
+		else:
+			self.output_directory(file, files)
+	def run_cmd(self, cmd):
+		print "ERROR - Tried to run",cmd
+	
+class TestStyle(CGIStyle):
+	def __init__(self, file):
+		self.outputfile = os.path.basename(file)
+	def run_cmd(self, cmd):
+		cmd += " | cat > /tmp/"+self.outputfile
+		cmd += " ; file /tmp/"+self.outputfile
+		print cmd
+		sys.stdout.flush()
+		os.execl('/bin/sh', 'sh', '-c', cmd)
+
+if __name__ == '__main__':
+	if len(sys.argv) > 1:
+		if sys.argv[1] == 'l':	# list all available extensions.
+			isTrusted = True
+			list_formats()
+			sys.exit()
+		elif sys.argv[1] == 't':	# test; show the commands on stdout.
+			isTrusted = True
+			TestStyle(sys.argv[2]).process_query(sys.argv[2])
+			sys.exit()
+		elif sys.argv[1] == 'h':	# html. Generate links to the available formats.
+			isTrusted = madrigalweb.isTrusted()
+			HtmlStyle().process_query(sys.argv[2])
+			sys.exit()
+	if os.environ.get('GATEWAY_INTERFACE'):
+		isTrusted = madrigalweb.isTrusted()
+		CGIStyle().process_query(cgi_file())
+	else:
+		print "Use option t to test it from command line"


More information about the OpenMadrigal-developers mailing list