diff --git a/wiki/parser/EXIF.py b/wiki/parser/EXIF.py index ab9dee0f..ab6d65b2 100644 --- a/wiki/parser/EXIF.py +++ b/wiki/parser/EXIF.py @@ -1,1193 +1,120 @@ -# Library to extract EXIF information in digital camera image files -# -# To use this library call with: -# f=open(path_name, 'rb') -# tags=EXIF.process_file(f) -# tags will now be a dictionary mapping names of EXIF tags to their -# values in the file named by path_name. You can process the tags -# as you wish. In particular, you can iterate through all the tags with: -# for tag in tags.keys(): -# if tag not in ('JPEGThumbnail', 'TIFFThumbnail', 'Filename', -# 'EXIF MakerNote'): -# print "Key: %s, value %s" % (tag, tags[tag]) -# (This code uses the if statement to avoid printing out a few of the -# tags that tend to be long or boring.) -# -# The tags dictionary will include keys for all of the usual EXIF -# tags, and will also include keys for Makernotes used by some -# cameras, for which we have a good specification. -# -# Contains code from "exifdump.py" originally written by Thierry Bousch -# and released into the public domain. -# -# Updated and turned into general-purpose library by Gene Cash -# -# This copyright license is intended to be similar to the FreeBSD license. -# -# Copyright 2002 Gene Cash All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the -# distribution. -# -# THIS SOFTWARE IS PROVIDED BY GENE CASH ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# This means you may do anything you want with this code, except claim you -# wrote it. Also, if it breaks you get to keep both pieces. -# -# Patch Contributors: -# * Simon J. Gerraty -# s2n fix & orientation decode -# * John T. Riedl -# Added support for newer Nikon type 3 Makernote format for D70 and some -# other Nikon cameras. -# * Joerg Schaefer -# Fixed subtle bug when faking an EXIF header, which affected maker notes -# using relative offsets, and a fix for Nikon D100. -# -# 21-AUG-99 TB Last update by Thierry Bousch to his code. -# 17-JAN-02 CEC Discovered code on web. -# Commented everything. -# Made small code improvements. -# Reformatted for readability. -# 19-JAN-02 CEC Added ability to read TIFFs and JFIF-format JPEGs. -# Added ability to extract JPEG formatted thumbnail. -# Added ability to read GPS IFD (not tested). -# Converted IFD data structure to dictionaries indexed by -# tag name. -# Factored into library returning dictionary of IFDs plus -# thumbnail, if any. -# 20-JAN-02 CEC Added MakerNote processing logic. -# Added Olympus MakerNote. -# Converted data structure to single-level dictionary, avoiding -# tag name collisions by prefixing with IFD name. This makes -# it much easier to use. -# 23-JAN-02 CEC Trimmed nulls from end of string values. -# 25-JAN-02 CEC Discovered JPEG thumbnail in Olympus TIFF MakerNote. -# 26-JAN-02 CEC Added ability to extract TIFF thumbnails. -# Added Nikon, Fujifilm, Casio MakerNotes. -# 30-NOV-03 CEC Fixed problem with canon_decode_tag() not creating an -# IFD_Tag() object. -# 15-FEB-04 CEC Finally fixed bit shift warning by converting Y to 0L. -# +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - Portail parser -# field type descriptions as (length, abbreviation, full name) tuples -FIELD_TYPES=( - (0, 'X', 'Proprietary'), # no such type - (1, 'B', 'Byte'), - (1, 'A', 'ASCII'), - (2, 'S', 'Short'), - (4, 'L', 'Long'), - (8, 'R', 'Ratio'), - (1, 'SB', 'Signed Byte'), - (1, 'U', 'Undefined'), - (2, 'SS', 'Signed Short'), - (4, 'SL', 'Signed Long'), - (8, 'SR', 'Signed Ratio') - ) + PURPOSE: + une boite jolie -# dictionary of main EXIF tag names -# first element of tuple is tag name, optional second element is -# another dictionary giving names to values -EXIF_TAGS={ - 0x0100: ('ImageWidth', ), - 0x0101: ('ImageLength', ), - 0x0102: ('BitsPerSample', ), - 0x0103: ('Compression', - {1: 'Uncompressed TIFF', - 6: 'JPEG Compressed'}), - 0x0106: ('PhotometricInterpretation', ), - 0x010A: ('FillOrder', ), - 0x010D: ('DocumentName', ), - 0x010E: ('ImageDescription', ), - 0x010F: ('Make', ), - 0x0110: ('Model', ), - 0x0111: ('StripOffsets', ), - 0x0112: ('Orientation', - {1: 'Horizontal (normal)', - 2: 'Mirrored horizontal', - 3: 'Rotated 180', - 4: 'Mirrored vertical', - 5: 'Mirrored horizontal then rotated 90 CCW', - 6: 'Rotated 90 CW', - 7: 'Mirrored horizontal then rotated 90 CW', - 8: 'Rotated 90 CCW'}), - 0x0115: ('SamplesPerPixel', ), - 0x0116: ('RowsPerStrip', ), - 0x0117: ('StripByteCounts', ), - 0x011A: ('XResolution', ), - 0x011B: ('YResolution', ), - 0x011C: ('PlanarConfiguration', ), - 0x0128: ('ResolutionUnit', - {1: 'Not Absolute', - 2: 'Pixels/Inch', - 3: 'Pixels/Centimeter'}), - 0x012D: ('TransferFunction', ), - 0x0131: ('Software', ), - 0x0132: ('DateTime', ), - 0x013B: ('Artist', ), - 0x013E: ('WhitePoint', ), - 0x013F: ('PrimaryChromaticities', ), - 0x0156: ('TransferRange', ), - 0x0200: ('JPEGProc', ), - 0x0201: ('JPEGInterchangeFormat', ), - 0x0202: ('JPEGInterchangeFormatLength', ), - 0x0211: ('YCbCrCoefficients', ), - 0x0212: ('YCbCrSubSampling', ), - 0x0213: ('YCbCrPositioning', ), - 0x0214: ('ReferenceBlackWhite', ), - 0x828D: ('CFARepeatPatternDim', ), - 0x828E: ('CFAPattern', ), - 0x828F: ('BatteryLevel', ), - 0x8298: ('Copyright', ), - 0x829A: ('ExposureTime', ), - 0x829D: ('FNumber', ), - 0x83BB: ('IPTC/NAA', ), - 0x8769: ('ExifOffset', ), - 0x8773: ('InterColorProfile', ), - 0x8822: ('ExposureProgram', - {0: 'Unidentified', - 1: 'Manual', - 2: 'Program Normal', - 3: 'Aperture Priority', - 4: 'Shutter Priority', - 5: 'Program Creative', - 6: 'Program Action', - 7: 'Portrait Mode', - 8: 'Landscape Mode'}), - 0x8824: ('SpectralSensitivity', ), - 0x8825: ('GPSInfo', ), - 0x8827: ('ISOSpeedRatings', ), - 0x8828: ('OECF', ), - # print as string - #0x9000: ('ExifVersion', lambda x: ''.join(map(chr, x))), - 0x9003: ('DateTimeOriginal', ), - 0x9004: ('DateTimeDigitized', ), - 0x9101: ('ComponentsConfiguration', - {0: '', - 1: 'Y', - 2: 'Cb', - 3: 'Cr', - 4: 'Red', - 5: 'Green', - 6: 'Blue'}), - 0x9102: ('CompressedBitsPerPixel', ), - 0x9201: ('ShutterSpeedValue', ), - 0x9202: ('ApertureValue', ), - 0x9203: ('BrightnessValue', ), - 0x9204: ('ExposureBiasValue', ), - 0x9205: ('MaxApertureValue', ), - 0x9206: ('SubjectDistance', ), - 0x9207: ('MeteringMode', - {0: 'Unidentified', - 1: 'Average', - 2: 'CenterWeightedAverage', - 3: 'Spot', - 4: 'MultiSpot'}), - 0x9208: ('LightSource', - {0: 'Unknown', - 1: 'Daylight', - 2: 'Fluorescent', - 3: 'Tungsten', - 10: 'Flash', - 17: 'Standard Light A', - 18: 'Standard Light B', - 19: 'Standard Light C', - 20: 'D55', - 21: 'D65', - 22: 'D75', - 255: 'Other'}), - 0x9209: ('Flash', {0: 'No', - 1: 'Fired', - 5: 'Fired (?)', # no return sensed - 7: 'Fired (!)', # return sensed - 9: 'Fill Fired', - 13: 'Fill Fired (?)', - 15: 'Fill Fired (!)', - 16: 'Off', - 24: 'Auto Off', - 25: 'Auto Fired', - 29: 'Auto Fired (?)', - 31: 'Auto Fired (!)', - 32: 'Not Available'}), - 0x920A: ('FocalLength', ), - 0x927C: ('MakerNote', ), - # print as string - #0x9286: ('UserComment', lambda x: ''.join(map(chr, x))), - 0x9290: ('SubSecTime', ), - 0x9291: ('SubSecTimeOriginal', ), - 0x9292: ('SubSecTimeDigitized', ), - # print as string - #0xA000: ('FlashPixVersion', lambda x: ''.join(map(chr, x))), - 0xA001: ('ColorSpace', ), - 0xA002: ('ExifImageWidth', ), - 0xA003: ('ExifImageLength', ), - 0xA005: ('InteroperabilityOffset', ), - 0xA20B: ('FlashEnergy', ), # 0x920B in TIFF/EP - 0xA20C: ('SpatialFrequencyResponse', ), # 0x920C - - - 0xA20E: ('FocalPlaneXResolution', ), # 0x920E - - - 0xA20F: ('FocalPlaneYResolution', ), # 0x920F - - - 0xA210: ('FocalPlaneResolutionUnit', ), # 0x9210 - - - 0xA214: ('SubjectLocation', ), # 0x9214 - - - 0xA215: ('ExposureIndex', ), # 0x9215 - - - 0xA217: ('SensingMethod', ), # 0x9217 - - - 0xA300: ('FileSource', - {3: 'Digital Camera'}), - 0xA301: ('SceneType', - {1: 'Directly Photographed'}), - 0xA302: ('CVAPattern',), - } + CALLING SEQUENCE: + {{{ + #!Box titre + blablabla + + tables, images.... + }}} -# interoperability tags -INTR_TAGS={ - 0x0001: ('InteroperabilityIndex', ), - 0x0002: ('InteroperabilityVersion', ), - 0x1000: ('RelatedImageFileFormat', ), - 0x1001: ('RelatedImageWidth', ), - 0x1002: ('RelatedImageLength', ), - } +""" +from MoinMoin.parser import wiki +import os,string,re,StringIO +from MoinMoin.action import AttachFile -# GPS tags (not used yet, haven't seen camera with GPS) -GPS_TAGS={ - 0x0000: ('GPSVersionID', ), - 0x0001: ('GPSLatitudeRef', ), - 0x0002: ('GPSLatitude', ), - 0x0003: ('GPSLongitudeRef', ), - 0x0004: ('GPSLongitude', ), - 0x0005: ('GPSAltitudeRef', ), - 0x0006: ('GPSAltitude', ), - 0x0007: ('GPSTimeStamp', ), - 0x0008: ('GPSSatellites', ), - 0x0009: ('GPSStatus', ), - 0x000A: ('GPSMeasureMode', ), - 0x000B: ('GPSDOP', ), - 0x000C: ('GPSSpeedRef', ), - 0x000D: ('GPSSpeed', ), - 0x000E: ('GPSTrackRef', ), - 0x000F: ('GPSTrack', ), - 0x0010: ('GPSImgDirectionRef', ), - 0x0011: ('GPSImgDirection', ), - 0x0012: ('GPSMapDatum', ), - 0x0013: ('GPSDestLatitudeRef', ), - 0x0014: ('GPSDestLatitude', ), - 0x0015: ('GPSDestLongitudeRef', ), - 0x0016: ('GPSDestLongitude', ), - 0x0017: ('GPSDestBearingRef', ), - 0x0018: ('GPSDestBearing', ), - 0x0019: ('GPSDestDistanceRef', ), - 0x001A: ('GPSDestDistance', ) - } +color_list = ['orange', 'black', 'red', 'green', 'blue', 'gray'] -# Nikon E99x MakerNote Tags -# http://members.tripod.com/~tawba/990exif.htm -MAKERNOTE_NIKON_NEWER_TAGS={ - 0x0002: ('ISOSetting', ), - 0x0003: ('ColorMode', ), - 0x0004: ('Quality', ), - 0x0005: ('Whitebalance', ), - 0x0006: ('ImageSharpening', ), - 0x0007: ('FocusMode', ), - 0x0008: ('FlashSetting', ), - 0x0009: ('AutoFlashMode', ), - 0x000B: ('WhiteBalanceBias', ), - 0x000C: ('WhiteBalanceRBCoeff', ), - 0x000F: ('ISOSelection', ), - 0x0012: ('FlashCompensation', ), - 0x0013: ('ISOSpeedRequested', ), - 0x0016: ('PhotoCornerCoordinates', ), - 0x0018: ('FlashBracketCompensationApplied', ), - 0x0019: ('AEBracketCompensationApplied', ), - 0x0080: ('ImageAdjustment', ), - 0x0081: ('ToneCompensation', ), - 0x0082: ('AuxiliaryLens', ), - 0x0083: ('LensType', ), - 0x0084: ('LensMinMaxFocalMaxAperture', ), - 0x0085: ('ManualFocusDistance', ), - 0x0086: ('DigitalZoomFactor', ), - 0x0088: ('AFFocusPosition', - {0x0000: 'Center', - 0x0100: 'Top', - 0x0200: 'Bottom', - 0x0300: 'Left', - 0x0400: 'Right'}), - 0x0089: ('BracketingMode', - {0x00: 'Single frame, no bracketing', - 0x01: 'Continuous, no bracketing', - 0x02: 'Timer, no bracketing', - 0x10: 'Single frame, exposure bracketing', - 0x11: 'Continuous, exposure bracketing', - 0x12: 'Timer, exposure bracketing', - 0x40: 'Single frame, white balance bracketing', - 0x41: 'Continuous, white balance bracketing', - 0x42: 'Timer, white balance bracketing'}), - 0x008D: ('ColorMode', ), - 0x008F: ('SceneMode?', ), - 0x0090: ('LightingType', ), - 0x0092: ('HueAdjustment', ), - 0x0094: ('Saturation', - {-3: 'B&W', - -2: '-2', - -1: '-1', - 0: '0', - 1: '1', - 2: '2'}), - 0x0095: ('NoiseReduction', ), - 0x00A7: ('TotalShutterReleases', ), - 0x00A9: ('ImageOptimization', ), - 0x00AA: ('Saturation', ), - 0x00AB: ('DigitalVariProgram', ), - 0x0010: ('DataDump', ) - } -MAKERNOTE_NIKON_OLDER_TAGS={ - 0x0003: ('Quality', - {1: 'VGA Basic', - 2: 'VGA Normal', - 3: 'VGA Fine', - 4: 'SXGA Basic', - 5: 'SXGA Normal', - 6: 'SXGA Fine'}), - 0x0004: ('ColorMode', - {1: 'Color', - 2: 'Monochrome'}), - 0x0005: ('ImageAdjustment', - {0: 'Normal', - 1: 'Bright+', - 2: 'Bright-', - 3: 'Contrast+', - 4: 'Contrast-'}), - 0x0006: ('CCDSpeed', - {0: 'ISO 80', - 2: 'ISO 160', - 4: 'ISO 320', - 5: 'ISO 100'}), - 0x0007: ('WhiteBalance', - {0: 'Auto', - 1: 'Preset', - 2: 'Daylight', - 3: 'Incandescent', - 4: 'Fluorescent', - 5: 'Cloudy', - 6: 'Speed Light'}) - } +##################################################################### +# Fonctions # +##################################################################### +# to_wikiname : transfome la chaine text avec le parser wiki +# classique et le formateur formatter +# (je sais pas a quoi sert request, mais faut pas +# l'enlever !) +####### -# decode Olympus SpecialMode tag in MakerNote -def olympus_special_mode(v): - a={ - 0: 'Normal', - 1: 'Unknown', - 2: 'Fast', - 3: 'Panorama'} - b={ - 0: 'Non-panoramic', - 1: 'Left to right', - 2: 'Right to left', - 3: 'Bottom to top', - 4: 'Top to bottom'} - return '%s - sequence %d - %s' % (a[v[0]], v[1], b[v[2]]) +def to_wikiname(request,formatter,text): + ##taken from MiniPage + out=StringIO.StringIO() + request.redirect(out) + wikiizer = wiki.Parser(text,request) + wikiizer.format(formatter) + result=out.getvalue() + request.redirect() + del out + + return result.strip() + +##################################################################### +# BoxFormatter : creer le code html +####### +class BoxFormatter: + + def __init__(self, request, formatter): + self.formatter = formatter + self.request = request + self.counter = 0 -MAKERNOTE_OLYMPUS_TAGS={ - # ah HAH! those sneeeeeaky bastids! this is how they get past the fact - # that a JPEG thumbnail is not allowed in an uncompressed TIFF file - 0x0100: ('JPEGThumbnail', ), - 0x0200: ('SpecialMode', olympus_special_mode), - 0x0201: ('JPEGQual', - {1: 'SQ', - 2: 'HQ', - 3: 'SHQ'}), - 0x0202: ('Macro', - {0: 'Normal', - 1: 'Macro'}), - 0x0204: ('DigitalZoom', ), - 0x0207: ('SoftwareRelease', ), - 0x0208: ('PictureInfo', ), - # print as string - 0x0209: ('CameraID', lambda x: ''.join(map(chr, x))), - 0x0F00: ('DataDump', ) - } - -MAKERNOTE_CASIO_TAGS={ - 0x0001: ('RecordingMode', - {1: 'Single Shutter', - 2: 'Panorama', - 3: 'Night Scene', - 4: 'Portrait', - 5: 'Landscape'}), - 0x0002: ('Quality', - {1: 'Economy', - 2: 'Normal', - 3: 'Fine'}), - 0x0003: ('FocusingMode', - {2: 'Macro', - 3: 'Auto Focus', - 4: 'Manual Focus', - 5: 'Infinity'}), - 0x0004: ('FlashMode', - {1: 'Auto', - 2: 'On', - 3: 'Off', - 4: 'Red Eye Reduction'}), - 0x0005: ('FlashIntensity', - {11: 'Weak', - 13: 'Normal', - 15: 'Strong'}), - 0x0006: ('Object Distance', ), - 0x0007: ('WhiteBalance', - {1: 'Auto', - 2: 'Tungsten', - 3: 'Daylight', - 4: 'Fluorescent', - 5: 'Shade', - 129: 'Manual'}), - 0x000B: ('Sharpness', - {0: 'Normal', - 1: 'Soft', - 2: 'Hard'}), - 0x000C: ('Contrast', - {0: 'Normal', - 1: 'Low', - 2: 'High'}), - 0x000D: ('Saturation', - {0: 'Normal', - 1: 'Low', - 2: 'High'}), - 0x0014: ('CCDSpeed', - {64: 'Normal', - 80: 'Normal', - 100: 'High', - 125: '+1.0', - 244: '+3.0', - 250: '+2.0',}) - } - -MAKERNOTE_FUJIFILM_TAGS={ - 0x0000: ('NoteVersion', lambda x: ''.join(map(chr, x))), - 0x1000: ('Quality', ), - 0x1001: ('Sharpness', - {1: 'Soft', - 2: 'Soft', - 3: 'Normal', - 4: 'Hard', - 5: 'Hard'}), - 0x1002: ('WhiteBalance', - {0: 'Auto', - 256: 'Daylight', - 512: 'Cloudy', - 768: 'DaylightColor-Fluorescent', - 769: 'DaywhiteColor-Fluorescent', - 770: 'White-Fluorescent', - 1024: 'Incandescent', - 3840: 'Custom'}), - 0x1003: ('Color', - {0: 'Normal', - 256: 'High', - 512: 'Low'}), - 0x1004: ('Tone', - {0: 'Normal', - 256: 'High', - 512: 'Low'}), - 0x1010: ('FlashMode', - {0: 'Auto', - 1: 'On', - 2: 'Off', - 3: 'Red Eye Reduction'}), - 0x1011: ('FlashStrength', ), - 0x1020: ('Macro', - {0: 'Off', - 1: 'On'}), - 0x1021: ('FocusMode', - {0: 'Auto', - 1: 'Manual'}), - 0x1030: ('SlowSync', - {0: 'Off', - 1: 'On'}), - 0x1031: ('PictureMode', - {0: 'Auto', - 1: 'Portrait', - 2: 'Landscape', - 4: 'Sports', - 5: 'Night', - 6: 'Program AE', - 256: 'Aperture Priority AE', - 512: 'Shutter Priority AE', - 768: 'Manual Exposure'}), - 0x1100: ('MotorOrBracket', - {0: 'Off', - 1: 'On'}), - 0x1300: ('BlurWarning', - {0: 'Off', - 1: 'On'}), - 0x1301: ('FocusWarning', - {0: 'Off', - 1: 'On'}), - 0x1302: ('AEWarning', - {0: 'Off', - 1: 'On'}) - } - -MAKERNOTE_CANON_TAGS={ - 0x0006: ('ImageType', ), - 0x0007: ('FirmwareVersion', ), - 0x0008: ('ImageNumber', ), - 0x0009: ('OwnerName', ) - } - -# see http://www.burren.cx/david/canon.html by David Burren -# this is in element offset, name, optional value dictionary format -MAKERNOTE_CANON_TAG_0x001={ - 1: ('Macromode', - {1: 'Macro', - 2: 'Normal'}), - 2: ('SelfTimer', ), - 3: ('Quality', - {2: 'Normal', - 3: 'Fine', - 5: 'Superfine'}), - 4: ('FlashMode', - {0: 'Flash Not Fired', - 1: 'Auto', - 2: 'On', - 3: 'Red-Eye Reduction', - 4: 'Slow Synchro', - 5: 'Auto + Red-Eye Reduction', - 6: 'On + Red-Eye Reduction', - 16: 'external flash'}), - 5: ('ContinuousDriveMode', - {0: 'Single Or Timer', - 1: 'Continuous'}), - 7: ('FocusMode', - {0: 'One-Shot', - 1: 'AI Servo', - 2: 'AI Focus', - 3: 'MF', - 4: 'Single', - 5: 'Continuous', - 6: 'MF'}), - 10: ('ImageSize', - {0: 'Large', - 1: 'Medium', - 2: 'Small'}), - 11: ('EasyShootingMode', - {0: 'Full Auto', - 1: 'Manual', - 2: 'Landscape', - 3: 'Fast Shutter', - 4: 'Slow Shutter', - 5: 'Night', - 6: 'B&W', - 7: 'Sepia', - 8: 'Portrait', - 9: 'Sports', - 10: 'Macro/Close-Up', - 11: 'Pan Focus'}), - 12: ('DigitalZoom', - {0: 'None', - 1: '2x', - 2: '4x'}), - 13: ('Contrast', - {0xFFFF: 'Low', - 0: 'Normal', - 1: 'High'}), - 14: ('Saturation', - {0xFFFF: 'Low', - 0: 'Normal', - 1: 'High'}), - 15: ('Sharpness', - {0xFFFF: 'Low', - 0: 'Normal', - 1: 'High'}), - 16: ('ISO', - {0: 'See ISOSpeedRatings Tag', - 15: 'Auto', - 16: '50', - 17: '100', - 18: '200', - 19: '400'}), - 17: ('MeteringMode', - {3: 'Evaluative', - 4: 'Partial', - 5: 'Center-weighted'}), - 18: ('FocusType', - {0: 'Manual', - 1: 'Auto', - 3: 'Close-Up (Macro)', - 8: 'Locked (Pan Mode)'}), - 19: ('AFPointSelected', - {0x3000: 'None (MF)', - 0x3001: 'Auto-Selected', - 0x3002: 'Right', - 0x3003: 'Center', - 0x3004: 'Left'}), - 20: ('ExposureMode', - {0: 'Easy Shooting', - 1: 'Program', - 2: 'Tv-priority', - 3: 'Av-priority', - 4: 'Manual', - 5: 'A-DEP'}), - 23: ('LongFocalLengthOfLensInFocalUnits', ), - 24: ('ShortFocalLengthOfLensInFocalUnits', ), - 25: ('FocalUnitsPerMM', ), - 28: ('FlashActivity', - {0: 'Did Not Fire', - 1: 'Fired'}), - 29: ('FlashDetails', - {14: 'External E-TTL', - 13: 'Internal Flash', - 11: 'FP Sync Used', - 7: '2nd("Rear")-Curtain Sync Used', - 4: 'FP Sync Enabled'}), - 32: ('FocusMode', - {0: 'Single', - 1: 'Continuous'}) - } - -MAKERNOTE_CANON_TAG_0x004={ - 7: ('WhiteBalance', - {0: 'Auto', - 1: 'Sunny', - 2: 'Cloudy', - 3: 'Tungsten', - 4: 'Fluorescent', - 5: 'Flash', - 6: 'Custom'}), - 9: ('SequenceNumber', ), - 14: ('AFPointUsed', ), - 15: ('FlashBias', - {0XFFC0: '-2 EV', - 0XFFCC: '-1.67 EV', - 0XFFD0: '-1.50 EV', - 0XFFD4: '-1.33 EV', - 0XFFE0: '-1 EV', - 0XFFEC: '-0.67 EV', - 0XFFF0: '-0.50 EV', - 0XFFF4: '-0.33 EV', - 0X0000: '0 EV', - 0X000C: '0.33 EV', - 0X0010: '0.50 EV', - 0X0014: '0.67 EV', - 0X0020: '1 EV', - 0X002C: '1.33 EV', - 0X0030: '1.50 EV', - 0X0034: '1.67 EV', - 0X0040: '2 EV'}), - 19: ('SubjectDistance', ) - } - -# extract multibyte integer in Motorola format (little endian) -def s2n_motorola(str): - x=0 - for c in str: - x=(x << 8) | ord(c) - return x - -# extract multibyte integer in Intel format (big endian) -def s2n_intel(str): - x=0 - y=0L - for c in str: - x=x | (ord(c) << y) - y=y+8 - return x - -# ratio object that eventually will be able to reduce itself to lowest -# common denominator for printing -def gcd(a, b): - if b == 0: - return a - else: - return gcd(b, a % b) - -class Ratio: - def __init__(self, num, den): - self.num=num - self.den=den - - def __repr__(self): - self.reduce() - if self.den == 1: - return str(self.num) - return '%d/%d' % (self.num, self.den) - - def reduce(self): - div=gcd(self.num, self.den) - if div > 1: - self.num=self.num/div - self.den=self.den/div - -# for ease of dealing with tags -class IFD_Tag: - def __init__(self, printable, tag, field_type, values, field_offset, - field_length): - # printable version of data - self.printable=printable - # tag ID number - self.tag=tag - # field type as index into FIELD_TYPES - self.field_type=field_type - # offset of start of field in bytes from beginning of IFD - self.field_offset=field_offset - # length of data field in bytes - self.field_length=field_length - # either a string or array of data items - self.values=values - - def __str__(self): - return self.printable - - def __repr__(self): - return '(0x%04X) %s=%s @ %d' % (self.tag, - FIELD_TYPES[self.field_type][2], - self.printable, - self.field_offset) - -# class that handles an EXIF header -class EXIF_header: - def __init__(self, file, endian, offset, fake_exif, debug=0): - self.file=file - self.endian=endian - self.offset=offset - self.fake_exif=fake_exif - self.debug=debug - self.tags={} - - # convert slice to integer, based on sign and endian flags - # usually this offset is assumed to be relative to the beginning of the - # start of the EXIF information. For some cameras that use relative tags, - # this offset may be relative to some other starting point. - def s2n(self, offset, length, signed=0): - self.file.seek(self.offset+offset) - slice=self.file.read(length) - if self.endian == 'I': - val=s2n_intel(slice) + def make(self, title, body_html, color=None): + if color==None: + css_color_class=u"" else: - val=s2n_motorola(slice) - # Sign extension ? - if signed: - msb=1L << (8*length-1) - if val & msb: - val=val-(msb << 1) - return val + css_color_class = u" %s_box" % color + html = [ + self.formatter.rawHTML(u'
' % css_color_class), + self.formatter.rawHTML(u'
'), + self.formatter.heading(1,3), + title, + self.formatter.heading(0,3), + self.formatter.rawHTML(u'
'), + self.formatter.rawHTML(u'
'), + self.formatter.rawHTML(u'
'), + body_html, + self.formatter.rawHTML(u'
'), + self.formatter.rawHTML(u'
'), + self.formatter.rawHTML(u'
'), + ] + self.request.write(u'\n'.join(html)) - # convert offset to string - def n2s(self, offset, length): - s='' - for i in range(length): - if self.endian == 'I': - s=s+chr(offset & 0xFF) - else: - s=chr(offset & 0xFF)+s - offset=offset >> 8 - return s +##################################################################### +# Parser : classe principale, c'est elle qui parse +####### +class Parser: - # return first IFD - def first_IFD(self): - return self.s2n(4, 4) + def __init__(self, raw, request, **kw): + self.request = request + self.raw = raw + self.settings={'title': u'|'.join([u" ".join(kw.keys()), u" ".join(kw.values())])} + self.settings = self.parseArgs(kw["format_args"]) - # return pointer to next IFD - def next_IFD(self, ifd): - entries=self.s2n(ifd, 2) - return self.s2n(ifd+2+12*entries, 4) - - # return list of IFDs in header - def list_IFDs(self): - i=self.first_IFD() - a=[] - while i: - a.append(i) - i=self.next_IFD(i) - return a - - # return list of entries in this IFD - def dump_IFD(self, ifd, ifd_name, dict=EXIF_TAGS, relative=0): - entries=self.s2n(ifd, 2) - for i in range(entries): - # entry is index of start of this IFD in the file - entry=ifd+2+12*i - tag=self.s2n(entry, 2) - # get tag name. We do it early to make debugging easier - tag_entry=dict.get(tag) - if tag_entry: - tag_name=tag_entry[0] - else: - tag_name='Tag 0x%04X' % tag - field_type=self.s2n(entry+2, 2) - if not 0 < field_type < len(FIELD_TYPES): - # unknown field type - raise ValueError, \ - 'unknown type %d in tag 0x%04X' % (field_type, tag) - typelen=FIELD_TYPES[field_type][0] - count=self.s2n(entry+4, 4) - offset=entry+8 - if count*typelen > 4: - # offset is not the value; it's a pointer to the value - # if relative we set things up so s2n will seek to the right - # place when it adds self.offset. Note that this 'relative' - # is for the Nikon type 3 makernote. Other cameras may use - # other relative offsets, which would have to be computed here - # slightly differently. - if relative: - tmp_offset=self.s2n(offset, 4) - offset=tmp_offset+ifd-self.offset+4 - if self.fake_exif: - offset=offset+18 - else: - offset=self.s2n(offset, 4) - field_offset=offset - if field_type == 2: - # special case: null-terminated ASCII string - if count != 0: - self.file.seek(self.offset+offset) - values=self.file.read(count) - values=values.strip().replace('\x00','') - else: - values='' - else: - values=[] - signed=(field_type in [6, 8, 9, 10]) - for j in range(count): - if field_type in (5, 10): - # a ratio - value_j=Ratio(self.s2n(offset, 4, signed), - self.s2n(offset+4, 4, signed)) - else: - value_j=self.s2n(offset, typelen, signed) - values.append(value_j) - offset=offset+typelen - # now "values" is either a string or an array - if count == 1 and field_type != 2: - printable=str(values[0]) - else: - printable=str(values) - # compute printable version of values - if tag_entry: - if len(tag_entry) != 1: - # optional 2nd tag element is present - if callable(tag_entry[1]): - # call mapping function - printable=tag_entry[1](values) - else: - printable='' - for i in values: - # use lookup table for this tag - printable+=tag_entry[1].get(i, repr(i)) - self.tags[ifd_name+' '+tag_name]=IFD_Tag(printable, tag, - field_type, - values, field_offset, - count*typelen) - if self.debug: - print ' debug: %s: %s' % (tag_name, - repr(self.tags[ifd_name+' '+tag_name])) - - # extract uncompressed TIFF thumbnail (like pulling teeth) - # we take advantage of the pre-existing layout in the thumbnail IFD as - # much as possible - def extract_TIFF_thumbnail(self, thumb_ifd): - entries=self.s2n(thumb_ifd, 2) - # this is header plus offset to IFD ... - if self.endian == 'M': - tiff='MM\x00*\x00\x00\x00\x08' - else: - tiff='II*\x00\x08\x00\x00\x00' - # ... plus thumbnail IFD data plus a null "next IFD" pointer - self.file.seek(self.offset+thumb_ifd) - tiff+=self.file.read(entries*12+2)+'\x00\x00\x00\x00' + def getRandomColor(self): + nb_of_colors = color_list.__len__() + from random import randint + colorNum = randint(0, nb_of_colors - 1) + return color_list[colorNum] - # fix up large value offset pointers into data area - for i in range(entries): - entry=thumb_ifd+2+12*i - tag=self.s2n(entry, 2) - field_type=self.s2n(entry+2, 2) - typelen=FIELD_TYPES[field_type][0] - count=self.s2n(entry+4, 4) - oldoff=self.s2n(entry+8, 4) - # start of the 4-byte pointer area in entry - ptr=i*12+18 - # remember strip offsets location - if tag == 0x0111: - strip_off=ptr - strip_len=count*typelen - # is it in the data area? - if count*typelen > 4: - # update offset pointer (nasty "strings are immutable" crap) - # should be able to say "tiff[ptr:ptr+4]=newoff" - newoff=len(tiff) - tiff=tiff[:ptr]+self.n2s(newoff, 4)+tiff[ptr+4:] - # remember strip offsets location - if tag == 0x0111: - strip_off=newoff - strip_len=4 - # get original data and store it - self.file.seek(self.offset+oldoff) - tiff+=self.file.read(count*typelen) - - # add pixel strips and update strip offset info - old_offsets=self.tags['Thumbnail StripOffsets'].values - old_counts=self.tags['Thumbnail StripByteCounts'].values - for i in range(len(old_offsets)): - # update offset pointer (more nasty "strings are immutable" crap) - offset=self.n2s(len(tiff), strip_len) - tiff=tiff[:strip_off]+offset+tiff[strip_off+strip_len:] - strip_off+=strip_len - # add pixel strip to end - self.file.seek(self.offset+old_offsets[i]) - tiff+=self.file.read(old_counts[i]) + def parseArgs(self, argsString): + argList = argsString.split(u',') + settings = {} + for anArg in argList: + anArg = anArg.strip(u' ') + if anArg.find(u'color=')!=-1: + theColor = anArg.split(u'=')[1].lower() + if theColor == 'random': + theColor = self.getRandomColor() + settings['color'] = theColor + else: + settings['title'] = anArg + return settings - self.tags['TIFFThumbnail']=tiff + def format(self, formatter): + quotes = self.raw - # decode all the camera-specific MakerNote formats - - # Note is the data that comprises this MakerNote. The MakerNote will - # likely have pointers in it that point to other parts of the file. We'll - # use self.offset as the starting point for most of those pointers, since - # they are relative to the beginning of the file. - # - # If the MakerNote is in a newer format, it may use relative addressing - # within the MakerNote. In that case we'll use relative addresses for the - # pointers. - # - # As an aside: it's not just to be annoying that the manufacturers use - # relative offsets. It's so that if the makernote has to be moved by the - # picture software all of the offsets don't have to be adjusted. Overall, - # this is probably the right strategy for makernotes, though the spec is - # ambiguous. (The spec does not appear to imagine that makernotes would - # follow EXIF format internally. Once they did, it's ambiguous whether - # the offsets should be from the header at the start of all the EXIF info, - # or from the header at the start of the makernote.) - def decode_maker_note(self): - note=self.tags['EXIF MakerNote'] - make=self.tags['Image Make'].printable - model=self.tags['Image Model'].printable - - # Nikon - # The maker note usually starts with the word Nikon, followed by the - # type of the makernote (1 or 2, as a short). If the word Nikon is - # not at the start of the makernote, it's probably type 2, since some - # cameras work that way. - if make in ('NIKON', 'NIKON CORPORATION'): - if note.values[0:7] == [78, 105, 107, 111, 110, 00, 01]: - if self.debug: - print "Looks like a type 1 Nikon MakerNote." - self.dump_IFD(note.field_offset+8, 'MakerNote', - dict=MAKERNOTE_NIKON_OLDER_TAGS) - elif note.values[0:7] == [78, 105, 107, 111, 110, 00, 02]: - if self.debug: - print "Looks like a labeled type 2 Nikon MakerNote" - if note.values[12:14] != [0, 42] and note.values[12:14] != [42L, 0L]: - raise ValueError, "Missing marker tag '42' in MakerNote." - # skip the Makernote label and the TIFF header - self.dump_IFD(note.field_offset+10+8, 'MakerNote', - dict=MAKERNOTE_NIKON_NEWER_TAGS, relative=1) - else: - # E99x or D1 - if self.debug: - print "Looks like an unlabeled type 2 Nikon MakerNote" - self.dump_IFD(note.field_offset, 'MakerNote', - dict=MAKERNOTE_NIKON_NEWER_TAGS) - return - - # Olympus - if make[:7] == 'OLYMPUS': - self.dump_IFD(note.field_offset+8, 'MakerNote', - dict=MAKERNOTE_OLYMPUS_TAGS) - return - - # Casio - if make == 'Casio': - self.dump_IFD(note.field_offset, 'MakerNote', - dict=MAKERNOTE_CASIO_TAGS) - return - - # Fujifilm - if make == 'FUJIFILM': - # bug: everything else is "Motorola" endian, but the MakerNote - # is "Intel" endian - endian=self.endian - self.endian='I' - # bug: IFD offsets are from beginning of MakerNote, not - # beginning of file header - offset=self.offset - self.offset+=note.field_offset - # process note with bogus values (note is actually at offset 12) - self.dump_IFD(12, 'MakerNote', dict=MAKERNOTE_FUJIFILM_TAGS) - # reset to correct values - self.endian=endian - self.offset=offset - return - - # Canon - if make == 'Canon': - self.dump_IFD(note.field_offset, 'MakerNote', - dict=MAKERNOTE_CANON_TAGS) - for i in (('MakerNote Tag 0x0001', MAKERNOTE_CANON_TAG_0x001), - ('MakerNote Tag 0x0004', MAKERNOTE_CANON_TAG_0x004)): - self.canon_decode_tag(self.tags[i[0]].values, i[1]) - return - - # decode Canon MakerNote tag based on offset within tag - # see http://www.burren.cx/david/canon.html by David Burren - def canon_decode_tag(self, value, dict): - for i in range(1, len(value)): - x=dict.get(i, ('Unknown', )) - if self.debug: - print i, x - name=x[0] - if len(x) > 1: - val=x[1].get(value[i], 'Unknown') - else: - val=value[i] - # it's not a real IFD Tag but we fake one to make everybody - # happy. this will have a "proprietary" type - self.tags['MakerNote '+name]=IFD_Tag(str(val), None, 0, None, - None, None) - -# process an image file (expects an open file object) -# this is the function that has to deal with all the arbitrary nasty bits -# of the EXIF standard -def process_file(file, debug=0): - # determine whether it's a JPEG or TIFF - data=file.read(12) - if data[0:4] in ['II*\x00', 'MM\x00*']: - # it's a TIFF file - file.seek(0) - endian=file.read(1) - file.read(1) - offset=0 - elif data[0:2] == '\xFF\xD8': - # it's a JPEG file - # skip JFIF style header(s) - fake_exif=0 - while data[2] == '\xFF' and data[6:10] in ('JFIF', 'JFXX', 'OLYM'): - length=ord(data[4])*256+ord(data[5]) - file.read(length-8) - # fake an EXIF beginning of file - data='\xFF\x00'+file.read(10) - fake_exif=1 - if data[2] == '\xFF' and data[6:10] == 'Exif': - # detected EXIF header - offset=file.tell() - endian=file.read(1) - else: - # no EXIF information - return {} - else: - # file format not recognized - return {} - - # deal with the EXIF info we found - if debug: - print {'I': 'Intel', 'M': 'Motorola'}[endian], 'format' - hdr=EXIF_header(file, endian, offset, fake_exif, debug) - ifd_list=hdr.list_IFDs() - ctr=0 - for i in ifd_list: - if ctr == 0: - IFD_name='Image' - elif ctr == 1: - IFD_name='Thumbnail' - thumb_ifd=i - else: - IFD_name='IFD %d' % ctr - if debug: - print ' IFD %d (%s) at offset %d:' % (ctr, IFD_name, i) - hdr.dump_IFD(i, IFD_name) - # EXIF IFD - exif_off=hdr.tags.get(IFD_name+' ExifOffset') - if exif_off: - if debug: - print ' EXIF SubIFD at offset %d:' % exif_off.values[0] - hdr.dump_IFD(exif_off.values[0], 'EXIF') - # Interoperability IFD contained in EXIF IFD - intr_off=hdr.tags.get('EXIF SubIFD InteroperabilityOffset') - if intr_off: - if debug: - print ' EXIF Interoperability SubSubIFD at offset %d:' \ - % intr_off.values[0] - hdr.dump_IFD(intr_off.values[0], 'EXIF Interoperability', - dict=INTR_TAGS) - # GPS IFD - gps_off=hdr.tags.get(IFD_name+' GPSInfo') - if gps_off: - if debug: - print ' GPS SubIFD at offset %d:' % gps_off.values[0] - hdr.dump_IFD(gps_off.values[0], 'GPS', dict=GPS_TAGS) - ctr+=1 - - # extract uncompressed TIFF thumbnail - thumb=hdr.tags.get('Thumbnail Compression') - if thumb and thumb.printable == 'Uncompressed TIFF': - hdr.extract_TIFF_thumbnail(thumb_ifd) - - # JPEG thumbnail (thankfully the JPEG data is stored as a unit) - thumb_off=hdr.tags.get('Thumbnail JPEGInterchangeFormat') - if thumb_off: - file.seek(offset+thumb_off.values[0]) - size=hdr.tags['Thumbnail JPEGInterchangeFormatLength'].values[0] - hdr.tags['JPEGThumbnail']=file.read(size) - - # deal with MakerNote contained in EXIF IFD - if hdr.tags.has_key('EXIF MakerNote'): - hdr.decode_maker_note() - - # Sometimes in a TIFF file, a JPEG thumbnail is hidden in the MakerNote - # since it's not allowed in a uncompressed TIFF IFD - if not hdr.tags.has_key('JPEGThumbnail'): - thumb_off=hdr.tags.get('MakerNote JPEGThumbnail') - if thumb_off: - file.seek(offset+thumb_off.values[0]) - hdr.tags['JPEGThumbnail']=file.read(thumb_off.field_length) - - return hdr.tags - -# library test/debug function (dump given files) -if __name__ == '__main__': - import sys - - if len(sys.argv) < 2: - print 'Usage: %s files...\n' % sys.argv[0] - sys.exit(0) - - for filename in sys.argv[1:]: + # on utilise la classe qui va fabriquer le code html + boite = BoxFormatter(self.request, formatter) + title = self.settings['title'] + content = to_wikiname(self.request, formatter, quotes) try: - file=open(filename, 'rb') + color = self.settings['color'] except: - print filename, 'unreadable' - print - continue - print filename+':' - # data=process_file(file, 1) # with debug info - data=process_file(file) - if not data: - print 'No EXIF information found' - continue + color=None + boite.make(title, content, color) + return - x=data.keys() - x.sort() - for i in x: - if i in ('JPEGThumbnail', 'TIFFThumbnail'): - continue - try: - print ' %s (%s): %s' % \ - (i, FIELD_TYPES[data[i].field_type][2], data[i].printable) - except: - print 'error', i, '"', data[i], '"' - if data.has_key('JPEGThumbnail'): - print 'File has JPEG thumbnail' - print diff --git a/wiki/parser/Gallery2.py b/wiki/parser/Gallery2.py deleted file mode 100644 index ee227b9a..00000000 --- a/wiki/parser/Gallery2.py +++ /dev/null @@ -1,890 +0,0 @@ -# -*- coding: iso-8859-1 -*- -""" - MoinMoin - Gallery2 parser - - PURPOSE: - This parser is used to visualize a couple of images as a thumbnail gallery. - Optional a description of an image could be added including WikiName. - On default the image name and it's creation date is shown. - If you click on a thumbnail you get the webnails shown. By a menue you are able to toggle between the slides. - - CALLING SEQUENCE: - {{{ - #!Gallery2 [columns=columns],[filter=filter],[mode=mode], - [show_text=show_text],[show_date=show_date], [show_tools=show_tools], - [sort_by_name=sort_by_name],[sort_by_date=sort_by_date], [sort_by_alias=sort_by_alias], - [reverse_sort=reverse_sort], - [only_items=only_items],[template_itemlist=template_itemlist], - [album=album],[album_name=album_name],[front_image=front_image], - [thumbnail_width=thumbnail_width],[webnail_width=webnail_width],[text_width=text_width], - [image_for_webnail=image_for_webnail], - [border_thick=border_thick],[renew=renew],[help=help] - * [image1.jpg alias] - * [image2.jpg alias] - }}} - - KEYWORD PARAMETERS: - columns: number of columns for thumbnails - filter: regex to select images - show_text: default is 1 description is shown - any other means no description - show_date: default is 1 date info from exif header if available is shown - show_tools: default is 1 icon toolbar is show any other disables this - sort_by_name: default is 1, the images are sorted by name, but not if only_items is 1 - sort_by_date: default is 0, if set to 1 the images are sorted to the modification time - sort_by_alias default is 0, if set to 1 and only_items set to 1 it is used to order the images by the alias name - reverse_sort: default is 0, if set to 1 the file list is reversed - any other means no description - mode: default is 1 this means description below the image - any other number means description right of image - only_items: default is 0 if it is set to 1 only images which are described in listitem are shown - dependend on the order of the items - template_itemlist: default is 0, if set to 1 an item list is shown which could be copied into the script. - album: default is 0 if set to 1 only the first image of a series is shown but slideshow over all images - album_name: useful for album. default is 'album' use it as short name for the album. - front_image: Useful for album. default is ''. The first image is shown in front of the album and slideshow. - If set to an existing image name this is shown in front of album and slideshow. - The slide show could start by this somewhere. - border_thick: default is 1 this is the thickness in pixeln of the outer frame - renew: default is 0 if set to 1 then all selected thumbnails_* and webnails_* removed. - Afterwards they are new created. - thumbnail_width: default is 128 - webnail_width: default is 640 - text_width: default is 140 - image_for_webnail default is 0 if set to 1 then the image is shown as preview and not the webnail - help: default is 0 if set a copy of the CALLING SEQUENCE is shown, - (there are some new ideas around to show help to an user so this will be later replaced) - - OPTIONAL INPUTS: - itemlist : if it is used and only_items is 1 then only the images in this list are ahown. - The alias text is used as description of the image instead of the file name - - - EXAMPLE: -= GalleryTest = - -== all images shown, one is decribed == -{{{ -{ { { -#!Gallery2 -* [100_1185.JPG Bremen, SpaceCenter] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 -* [100_1185.JPG Bremen, SpaceCenter] -}}} - -== only thumbnails and only_items == -{{{ -{ { { -#!Gallery2 show_text=0,show_tools=0,show_date=0,columns=2,only_items=1 - * [100_1185.JPG Bremen, SpaceCenter] - * [100_1194.JPG Bremen] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 show_text=0,show_tools=0,show_date=0,columns=2,only_items=1 - * [100_1185.JPG Bremen, SpaceCenter] - * [100_1194.JPG Bremen] -}}} - -== only_items by two columns and text right == - -{{{ -{ { { -#!Gallery2 mode=2,columns=2,only_items=1 - * [100_1185.JPG Bremen, SpaceCenter] - * [100_1194.JPG Bremen] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 mode=2,columns=2,only_items=1 - * [100_1185.JPG Bremen, SpaceCenter] - * [100_1194.JPG Bremen, behind SpaceCenter] -}}} - ----- - -== only_items by two columns, date supressed == - -{{{ -{ { { -#!Gallery2 columns=2,only_items=1,show_date=0 - * [100_1185.JPG Bremen, SpaceCenter] - * [100_1194.JPG Bremen, behind SpaceCenter] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 columns=2,only_items=1,show_date=0 - * [100_1185.JPG Bremen, SpaceCenter] - * [100_1194.JPG Bremen, behind SpaceCenter] -}}} - - -== filter regex used, mode 2, icons and date supressed, one column and border_thick=5 == -{{{ -{ { { -#!Gallery2 columns=1,filter=100_118[0-5],mode=2,show_date=0,show_tools=0,border_thick=5 -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 columns=1,filter=100_118[0-7],mode=2,show_date=0,show_tools=0,border_thick=5 -}}} - -== other macro calls == -{{{ -{ { { -#!Gallery2 only_items=1,show_date=0 - * [100_1189.JPG [[MiniPage(||Bremen||SpaceCenter||\n|| ||SpaceJump||)]]] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 only_items=1,show_date=0 - * [100_1189.JPG [[MiniPage(||Bremen||SpaceCenter||\n|| ||SpaceJump||)]]] -}}} - -== renew means always new thumbnails and webnails of selection == -{{{ -{ { { -#!Gallery2 only_items=1,show_date=0,show_tools=0,renew=1 - * [100_1189.JPG [[MiniPage(||["Bremen"]||SpaceCenter||\n|| ||SpaceJump||)]]] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 only_items=1,show_date=0,renew=1 - * [100_1189.JPG [[MiniPage(||["Bremen"]||SpaceCenter||\n|| ||SpaceJump||)]]] -}}} - -== template_itemlist == -{{{ -{ { { -#!Gallery2 template_itemlist=1 -* [100_1185.JPG Bremen, SpaceCenter] -} } } -}}} - -Result: [[BR]] - {{{ -#!Gallery2 template_itemlist=1 -* [100_1185.JPG Bremen, SpaceCenter] -}}} - -== help to show Calling Sequence == - {{{ -{ { { -#!Gallery2 help=1 -} } } -}}} - -Result: [[BR]] -{{{ -#!Gallery2 help=1 -}}} - - - PROCEDURE: - Download some images to a page and start with the examples. - Aliasing of the filenames are done by adding an itemlist, see example. - - This routine requires the PIL (Python Imaging Library). - And the EXIF routine from http://home.cfl.rr.com/genecash/digital_camera.html - - - At the moment I have added the EXIF routine to the parsers dir. - It's not the best place but during developing it is nice to have it there - If you put it to another place you have to change the line - from MoinMoin.parser import EXIF too. - - This routine requires the Action macro gallery2Image which is used to rotate or delete a selected image. - Only users which have the rights to delete are able to execute this action macro. - The icons of these are only shown if you have enough rights. - - The gallery2image macro does not take care on the EXIF header. This is lost by rotating. - If a file is deleted by this macro it is moved to a bak file. - - Please remove the Version number from the code! - - RESTRICTIONS: - The movie mode is not implemented at the moment. The implementation will be done in the action macro. - - - If you rotate an image at the moment the exif is destroyed. PIL ignores the exif header. - This is not a quite big problem normally files with an EXIF header are right rotated. - - Required Images: - I have put them to wiki/modern/img/ dir. The icons were created by me. License: GPL - - attachment:to_bak.png - attachment:to_left.png - attachment:to_right.png - attachment:to_slide.png - attachment:to_full.png - - HISTORY: - While recognizing how to write MiniPage I got the idea to write a Gallery Parser. - We have used in our wikis in the past the Gallery macro of SimonRyan. - I have tried to modify it a bit to change it for 1.3 but my python skills weren't enough - or it was easier to write it completly new. - So this one shows now a way how a Gallery could be used by the parser and an action Macro. - Probably it is a good example for others who like to know how to do this - - MODIFICATION HISTORY: - Version 1.3.3.-1 - @copyright: 2005 by Reimar Bauer (R.Bauer@fz-juelich.de) - @license: GNU GPL, see COPYING for details. - 2005-03-26: Version 1.3.3-2 keyword renew added - creation of thumbnails and webnails in two calls splitted - Version 1.3.3-3 bug fixed if itemlist is given to describe only some of the images - but only_items is not set to 1 - Example code changed - 2005-03-27: Version 1.3.3-4 Action macro added and the form to call it. User which have rights to delete - could use the functions of gallery2Image. - 2005-08-03: Version 1.3.3-5 theme path for icons corrected and a platform independent path joining - os.unlink removed as suggested by CraigJohnson - sort_by_name is default if not only_items is 1 - optional sort_by_date could be used - keyword template_itemlist added - keyword help added - extra frame by mode=2 removed - 2005-08-06: Version 1.3.5-6 slideshow mode added - keyword image_for_webnail added - 2005-08-13: Version 1.3.5-7 syntax changed from GET to POST - forms instead of links - filenames from images submitted to gallery2image too - new keyword sort_by_alias - internal code clean up - this version needs: gallery2image-1.3.5-5.py - 2005-08-14: Version 1.3.5-8 (TW) cleanup - 2005-08-14: Version 1.3.5-9 html code for tables changed - because of the ugly extra space of form elements - tag removed so now we use the page style - slide show action goes to right webnail now - this version needs: gallery2image-1.3.5-5.py - 2005-08-17: Version 1.3.5-10 html code separated in functions - structure of code changed, now you see the thumbnails after creation - bug removed if quote is given but file does not exist - 2005-09-02: Version 1.3.5-11 keyword album, album_name and front_image added - image urls changed to complete server url - - -""" -Dependencies = [] -from MoinMoin.action import AttachFile -from MoinMoin import wikiutil, config -from MoinMoin.Page import Page - -import os,string,re,Image,StringIO - -#from MoinMoin.parser import EXIF -import EXIF - -from MoinMoin.parser import wiki - -def show_tools_restricted(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request): - if request.user.may.delete(pagename): - return tools_restricted_html(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request) - else: - return '' - -def tools_restricted_html(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request): - text=''' -
- - - - - - -
-
- - - - - - -
-
- - - - - - -
''' % { - "server" : 'https://'+request.server_name, - 'baseurl': request.getScriptname(), - "pagename":pagename, - "this_target":this_target} - return text - -def tools_html(pagename,this_image,thumbnail_width,full,alias,target,exif_date,request): - text=''' - - - - - - - - - %(show_tools_restricted)s - -
- - - - -
- - - - - - - -
''' % { - "server" : 'https://'+request.server_name, - 'baseurl': request.getScriptname(), - "pagename":pagename, - "thumbnail_width":thumbnail_width, - "full":full, - "alias":alias, - "exif_date":exif_date, - "target":target, - "this_target":this_image, - "show_tools_restricted":show_tools_restricted(pagename,this_image,thumbnail_width,full,alias,target,exif_date,request) - } - - return text - -def show_alias_mode2(show_alias,thumbnail_width,this_alias,text_width): - if show_alias == '1': - return ''' - - %(this_alias)s - ''' % { - "this_alias":this_alias, - "text_width":text_width} - else: - return '' - -def show_date_mode2(show_date,this_exif_date): - if show_date == '1': - return ''' - -

%(this_exif_date)s

- ''' % { - "this_exif_date":this_exif_date } - else: - return ''; - -def show_tools_mode2(show_tools,pagename,this_target,thumbnail_width,full,alias,target,exif_date,request): - if show_tools == '1' : - return " %s " % tools_html(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request) - else: - return '' - - - -def mode2_html(pagename,border_thick,width,thumbnail_width,text_width,full,this_image,alias,this_alias,exif_date,this_exif_date,target,this_target,submit,show_tools,show_date,show_alias,request): - text=''' - -
- - - - - - - - - -
- %(alias_html)s - - %(tools_html)s%(date_html)s'''% { - "server" : 'https://'+request.server_name, - "baseurl": request.getScriptname(), - "pagename":pagename, - "thumbnail_width":thumbnail_width, - "full":full, - "alias":alias, - "exif_date":exif_date, - "target":target, - "submit":submit, - "tools_html":show_tools_mode2(show_tools,pagename,this_image,thumbnail_width,full,alias,target,exif_date,request), - "date_html": show_date_mode2(show_date,this_exif_date), - "alias_html": show_alias_mode2(show_alias,thumbnail_width,this_alias,text_width) - } - - return text - -def show_tools_mode1(show_tools,pagename,this_image,thumbnail_width,full,alias,target,exif_date,request): - if show_tools == '1' : - text="%s " % tools_html(pagename,this_image,thumbnail_width,full,alias,target,exif_date,request) - else: - text='' - return text - -def show_date_mode1(show_date,this_exif_date): - if show_date == '1': - return ''' - - %(this_exif_date)s - ''' % { - "this_exif_date":this_exif_date} - else: - return '' - -def show_alias_mode1(show_alias,thumbnail_width,this_alias,text_width): - if show_alias == '1': - return ''' - - %(this_alias)s - ''' % { - "thumbnail_width": thumbnail_width, - "this_alias":this_alias} - else: - return '' - -def mode1_html(pagename,border_thick,width,thumbnail_width,text_width,full,this_image,alias,this_alias,exif_date,this_exif_date,target,this_target,submit, - show_tools,show_date,show_alias,request): - text=''' - - - - - - - %(alias_html)s - %(date_html)s - %(tools_html)s -
- - - - - - - -
'''% { - "server" : 'https://'+request.server_name, - "baseurl": request.getScriptname() , - "pagename":pagename, - "full":full, - "alias":alias, - "exif_date":exif_date, - "target":target, - "submit":submit, - "thumbnail_width":thumbnail_width, - "tools_html": show_tools_mode1(show_tools,pagename,this_image,thumbnail_width,full,alias,target,exif_date,request), - "date_html":show_date_mode1(show_date,this_exif_date), - "alias_html": show_alias_mode1(show_alias,thumbnail_width,this_alias,text_width) - } - - return text - -def get_files(kw,path,files,quotes,request): - web=[] - full=[] - thumb=[] - exif_date=[] - img_type=[] - description=[] - - - ddict={} - n=len(quotes['image']) - if n > 0 : - i = 0 - for txt in quotes['image']: - ddict[txt]=quotes['alias'][i] - i += 1 - - for attfile in files: - # only files not thumb or webnails - if attfile.find('thumbnail_') == -1 and attfile.find('webnail_') == -1: - # only images - if wikiutil.isPicture(attfile): - description.append(ddict.get(attfile, attfile)) - full.append(attfile) - - if kw['image_for_webnail'] == '1': - webnail = attfile - else: - fname, ext = os.path.splitext(attfile) - if ext in ('.gif', '.png'): - img_type.append('PNG') - webnail = 'webnail_%s.png' % fname - thumbfile = 'thumbnail_%s.png' % fname - else: - img_type.append("JPEG") - webnail = 'webnail_%s.jpg' % fname - thumbfile = 'thumbnail_%s.jpg' % fname - - infile = os.path.join(path, attfile) - if os.path.exists(infile): - web.append(webnail) - thumb.append(thumbfile) - - f = open(infile, 'rb') - tags = EXIF.process_file(f) - if tags.has_key('EXIF DateTimeOriginal'): - date = str(tags['EXIF DateTimeOriginal']) - date = date.replace(':', '-', 2) - else: - date = '--' - exif_date.append(date) - f.close() - - return thumb,web,full,exif_date,img_type,description - -def to_htmltext(text): - text = text.split(' ') - text = ' '.join(text) - return text - -def to_wikiname(request,formatter,text): - ##taken from MiniPage - out=StringIO.StringIO() - request.redirect(out) - wikiizer = wiki.Parser(text.strip(),request) - wikiizer.format(formatter) - result=out.getvalue() - request.redirect() - del out - - return result.strip() - - -def get_quotes(self,formatter): - quotes = self.raw.split('\n') - quotes = [quote.strip() for quote in quotes] - quotes = [quote[2:] for quote in quotes if quote.startswith('* ')] - - - image=[] - text=[] - - for line in quotes: - im, na=line[1:-1].split(' ',1) - text.append(na.strip()) - image.append(im.strip()) - - return { - 'alias': text, - 'image': image, - } - - - -class Parser: - - def __init__(self, raw, request, **kw): - self.raw = raw - self.request = request - self.form = request.form - self._ = request.getText - self.kw = { - 'sort_by_date': '0', - 'sort_by_name': '1', - 'sort_by_alias': '0', - 'album': '0', - 'album_name': 'album', - 'front_image':'', - 'template_itemlist': '0', - 'reverse_sort': '0', - 'border_thick': '1', - 'columns': '4', - 'filter': '.', - 'mode': '1', - 'help': '0', - 'show_text': '1', - 'show_date': '1', - 'show_tools': '1', - 'only_items': '0', - 'image_for_webnail': '0', - 'renew': '0', - 'thumbnail_width': '128', - 'webnail_width': '640', - 'text_width': '140', - } - - - for arg in kw.get('format_args','').split(','): - - if arg.find('=') > -1: - key, value=arg.split('=') - self.kw[key]=wikiutil.escape(value, quote=1) - - - self.kw['width']=str((int(self.kw['thumbnail_width'])+int(self.kw['text_width']))) - - - def format(self, formatter): - kw=self.kw - Dict = {} - quotes=get_quotes(self,formatter) - current_pagename=formatter.page.page_name - attachment_path = AttachFile.getAttachDir(self.request, current_pagename, create=1) - - if kw['help'] == '1': - self.request.write(''' -
-{{{
-#!Gallery2 [columns=columns],[filter=filter],[mode=mode],
- [show_text=show_text],[show_date=show_date], [show_tools=show_tools],
- [sort_by_name=sort_by_name],[sort_by_date=sort_by_date],[sort_by_alias=sort_by_alias]
- [reverse_sort=reverse_sort],
- [only_items=only_items],[template_itemlist=template_itemlist],
- [album=album],[album_name=album_name],[front_image=front_image],
- [thumbnail_width=thumbnail_width],[webnail_width=webnail_width],[text_width=text_width],
- [image_for_webnail=image_for_webnail],
- [border_thick=border_thick],[renew=renew],[help=help]
- * [image1.jpg alias]
- * [image2.jpg alias]
-}}}
''') - return - - - if kw['only_items'] == '1': - all_files=quotes['image'] - result=[] - for attfile in all_files: - infile=os.path.join(attachment_path,attfile) - if os.path.exists(infile): - result.append(attfile) - all_files = result - - if kw['sort_by_alias'] == '1': - new_ordered_files=[] - alias_text=quotes['alias'] - - i=0 - for attfile in all_files: - infile=os.path.join(attachment_path,attfile) - ft_file=str(os.path.getmtime(infile))+os.tmpnam() - Dict[alias_text[i]]=attfile - i += 1 - - keys = Dict.keys() - keys.sort() - for txt in keys: - new_ordered_files.append(Dict[txt]) - - - all_files=new_ordered_files - Dict.clear() - - else: - all_files=os.listdir(attachment_path) - - result = [] - - for test in all_files: - if re.match(kw['filter'], test): - result.append(test) - all_files=result - - if not all_files: - self.request.write("

No matching image file found!

") - return - - - - if kw['sort_by_name'] == '1' and kw['only_items'] == '0': - all_files.sort() - - if kw['sort_by_date']=='1': - for attfile in all_files: - infile=os.path.join(attachment_path,attfile) - ft_file=str(os.path.getmtime(infile))+os.tmpnam() - Dict[ft_file]=attfile - - keys = Dict.keys() - keys.sort() - file_mdate=[] - for txt in keys: - file_mdate.append(Dict[txt]) - all_files=file_mdate - Dict.clear() - - if kw['reverse_sort']=='1': - all_files.reverse() - - - cells=[] - cell_name=[] - img=[] - - thumb, web, full, exif_date, imgtype, description = get_files(kw, attachment_path, all_files, quotes, self.request) - - if kw['template_itemlist'] == '1': - self.request.write('Copy the following listitems into the script. Replace alias with the label you want. Afterwards disable template_itemlist by setting it to 0:
') - for attfile in full : - self.request.write(' * [%(attfile)s %(date)s]
' % { - 'attfile' : attfile, - 'date' : 'alias' - }) - - - i = 0 - z = 1 - cols = int(kw['columns']) - - - n = len(full) - if kw['album'] == '0' : - self.request.write("" % self.kw['border_thick']) - if kw['mode'] == '1' or cols > 1 : - self.request.write('') - self.request.write('') - if z < n : - self.request.write('') - self.request.write('') - if i < n-1 : - self.request.write('') - self.request.write('') - self.request.write('') - - - self.request.write('
') - - - if kw['album'] == '1' : - if kw['front_image'] == '' : - front_image = full[0] - else: - front_image = kw['front_image'] - ii = 0 - for tst in full : - if tst == front_image : - break - ii += 1 - - - for attfile in full : - if kw['album'] == '1' : - if tst == front_image : - i = ii - - - this_description=description[i] - this_exif_date=exif_date[i] - this_webnail=web[i] - this_imgtype=imgtype[i] - this_thumbfile=thumb[i] - - - thumbf=os.path.join(attachment_path,this_thumbfile) - webf=os.path.join(attachment_path,this_webnail) - - - if kw['renew'] == '1': - if os.path.exists(thumbf): - os.unlink(thumbf) - if os.path.exists(webf): - os.unlink(webf) - - if not os.path.exists(webf) or not os.path.exists(thumbf): - infile=os.path.join(attachment_path,attfile) - im = Image.open(infile) - if not os.path.exists(webf): - im.thumbnail(((int(kw['webnail_width'])),((int(kw['webnail_width'])))), Image.ANTIALIAS) - im.save(webf, this_imgtype) - if not os.path.exists(thumbf): - im.thumbnail(((int(kw['thumbnail_width'])),((int(kw['thumbnail_width'])))), - Image.ANTIALIAS) - im.save(thumbf, this_imgtype) - - - if kw['mode'] == '1': - text=mode1_html(current_pagename, - kw['border_thick'], - kw['width'], - kw['thumbnail_width'], - kw['text_width'], - attfile + "," + ','.join(full), - attfile, - to_htmltext(this_description + "!,!" + '!,!'.join(description)), - to_wikiname(self.request,formatter,this_description), - to_htmltext(this_exif_date + "," + ','.join(exif_date)), - this_exif_date, - this_webnail + "," + ','.join(web), - this_webnail, - AttachFile.getAttachUrl(current_pagename, this_thumbfile, self.request), - kw['show_tools'], - kw['show_date'], - kw['show_text'], - self.request - ) - self.request.write(''.join(text)) - - if kw['mode'] == '2': - text=mode2_html(current_pagename, - kw['border_thick'], - kw['width'], - kw['thumbnail_width'], - kw['text_width'], - attfile + "," + ','.join(full), - attfile, - to_htmltext(this_description + "!,!" + '!,!'.join(description)), - to_wikiname(self.request,formatter,this_description), - to_htmltext(this_exif_date + "," + ','.join(exif_date)), - this_exif_date, - this_webnail + "," + ','.join(web), - this_webnail, - AttachFile.getAttachUrl(current_pagename, this_thumbfile, self.request), - kw['show_tools'], - kw['show_date'], - kw['show_text'], - self.request - ) - - if cols > 1 : self.request.write('') - self.request.write(''.join(text)) - if cols > 1 : self.request.write('
') - - if kw['mode'] == '1' or cols > 1: - if kw['album'] == '0' : - if z < cols: - self.request.write('
') - else: - self.request.write('
') - - i += 1 - z += 1 - if z > cols : - z = 1 - - if kw['album'] == '1' : - self.request.write("%(n)s images (%(album_name)s)" % {"n": str(n), "album_name":kw['album_name']}) - break - if kw['album'] == '0' : - if i < n : - self.request.write('
') - - ############################################ - ##TODO: syntax change to formatter - later # - ############################################ - - - - - diff --git a/wiki/parser/Portail.py b/wiki/parser/Portail.py index 11e680c5..fe91b988 100644 --- a/wiki/parser/Portail.py +++ b/wiki/parser/Portail.py @@ -1,6 +1,23 @@ # -*- coding: iso-8859-1 -*- """ - MoinMoin - Portail parser +# -*- coding: utf-8 -*- + .. + .... ............ ........ + . ....... . .... .. + . ... .. .. .. .. ..... . .. + .. .. ....@@@. .. . ........ . + .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. .... + .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... .... + @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. .. + .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. ..... + ...@@@.... @@@ .@@.......... ........ ..... .. + . ..@@@@.. . .@@@@. .. ....... . ............. + . .. .... .. .. . ... .... + . . .... ............. .. ... + .. .. ... ........ ... ... + ................................ + +MoinMoin - Portail parser PURPOSE: Pour afficher un portail a la wikipedia. @@ -107,7 +124,7 @@ class Parser: else: description=items.pop().strip() link=items.pop().strip() - image = AttachFile.getAttachUrl(current_pagename, items.pop().strip(), self.request).escaped('& ','&') + image = AttachFile.getAttachUrl(current_pagename, items.pop().strip(), self.request).replace('& ','&') link = to_wikiname(self.request, formatter, link) description = to_wikiname(self.request, formatter, description)