def create_working_directory():
    """Create a "working directory" in which the files related to the icon-
       creation process can be stored, and return the full path to it.
       The working directory will be created in ~invenio/var/tmp.
       If it cannot be created there, an exception
       (InvenioWebSubmitIconCreatorError) will be raised.
       The working directory will have the prefix
       "websubmit_icon_creator_", and could be given a name something like:
                 - websubmit_icon_creator_Tzs3St
       @return: (string) - the full path to the working directory.
       @Exceptions raised: InvenioWebSubmitIconCreatorError.
    """
    ## Create the temporary directory in which to place the files related to
    ## icon creation in ~invenio/var/tmp:
    path_workingdir = None
    try:
        path_workingdir = tempfile.mkdtemp(prefix="websubmit_icon_creator_", \
                                           dir="%s" % CFG_TMPDIR)
    except OSError, err:
        ## Unable to create the temporary directory in ~invenio/var/tmp
        msg = "Error: Unable to create a temporary working directory in " \
              "which to carry out the icon creation process. An attempt was " \
              "made to create the directory in [%s]; the error encountered " \
              "was <%s>. Icon creation has failed." % (CFG_TMPDIR, str(err))
        raise InvenioWebSubmitIconCreatorError(msg)
def create_icon(options):
    """The driver for the icon creation process. This is effectively the
       function that is responsible for coordinating the icon creation.
       It is the API for the creation of an icon.
       @param options: (dictionary) - a dictionary of options that are required
        by the function in order to carry out the icon-creation process.

        The dictionary must have the following structure:
           + input-file: (string) - the path to the input file (i.e. that
              which is to be stamped;
           + icon-name: (string) - the name of the icon that is to be created
              by the program. This is optional - if not provided,
              a default name will be applied to the icon file instead;
           + multipage-icon: (boolean) - used only when the original file
              is a PDF or PS file. If False, the created icon will feature ONLY
              the first page of the PDF. If True, ALL pages of the PDF will
              be included in the created icon. Note: If the icon type is not
              gif, this flag will be forced as False.
           + multipage-icon-delay: (integer) - used only when the original
              file is a PDF or PS AND use-first-page-only is False AND
              the icon type is gif.
              This allows the user to specify the delay between "pages"
              of a multi-page (animated) icon.
           + icon-scale: ('geometry') - the scaling information to be used for the
              creation of the new icon. Type 'geometry' as defined in ImageMagick.
              (eg. 320 or 320x240 or 100> or 5%)
           + icon-file-format: (string) - the file format of the icon that is
              to be created. Legal values are:
              * pdf
              * gif
              * jpg
              * jpeg
              * ps
              * png
              * bmp
           + verbosity: (integer) - the verbosity level under which the program
              is to run;
        So, an example of the returned dictionary could be something like:
              { 'input-file'           : "demo-picture-file.jpg",
                'icon-name'            : "icon-demo-picture-file",
                'icon-file-format'     : "gif",
                'multipage-icon'       : True,
                'multipage-icon-delay' : 100,
                'icon-scale'           : 180,
                'verbosity'            : 0,
              }
       @return: (tuple) - consisting of two strings:
          1. the path to the working directory in which all files related to
              icon creation are stored;
          2. The name of the "icon" file;
       @Exceptions raised: (InvenioWebSubmitIconCreatorError)
        be raised or propagated by this function when the icon creation process
        fails for one reason or another.
    """
    ## SANITY CHECKS:
    ## Does the options dictionary contain all expected keys?
    ##
    ## A list of the names of the expected options:
    expected_option_names = ['input-file', \
                             'icon-name', \
                             'icon-file-format', \
                             'multipage-icon', \
                             'multipage-icon-delay', \
                             'icon-scale', \
                             'verbosity']
    expected_option_names.sort()
    ## A list of the option names that have been received:
    received_option_names = options.keys()
    received_option_names.sort()

    if expected_option_names != received_option_names:
        ## Error: he dictionary of options had an illegal structure:
        msg = """Error: Unexpected value received for "options" parameter."""
        raise InvenioWebSubmitIconCreatorError(msg)

    ## Do we have an input file to work on?
    if options["input-file"] in (None, ""):
        ## No input file - stop the icon creation:
        msg = "Error: unable to determine the name of the file from which " \
              "the icon is to be created."
        raise InvenioWebSubmitIconCreatorError(msg)
    else:
        ## Get the file type of the input file:
        tmp_file_extension = options["input-file"].split(".")[-1]
        ## allow also Invenio files that use the format: filename.ext;format;subformat;version
        tmp_file_extension = tmp_file_extension.split(';')[0]
        if tmp_file_extension.lower() not in CFG_ALLOWED_FILE_EXTENSIONS:
            ## Ilegal input file type.
            msg = "Error: icons can be only be created from %s files, " \
                  "not [%s]." % (str(CFG_ALLOWED_FILE_EXTENSIONS), \
                                 tmp_file_extension.lower())
            raise InvenioWebSubmitIconCreatorError(msg)
        else:
            subject_filetype = tmp_file_extension.lower()

    ## Wash the requested icon name:
    if type(options["icon-name"]) is not str:
        options["icon-name"] = ""
    else:
        (dummy, name_iconfile) = os.path.split(options["icon-name"])
        if name_iconfile != "":
            ## Take just the basename component of the icon file:
            options["icon-name"] = name_iconfile

    ## Do we have an icon file format?
    icon_format = options["icon-file-format"]
    if icon_format in (None, ""):
        ## gif by default:
        options["icon-file-format"] = "gif"
    elif str(icon_format).lower() not in CFG_ALLOWED_FILE_EXTENSIONS:
        ## gif if an invalid icon type was supplied:
        options["icon-file-format"] = "gif"
    else:
        ## Use the provided icon type:
        options["icon-file-format"] = icon_format.lower()

    ## Wash the use-first-page-only flag according to the type of the
    ## requested icon:
    if options["icon-file-format"] != "gif":
        ## Since the request icon isn't a gif file, it can't be animated
        ## and should be created from the first "page" of the original file:
        options["multipage-icon"] = False
    else:
        ## The requested icon is a gif. Verify that the multipage-icon
        ## flag is a boolean value. If not, set it to False by default:
        if type(options["multipage-icon"]) is not bool:
            ## Non-boolean value: default to False:
            options["multipage-icon"] = False

    ## Wash the delay time for frames in an animated gif icon:
    if type(options["multipage-icon-delay"]) is not int:
        ## Invalid value - set it to default:
        options["multipage-icon-delay"] = 100
    elif options["multipage-icon-delay"] < 0:
        ## Can't have negative delays:
        options["multipage-icon-delay"] = 100

    ## Wash the icon scaling information:
    if not re_imagemagic_scale_parameter_format.match(options["icon-scale"]):
        ## Ivalid value - set it to default:
        options["icon-scale"] = "180"

    ## OK. Begin the icon creation process:
    ##
    ## Create a working directory for the icon creation process and get the
    ## full path to it:
    path_workingdir = create_working_directory()

    ## Copy the file from which the icon is to be created into the
    ## working directory:
    try:
        basename_source_file = \
                copy_file_to_directory(options["input-file"], path_workingdir)
    except IOError, err:
        ## Unable to copy the source file to the working directory.
        msg = "Icon creation failed: unable to copy the source image file " \
              "to the working directory. Got this error: [%s]" % str(err)
        raise InvenioWebSubmitIconCreatorError(msg)
def build_icon(path_workingdir,
               source_filename,
               source_filetype,
               icon_name,
               icon_filetype,
               multipage_icon,
               multipage_icon_delay,
               icon_scale):
    """Whereas create_icon acts as the API for icon creation and therefore
       deals with argument washing, temporary working directory creation,
       etc, the build_icon function takes care of the actual creation of the
       icon file itself by calling various shell tools.
       To accomplish this, it relies upon the following parameters:
       @param path_workingdir: (string) - the path to the working directory
        in which all files related to the icon creation are stored.
       @param source_filename: (string) - the filename of the original image
        file.
       @param source_filetype: (string) - the file type of the original image
        file.
       @param icon_name: (string) - the name that is to be given to the icon.
       @param icon_filetype: (string) - the file type of the icon that is
        to be created.
       @param multipage_icon: (boolean) - a flag indicating whether or not
        an icon with multiple pages (i.e. an animated gif icon) should be
        created.
       @param multipage_icon_delay: (integer) - the delay to be used between
        frame changing for an icon with multiple pages (i.e. an animated gif.)
       @param icon_scale: (integer) - the scaling information for the created
        icon.
       @return: (string) - the name of the created icon file (which will have
        been created in the working directory "path_workingdir".)
       @Exceptions raised: (InvenioWebSubmitIconCreatorError) - raised when
        the icon creation process fails.
    """
    ##
    ## If the source file is a PS, convert it into a PDF:
    if source_filetype == "ps":
        ## Convert the subject file from PostScript to PDF:
        if source_filename[-3:].lower() == ".ps":
            ## The name of the file to be stamped has a PostScript extension.
            ## Strip it and give the name of the PDF file to be created a
            ## PDF extension:
            created_pdfname = "%s.pdf" % source_filename[:-3]
        elif len(source_filename.split(".")) > 1:
            ## The file name has an extension - strip it and add a PDF
            ## extension:
            raw_name = source_filename[:source_filename.rfind(".")]
            if raw_name != "":
                created_pdfname = "%s.pdf" % raw_name
            else:
                ## It would appear that the file had no extension and that its
                ## name started with a period. Just use the original name with
                ## a .pdf suffix:
                created_pdfname = "%s.pdf" % source_filename
        else:
            ## No extension - use the original name with a .pdf suffix:
            created_pdfname = "%s.pdf" % source_filename

        ## Build the distilling command:
        cmd_distill = """%(distiller)s %(ps-file-path)s """ \
                      """%(pdf-file-path)s 2>/dev/null""" % \
                      { 'distiller'     : CFG_PATH_PS2PDF,
                        'ps-file-path'  : escape_shell_arg("%s/%s" % \
                                                          (path_workingdir, \
                                                           source_filename)),
                        'pdf-file-path' : escape_shell_arg("%s/%s" % \
                                                          (path_workingdir, \
                                                           created_pdfname)),
                      }
        ## Distill the PS into a PDF:
        errcode_distill = os.system(cmd_distill)

        ## Test to see whether the PS was distilled into a PDF without error:
        if errcode_distill or \
           not os.access("%s/%s" % (path_workingdir, created_pdfname), os.F_OK):
            ## The PDF file was not correctly created in the working directory.
            ## Unable to continue.
            msg = "Error: Unable to correctly convert PostScript file [%s] to" \
                  " PDF. Cannot create icon." % source_filename
            raise InvenioWebSubmitIconCreatorError(msg)

        ## Now assign the name of the created PDF file to subject_file:
        source_filename = created_pdfname

    ##
    ## Treat the name of the icon:
    if icon_name in (None, ""):
        ## Since no name has been provided for the icon, give it the same name
        ## as the source file, but with the prefix "icon-":
        icon_name = "icon-%s" % source_filename
    ## Now if the icon name has an extension, strip it and add that of the
    ## icon file type:
    if len(icon_name.split(".")) > 1:
        ## The icon file name has an extension - strip it and add the icon
        ## file type extension:
        raw_name = icon_name[:icon_name.rfind(".")]
        if raw_name != "":
            icon_name = "%s.%s" % (raw_name, icon_filetype)
        else:
            ## It would appear that the file had no extension and that its
            ## name started with a period. Just use the original name with
            ## the icon file type's suffix:
            icon_name = "%s.%s" % (icon_name, icon_filetype)
    else:
        ## The icon name had no extension. Use the original name with the
        ## icon file type's suffix:
        icon_name = "%s.%s" % (icon_name, icon_filetype)

    ##
    ## If the source file type is PS or PDF, it may be necessary to separate
    ## the first page from the rest of the document and keep it for use as
    ## the icon. Do this if necessary:
    if source_filetype in ("ps", "pdf") and \
           (icon_filetype != "gif" or not multipage_icon):
        ## Either (a) the icon type isn't GIF (in which case it cannot
        ## be animated and must therefore be created _only_ from the
        ## document's first page; or (b) the icon type is GIF, but the
        ## icon is to be created from the first page of the document only.
        ## The first page of the PDF document must be separated and is to
        ## be used for icon creation:
        source_file_first_page = "p1-%s" % source_filename
        ## Perform the separation:
        cmd_get_first_page = \
             "%(pdftk)s A=%(source-file-path)s " \
             "cat A1 output %(first-page-path)s " \
             "2>/dev/null" \
             % { 'pdftk'            : CFG_PATH_PDFTK,
                 'source-file-path' : escape_shell_arg("%s/%s" % \
                                           (path_workingdir, source_filename)),
                 'first-page-path'  : escape_shell_arg("%s/%s" % \
                                           (path_workingdir, \
                                            source_file_first_page)),
               }
        errcode_get_first_page = os.system(cmd_get_first_page)
        ## Check that the separation was successful:
        if errcode_get_first_page or \
               not os.access("%s/%s" % (path_workingdir, \
                                        source_file_first_page), os.F_OK):
            ## Separation was unsuccessful.
            msg = "Error: Unable to create an icon for file [%s/%s] - it " \
                  "wasn't possible to separate the first page from the " \
                  "rest of the document (error code [%s].)" \
                  % (path_workingdir, source_filename, errcode_get_first_page)
            raise InvenioWebSubmitIconCreatorError(msg)
        else:
            ## Successfully extracted the first page. Treat it as the source
            ## file for icon creation from now on:
            source_filename = source_file_first_page

    ##
    ## Create the icon:
    ## If a delay is necessary for an animated gif icon, create the
    ## delay string:
    delay_info = ""
    if source_filetype in ("ps", "pdf") and \
           icon_filetype == "gif" and multipage_icon:
        ## Include delay information:
        delay_info = "-delay %s" % escape_shell_arg(str(multipage_icon_delay))

    ## Command for icon creation:
    cmd_create_icon = "%(convert)s -colorspace rgb -auto-orient -scale %(scale)s %(delay)s " \
                      "%(source-file-path)s %(icon-file-path)s 2>/dev/null" \
                      % { 'convert'          : CFG_PATH_CONVERT,
                          'scale'            : \
                                             escape_shell_arg(icon_scale),
                          'delay'            : delay_info,
                          'source-file-path' : \
                                      escape_shell_arg("%s/%s" \
                                                      % (path_workingdir, \
                                                         source_filename)),
                          'icon-file-path'   : \
                                      escape_shell_arg("%s/%s" \
                                                      % (path_workingdir, \
                                                         icon_name)),
                        }
    errcode_create_icon = os.system(cmd_create_icon)
    ## Check that the icon creation was successful:
    if errcode_create_icon or \
           not os.access("%s/%s" % (path_workingdir, icon_name), os.F_OK):
        ## Icon creation was unsuccessful.
        msg = "Error: Unable to create an icon for file [%s/%s] (error " \
              "code [%s].)" \
              % (path_workingdir, source_filename, errcode_create_icon)
        raise InvenioWebSubmitIconCreatorError(msg)

    ##
    ## The icon was successfully created. Return its name:
    return icon_name