Image crop, resize and color conversion GUI
Jump to navigation
Jump to search
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 #-*- coding: utf-8 -*- ''' 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. 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") #sys.path.append("/usr/lib/python2.7") #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 ############# # PIL library ############# # 'import Image' cause Scribus freeze after script run, in Ubuntu 10.04-10.10. # OK in Ubuntu 12.04, Windows XP, Python 2.5.4, PIL 1.1.7 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) ############################################# # OS string encoding for PIL handle filenames # (too much time to discover this) ############################################# encoding = sys.getfilesystemencoding() 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' ################### # Scribus Constants UNDER_FRAME = 0 AROUND_FRAME = 1 BOUNDING_BOX = 2 CONTOUR_LINE = 3 SCALETOFRAME = 1 FREESCALE = 0 PROPORTIONAL = 1 DISTORTED = 0 ################ # Locale strings ################ openImageErrorLabel = 'Open error' openImageErrorMsg = '<h2>'+openImageErrorLabel+'</h2>' \ + '<p>There was an error trying to open the photo file.<br>Maybe the file has been lost.</p>' # openDocLabel="Open an document" askOpenDoc = '<h2>'+openDocLabel+'</h2>' \ + '<p>Please, open a document and select a frame before using this script.</p>' # Strings to open frame imageFrameLabel = "Select a image frame" askSelectImageFrame = '<h2>'+imageFrameLabel+'</h2>' \ + "Select <b> a image frame </b>\n" \ + "to crop the photo inside." # Out of range image strings outOfRangeLabel = 'Rescale the picture' msgOutOfRange = '<h2>'+outOfRangeLabel+'</h2><p>The photo is not well framed. Make a better framming.' # Error on save image errorOnSaveLabel = 'Saving error' errorOnSaveMsg = '<h2>'+errorOnSaveLabel+'</h2><p>There was an error writing the image file.' # Error on cropping cropErrorLabel = 'Error cropping the image' cropErrorMsg = '<h2>'+cropErrorLabel+'</h2><p>There was an error while cropping the image. Maybe this type of file is not supported.' # Error on loading image errorOnLoadLabel = 'Importing error' errorOnLoadMsg = '<h2>'+errorOnLoadLabel+'</h2>There was an error when importing the image. Make sure the file exists.' 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) ###################### # Dialog definitons ###################### 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) 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") ################## # 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 ###################### # Open image ###################### 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 ################## # Crop 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 #################### # Test if file exist #################### def fileExist(fileName): try: f = open(fileName,'r') return 1 except: return 0 ################# # Save Image ################# 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 ####################### # Reload image to frame ####################### def reloadImage(newImageFile,imageFrame): try: scribus.loadImage(newImageFile,imageFrame) except: scribus.messageBox(errorOnLoadLabel,errorOnLoadMsg,ICON_WARNING,BUTTON_OK) earlyExit() scribus.setScaleImageToFrame(SCALETOFRAME,PROPORTIONAL,imageFrame) ################## # Handle Image ################## def handleImage(): global image,imageFile,imageFrame image,imageFile = openImage(imageFrame) newImage = cropImage(image,imageFrame) newImageFile = saveImage(newImage,imageFile) reloadImage(newImageFile,imageFrame) # ################## # Do all the works ################## def ok(): try: root.destroy() except: pass try: handleImage() except: pass quit() 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 ################## # Handle selection ################## 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() ##################### # Handle document ##################### 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() ################### 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)