Image crop, resize and CMYK conversion. Save and reload in TIFF format: Difference between revisions

From Scribus Wiki
Jump to navigation Jump to search
mNo edit summary
mNo edit summary
 
(24 intermediate revisions by 2 users not shown)
Line 1: Line 1:
This scripter script crop and resize an image, save it and reload it in TIFF format. Optionaly, convert color space from RGB to CMYK. Use Python Image Library. Caveat: PIL currently freeze Scribus in Ubuntu 10.04. Run OK in Windows XP.
[[Category:Scripts]]
{{Scripting Index}}
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.  


<pre><nowiki>
'''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.
 
<syntaxhighlight lang='python'>
#!/usr/bin/python
#!/usr/bin/python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-


"""
"""
Crop and resample selected photo in Scribus
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
By prof. MS. José Antonio Meira da Rocha
mailto:joseantoniomeira@gmail.com
mailto:joseantoniorocha@gmail.com
http://meiradarocha.jor.br
http://meiradarocha.jor.br
License GPL
License GPL
2010-08-04a
2010-08-15a
PIL docs: http://infohost.nmt.edu/tcc/help/pubs/pil/image-constructors.html
PIL docs: http://infohost.nmt.edu/tcc/help/pubs/pil/image-constructors.html
http://www.pythonware.com/library/pil/handbook/index.htm
http://www.pythonware.com/library/pil/handbook/index.htm
Line 30: Line 40:


#########################
#########################
# USER IMPORTS GO HERE #
# USER IMPORTS GOES HERE
#########################
#########################
import os
import os
from string import Template


#############################################
#############################################
Line 43: Line 54:
# User can change this variables
# User can change this variables
################################
################################
imageResolution = 200  # resolution for printing
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.


###################
###################
Line 59: Line 73:
# Locale strings
# Locale strings
######################
######################
# Textos para abrir documento
# Dialog to open document
askResolutionLabel="Abra um documento"
askResolutionLabel="Choose the resolution"
askResolution = "<h2>Qual a resolução</h2>" \
askResolution = '<h2>'+askResolutionLabel+'</h2>' \
     + "<p></p>"
     + '<p>Choose the resolution to resample the image (200 ppi, 300ppi)</p>'
#     
#     
openImageErrorLabel = 'Erro de abertura'
openImageErrorLabel = 'Open error'
openImageErrorMsg = '<h2>Erro de abertura</h2>' \
openImageErrorMsg = '<h2>'+openImageErrorLabel+'</h2>' \
     + '<p>Houve um erro ao tentar abrir o arquivo de foto. <br>Talvez o arquivo tenha sido perdido.</p>'
     + '<p>Error opening the image file.</p>'
#     
#     
openDocLabel="Abra um documento"
openDocLabel="Open a document"
askOpenDoc = "<h2>Abra um documento</h2>" \
askOpenDoc = '<h2>'+openDocLabel+'</h2>' \
     + "<p>Por favor, abra um documento e selecione um quadro antes de usar este script.</p>"
     + '<p>Please, open a document and select a image frame before using this script.</p>'
# Textos para abrir quadro
# Strings to open frame
imageFrameLabel = "Selecione um quadro de imagem"
imageFrameLabel = "Select a image frame"
askSelectImageFrame = "<h2>Selecione um quadro de imagem</h2>\n" \
askSelectImageFrame = '<h2>'+imageFrameLabel+'</h2>' \
     + "Selecione <b>um quadro de imagem</b>\n" \
     + "Select <b>a image frame</b>\n" \
     + "para cortar a foto dentro dele."  
     + "to crop it image."  
#  
# Out of range image strings
outOfRangeLabel = 'Foto fora de faixa'
outOfRangeLabel = 'Image out of limits'
msgOutOfRange = '<h2>Foto fora de faixa</h2>A foto não está ocupando todo o quadro.'
msgOutOfRange = '<h2>'+outOfRangeLabel+'</h2><p>Choose a beetter crop area.'
# Error on save image
# Error on save image
errorOnSaveLabel = 'Erro ao gravar'
errorOnSaveLabel = 'Save error'
errorOnSaveMsg = '<h2>Erro ao gravar</h2>Houve um erro ao gravar o arquivo de imagem.'
errorOnSaveMsg = '<h2>'+errorOnSaveLabel+'</h2><p>Error saving the cropped image.'
#Error on cropping
# Error on cropping
cropErrorLabel = 'Erro ao cortar a imagem'
cropErrorLabel = 'Error cropping image'
cropErrorMsg = '<h2>Erro ao cortar a imagem</h2><p>Houve um erro ao cortar a imagem. Talvez este tipo de arquivo não seja suportado.'
cropErrorMsg = '<h2>'+cropErrorLabel+'</h2><p>.'
# Error on loading image
# Error on loading image
errorOnLoadLabel = 'Erro ao importar a imagem'
errorOnLoadLabel = 'Error loading image'
errorOnLoadMsg = '<h2>Erro ao importar a imagem</h2>Houve um erro ao importar a imagem.'
errorOnLoadMsg = '<h2>'+errorOnLoadLabel+'</h2>Verify if image exist.'


#########################################
#########################################
Line 101: Line 115:
     sys.exit(1)
     sys.exit(1)


# cropped sufix
cropsufix = '_cortado'  # '_cropped'
   
# foot msg
# foot msg
statusText = "Running script..."
statusText = "Running script..."
Line 128: Line 139:


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


def isTextFrame(obj):
def isTextFrame(obj):
     """Verify if object is text frame.
     """Verify if object is text frame."""
    Devolve zero se não é texto."""
     type = scribus.getObjectType(obj)
     type = scribus.getObjectType(obj)
     if (type == 'TextFrame'):
     if (type == 'TextFrame'):
Line 145: Line 155:


def isGraphicFrame(obj):
def isGraphicFrame(obj):
     """Verify if object is image frame
     """Verify if object is image frame."""
    Devolve zero se não é grupo imagem."""
     type = scribus.getObjectType(obj)
     type = scribus.getObjectType(obj)
     if (type == 'ImageFrame'):
     if (type == 'ImageFrame'):
Line 162: Line 171:
     except:
     except:
         scribus.messageBox(openImageErrorLabel,openImageErrorMsg,ICON_WARNING,BUTTON_OK)
         scribus.messageBox(openImageErrorLabel,openImageErrorMsg,ICON_WARNING,BUTTON_OK)
        image,imageFile = "",""
         sys.exit(1)
         sys.exit(1)
     return image,imf
     return image,imf
Line 172: Line 180:
     imageXOffset = scribus.getProperty(imageFrame,'imageXOffset')
     imageXOffset = scribus.getProperty(imageFrame,'imageXOffset')
     imageYOffset = scribus.getProperty(imageFrame,'imageYOffset')
     imageYOffset = scribus.getProperty(imageFrame,'imageYOffset')
     # spawn error if offset is positive
     # avoid offset positive
     if (imageXOffset > 0) or (imageYOffset > 0):
     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)
         scribus.messageBox(outOfRangeLabel,msgOutOfRange,ICON_WARNING,BUTTON_OK)
         sys.exit(1)
         sys.exit(1)
Line 192: Line 203:
     right = left + (frameSizeX / imageXScale)
     right = left + (frameSizeX / imageXScale)
     bottom = top + (frameSizeY / imageYScale)
     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:
     try:
         im = image.crop((left,top,right,bottom))
         im = image.crop((left,top,right,bottom))
Line 198: Line 217:
         scribus.messageBox(cropErrorLabel,cropErrorMsg,ICON_WARNING,BUTTON_OK)
         scribus.messageBox(cropErrorLabel,cropErrorMsg,ICON_WARNING,BUTTON_OK)
         sys.exit(1)
         sys.exit(1)
     # Uncomment this 2 lines if you wish color space conversion.
     # Colorspace conversion
     #if im.mode == 'RGB':
     if im.mode != colorspace:
    #    im = im.convert('CMYK')
        im = im.convert(colorspace)
     return im
     return im


####################
# Test if file exist
####################
def fileExist(fileName):
    try:
        f = open(fileName,'r')
        return 1
    except:
        return 0
   
#################
#################
# Save Image
# Save Image
Line 208: Line 237:
def saveImage(newImage,imageFile):
def saveImage(newImage,imageFile):
     name,ext = os.path.splitext(imageFile)
     name,ext = os.path.splitext(imageFile)
     newImageFile = name+cropsufix+'.tif' # force tiff format
     # 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:
     try:
         # 'encode' let PIL handle accented filenames
         #'encode' let PIL handle accented filenames
         newImage.save(newImageFile.encode(encoding),dpi=(imageResolution,imageResolution))
         newImage.save(newImageFile.encode(encoding),dpi=(imageResolution,imageResolution))
     except:
     except:
Line 262: Line 297:
         scribus.setUnit(UNIT_POINTS) # pre-press mesure system
         scribus.setUnit(UNIT_POINTS) # pre-press mesure system
         print
         print
        ###############################
        # Handle selection
        ###############################
         handleSelection()
         handleSelection()
         # Recover units
         # Recover units
Line 273: Line 305:
def myCode():  
def myCode():  
     """ User code """
     """ User code """
    #########################
    #  USER CODE GOES HERE  #
    #########################
    # Gerencia documento (aberto, abre etc)
     handleDocument()
     handleDocument()
    #########################
    #  USER CODE ENDS HERE  #
    #########################


def main(argv):
def main(argv):
Line 299: Line 324:
if __name__ == '__main__':
if __name__ == '__main__':
     main_wrapper(sys.argv)
     main_wrapper(sys.argv)
 
</syntaxhighlight>
</nowiki></pre>

Latest revision as of 04:42, 31 May 2017

This article is part of the Scripts series.

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.

#!/usr/bin/python
# -*- 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

#########################
# USER IMPORTS GOES HERE
#########################
import os
from string import Template

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

################################
# User can change this variables
################################
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.

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

######################
# Locale strings
######################
# Dialog to open document
askResolutionLabel="Choose the resolution"
askResolution = '<h2>'+askResolutionLabel+'</h2>' \
    + '<p>Choose the resolution to resample the image (200 ppi, 300ppi)</p>'
#    
openImageErrorLabel = 'Open error'
openImageErrorMsg = '<h2>'+openImageErrorLabel+'</h2>' \
    + '<p>Error opening the image file.</p>'
#    
openDocLabel="Open a document"
askOpenDoc = '<h2>'+openDocLabel+'</h2>' \
    + '<p>Please, open a document and select a image 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 it image." 
# Out of range image strings
outOfRangeLabel = 'Image out of limits'
msgOutOfRange = '<h2>'+outOfRangeLabel+'</h2><p>Choose a beetter crop area.'
# Error on save image
errorOnSaveLabel = 'Save error'
errorOnSaveMsg = '<h2>'+errorOnSaveLabel+'</h2><p>Error saving the cropped image.'
# Error on cropping
cropErrorLabel = 'Error cropping image'
cropErrorMsg = '<h2>'+cropErrorLabel+'</h2><p>.'
# Error on loading image
errorOnLoadLabel = 'Error loading image'
errorOnLoadMsg = '<h2>'+errorOnLoadLabel+'</h2>Verify if image exist.'

#########################################
# Localized group name
# Put your language string for group names in dictionary
# For use by isGroup() function
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 <b>group</b> string in your language in "stringsDic".',ICON_WARNING,BUTTON_OK)
    sys.exit(1)

# foot msg
statusText = "Running script..."

#
# End Locale strings
####################

#############
# PIL library
#############
# 'import Image' cause Scribus freeze after script run, in Ubuntu 10.04. 
#  OK in 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)

##############################################
# 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):
    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

####################
# Test if file exist
####################
def fileExist(fileName):
    try:
        f = open(fileName,'r')
        return 1
    except:
        return 0
    
#################
# Save Image
#################    
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

#######################
# Reload image to frame
#######################
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)

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

##################
# Handle selection
##################
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)

#####################
# Handle document
#####################
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)

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)