Image crop, resize and CMYK conversion. Save and reload in TIFF format

This scribus scripter script crops and resizes an image, saves it and reloads it in TIFF format. Optionaly, converts color space to grayscale, RGB or CMYK (not recomended, as PIL CMYK don't generate the black plate. Let Scribus do the job). Needs Scribus 1.3.8 and uses Python Image Library.

Use: select a frame with image and run the script. It will create and load a new cropped TIFF image.

Quality matters: The major issue with automatic cropping is JPEG images. JPEG images are compressed in blocks (I think 4x4 pixels, but it might be 16x16). If you crop through a block, that block needs to re-compressed (affecting the quality of the image). So cropping needs to be "rounded" to the nearest block border to make it lossless. I think most other format can be losslessly cropped without problems.

Caveat: PIL currently freezes Scribus in Ubuntu 10, if run as menu script, due to an Ubuntu bug. Runs OK in Scripter console (F9 keystroke) and in Windows XP or Wine.


 * 1) !/usr/bin/python
 * 2) -*- coding: utf-8 -*-

""" Crop, resample and convert color space on 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 2010-08-15a PIL docs: http://infohost.nmt.edu/tcc/help/pubs/pil/image-constructors.html http://www.pythonware.com/library/pil/handbook/index.htm """

import sys 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
 * 1) USER IMPORTS GOES HERE
 * 1) USER IMPORTS GOES HERE

encoding = sys.getfilesystemencoding
 * 1) OS string encoding for PIL handle filenames
 * 2) (too much time to discover this)
 * 1) (too much time to discover this)

imageResolution = 200 # DPI resolution for newspaper fileType = '.tif' # force tiff format cropsufix = '_cropped' # cropped image sufix colorspace = 'RGB' ## 'L' for grayscale, 'CMYK' for cmyk, 'RGB' for rgb.
 * 1) User can change this variables
 * 1) User can change this variables

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

askResolutionLabel="Choose the resolution" askResolution = ' '+askResolutionLabel+' ' \ + ' Choose the resolution to resample the image (200 ppi, 300ppi) ' openImageErrorLabel = 'Open error' openImageErrorMsg = ' '+openImageErrorLabel+' ' \ + ' Error opening the image file. ' openDocLabel="Open a document" askOpenDoc = ' '+openDocLabel+' ' \ + ' Please, open a document and select a image frame before using this script. ' imageFrameLabel = "Select a image frame" askSelectImageFrame = ' '+imageFrameLabel+' ' \ + "Select a image frame\n" \ + "to crop it image." outOfRangeLabel = 'Image out of limits' msgOutOfRange = ' '+outOfRangeLabel+' Choose a beetter crop area.' errorOnSaveLabel = 'Save error' errorOnSaveMsg = ' '+errorOnSaveLabel+' Error saving the cropped image.' cropErrorLabel = 'Error cropping image' cropErrorMsg = ' '+cropErrorLabel+' .' errorOnLoadLabel = 'Error loading image' errorOnLoadMsg = ' '+errorOnLoadLabel+' Verify if image exist.'
 * 1) Locale strings
 * 2) Dialog to open document
 * 1) Dialog to open document
 * 1) Dialog to open document
 * 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

stringsDic = {'en_US':'Group', 'en_GB':'Group', 'en_AU':'Group', 'pt_BR':'Agrup','pt': 'Agrup'} lang = scribus.getGuiLanguage try: groupString = stringsDic[lang] except: scribus.messageBox('Missing group string','Put group string in your language in "stringsDic".',ICON_WARNING,BUTTON_OK) sys.exit(1)
 * 1) Localized group name
 * 2) Put your language string for group names in dictionary
 * 3) For use by isGroup function
 * 1) For use by isGroup function

statusText = "Running script..."
 * 1) foot msg


 * 1) End Locale strings
 * 1) End Locale strings

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)
 * 1) PIL library
 * 2) 'import Image' cause Scribus freeze after script run, in Ubuntu 10.04.
 * 3)  OK in Windows XP, Python 2.5.4, PIL 1.1.7
 * 1) 'import Image' cause Scribus freeze after script run, in Ubuntu 10.04.
 * 2)  OK in Windows XP, Python 2.5.4, PIL 1.1.7


 * 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): imageXOffset = scribus.getProperty(imageFrame,'imageXOffset') imageYOffset = scribus.getProperty(imageFrame,'imageYOffset') # avoid offset positive if (imageXOffset > 0) or (imageYOffset > 0): scribus.setScaleImageToFrame(SCALETOFRAME,PROPORTIONAL,imageFrame) scribus.setRedraw(True) scribus.setScaleImageToFrame(FREESCALE,PROPORTIONAL,imageFrame) scribus.messageBox(outOfRangeLabel,msgOutOfRange,ICON_WARNING,BUTTON_OK) sys.exit(1) # Calculate image resample to press resolution (DPI) unit = scribus.getUnit scribus.setUnit(UNIT_INCHES) frameSizeX,frameSizeY = scribus.getSize(imageFrame) newWidth = frameSizeX * imageResolution newHeight = frameSizeY * imageResolution scribus.setUnit(unit) # Calculate cropping frameSizeX,frameSizeY = scribus.getSize(imageFrame) imageXScale = scribus.getProperty(imageFrame,'imageXScale') imageYScale = scribus.getProperty(imageFrame,'imageYScale') imageSizeX, imageSizeY = image.size left = imageXOffset * -1 # top = imageYOffset * -1 # right = left + (frameSizeX / imageXScale) bottom = top + (frameSizeY / imageYScale) # avoid frame out of image if (right > imageSizeX) or (bottom > imageSizeY): scribus.setScaleImageToFrame(SCALETOFRAME,PROPORTIONAL,imageFrame) scribus.setRedraw(True) scribus.setScaleImageToFrame(FREESCALE,PROPORTIONAL,imageFrame) scribus.messageBox(outOfRangeLabel,msgOutOfRange,ICON_WARNING,BUTTON_OK) sys.exit(1) # do cropping try: im = image.crop((left,top,right,bottom)) im = im.resize((int(newWidth),int(newHeight)),Image.ANTIALIAS)# BICUBIC,NEAREST, BILINEAR, ANTIALIAS except: scribus.messageBox(cropErrorLabel,cropErrorMsg,ICON_WARNING,BUTTON_OK) sys.exit(1) # Colorspace conversion if im.mode != colorspace: im = im.convert(colorspace) 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): name,ext = os.path.splitext(imageFile) # 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.encode(encoding)): 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) sys.exit(1) 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) sys.exit(1) scribus.setScaleImageToFrame(SCALETOFRAME,PROPORTIONAL,imageFrame)
 * 1) Reload image to frame
 * 1) Reload image to frame

def handleImage(imageFrame): image,imageFile = openImage(imageFrame) newImage = cropImage(image,imageFrame) newImageFile = saveImage(newImage,imageFile) reloadImage(newImageFile,imageFrame)
 * 1) Handle Image
 * 1) Handle Image

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

def handleDocument: """Handle documents """ # If there are open document 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 print handleSelection # Recover units scribus.setUnit(unit) else: # If there not open document. scribus.messageBox(openDocLabel,askOpenDoc,ICON_WARNING,BUTTON_OK)
 * 1) Handle document
 * 1) Handle document

def myCode: """ User code """ handleDocument

def main(argv): """Main entry point""" myCode

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)