Пример #1
0
class Template (object) :

    def __init__(self, pid) :
        '''Intitate the whole class and create the object.'''

        self.pid            = pid
        self.tools          = Tools()
        self.user           = UserConfig()
        self.userConfig     = self.user.userConfig
        self.local          = ProjLocal(pid)
        self.log            = ProjLog(pid)
        self.projData       = ProjData(pid)


    def projectToTemplate (self, tid = None) :
        '''Preserve critical project information in a template. The pid is the project
        that the template will be bassed from. The template will go in the template lib.'''

        # Set needed vals
        if not tid :
            tid             = self.pid
        tempDir             = os.path.join(tempfile.mkdtemp(), tid)
        target              = os.path.join(self.local.userLibTemplate, tid + '.zip')

        # Make a temp copy of the project that we can manipulate
        shutil.copytree(self.local.projHome, tempDir)

        # Now make the config files generic for use with any project
        tc = ConfigObj(os.path.join(tempDir, 'Config', 'project.conf'), encoding='utf-8')
        tc['ProjectInfo']['projectTitle']               = ''
        tc['ProjectInfo']['projectIDCode']              = ''
        tc['ProjectInfo']['projectCreateDate']          = ''
        tc['ProjectInfo']['projectCreateDate']          = ''
        # Remove unnecessary folders
        needNot = ['Component', 'Deliverable', 'Illustration']
        for f in needNot :
            fld = os.path.join(tempDir, f)
            if os.path.exists(fld) :
                shutil.rmtree(fld)
        # Remove unnecessary config files
        needNot = ['adjustment', 'illustration']
        for f in needNot :
            fl = os.path.join(tempDir, 'Config', f + '.conf')
            if os.path.exists(fl) :
                os.remove(fl)
        # Remove unnecessary project config stuff
        needNot = ['Groups', 'Backup']
        for s in needNot :
            if tc.has_key(s) :
                del tc[s]
        # Write out the new template project config file
        tc.filename = os.path.join(tempDir, 'Config', 'project.conf')
        tc.write()

        # Kill the log file
        os.remove(os.path.join(tempDir, 'rapuma.log'))

        # Exclude files
        excludeFiles = self.projData.makeExcludeFileList(source)

        # Zip it up using the above params
        root_len = len(tempDir)
        with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as myzip :
            sys.stdout.write('Creating template')
            sys.stdout.flush()
            for root, dirs, files in os.walk(tempDir):
                # Chop off the part of the path we do not need to store
                zip_root = os.path.abspath(root)[root_len:]
                for f in files:
                    if f[-1] == '~' :
                        continue
                    elif f in excludeFiles :
                        continue
                    elif f.rfind('.') != -1 :
                        fullpath = os.path.join(root, f)
                        zip_name = os.path.join(zip_root, f)
                        sys.stdout.write('.')
                        sys.stdout.flush()
                        myzip.write(fullpath, zip_name, zipfile.ZIP_DEFLATED)

            # Add space for next message
            sys.stdout.write('\n')

        # Remove the temp project dir we made
        self.tools.terminal('\nCompleted creating template: ' + target + '\n')


    def templateToProject (self, targetDir = None, source = None) :
        '''Create a new project based on the provided template ID. If a path to
        the template is not provided it will look in the users template lib. A PID
        must be provided. That is checked with the system. If the same PID is
        found in the system, it must be removed before reruning this function. If
        a non-default location is needed, a target path must be provided.'''

#        import pdb; pdb.set_trace()

        # Set a default target path
        projHome = os.path.join(self.local.projParentDefaultFolder, self.pid)
        # See if we can build a better target path
        if targetDir != '' :
            projHome = os.path.join(targetDir, self.pid)
        elif self.local.projHome :
            projHome = os.path.join(self.local.projHome, self.pid)

#        self.tools.dieNow()

        # Test to see if the project already exists
        if self.pid in self.projList :
            self.tools.terminal('\nError: Project ID [' + self.pid + '] is already exists on this system. Use the remove command to remove it.')
            self.tools.dieNow()
        # Test for source template file
        if not source :
            source = os.path.join(self.local.userLibTemplate, self.pid + '.zip')
            if not os.path.exists(source) :
                self.tools.terminal('\nError: No template can be found for [' + self.pid + ']\n')
                self.tools.dieNow()

        # Unzip the template in place to start the new project
        with zipfile.ZipFile(source, 'r') as myzip :
            myzip.extractall(projHome)

        # Peek into the project
        pc = ConfigObj(os.path.join(projHome, 'Config', 'project.conf'), encoding='utf-8')
        pc['ProjectInfo']['projectCreateDate']         = self.tools.tStamp()
        pc['ProjectInfo']['projectIDCode']             = self.pid
        pc.filename                                    = os.path.join(projHome, 'Config', 'project.conf')
        pc.write()

        # Get the media type from the newly placed project for registration
        projectMediaIDCode = pc['ProjectInfo']['projectMediaIDCode']

        # Reset the local settings
        self.local = ProjLocal(self.pid)

        # Create any folders that might be needed
        for fld in self.local.projFolders :
            folder = os.path.join(self.local.projHome, fld)
            if not os.path.exists(folder) :
                os.makedirs(folder)

        # Report what happened
        self.tools.terminal('A new project [' + self.pid + '] has been created based on the [' + self.tools.fName(source) + '] template.')
Пример #2
0
class UserConfig (object) :

    def __init__(self) :
        '''Intitate the whole class and create the object.'''

#        import pdb; pdb.set_trace()

        self.rapumaHome         = os.environ.get('RAPUMA_BASE')
        self.defaultUserHome    = os.environ.get('RAPUMA_USER')
        self.userConfFileName   = 'rapuma.conf'
        self.tools              = Tools()

        # Point to the right user config either server or desktop
        # The RAPUMA_USER setting should have the right path from
        # the mother script (rapuma)
        self.userConfFile       = os.path.join(self.defaultUserHome, self.userConfFileName)

        # Check to see if the file is there, then read it in and break it into
        # sections. If it fails, scream really loud!
        rapumaXMLDefaults = os.path.join(self.rapumaHome, 'config', 'rapuma.xml')
        if os.path.exists(rapumaXMLDefaults) :
            self.tools.sysXmlConfig = self.tools.xml_to_section(rapumaXMLDefaults)
        else :
            raise IOError, "Can't open " + rapumaXMLDefaults

        # Now make the users local rapuma.conf file if it isn't there
        if not os.path.isfile(self.userConfFile) :
            self.initUserHome()

        # Load the system.conf file into an object
        self.userConfig = ConfigObj(self.userConfFile, encoding='utf-8')

        # Log messages for this module
        self.errorCodes     = {
            '0000' : ['MSG', 'Placeholder message'],
        }

###############################################################################
############################ User Config Functions ############################
###############################################################################

    def initUserHome (self) :
        '''Initialize a user config file on a new install or system re-init.'''

        # Create home folders
        if not os.path.isdir(self.defaultUserHome) :
            os.mkdir(self.defaultUserHome)

        # Make the default global rapuma.conf for custom environment settings
        if not os.path.isfile(self.userConfFile) :
            self.userConfig = ConfigObj(self.tools.sysXmlConfig.dict(), encoding='utf-8')
            self.userConfig.filename = self.userConfFile
            self.userConfig['System']['initDate'] = self.tools.tStamp()
            self.userConfig.write()


    def setSystemSettings (self, section, key, value) :
        '''Function to make system settings.'''

        oldValue = self.userConfig[section][key]
        if oldValue != value :
            self.userConfig[section][key] = value
            # Write out the results
            self.userConfig.write()
            self.tools.terminal('\nRapuma user name setting changed from [' + oldValue + '] to [' + value + '].\n\n')
        else :
            self.tools.terminal('\nSame value given, nothing to changed.\n\n')
Пример #3
0
class Config (object) :

    def __init__(self, pid, gid=None) :
        '''Do the primary initialization for this class.'''

        self.pid                            = pid
        self.gid                            = gid
        self.user                           = UserConfig()
        self.userConfig                     = self.user.userConfig
        self.projHome                       = os.path.join(os.path.expanduser(self.userConfig['Resources']['projects']), self.pid)
        self.local                          = ProjLocal(pid, gid)
        self.tools                          = Tools()
        self.log                            = ProjLog(pid)
        # Create config placeholders
        self.projectConfig                  = None
        self.adjustmentConfig               = None
        self.layoutConfig                   = None
        self.illustrationConfig             = None
        self.fontConfig                     = None
        self.macroConfig                    = None

        # Log messages for this module
        self.errorCodes     = {
            '3100' : ['ERR', 'Macro package: [<<1>>] already exists in the project. Use force (-f) to reinstall.'],
            '3200' : ['ERR', 'Failed to install macro package: [<<1>>]'],
            '3300' : ['MSG', 'Install macro package: [<<1>>], Reinitialized [<<2>>]'],
            '3310' : ['ERR', 'Failed to copy [<<1>>] to folder [<<2>>].'],
            '3400' : ['MSG', 'Force set to True. Removed macro package configuration file: [<<1>>]'],
            '3500' : ['MSG', 'Removed macro package [<<1>>] folder and all files contained.'],
            '3600' : ['MSG', 'Updated macro package [<<1>>]'],
            '3650' : ['ERR', 'Failed to updated macro package [<<1>>]']
        }

        # Test for gid before trying to finish the init

#        import pdb; pdb.set_trace()

        if gid :
            if not self.projectConfig :
                self.getProjectConfig()
            # We need to skip over this if the group doesn't exist
            try :
                # Reinitialize local
                self.cType                      = self.projectConfig['Groups'][gid]['cType']
                self.Ctype                      = self.cType.capitalize()
                self.local                      = ProjLocal(pid, gid, self.cType)
            except :
                self.cType                      = None
                self.Ctype                      = None
        else :
            self.cType                      = None
            self.Ctype                      = None



###############################################################################
############################# Get Config Functions ############################
###############################################################################
####################### Error Code Block Series = 0500 ########################
###############################################################################


    def getProjectConfig (self) :
        '''Load/return the project configuation object.'''

#        import pdb; pdb.set_trace()

        self.projectConfig = self.tools.loadConfig(self.local.projectConfFile, self.local.projectConfXmlFile)


    def getAdjustmentConfig (self) :
        '''Load/return the adjustment configuation object.'''

        self.adjustmentConfig = self.tools.loadConfig(self.local.adjustmentConfFile, self.local.adjustmentConfXmlFile)


    def getLayoutConfig (self) :
        '''Load/return the layout configuation object.'''

        self.layoutConfig = self.tools.loadConfig(self.local.layoutConfFile, self.local.layoutConfXmlFile)


    def getIllustrationConfig (self) :
        '''Load/return the illustration configuation object.'''
        
        self.illustrationConfig = self.tools.loadConfig(self.local.illustrationConfFile, self.local.illustrationConfXmlFile)


    def getFontConfig (self) :
        '''Load/return the font configuation object.'''
        
        self.fontConfig = self.tools.loadConfig(self.local.fontConfFile, self.local.fontConfXmlFile)


    def getMacroConfig (self) :
        '''Load/return the macro configuration object.'''

        self.macroConfig = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)


###############################################################################
############################ Manager Level Functions ##########################
###############################################################################
####################### Error Code Block Series = 1000 ########################
###############################################################################


    def makeNewprojectConf (self, local, pid, cVersion, pmid='book') : 
        '''Create a new project configuration file for a new project.'''

        self.projectConfig = ConfigObj(self.tools.getXMLSettings(os.path.join(local.rapumaConfigFolder, pmid + '.xml')), encoding='utf-8')
        # Insert intitial project settings
        self.projectConfig['ProjectInfo']['projectMediaIDCode']        = pmid
        self.projectConfig['ProjectInfo']['creatorID']                 = self.userConfig['System']['userID']
        self.projectConfig['ProjectInfo']['projectCreatorVersion']     = cVersion
        self.projectConfig['ProjectInfo']['projectCreateDate']         = self.tools.tStamp()
        self.projectConfig['ProjectInfo']['projectIDCode']             = pid
        self.projectConfig['Backup']['ownerID']                        = self.userConfig['System']['userID']
        self.projectConfig['Groups']                                   = {}
        # Even though there was no push, we need a time stamp to avoid confusion
        self.projectConfig['Backup']['lastCloudPush']                  = self.tools.fullFileTimeStamp()
        self.projectConfig.filename                                    = local.projectConfFile
        self.projectConfig.write()


###############################################################################
######################## Basic Config Handling Functions ######################
###############################################################################
####################### Error Code Block Series = 2000 ########################
###############################################################################


    def processSinglePlaceholder (self, ph, value) :
        '''Once we are sure we have a single placeholder (noting embedded) this
        will process it and replace it with the correct value.'''

        holderType = ph.split(':')[0]
        try :
            holderKey = ph.split(':')[1]
        except :
            holderKey = ''

        if self.hasPlaceHolder(value):
            value = self.processNestedPlaceholders(value, '')

        result = ph # If nothing matches below, default to returning placeholder unchanged
        if holderType == 'val' :
            result = value
        # A value that needs a measurement unit attached
        elif holderType == 'mu' :
            result = self.getMeasureUnit()
        # A value that is from a configObj
        elif holderKey and holderType == 'config' :
            result = self.getConfigValue(holderKey)
        # A value that is a path
        elif holderKey and holderType == 'path' :
            result = getattr(self.local, holderKey)
        # A value that is from a configObj
# FIXME: To work around a circular init problem between Config() and Macro()
# the macro package name (UsfmTex) has been hard coded. Not sure how to call
# another function in another class that relies on this class to work.
# This will break when the time comes that another macro family than UsfmTex
# is used to hold the functions needed to process values to be used
        elif holderKey and holderType == 'function' :
#            import pdb; pdb.set_trace()
            fnc = getattr(UsfmTex(self.layoutConfig), holderKey)
            result = fnc()
        # A value that is a special character (escaped character)
        elif holderKey and holderType == 'esc' :
            result = self.getEscapeCharacter(holderKey)
        # A value that is a font setting
        elif holderKey and holderType == 'font' :
            result = self.getFontSetting(holderKey)
        # A value that is a path separator character
        elif holderType == 'pathSep' :
            result = os.sep
        # A value that contains a system declared value
        # Note this only works if the value we are looking for has
        # been declared above in the module init
        elif holderType == 'self' :
#            if holderKey.find('.') >= 0 :
#                splitKey = holderKey.split('.')
#                if splitKey[0] == 'local' :
#                    result = getattr(self.local, splitKey[1])
#            else :
#                result = getattr(self, holderKey)
            result = getattr(self.local, holderKey)

        return result


    def getFontSetting (self, value) :
        '''Get a special font setting if there is one. Otherwise
        return null.'''

        # FIXME: This may need to be moved to Fonts, plus it might be a
        # little brittle. Using primaryFont for a default might be asking
        # for trouble
        result = ''
        if value == 'mapping' :
            useMapping      = self.fontConfig['GeneralSettings']['useMapping']
            primaryFont     = self.projectConfig['CompTypes'][self.cType.capitalize()]['fontName']
            if useMapping :
                result = ':mapping=' + os.path.join(self.local.projFontFolder, primaryFont, useMapping)
        elif value == 'renderer' :
            useRenderingSystem = self.fontConfig['GeneralSettings']['useRenderingSystem']
            if useRenderingSystem :
                result = '/' + useRenderingSystem
        elif value == 'language' :
            useLanguage = self.fontConfig['GeneralSettings']['useLanguage']
            if useLanguage :
                result = ':language=' + useLanguage
        elif value == 'feature' :
            useFeature = self.fontConfig['GeneralSettings']['useFeature']
            if useFeature :
                result = ':' + useFeature

        return result


    def getEscapeCharacter (self, value) :
        '''Return the character specified by the escape code.'''

        if value == 'lsBracket' :
            return '['
        elif value == 'rsBracket' :
            return ']'
        
        # Add more as needed...


    def processNestedPlaceholders (self, line, value = '') :
        '''Search a string (or line) for a type of Rapuma placeholder and
        insert the value. This is for building certain kinds of config values.'''

        result = []
        end_of_previous_segment = 0
        for (ph_start, ph_end) in self.getPlaceHolder(line) :
            unchanged_segment = line[end_of_previous_segment:ph_start]
            result.append(unchanged_segment)
            ph_text = line[ph_start+1:ph_end]
            replacement = self.processNestedPlaceholders(ph_text, value)
            result.append(unicode(replacement))
            end_of_previous_segment = ph_end+1  # Skip the closing bracket
        result.append(line[end_of_previous_segment:])
        resultline = "".join(result)
        result_text = self.processSinglePlaceholder(resultline, value)
        return result_text


    def hasPlaceHolder (self, line) :
        '''Return True if this line has a data place holder in it.'''

        # If things get more complicated we may need to beef this up a bit
        if line.find('[') > -1 and line.find(']') > -1 :
            return True


    def getPlaceHolder (self, line) :
        '''Return place holder type and a key if one exists from a TeX setting line.
        Pass over the line and return (yield) each placeholder found.'''

        nesting_level = 0
        remembered_idx = None
        for idx, ch in enumerate(line):
            if ch == '[':
                nesting_level += 1
                if remembered_idx is None:
                    remembered_idx = idx
            elif ch == ']':
                nesting_level -= 1
                if nesting_level <= 0:
                    found_idx = remembered_idx
                    remembered_idx = None
                    yield (found_idx, idx)


    def getConfigValue (self, val, default=None) :
        '''Return the value from a config function or just pass the
        value through, unchanged.'''

        keyparts = val.split('|')
        curval = getattr(self, keyparts[0], None)
        if curval is None: return default
        for key in keyparts[1:]:
            curval = curval.get(key, None)
            if curval is None: return default
        return curval


    def getMeasureUnit (self) :
        '''Return the value with the specified measurement unit attached.'''
        
        return self.layoutConfig['GeneralSettings']['measurementUnit']
Пример #4
0
class ProjBackground (object) :

    def __init__(self, pid, gid = None) :
        '''Intitate the whole class and create the object.'''

#        import pdb; pdb.set_trace()

        self.pid                        = pid
        self.gid                        = gid
        self.local                      = ProjLocal(pid, gid)
        self.tools                      = Tools()
        self.proj_config                = Config(pid, gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        self.projectConfig              = self.proj_config.projectConfig
        self.layoutConfig               = self.proj_config.layoutConfig
        self.log                        = ProjLog(pid)
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.projHome                   = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid)
        self.mmToPx                     = 72 / 25.4
        # For debugging purposes a switch can be set here for verbose
        # message output via the terminal
        self.debugMode                  = self.tools.str2bool(self.userConfig['System']['debugging'])

        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],
            '1110' : ['MSG', 'File exsits: [<<1>>]. Use \"force\" to remove it.'],
            '1280' : ['ERR', 'Failed to merge background file with command: [<<1>>]. This is the error: [<<2>>]'],
            '1290' : ['ERR', 'Failed to convert background file [<<1>>]. Error: [<<2>>] The command was: [<<3>>]'],
            '1300' : ['MSG', 'Background merge operation in process, please wait...'],
            '1305' : ['MSG', 'Adding document information, please wait...'],
            '1310' : ['WRN', 'Failed to add background component: [<<1>>] with error: [<<2>>]'],
            '1320' : ['MSG', 'New background created.']

        }


###############################################################################
############################### Basic Functions ###############################
###############################################################################
######################## Error Code Block Series = 1000 #######################
###############################################################################

    def turnOnBackground (self) :
        '''Change the layout config settings to turn on the background.'''

        if not self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground']) :
            self.layoutConfig['DocumentFeatures']['useBackground'] = True
            self.tools.writeConfFile(self.layoutConfig)


    def turnOffBackground (self) :
        '''Change the layout config settings to turn off the background.'''

        if self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground']) :
            self.layoutConfig['DocumentFeatures']['useBackground'] = False
            self.tools.writeConfFile(self.layoutConfig)


    def turnOnDocInfo (self) :
        '''Change the layout config settings to turn on the doc info in the background.'''

        if not self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo']) :
            self.layoutConfig['DocumentFeatures']['useDocInfo'] = True
            self.tools.writeConfFile(self.layoutConfig)


    def turnOffDocInfo (self) :
        '''Change the layout config settings to turn off the doc info in the background.'''

        if self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo']) :
            self.layoutConfig['DocumentFeatures']['useDocInfo'] = False
            self.tools.writeConfFile(self.layoutConfig)


    def addBackground (self, target) :
        '''Add a background (watermark) to a rendered PDF file. This will
        figure out what the background is to be composed of and create
        a master background page. Using force will cause it to be remade.'''

        # Do a quick check if the background needs to be remade
        # The background normally is not remade if one already exists.
        # If one is there, it can be remade if regenerate is set to
        # to True. Obviously, if one is not there, it will be made.
        if self.tools.str2bool(self.layoutConfig['DocumentFeatures']['regenerateBackground']) :
            self.createBackground()
        else :
            # If there isn't one, make it
            if not os.path.exists(self.local.backgroundFile) :
                self.createBackground()

        # Merge target with the project's background file in the Illustraton folder
        self.log.writeToLog(self.errorCodes['1300'])

        # Create a special name for the file with the background
        # Then merge and save it
        viewFile = self.tools.alterFileName(target, 'view')
        
        shutil.copy(self.tools.mergePdfFilesPdftk(self.centerOnPrintPage(target), self.local.backgroundFile), viewFile)

        # Not returning a file name would mean it failed
        if os.path.exists(viewFile) :
            return viewFile


    def addDocInfo (self, target) :
        '''Add (merge) document information to the rendered target doc.'''

        # Initialize the process
        docInfoText         = self.layoutConfig['DocumentFeatures']['docInfoText']
        timestamp           = self.tools.tStamp()
        if self.gid :
            headerLine          = self.pid + ' / ' + self.gid + ' / ' + timestamp
        else :
            headerLine          = self.pid + ' / ' + timestamp
        svgFile             = tempfile.NamedTemporaryFile().name

        ## RENDERED PAGE DIMENSIONS (body)
        # This can be determined with the pyPdf element 
        # "pdf.getPage(0).mediaBox", which returns a 
        # RectangleObject([0, 0, Width, Height]). The
        # width and height are in points. Hopefully we will
        # always be safe by measuring the gidPdfFile size.
        #pdf         = PdfFileReader(open(self.local.gidPdfFile,'rb'))
        #var2        = pdf.getPage(0).mediaBox
        #trimWidth     = float(var2.getWidth())
        #trimHeight    = float(var2.getHeight())
        trimWidth = float(self.layoutConfig['PageLayout']['pageWidth'])
        trimHeight = float(self.layoutConfig['PageLayout']['pageHeight'])
        # Printer page size
        pps         = self.printerPageSize()
        ppsWidth    = pps[0]
        ppsHeight   = pps[1]
        ppsCenter   = ppsWidth/2

        #   Write out SVG document text 
        with codecs.open(svgFile, 'wb') as fbackgr :            # open file for writing 
            fbackgr.write( '''<svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" width = "''' + str(ppsWidth) + '''" height = "''' + str(ppsHeight) + '''">
                <g><text x = "''' + str(ppsCenter) + '''" y = "''' + str(20) + '''" style="font-family:DejaVu Sans;font-style:regular;font-size:8;text-anchor:middle;fill:#000000;fill-opacity:1">''' + headerLine + '''</text></g>
                <g><text x = "''' + str(ppsCenter) + '''" y = "''' + str(ppsHeight-30) + '''" style="font-family:DejaVu Sans;font-style:regular;font-size:8;text-anchor:middle;fill:#000000;fill-opacity:1">''' + self.tools.fName(target) + '''</text></g>
                <g><text x = "''' + str(ppsCenter) + '''" y = "''' + str(ppsHeight-20) + '''" style="font-family:DejaVu Sans;font-style:regular;font-size:8;text-anchor:middle;fill:#000000;fill-opacity:1">''' + docInfoText + '''</text></g>
                </svg>''')

        # Merge target with the background
        self.log.writeToLog(self.errorCodes['1305'])

        # Create a special name for the file with the background
        viewFile = self.tools.alterFileName(target, 'view')
        # Compare the new file name with the target to see if we are
        # already working with a background file
        if viewFile == target :
            # If the target is a BG file all we need to do is merge it
            self.tools.mergePdfFilesPdftk(viewFile, self.tools.convertSvgToPdfRsvg(svgFile))
        else :
            # If not a BG file, we need to be sure the target is the same
            # size as the print page to merge with pdftk
            shutil.copy(self.tools.mergePdfFilesPdftk(self.centerOnPrintPage(target), self.tools.convertSvgToPdfRsvg(svgFile)), viewFile)

        # Not returning a file name would mean it failed
        if os.path.exists(viewFile) :
            return viewFile


    ##### Background Creation Functions #####

    def createBackground (self) :
        '''Create a background file. This will overwrite any existing
        background file and will add each recognized background type
        found in the bacgroundComponents config setting.'''

        self.createBlankBackground()

        # Add each component to the blank background file
        for comp in self.layoutConfig['DocumentFeatures']['backgroundComponents'] :
            try :
                getattr(self, 'merge' + comp.capitalize())()
            except Exception as e :
                self.log.writeToLog(self.errorCodes['1310'],[comp,str(e)])
                pass

        self.log.writeToLog(self.errorCodes['1320'])
        return True


    def createBlankBackground (self) :
        '''Create a blank background page according to the print page
        size specified.'''
        
        # Set the temp svg file name
        svgFile   = tempfile.NamedTemporaryFile().name

        # Printer page size
        pps = self.printerPageSize()
        ppsWidth = pps[0]
        ppsHeight = pps[1]

        # Be sure there is an illustrations folder in place
        if not os.path.isdir(self.local.projIllustrationFolder) :
            os.mkdir(self.local.projIllustrationFolder)

        #   Write out SVG document text 
        with codecs.open(svgFile, 'wb') as fbackgr :            # open file for writing 
            fbackgr.write( '''<svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" width = "''' + str(ppsWidth) + '''" height = "''' + str(ppsHeight)+ '''">
                </svg>''')

        shutil.copy(self.tools.convertSvgToPdfRsvg(svgFile), self.local.backgroundFile)


    def mergeWatermark (self) :
        '''Create a watermark file and return the file name. Using force
        will cause any exsiting versions to be recreated.'''

#        import pdb; pdb.set_trace()

        # Initialize the process
        pubProg             = "Rapuma"
        watermarkText       = self.layoutConfig['DocumentFeatures']['watermarkText']
        svgFile             = tempfile.NamedTemporaryFile().name

        ## RENDERED PAGE DIMENSIONS (body)
        # Trim page width [px]
        trimWidth = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageWidth']),1)
        # Trim page height [px]
        trimHeight = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageHeight']),1)
        # Printer page size [px]
        (ppsWidth, ppsHeight) = self.printerPageSize()

        pageX = ppsWidth/2
        pageY = ppsHeight/2
        
        #   Write out SVG document text 
        with codecs.open(svgFile, 'wb') as fbackgr :            # open file for writing 
            fbackgr.write( '''<svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" width = "''' + str(ppsWidth) + '''" height = "''' + str(ppsHeight) + '''">
                <g><text x = "''' + str(pageX-trimWidth*.42) + '''" y = "''' + str(pageY-trimHeight*0.39) + '''" style="font-family:DejaVu Sans;font-style:regular;font-size:32;text-anchor:start;fill:#d5d5f5;fill-opacity:1">''' + str(pubProg) + '''
                <tspan x = "''' + str(pageX) + '''" y = "''' + str(pageY - trimHeight*0.22) + '''" style="text-anchor:middle">''' + str(pubProg) + '''</tspan>
                <tspan x = "''' + str(pageX+trimWidth*0.42)+ '''" y = "''' + str(pageY-trimHeight*0.05) + '''" style="text-anchor:end">''' + str(pubProg) + '''</tspan>
                <tspan x = "''' + str(pageX) + '''" y = "''' + str(pageY+trimHeight*0.12) + '''" style="text-anchor:middle">''' + str(pubProg) + '''</tspan>
                <tspan x = "''' + str(pageX-trimWidth*0.42) + '''" y = "''' + str(pageY+trimHeight*0.29) + '''" style="text-anchor:start">''' + str(pubProg) + '''</tspan>
                <tspan x = "''' + str(pageX+trimWidth*0.49) + '''" y = "''' + str(pageY+trimHeight*0.455) + '''" style="font-weight:bold;font-size:68;text-anchor:end">''' + watermarkText + ''' </tspan>
                </text></g></svg>''')

        # Convert the temp svg to pdf and merge into backgroundFile
        results = self.tools.mergePdfFilesPdftk(self.local.backgroundFile, self.tools.convertSvgToPdfRsvg(svgFile))
        if os.path.isfile(results) :
            return True


    def mergeCropmarks (self) :
        '''Merge cropmarks on to the background page.'''

        # Initialize the process
        svgFile             = tempfile.NamedTemporaryFile().name

        ## RENDERED PAGE DIMENSIONS (body)
        # Trim page width [px]
        trimWidth = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageWidth']),1)
        # Trim page height [px]
        trimHeight = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageHeight']),1)
        # Printer page size [px]
        pps = self.printerPageSize()
        ppsWidth = pps[0]
        ppsHeight = pps[1]

        with codecs.open(svgFile, 'wb') as fbackgr : 
                    # starting lines of SVG xml
            fbackgr.write( '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width = "''' + str(ppsWidth) + '''" height = "''' + str(ppsHeight) + '''">\n''') 
                    # vertical top left
            fbackgr.write( '''<path d = "m''' + str((ppsWidth - trimWidth)/2) + ''',''' + str((ppsHeight - trimHeight)/2 - 32.0) + ''',v27," style="stroke:#000000;stroke-width:.2"/>\n''')   
                    # vertical bottom left
            fbackgr.write( '''<path d = "m''' + str((ppsWidth - trimWidth)/2) + ''',''' + str((ppsHeight + trimHeight)/2 + 5.0) + ''',v27" style="stroke:#000000;stroke-width:.2" />\n''')
                    # vertical bottom right
            fbackgr.write( '''<path d = "m''' + str((ppsWidth + trimWidth)/2) + ''',''' + str((ppsHeight - trimHeight)/2 - 32.0) + ''',v27" style="stroke:#000000;stroke-width:.2"/>\n''')
                    # vertical top right
            fbackgr.write( '''<path d = "m''' + str((ppsWidth + trimWidth)/2) + ''',''' + str((ppsHeight + trimHeight)/2 + 5.0) + ''',v27" style="stroke:#000000;stroke-width:.2" />\n''')
                    # horzontal top left
            fbackgr.write( '''<path d =" m''' + str((ppsWidth - trimWidth)/2 - 32.0) + ''',''' + str((ppsHeight - trimHeight)/2) + ''',h27" style="stroke:#000000;stroke-width:.2" />\n''')
                    # horzontal top right
            fbackgr.write( '''<path d =" m''' + str((ppsWidth + trimWidth)/2 + 5.0) + ''',''' + str((ppsHeight - trimHeight)/2) + ''',h27" style="stroke:#000000;stroke-width:.2" />\n''')
                    # horzontal bottom right
            fbackgr.write( '''<path d =" m''' + str((ppsWidth - trimWidth)/2 - 32.0) + ''',''' + str((ppsHeight + trimHeight)/2) + ''',h27" style="stroke:#000000;stroke-width:.2" />\n''')
                    # horzontal bottom left
            fbackgr.write( '''<path d =" m''' + str((ppsWidth + trimWidth)/2 +5.0) + ''',''' + str((ppsHeight + trimHeight)/2) + ''',h27" style="stroke:#000000;stroke-width:.2" />\n''')
            fbackgr.write( '''</svg>''')

        # Convert the temp svg to pdf and merge into backgroundFile
        results = self.tools.mergePdfFilesPdftk(self.local.backgroundFile, self.tools.convertSvgToPdfRsvg(svgFile))
        if os.path.isfile(results) :
            return True


    def mergePagebox (self) :
        '''Merge a page box with the background page to be used for proof reading.'''

        # Initialize the process
        svgFile             = tempfile.NamedTemporaryFile().name

        ## RENDERED PAGE DIMENSIONS (body)
        # Trim page width [px]
        trimWidth = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageWidth']),1)
        # Trim page height [px]
        trimHeight = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageHeight']),1)
        # Printer page size [px]
        pps = self.printerPageSize()
        ppsWidth = pps[0]
        ppsHeight = pps[1]

        with codecs.open(svgFile, 'wb') as fbackgr :
                    # starting lines of SVG xml
            fbackgr.write( '''<svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" width = "''' + str(ppsWidth) + '''" height = "''' + str(ppsHeight) + '''">''')
                    # rectangle
            fbackgr.write( '''<rect x = "''' + str((ppsWidth - trimWidth)/2) + '''" y= "''' + str((ppsHeight - trimHeight)/2) + '''" height = "''' + str(trimHeight) + '''" width = "''' + str(trimWidth) + '''" style = "fill:none;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:.2"/>
                </svg>''')

        # Convert the temp svg to pdf and merge into backgroundFile
        results = self.tools.mergePdfFilesPdftk(self.local.backgroundFile, self.tools.convertSvgToPdfRsvg(svgFile))
        if os.path.isfile(results) :
            return True


# Depricated
    #def makeBgFileName (self, orgName) :
        #'''Alter the file name to reflect the fact it has a background
        #added to it. This assumes the file only has a single extention.
        #If that's not the case we're hosed.'''

        #name    = orgName.split('.')[0]
        #ext     = orgName.split('.')[1]
        ## Just in case this is the second pass
        #name    = name.replace('-bg', '')
        #return name + '-bg.' + ext


    def centerOnPrintPage (self, contents) :
        '''Center a PDF file on the printerPageSize page. GhostScript
        is the only way to do this at this point.'''

#        import pdb; pdb.set_trace()

        tmpFile = tempfile.NamedTemporaryFile().name
        # Get the size of our printer page
        (ppsWidth, ppsHeight) = self.printerPageSize()
        (wo, ho) = self.getPageOffset()
        pageOffset = str(wo) + ' ' + str(ho)
        
        # Assemble the GhostScript command
        cmd = [ 'gs', 
                '-o', tmpFile, 
                '-sDEVICE=pdfwrite', 
                '-dQUIET', 
                '-dDEVICEWIDTHPOINTS=' + str(ppsWidth),
                '-dDEVICEHEIGHTPOINTS=' + str(ppsHeight),
                '-dFIXEDMEDIA', 
                '-c', 
                '<</PageOffset [' + str(pageOffset) + ']>>', 
                'setpagedevice', 
                '-f', 
                contents]

        if self.debugMode :
            self.tools.terminal('Debug Mode On: \centerOnPrintPage() command: ' + str(cmd))

        # Run the process
        try:
            subprocess.call(cmd) 
            # Return the name of the temp PDF
            return tmpFile
        except Exception as e :
            self.log.writeToLog(self.errorCodes['1280'], [str(cmd), str(e)])
        # Return the temp file name for further processing


    def printerPageSize (self) :
        '''Return the width and height of the printer page size in
        points. Only US Letter and A4 are supported. If not specified
        return the page trim size.'''
    
        printerPageSizeCode = self.layoutConfig['PageLayout']['printerPageSizeCode'].lower()
        ## STANDARD PRINTER PAGE SIZES
        # The output page (printer page) is what the typeset page will be placed on
        # with a watermark behind it. There are only two page sizes supported.
        # They are A4 and US Letter. We will determine the size by the ID code
        # found in the layout.conf file. However, in some cases, for example
        # during the layout process, the trim size is what is needed. If
        # one of the two supported pages sizes are not used, this will defult
        # to the trim size.
        if printerPageSizeCode == 'a4' :
            return float(595), float(842)
        elif printerPageSizeCode == 'letter' :
            return float(612), float(792)
        else :
            # Just default to the page trim size (assumed mm coming in) factor mmToPx = 72 / 25.4
            return float(int(self.layoutConfig['PageLayout']['pageWidth']) * self.mmToPx), float(int(self.layoutConfig['PageLayout']['pageHeight']) * self.mmToPx)

    
    def getPageOffset (self) :
        '''Return the amount of horizontal and vertical offset that will
        enable the page trim size to be centered on the printer page. If
        something other than A4 or Letter is being used, the offset returned
        will be zero as this only supports those two sizes. The offset is
        based on the starting point being the lower-left side corner
        of the page. '''
        
        # Get the printer page size
        printerPageSizeCode = self.layoutConfig['PageLayout']['printerPageSizeCode'].lower()
        # Get the page trim size (assuming mm input)factor mmToPx = 72 / 25.4
        trimWidth = int(self.layoutConfig['PageLayout']['pageWidth']) * self.mmToPx
        trimHeight = int(self.layoutConfig['PageLayout']['pageHeight']) * self.mmToPx

        # The trim size of the content page can never be bigger than
        # the printer page size. If so, the offset is 0
        if printerPageSizeCode == 'a4' or printerPageSizeCode == 'letter' :
            (bw, bh) = self.printerPageSize()
            wo = float((bw/2)-(trimWidth/2))
            ho = float((bh/2)-(trimHeight/2))
            return wo, ho
        else :
            return 0, 0
Пример #5
0
class ProjLog (object) :

    def __init__(self, pid) :
        '''Do the primary initialization for this manager.'''

        self.tools              = Tools()
        self.pid                = pid
        self.rapumaHome         = os.environ.get('RAPUMA_BASE')
        self.userHome           = os.environ.get('RAPUMA_USER')
        self.user               = UserConfig()
        self.userConfig         = self.user.userConfig
        self.local              = ProjLocal(pid)


###############################################################################
############################### Logging Functions #############################
###############################################################################

# These have to do with keeping a running project log file.  Everything done is
# recorded in the log file and that file is trimmed to a length that is
# specified in the system settings.  Everything is channeled to the log file but
# depending on what has happened, they are classed in three levels:
#   1) [MSG] - Common event going to log and terminal
#   2) [WRN] - Warning event going to log and terminal if debugging is turned on
#   3) [ERR] - Error event going to the log and terminal and kills the process
#   4) [LOG] - Messages that go only to the log file to help with debugging
# FIXME: Following not implemented yet
#   5) [TOD] - To do list. Output to a file that helps guide the user.


    def writeToLog (self, errCode, args=None, location=None) :
        '''Send an event to one of the log files or the terminal if specified.
        Everything gets written to a log.  Where a message gets written to
        depends on what type code it is.  The type code is in with the error
        code data.  There are five type codes:
            MSG = General messages go to both the terminal and log file
            LOG = Messages that go only to the log file
            WRN = Warnings that go to the terminal and log file
            ERR = Errors that go to both the terminal and log file
            TOD = Messages that will go to a special todo file to guide the user
        The errCode points to a specific message that will be sent to a log
        file. The args parameter can contain extra information like file names
        to help the user better figure out what happened.'''

        # Get the message from the errorCode list the module sent
        if type(errCode) == list :
            if location :
                msg = errCode[1] + ' : (' + location + ')'
            else :
                msg = errCode[1]
            code = errCode[0]
        else :
            self.tools.terminal('\nThe code: [' + errCode + '] is not recognized by the Rapuma system.')
            return

        # If args were given, do s/r on them and add 
        # args info that needs to be added to msg.
        # Look for a <<#>> pattern replace it with
        # the corresponding position in the args list.
        if args :
            for count, arg in enumerate(args) :
                msg = msg.replace('<<' + str(count+1) + '>>', arg)

        # Write out everything but LOG messages to the terminal
        if code != 'LOG' and code != 'TOD' :
            self.tools.terminal('\n' + code + ' - ' + msg)

        # Test to see if this is a live project by seeing if the project conf is
        # there.  If it is, we can write out log files.  Otherwise, why bother?
        if self.local.projectConfFile and os.path.exists(self.local.projectConfFile) :

            # Build the event line
            eventLine = '\"' + self.tools.tStamp() + '\", \"' + code + '\", \"' + msg + '\"'

            # Do we need a log file made?
            try :
                if not os.path.isfile(self.local.projLogFile) or os.path.getsize(self.local.projLogFile) == 0 :
                    writeObject = codecs.open(self.local.projLogFile, "w", encoding='utf_8')
                    writeObject.write('Rapuma event log file created: ' + self.tools.tStamp() + '\n')
                    writeObject.close()

                # Now log the event to the top of the log file using preAppend().
                self.preAppend(eventLine, self.local.projLogFile)

                # FIXME: Add the TOD list output here, also, output any TODs 
                # to the error log as well as these are bad errors.

                # Write errors and warnings to the error log file
                if code == 'WRN' and self.userConfig['System']['debugging'] == 'True':
                    self.writeToErrorLog(self.local.projErrorLogFile, eventLine)

                if code == 'ERR' :
                    self.writeToErrorLog(self.local.projErrorLogFile, eventLine)

            except Exception as e :
                # If we don't succeed, we should probably quite here
                self.tools.terminal("Failed to write message to log file: " + msg)
                self.tools.terminal('Internal error: [' + str(e) + ']')
                self.tools.dieNow()
            
        # Halt the process if this was an 'ERR' level type code
        if code == 'ERR' :
            self.tools.dieNow('Sorry, I have to stop.')
        else :
            return


    def writeToErrorLog (self, errorLog, eventLine) :
        '''In a perfect world there would be no errors, but alas there are and
        we need to put them in a special file that can be accessed after the
        process is run.  The error file from the previous session is deleted at
        the beginning of each new run.'''

        try :
            # Because we want to read errors from top to bottom, we don't pre append
            # them to the error log file.
            if not os.path.isfile(errorLog) :
                writeObject = codecs.open(errorLog, "w", encoding='utf_8')
            else :
                writeObject = codecs.open(errorLog, "a", encoding='utf_8')

            # Write and close
            writeObject.write(eventLine + '\n')
            writeObject.close()
        except :
            self.tools.terminal('Error writing this event to error log: ' + eventLine)

        return


    def preAppend (self, line, file_name) :
        '''Got the following code out of a Python forum.  This will pre-append a
        line to the beginning of a file.'''

#        import pdb; pdb.set_trace()

        fobj = fileinput.FileInput(file_name, inplace=1)
        first_line = fobj.readline()
        sys.stdout.write("%s\n%s" % (line, first_line))
        for line in fobj:
            sys.stdout.write("%s" % line)

        fobj.close()
Пример #6
0
class UserConfig(object):
    def __init__(self):
        """Intitate the whole class and create the object."""

        self.rapumaHome = os.environ.get("RAPUMA_BASE")
        self.defaultUserHome = os.environ.get("RAPUMA_USER")
        self.userConfFileName = "rapuma.conf"
        self.tools = Tools()

        # Point to the right user config
        # Look for a web installation first, if not go to default
        # Note that a slash is put before var as it is off of root
        # That kind of stops this from being cross-platform
        rapumaWebConfig = os.path.join("/var", "lib", "rapuma", "config", self.userConfFileName)
        defaultConfig = os.path.join(self.defaultUserHome, self.userConfFileName)
        if os.path.exists(rapumaWebConfig):
            self.userConfFile = rapumaWebConfig
        else:
            self.userConfFile = defaultConfig

        # Check to see if the file is there, then read it in and break it into
        # sections. If it fails, scream really loud!
        rapumaXMLDefaults = os.path.join(self.rapumaHome, "config", "rapuma.xml")
        if os.path.exists(rapumaXMLDefaults):
            self.tools.sysXmlConfig = self.tools.xml_to_section(rapumaXMLDefaults)
        else:
            raise IOError, "Can't open " + rapumaXMLDefaults

        #        import pdb; pdb.set_trace()

        # Now make the users local rapuma.conf file if it isn't there
        if not os.path.exists(self.userConfFile):
            self.initUserHome()

        # Load the Rapuma conf file into an object
        self.userConfig = ConfigObj(self.userConfFile, encoding="utf-8")

        # Initialize the user's home folders, like resources, etc
        self.makeHomeFolders()

        # Log messages for this module
        self.errorCodes = {"0000": ["MSG", "Placeholder message"]}

    ###############################################################################
    ############################ User Config Functions ############################
    ###############################################################################

    def initUserHome(self):
        """Initialize a user config file on a new install or system re-init."""

        # Create home folders
        if not os.path.isdir(self.defaultUserHome):
            os.mkdir(self.defaultUserHome)

        # Make the default global rapuma.conf for custom environment settings
        if not os.path.isfile(self.userConfFile):
            self.userConfig = ConfigObj(self.tools.sysXmlConfig.dict(), encoding="utf-8")
            self.userConfig.filename = self.userConfFile
            self.userConfig["System"]["initDate"] = self.tools.tStamp()
            self.userConfig.write()

    def setSystemSettings(self, section, key, value):
        """Function to make system settings."""

        oldValue = self.userConfig[section][key]
        if oldValue != value:
            self.userConfig[section][key] = value
            # Write out the results
            self.userConfig.write()
            self.tools.terminal("\nRapuma user name setting changed from [" + oldValue + "] to [" + value + "].\n\n")
        else:
            self.tools.terminal("\nSame value given, nothing to changed.\n\n")

    def makeHomeFolders(self):
        """Setup the default Rapuma resource folders."""

        #        import pdb; pdb.set_trace()

        # We do not write out unless this flag is set
        confWriteFlag = False

        # Setup Resources section if needed
        if not self.userConfig.has_key("Resources"):
            self.tools.buildConfSection(self.userConfig, "Resources")

        # Get the user config project folder location (or set a default)
        if not self.userConfig["Resources"].has_key("projects") or not self.userConfig["Resources"]["projects"]:
            projects = os.path.join(os.environ.get("HOME"), "Publishing")
            if not os.path.exists(projects):
                os.makedirs(projects)
            self.userConfig["Resources"]["projects"] = projects
            confWriteFlag = True
        elif not os.path.exists(self.tools.resolvePath(self.userConfig["Resources"]["projects"])):
            sys.exit(
                "\nERROR: Invalid projects folder path: "
                + self.userConfig["Resources"]["projects"]
                + "\n\nProcess halted.\n"
            )
        else:
            projects = self.tools.resolvePath(self.userConfig["Resources"]["projects"])

        # Note: The following was commented because it no longer is necessary to load in locations
        # for various resouces. That will be dynamically. If the program ever reaches the point
        # where this would be good to have, it can be handled by a GUI. (djd - 20160223)

        #        # Get the user config Rapuma resource folder location
        #        if not self.userConfig['Resources'].has_key('rapumaResource') :
        #            # Check for pre-typo-fix value ('rapumaResouce') before creating new section
        #            if self.userConfig['Resources'].has_key('rapumaResouce') :
        #                self.userConfig['Resources'].rename('rapumaResouce', 'rapumaResource')
        #                confWriteFlag = True
        #            else:
        #                self.tools.buildConfSection(self.userConfig['Resources'], 'rapumaResource')
        #                confWriteFlag = True
        #        if len(self.userConfig['Resources']['rapumaResource']) > 0 :
        #            rapumaResource = self.userConfig['Resources']['rapumaResource']
        #        else :
        #            # This is the default location
        #            rapumaResource = os.path.join(site.USER_BASE, 'share', 'rapuma')
        #            self.userConfig['Resources']['rapumaResource'] = rapumaResource
        #            confWriteFlag = True

        #        # Make a list of sub-folders to make in the Rapuma resourcs folder
        #        resourceFolders = ['archive', 'backup', 'font', 'illustration', \
        #                            'macro','script', 'template']

        #        for r in resourceFolders :
        #            # Build the path and check if it can be made
        #            thisPath = os.path.join(rapumaResource, r)
        #            if not os.path.isdir(thisPath) :
        #                os.makedirs(thisPath)
        #            self.userConfig['Resources'][r] = thisPath
        #            confWriteFlag = True

        # Write out if needed
        if confWriteFlag:
            self.userConfig.write()
        return True