Image crop, resize and color conversion GUI

This script cuts, converts the color space and saves an image in chosen file format. Needs Scribus >1.3.8 and uses Python Image Library (PIL).

Use: select a frame with image and run the script. Choose DPI, color space, file format and resize algorithm.

Caveats: CMYK colorspace is not recomended, as PIL CMYK generate a blank black plate. Let Scribus do the job.

PIL freezes Scribus in Ubuntu >10.04 and <12.04, if run as menu script, due to an Ubuntu bug. Runs OK in Scripter console (F9 keystroke) and in Windows XP or Wine. The script run without problems in Ubuntu >=12.04.

#! /usr/bin/env python ''' Crop and resample selected photo in Scribus Use: select a frame with image and run the script. It will create and load a new cropped TIFF image.
 * 1) -*- coding: utf-8 -*-

By prof. MS. José Antonio Meira da Rocha mailto:joseantoniorocha@gmail.com http://meiradarocha.jor.br License GPL 2011-01-05a PIL docs: http://infohost.nmt.edu/tcc/help/pubs/pil/image-constructors.html http://www.pythonware.com/library/pil/handbook/image.htm '''

import sys sys.path.insert(0, "%1") sys.path.insert(1, "/usr/lib/python2.7")
 * 1) sys.path.append("/usr/lib/python2.7")
 * 2) sys.path.append("")

py2 = py30 = py31 = False version = sys.hexversion if version >= 0x020504F0 and version < 0x03000000 : py2 = True   # Python 2.6 or 2.7 from Tkinter import * import ttk elif version >= 0x03000000 and version < 0x03010000 : py30 = True from tkinter import * import ttk elif version >= 0x03010000: py31 = True from tkinter import * import tkinter.ttk as ttk else: print ("""   You do not have a version of python supporting ttk widgets..    You need a version >= 2.6 to execute PAGE modules.    """) sys.exit

if sys.platform != 'win32': sys.path.append("/usr/lib/python2.6") # prevent 'import re' PIL bug in Scribus 1.3.8, Ubuntu 10.04 try: import scribus except ImportError,err: print "This Python script is written for the Scribus scripting interface." print "It can only be run from within Scribus." sys.exit(1)

from scribus import UNIT_POINTS, UNIT_MILLIMETERS, UNIT_INCHES, LINE_SOLID, JOIN_MITTER, BUTTON_OK, BUTTON_NO, BUTTON_YES, ICON_WARNING, ICON_INFORMATION, pt, mm

import os from string import Template

try: import Image except ImportError,err: print "This Python script is written for the PIL graphic interface." print "It should be instaled in Scribus Python tree." sys.exit(1) encoding = sys.getfilesystemencoding
 * 1) PIL library
 * 2) 'import Image' cause Scribus freeze after script run, in Ubuntu 10.04-10.10.
 * 3)  OK in Ubuntu 12.04, Windows XP, Python 2.5.4, PIL 1.1.7
 * 1) 'import Image' cause Scribus freeze after script run, in Ubuntu 10.04-10.10.
 * 2)  OK in Ubuntu 12.04, Windows XP, Python 2.5.4, PIL 1.1.7
 * 1) OS string encoding for PIL handle filenames
 * 2) (too much time to discover this)
 * 1) (too much time to discover this)

statusText = 'Running script...' resolutionValues = ['72','75','96','144','150','200','288','300','600','1200'] resolutionDefault = '200' imageResolution = 200 colorValues = ['RGB','B&W','Grey scale','CMYK'] colorDefault = 'RGB' filterDefault = 'ANTIALIAS' fileTypeValues = ['.TIF','.JPG','.PNG','.GIF','.BMP'] fileTypeDefault = '.TIF' imageFrame = '' cropsufix = '_cropped' # '_cropped'

UNDER_FRAME = 0 AROUND_FRAME = 1 BOUNDING_BOX = 2 CONTOUR_LINE = 3 SCALETOFRAME = 1 FREESCALE = 0 PROPORTIONAL = 1 DISTORTED = 0
 * 1) Scribus Constants
 * 1) Scribus Constants

openImageErrorLabel = 'Open error' openImageErrorMsg = ' '+openImageErrorLabel+' ' \ + ' There was an error trying to open the photo file. Maybe the file has been lost. ' openDocLabel="Open an document" askOpenDoc = ' '+openDocLabel+' ' \ + ' Please, open a document and select a frame before using this script. ' imageFrameLabel = "Select a image frame" askSelectImageFrame = ' '+imageFrameLabel+' ' \ + "Select  a image frame \n" \ + "to crop the photo inside." outOfRangeLabel = 'Rescale the picture' msgOutOfRange = ' '+outOfRangeLabel+' The photo is not well framed. Make a better framming.' errorOnSaveLabel = 'Saving error' errorOnSaveMsg = ' '+errorOnSaveLabel+' There was an error writing the image file.' cropErrorLabel = 'Error cropping the image' cropErrorMsg = ' '+cropErrorLabel+' There was an error while cropping the image. Maybe this type of file is not supported.' errorOnLoadLabel = 'Importing error' errorOnLoadMsg = ' '+errorOnLoadLabel+' There was an error when importing the image. Make sure the file exists.'
 * 1) Locale strings
 * 1) Locale strings
 * 1) Strings to open frame
 * 1) Out of range image strings
 * 1) Error on save image
 * 1) Error on cropping
 * 1) Error on loading image

def set_Tk_var: # These are Tk variables used passed to Tkinter and must be   # defined before the widgets using them are created. global resolution resolution = StringVar resolution.set(resolutionDefault) global color color = StringVar color.set(colorDefault) global filter filter = StringVar filter.set(filterDefault) global fileType fileType = StringVar fileType.set(fileTypeDefault)

class cropImageDialog: def __init__(self, master=None): # Set background of toplevel window to match # current style style = ttk.Style theme = style.theme_use default = style.lookup(theme, 'background') master.configure(background=default)
 * 1) Dialog definitons
 * 1) Dialog definitons

self.tLa34 = ttk.Label (master) self.tLa34.place(relx=0.04,rely=0.05) self.tLa34.configure(text="Resolution")

self.tCo35 = ttk.Combobox (master) self.tCo35.place(relx=0.04,rely=0.15,relheight=0.11,relwidth=0.41) self.tCo35.configure(values=resolutionValues) self.tCo35.configure(justify="center") self.tCo35.configure(textvariable=resolution)

self.tLa36 = ttk.Label (master) self.tLa36.place(relx=0.04,rely=0.3) self.tLa36.configure(text="Color")

self.tCo37 = ttk.Combobox (master) self.tCo37.place(relx=0.04,rely=0.4,relheight=0.11,relwidth=0.41) self.tCo37.configure(justify="center") self.tCo37.configure(values=colorValues) self.tCo37.configure(textvariable=color)

self.tLa32 = ttk.Label (master) self.tLa32.place(relx=0.04,rely=0.55) self.tLa32.configure(text="File type")

self.tCo33 = ttk.Combobox (master) self.tCo33.place(relx=0.04,rely=0.65,relheight=0.11,relwidth=0.41) self.tCo33.configure(justify="center") self.tCo33.configure(values=fileTypeValues) self.tCo33.configure(textvariable=fileType)

self.tLa38 = ttk.Labelframe (master) self.tLa38.place(relx=0.52,rely=0.05,relheight=0.72,relwidth=0.45) self.tLa38.configure(text="Filters") self.tLa38.configure(width="105") self.tLa38.configure(height="145")

self.tLa38_tRa40 = ttk.Radiobutton (self.tLa38) self.tLa38_tRa40.place(relx=0.1,rely=0.10,relheight=0.2,relwidth=0.6) self.tLa38_tRa40.configure(variable=filter) self.tLa38_tRa40.configure(value="BICUBIC") self.tLa38_tRa40.configure(text="Bicubic")

self.tLa38_tRa41 = ttk.Radiobutton (self.tLa38) self.tLa38_tRa41.place(relx=0.1,rely=0.30,relheight=0.2,relwidth=0.6) self.tLa38_tRa41.configure(variable=filter) self.tLa38_tRa41.configure(value="NEAREST") self.tLa38_tRa41.configure(text="Nearest")

self.tLa38_tRa42 = ttk.Radiobutton (self.tLa38) self.tLa38_tRa42.place(relx=0.1,rely=0.50,relheight=0.2,relwidth=0.6) self.tLa38_tRa42.configure(variable=filter) self.tLa38_tRa42.configure(value="BILINEAR") self.tLa38_tRa42.configure(text="Bilinear")

self.tLa38_tRa43 = ttk.Radiobutton (self.tLa38) self.tLa38_tRa43.place(relx=0.1,rely=0.70,relheight=0.2,relwidth=0.6) self.tLa38_tRa43.configure(variable=filter) self.tLa38_tRa43.configure(value="ANTIALIAS") self.tLa38_tRa43.configure(text="Antialias")

#Buttons self.tBu44 = ttk.Button (master) self.tBu44.place(relx=0.13,rely=0.81) self.tBu44.configure(command=earlyExit) self.tBu44.configure(text="Cancel")

self.tBu45 = ttk.Button (master) self.tBu45.place(relx=0.52,rely=0.81) self.tBu45.configure(command=ok) self.tBu45.configure(text="OK")


 * 1) Test object type
 * 1) Test object type

def isGroup(obj): """Verify if object is group.""" type = scribus.getObjectType(obj) if (type == 'Polygon') and (scribus.selectionCount > 1) and (obj.find(groupString) != -1): return 1 else: return 0

def isTextFrame(obj): """Verify if object is text frame.""" type = scribus.getObjectType(obj) if (type == 'TextFrame'): return 1 else: return 0

def isGraphicFrame(obj): """Verify if object is image frame.""" type = scribus.getObjectType(obj) if (type == 'ImageFrame'): return 1 else: return 0

def openImage(imageFrame): imf = scribus.getImageFile(imageFrame) try: image = Image.open(imf.encode(encoding)) # encode to let PIL handle accented filenames except: scribus.messageBox(openImageErrorLabel,openImageErrorMsg,ICON_WARNING,BUTTON_OK) sys.exit(1) return image,imf
 * 1) Open image
 * 1) Open image

def cropImage(image,imageFrame):    global unit,imageResolution ###########################   # Calculate image resample # to press resolution (DPI) unit = scribus.getUnit scribus.setUnit(UNIT_INCHES) if resolutionValues.count(resolution.get) == 0: imageResolution = int(resolutionDefault) else: imageResolution = int(resolution.get) frameSizeX,frameSizeY = scribus.getSize(imageFrame) newWidth = int(frameSizeX * imageResolution) newHeight = int(frameSizeY * imageResolution) scribus.setUnit(unit) ####################   # Calculate cropping imageXOffset = scribus.getProperty(imageFrame,'imageXOffset') imageYOffset = scribus.getProperty(imageFrame,'imageYOffset') frameSizeX,frameSizeY = scribus.getSize(imageFrame) imageXScale = scribus.getProperty(imageFrame,'imageXScale') imageYScale = scribus.getProperty(imageFrame,'imageYScale') imageSizeX, imageSizeY = image.size # calculate crop box left = int(imageXOffset * -1) top = int(imageYOffset * -1) right = int(left + (frameSizeX / imageXScale)) bottom = int(top + (frameSizeY / imageYScale)) # Limit crop to image area # (avoid black areas due crop bigger than image) if right > imageSizeX: right = imageSizeX if bottom > imageSizeY: bottom = imageSizeY if imageXOffset > 0: left = 0 if imageYOffset > 0: top = 0 # Recalculate new image dimensions # to fit proportionaly proportionX = newWidth / (right-left) proportionY = newHeight / (bottom-top) if proportionX > proportionY: newHeight = newWidth * (bottom-top) /(right-left) else: newWidth = newHeight * (right-left) / (bottom-top) print left,top,right,bottom print imageSizeX, imageSizeY try: # Crop im = image.crop((left,top,right,bottom)) if fileTypeValues.count(filter.get) == 0: filterType = filterDefault else: filterType = filter.get # Resize if filterType == 'BICUBIC': im = im.resize((newWidth,newHeight),Image.BICUBIC) elif filterType == 'NEAREST': im = im.resize((newWidth,newHeight),Image.NEAREST) elif filterType == 'BILINEAR': im = im.resize((newWidth,newHeight),Image.BILINEAR) else: # filterType == 'ANTIALIAS' im = im.resize((newWidth,newHeight),Image.ANTIALIAS) except: scribus.messageBox(cropErrorLabel,cropErrorMsg,ICON_WARNING,BUTTON_OK) earlyExit #########################   # color space conversion. #    try: if colorValues.count(color.get) == 0: colorType = colorDefault else: colorType = color.get # 'B&W','Grey scale','RGB','CMYK' if colorType == 'CMYK': if im.mode != 'CMYK': im = im.convert('CMYK') elif colorType == 'RGB': if im.mode != 'RGB': im = im.convert('RGB') elif colorType == 'B&W': if im.mode != '1': im = im.convert('1') elif colorType == 'Grey scale': if im.mode != 'L': im = im.convert('L') except: print 'WARNING: Error in color conversion.' return im
 * 1) Crop Image
 * 1) Crop Image

def fileExist(fileName): try: f = open(fileName,'r') return 1 except: return 0 def saveImage(newImage,imageFile): global fileType,filterDefault,imageResolution name,ext = os.path.splitext(imageFile) # choose file type if fileTypeValues.count(fileType.get) == 0: fileType = fileTypeDefault else: fileType = fileType.get fileType = fileType.lower # increment file version to avoid overwriting old files f = Template(name+cropsufix+'($version)'+fileType) count = 1 newImageFile = f.substitute(version=str(count)) while fileExist(newImageFile): count += 1 newImageFile = f.substitute(version=str(count)) try: #'encode' let PIL handle accented filenames newImage.save(newImageFile.encode(encoding),dpi=(imageResolution,imageResolution)) except: scribus.messageBox(errorOnSaveLabel,errorOnSaveMsg,ICON_WARNING,BUTTON_OK) earlyExit return newImageFile
 * 1) Test if file exist
 * 1) Test if file exist
 * 1) Save Image
 * 1) Save Image

def reloadImage(newImageFile,imageFrame): try: scribus.loadImage(newImageFile,imageFrame) except: scribus.messageBox(errorOnLoadLabel,errorOnLoadMsg,ICON_WARNING,BUTTON_OK) earlyExit scribus.setScaleImageToFrame(SCALETOFRAME,PROPORTIONAL,imageFrame)
 * 1) Reload image to frame
 * 1) Reload image to frame

def handleImage: global image,imageFile,imageFrame image,imageFile = openImage(imageFrame) newImage = cropImage(image,imageFrame) newImageFile = saveImage(newImage,imageFile) reloadImage(newImageFile,imageFrame) def ok: try: root.destroy except: pass try: handleImage except: pass quit
 * 1) Handle Image
 * 1) Handle Image
 * 1) Do all the works
 * 1) Do all the works
 * 1) Do all the works

def quit: earlyExit

def earlyExit: if scribus.haveDoc: global unit scribus.setUnit(unit) scribus.setRedraw(True) scribus.statusMessage("") scribus.progressReset try: root.destroy except: pass scribus.setUnit(unit) if 'Image' in sys.modules: del(sys.modules["Image"]) sys.exit

def init: pass

def handleSelection: """Handle selected objects.""" global imageFrame if (scribus.selectionCount == 1) and isGraphicFrame(scribus.getSelectedObject): imageFrame = scribus.getSelectedObject else: scribus.messageBox(imageFrameLabel,askSelectImageFrame,ICON_WARNING,BUTTON_OK) earlyExit
 * 1) Handle selection
 * 1) Handle selection

def handleDocument: """Handle documents """ # If there are open document global unit if scribus.haveDoc: # turn off redraw scribus.setRedraw(False) # Save unit unit = scribus.getUnit # Define unit in points scribus.setUnit(UNIT_POINTS) # pre-press mesure system handleSelection else: # If there not open document. scribus.messageBox(openDocLabel,askOpenDoc,ICON_WARNING,BUTTON_OK) if 'Image' in sys.modules: del(sys.modules["Image"]) sys.exit
 * 1) Handle document
 * 1) Handle document

def vp_start_gui: global w, root root = Tk root.title('Crop Image') root.geometry('235x180+263+103') set_Tk_var handleDocument w = cropImageDialog(root) init root.mainloop def main(argv): """Main entry point""" vp_start_gui

def main_wrapper(argv): try: scribus.statusMessage(statusText) scribus.progressReset main(argv) finally: if scribus.haveDoc: scribus.setRedraw(True) scribus.statusMessage("") scribus.progressReset

if __name__ == '__main__': main_wrapper(sys.argv)