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

        self.pid                    = pid
        self.tools                  = Tools()
        self.user                   = UserConfig()
        self.userConfig             = self.user.userConfig
        if projectConfig :
            self.projectConfig      = projectConfig
        else :
            self.proj_config        = Config(self.pid)
            self.proj_config.getProjectConfig()
            self.projectConfig      = self.proj_config.projectConfig
        self.local                  = ProjLocal(pid, gid, self.projectConfig)
        self.log                    = ProjLog(pid)

        # Log messages for this module
        self.errorCodes     = {
            '0000' : ['MSG', 'Placeholder message'],
            'XPRT-000' : ['MSG', 'Messages for export issues (probably only in project.py)'],
            'XPRT-005' : ['MSG', 'Unassigned error message ID.'],
            'XPRT-010' : ['ERR', 'Export file name could not be formed with available configuration information.'],
            'XPRT-020' : ['ERR', 'Unable to export: [<<1>>].'],
            'XPRT-030' : ['MSG', 'Files exported to [<<1>>].'],
            'XPRT-040' : ['MSG', 'Beginning export, please wait...'],
            'XPRT-050' : ['MSG', 'Unassigned error message ID.'],

            '1210' : ['MSG', 'Processes completed successfully on: [<<1>>] by [<<2>>]'],
            '1220' : ['ERR', 'Processes for [<<1>>] failed. Script [<<2>>] returned this error: [<<3>>]'],
            '1240' : ['MSG', 'Component group preprocessing [<<1>>] for group [<<2>>].'],
            '1260' : ['ERR', 'Installed the default component preprocessing script. Editing will be required for it to work with your project.'],
            '1265' : ['LOG', 'Component preprocessing script is already installed.'],

        }
Beispiel #2
0
    def __init__(self, pid) :
        '''Intitate the whole class and create the object.'''

        self.pid                            = pid
        self.log                            = ProjLog(self.pid)
        self.tools                          = Tools()
        self.user                           = UserConfig()
        self.config                         = Config(pid)
        self.local                          = ProjLocal(self.pid)
        self.userConfig                     = self.user.userConfig
        self.config.getProjectConfig()
        self.projectConfig                  = self.config.projectConfig
        if self.userConfig['System']['textDifferentialViewerCommand'] == '' :
            self.diffViewCmd                = None
        else :
            self.diffViewCmd                = self.userConfig['System']['textDifferentialViewerCommand']
            # Make sure the diff view command is a list
            if type(self.diffViewCmd) != list :
                self.diffViewCmd            = [self.userConfig['System']['textDifferentialViewerCommand']]

        # Log messages for this module
        self.errorCodes     = {
            '0210' : ['WRN', 'File does not exist: [<<1>>] compare cannot be done.'],
            '0280' : ['ERR', 'Failed to compare files with error: [<<1>>]'],
            '0285' : ['ERR', 'Cannot compare component [<<1>>] because a coresponding subcomponent could not be found.'],
            '0290' : ['ERR', 'Compare test type: [<<1>>] is not valid.'],
            '0295' : ['MSG', 'Comparing: [<<1>>] with parent: [<<2>>] Close the viewer to return to the terminal prompt.'],
            '0220' : ['MSG', 'Comparison not needed, files seem to be the same.'],
            '0300' : ['WRN', 'Files are different but visual compare is not enabled.']
        }
Beispiel #3
0
    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.layoutConfig               = self.proj_config.layoutConfig
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.log                        = ProjLog(pid)

        # to [px] is 72/25.4 
        self.mmToPx                     = 72 / 25.4
        # page width [px]
        self.paperPxWidth               = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageWidth']),1)
        # page height [px]
        self.paperPxHeight              = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageHeight']),1)

        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],
            '1310' : ['WRN', 'Failed to add diagnostic component: [<<1>>] with error: [<<2>>]']

        }
Beispiel #4
0
    def backupProject (self, targetPath=None) :
        '''Backup a project. Send the compressed backup file with a date-stamp
        file name to the user-specified backup folder. If a target path is 
        specified, put the archive there but use the PID in the name. If other
        backups with the same name exist there, increment with a number.'''

        # First see if this is even a valid project
        if self.pid not in self.projList :
            self.log.writeToLog(self.errorCodes['3610'], [self.pid])

        # Set some paths and file names
        if not targetPath :
            # Now check for a valid location to backup to
            if self.local.userLibBackup == '' :
                self.log.writeToLog(self.errorCodes['3622'])
            elif not os.path.exists(self.local.userLibBackup) :
                self.log.writeToLog(self.errorCodes['3620'], [self.local.userLibBackup])
            projBackupFolder    = os.path.join(self.local.userLibBackup, self.pid)
            backupTarget        = os.path.join(projBackupFolder, self.tools.fullFileTimeStamp() + '.zip')
        else :
            projBackupFolder    = self.tools.resolvePath(targetPath)
            # Now check for a valid target path
            if not os.path.exists(projBackupFolder) :
                self.log.writeToLog(self.errorCodes['3625'], [targetPath])
            backupTarget        = self.tools.incrementFileName(os.path.join(projBackupFolder, self.pid + '.zip'))

        # Make sure the dir is there
        if not os.path.exists(projBackupFolder) :
            os.makedirs(projBackupFolder)

#        import pdb; pdb.set_trace()

        # Zip up but use a list of files we don't want
        self.zipUpProject(backupTarget, self.makeExcludeFileList(source))

        # Cull out any excess backups
        if not targetPath :
            self.cullBackups(self.userConfig['System']['maxStoreBackups'], projBackupFolder)

        # Finish here
        pc = Config(self.pid)
        pc.getProjectConfig()
        pc.projectConfig['Backup']['lastBackup'] = self.tools.fullFileTimeStamp()
        self.tools.writeConfFile(pc.projectConfig)
        self.log.writeToLog(self.errorCodes['3630'], [self.pid,backupTarget])
        return True
Beispiel #5
0
    def __init__(self, project, cfg) :
        super(Pdf, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Set values for this manager
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = 'pdf'
        self.Ctype                  = self.cType.capitalize()
        self.project                = project
        self.local                  = project.local
        self.tools                  = Tools()
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getAdjustmentConfig()
        self.projectConfig          = self.proj_config.projectConfig
        self.log                    = project.log
        self.cfg                    = cfg
        self.mType                  = project.projectMediaIDCode
        self.renderer               = project.projectConfig['CompTypes'][self.Ctype]['renderer']
        self.compSource             = project.projectConfig['CompTypes'][self.Ctype]['compSource']

        # Get the comp settings
        self.compSettings           = project.projectConfig['CompTypes'][self.Ctype]

        # Build a tuple of managers this component type needs to use
        self.pdfManagers = ('fixed', self.renderer)

        # Init the general managers
        for self.mType in self.pdfManagers :
            self.project.createManager(self.mType)

        # Create the internal ref names we use in this module
        self.text                   = self.project.managers[self.cType + '_Fixed']
        # File names

        # Folder paths
        self.projScriptFolder       = self.local.projScriptFolder
        self.projComponentFolder    = self.local.projComponentFolder
        self.gidFolder              = os.path.join(self.projComponentFolder, self.gid)
        # File names with folder paths
        self.rapumaXmlCompConfig    = os.path.join(self.project.local.rapumaConfigFolder, self.xmlConfFile)

        # Get persistant values from the config if there are any
        newSectionSettings = self.tools.getPersistantSettings(self.projectConfig['CompTypes'][self.Ctype], self.rapumaXmlCompConfig)
        if newSectionSettings != self.projectConfig['CompTypes'][self.Ctype] :
            self.projectConfig['CompTypes'][self.Ctype] = newSectionSettings
        # Set them here
        for k, v in self.compSettings.iteritems() :
            setattr(self, k, v)

        # Module Error Codes
        self.errorCodes     = {

            '0000' : ['MSG', 'Messages for the PDF module.'],

        }
Beispiel #6
0
    def __init__(self, pid) :
        '''Do the primary initialization for this class.'''

        self.pid                            = pid
        self.tools                          = Tools()
        self.user                           = UserConfig()
        self.log                            = ProjLog(pid)
        self.userConfig                     = self.user.userConfig
        self.proj_config                    = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig                  = self.proj_config.projectConfig
        self.proj_config.getFontConfig()
        self.fontConfig                     = self.proj_config.fontConfig
        self.local                          = ProjLocal(pid, self.projectConfig)

        # Load all font settings for use in this module
        if self.fontConfig :
            for k, v in self.fontConfig['GeneralSettings'].iteritems() :
                setattr(self, k, v)

        # Log messages for this module
        self.errorCodes     = {
            'FONT-000' : ['MSG', 'Font module messages'],
            'FONT-005' : ['MSG', 'FONT-005 - Unassigned error message ID.'],
            'FONT-015' : ['MSG', 'FONT-015 - Unassigned error message ID.'],
            'FONT-020' : ['ERR', 'Failed to find font setting in ParaTExt project (.ssf file). A primary font must be set before this component can be successfully rendered.'],
            'FONT-025' : ['ERR', 'No source editor was found for this project. Please enter this setting before continuing.'],
            'FONT-042' : ['MSG', 'The [<<1>>] font setup information was added to project config'],
            'FONT-050' : ['ERR', 'Halt! [<<1>>] not found. - font.copyInFont()'],
            'FONT-070' : ['LOG', 'Copied the [<<1>>] font file into the project. - proj_font.copyInFont()'],
            'FONT-100' : ['ERR', 'This function has not been implemented yet!.  - proj_font.setGlyphMap()'],

            '0010' : ['LOG', 'Wrote out new font configuration (font.__init__())'],

            '1220' : ['ERR', 'The Font bundle file [<<1>>] could not be found. Process halted.'],
            '1235' : ['MSG', 'Font [<<1>>] has been installed into the project.'],
            '1237' : ['MSG', 'Font [<<1>>] has been updated.'],
            '1240' : ['ERR', 'Font bundle file [<<1>>] not found.'],
            '1241' : ['ERR', 'Font bundle [<<1>>] not found.'],
            '1245' : ['LOG', '<<1>> font setup information added to project config'],
            '1250' : ['ERR', 'The [<<1>>] font is apparently part of this project. Please remove before trying to re-add this font.'],
            '1260' : ['MSG', 'The <<1>> font bundle has been copied into the project font folder.'],
            '1262' : ['LOG', 'The <<1>> font bundle already exsits in the font folder.'],
            '1265' : ['ERR', 'Failed to extract the [<<1>>] font bundle into the project. Font install process failed.'],
            '1267' : ['LOG', 'The <<1>> font bundle has been copied into the project font folder.'],
            '1280' : ['MSG', 'Failed to install the font: [<<1>>] into the project.'],
            '1370' : ['LOG', 'Removed [<<1>>] font name from project component type: [<<2>>].'],
            '1380' : ['MSG', 'Removed the [<<1>>] font from the project.'],
            '1382' : ['MSG', 'Force switch was set (-f). This process has completely removed the [<<1>>] font and settings from the project. - proj_font.removeFont()'],
            '1390' : ['LOG', 'Removed the [<<1>>] font package.'],
            '1395' : ['MSG', 'Could not remove the [<<1>>] font package. It may be used by another group. Use force (-f) to remove the package from the font folder.']

        }
Beispiel #7
0
    def __init__(self, pid, gid) :
        '''Do the primary initialization for this class.'''

        self.pid                        = pid
        self.gid                        = gid
        self.tools                      = Tools()
        self.local                      = ProjLocal(pid)
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.proj_config                = Config(pid, gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        self.proj_config.getIllustrationConfig()
        self.projectConfig              = self.proj_config.projectConfig
        self.layoutConfig               = self.proj_config.layoutConfig
        self.illustrationConfig         = self.proj_config.illustrationConfig
        self.cType                      = self.projectConfig['Groups'][gid]['cType']
        self.Ctype                      = self.cType.capitalize()
        self.log                        = ProjLog(pid)
        self.backgroundTypes            = ['watermark', 'lines']

        # Folder paths
        self.projComponentFolder        = self.local.projComponentFolder
        self.projIllustrationFolder     = self.local.projIllustrationFolder
        self.projConfFolder             = self.local.projConfFolder

        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],
            '0010' : ['ERR', 'Component type [<<1>>] not recognized!'],
            '0210' : ['WRN', 'Cannot copy [<<1>>] into the project Illustration folder. File already exists. Please use remove or update commands if the file needs to be replaced.'],
            '0220' : ['MSG', 'Copied [<<1>>] into the project Illustration folder.'],
            '0230' : ['ERR', 'Failed to Copy [<<1>>] into the project Illustration folder.'],
            '0240' : ['MSG', 'Illustration add operation complete!'],
            '0265' : ['LOG', 'Piclist file for [<<1>>] has been created.'],
            '0270' : ['WRN', 'Illustration file [<<1>>] not found in Illustration folder.'],
            '0280' : ['ERR', 'There was a problem trying to import the illustration file(s). A possible cause could be that there was no \\fig markers in the source imported into the project. I\'m just say\'n.'],

            '1010' : ['MSG', 'Removed illustration file [<<1>>] from project Illustration folder.'],
            '1020' : ['LOG', 'Request to removed illustration file [<<1>>] from project Illustration folder. File not found. Operation not complete'],
            '1030' : ['MSG', 'Illustration remove operation complete!'],

            '2010' : ['MSG', 'Updated illustration file [<<1>>] in project Illustration folder.'],
            '2020' : ['ERR', 'Update failed, file [<<1>>] not found in project Illustration folder. Use add illustration command to install the illustration.'],
            '2030' : ['ERR', 'Update failed on file [<<1>>]. Copy proceedure failed.'],
            '2040' : ['MSG', 'Illustration update operation complete!']

        }
Beispiel #8
0
    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.projHome               = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid)
        self.local                  = ProjLocal(self.pid)
        self.proj_config            = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig          = self.proj_config.projectConfig
        self.projectMediaIDCode     = self.projectConfig['ProjectInfo']['projectMediaIDCode']

        # Log messages for this module
        self.errorCodes     = {
            '0000' : ['MSG', 'Placeholder message'],
        }
Beispiel #9
0
    def __init__(self, pid, cType, gid=None):
        """Do the primary initialization for this class."""

        self.pid = pid
        self.gid = gid
        self.cType = cType
        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, cType)

        #        import pdb; pdb.set_trace()

        self.proj_config = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig = self.proj_config.projectConfig
        self.layoutConfig = self.proj_config.layoutConfig
        self.tools = Tools()
        self.log = ProjLog(pid)
        # Create config placeholders
        self.layoutConfig = None
        self.illustrationConfig = None
        self.macroConfig = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)

        # Log messages for this module
        self.errorCodes = {
            "3010": ["ERR", "No macro package is registered for the [<<1>>] component type."],
            "3020": ["ERR", "Cannot update! No macro package is registered for the [<<1>>] component type."],
            "3050": ["ERR", "Macro package file not found: [<<1>>]"],
            "3100": [
                "ERR",
                "Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.",
            ],
            "3200": ["ERR", "Failed to install macro package: [<<1>>]"],
            "3300": ["MSG", "Installed macro package: [<<1>>], Reinitialized [<<2>>]"],
            "3310": ["ERR", "Failed to copy [<<1>>] to folder [<<2>>]."],
            "3400": ["MSG", "Removed macro package configuration settings for: [<<1>>] from the macro.conf file."],
            "3500": ["MSG", "Removed macro package [<<1>>] folder and all files contained."],
            "3600": ["MSG", "Updated component type [<<1>>] with macro package [<<2>>]"],
            "3650": ["ERR", "Failed to updated macro package [<<1>>]"],
        }
Beispiel #10
0
    def __init__(self, pid, cType, gid=None) :
        '''Do the primary initialization for this class.'''

        self.pid                            = pid
        self.gid                            = gid
        self.cType                          = cType
        self.user                           = UserConfig()
        self.userConfig                     = self.user.userConfig
        self.projHome                       = os.path.join(os.path.expanduser(os.environ['RAPUMA_PROJECTS']), self.pid)
        self.local                          = ProjLocal(pid, gid, cType)


#        import pdb; pdb.set_trace()


        self.proj_config                    = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig                  = self.proj_config.projectConfig
        self.layoutConfig                   = self.proj_config.layoutConfig
        self.tools                          = Tools()
        self.log                            = ProjLog(pid)
        # Create config placeholders
        self.layoutConfig                   = None
        self.illustrationConfig             = None
        self.macroConfig                    = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)

        # Log messages for this module
        self.errorCodes     = {
            '3010' : ['ERR', 'No macro package is registered for the [<<1>>] component type.'],
            '3020' : ['ERR', 'Cannot update! No macro package is registered for the [<<1>>] component type.'],
            '3050' : ['ERR', 'Macro package file not found: [<<1>>]'],
            '3100' : ['ERR', 'Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.'],
            '3200' : ['ERR', 'Failed to install macro package: [<<1>>]'],
            '3300' : ['MSG', 'Installed macro package: [<<1>>], Reinitialized [<<2>>]'],
            '3310' : ['ERR', 'Failed to copy [<<1>>] to folder [<<2>>].'],
            '3400' : ['MSG', 'Removed macro package configuration settings for: [<<1>>] from the macro.conf file.'],
            '3500' : ['MSG', 'Removed macro package [<<1>>] folder and all files contained.'],
            '3600' : ['MSG', 'Updated component type [<<1>>] with macro package [<<2>>]'],
            '3650' : ['ERR', 'Failed to updated macro package [<<1>>]']
        }
Beispiel #11
0
    def __init__(self, pid, gid) :
        '''Do the primary initialization for this class.'''

        self.pid                        = pid
        self.gid                        = gid
        self.tools                      = Tools()
        self.local                      = ProjLocal(pid)
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.proj_config                = Config(pid, gid)
        self.proj_config.getProjectConfig()
        self.projectConfig              = self.proj_config.projectConfig
        self.cType                      = self.projectConfig['Groups'][gid]['cType']
        self.Ctype                      = self.cType.capitalize()
        self.log                        = ProjLog(pid)



        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],

            '1010' : ['MSG', 'Script install process for [<<1>>] Succeeded.'],
            '1020' : ['ERR', 'Script install process for [<<1>>] failed.'],
            '1030' : ['ERR', 'Script type [<<1>>] not supported.'],
            '1040' : ['ERR', 'Script install cannot proceed for [<<1>>] because this script already exists in the project. You must remove it first before you can add another script.'],

            '2010' : ['MSG', 'Script remove process for [<<1>>] Succeeded.'],
            '2020' : ['ERR', 'Script remove process for [<<1>>] failed.'],

            '4210' : ['MSG', 'Processes completed successfully on: [<<1>>] by [<<2>>]'],
            '4220' : ['ERR', 'Processes for [<<1>>] failed. Script [<<2>>] returned this error: [<<3>>]'],
            '4260' : ['ERR', 'Installed the default component preprocessing script. Editing will be required for it to work with your project.'],
            '4265' : ['LOG', 'Component preprocessing script is already installed.'],
            '4310' : ['ERR', 'Script is an unrecognized type: [<<1>>] Cannot continue with installation.']


        }
Beispiel #12
0
    def __init__(self, pid) :
        '''Do the primary initialization for this manager.'''

        self.pid                = pid
        self.tools              = Tools()
        self.user               = UserConfig()
        self.userConfig         = self.user.userConfig
        self.config             = Config(pid)
        self.pg_back            = ProjBackground(self.pid)
        self.config.getProjectConfig()
        self.config.getLayoutConfig()
        self.projectConfig      = self.config.projectConfig
        self.layoutConfig       = self.config.layoutConfig
        self.useBackground      = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground'])
        self.useDocInfo         = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo'])
        self.projHome           = os.path.join(self.userConfig['Resources']['projects'], self.pid)
        self.local              = ProjLocal(self.pid)
        self.log                = ProjLog(self.pid)
        self.pdfViewerCmd       = self.userConfig['System']['pdfViewerCommand']


        # Log messages for this module
        self.errorCodes     = {
            '0205' : ['MSG', 'Unassigned message.'],
            '0210' : ['MSG', 'No contents are specified for binding.'],
            '0215' : ['ERR', 'Failed to bind contents into the [<<1>>] fille. Got error: [<<2>>]'],
            '0220' : ['ERR', 'Could not copy [<<1>>] temp file to [<<2>>] saved binding file.'],
            '0230' : ['MSG', 'Completed proccessing on the [<<1>>] binding file.'],
            '0235' : ['ERR', 'Failed to complete proccessing on the [<<1>>] binding file.'],
            '0240' : ['LOG', 'Recorded [<<1>>] rendered pages in the [<<2>>] binding file.'],
            '0260' : ['ERR', 'PDF viewer failed with this error: [<<1>>]'],
            '0265' : ['ERR', 'Rendered file not found: <<1>>'],
            '0270' : ['WRN', 'PDF viewing is disabled.'],
            '0280' : ['ERR', 'GS PDF file merge failed with this error: [<<1>>]'],
            '0300' : ['MSG', 'File binding operation in process, please wait...']

        }
Beispiel #13
0
    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.']

        }
Beispiel #14
0
class Xetex (Manager) :

    # Shared values
    xmlConfFile     = 'xetex.xml'

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

        super(Xetex, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Create all the values we can right now for this manager.
        # Others will be created at run time when we know the cid.
        self.tools                  = Tools()
        self.project                = project
        self.local                  = project.local
        self.log                    = project.log
        self.cfg                    = cfg
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = cType
        self.Ctype                  = cType.capitalize()
        self.mType                  = project.projectMediaIDCode
        self.renderer               = 'xetex'
        self.manager                = self.cType + '_' + self.renderer.capitalize()
        self.managers               = project.managers
        self.pg_back                = ProjBackground(self.pid, self.gid)
        self.fmt_diagnose           = ProjDiagnose(self.pid, self.gid)
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        self.proj_config.getFontConfig()
        self.proj_config.getMacroConfig()
        # Bring in some manager objects we will need
        self.proj_font              = ProjFont(self.pid)
        self.proj_illustration      = ProjIllustration(self.pid, self.gid)
        self.proj_hyphenation       = ProjHyphenation(self.pid, self.gid)
        self.usfmData               = UsfmData()
        self.cidChapNumDict         = self.usfmData.cidChapNumDict()
        self.cidPtIdDict            = self.usfmData.cidPtIdDict()
        # Get config objs
        self.projectConfig          = self.proj_config.projectConfig
        self.layoutConfig           = self.proj_config.layoutConfig
        self.fontConfig             = self.proj_config.fontConfig
        self.macroConfig            = self.proj_config.macroConfig
        self.userConfig             = self.project.userConfig
        self.macPackId              = self.projectConfig['CompTypes'][self.Ctype]['macroPackage']
        # Some config settings

        self.pdfViewerCmd           = self.tools.getPdfViewerCommand(self.userConfig, self.projectConfig)
        self.sourceEditor           = self.projectConfig['CompTypes'][self.Ctype]['sourceEditor']
        self.useBackground          = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground'])
        self.useDiagnostic          = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDiagnostic'])
        self.useDocInfo             = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo'])

        # Get settings for this component
        self.managerSettings = self.projectConfig['Managers'][self.manager]
        for k, v in self.managerSettings.iteritems() :
            if v == 'True' or v == 'False' :
                setattr(self, k, self.tools.str2bool(v))
            else :
                setattr(self, k, v)

        # Set some Booleans (this comes after persistant values are set)
        # Setting hyphenation is a 2 step process, first check global, then group
        self.useHyphenation         = False
        if self.tools.str2bool(self.projectConfig['ProjectInfo']['hyphenationOn']) :
            if self.tools.str2bool(self.projectConfig['Groups'][self.gid]['useHyphenation']) :
                self.useHyphenation = True
                
        # In case the macro is not installed we need to skip over this
        try :
            self.chapNumOffSingChap     = self.tools.str2bool(self.macroConfig['Macros'][self.macPackId]['ChapterVerse']['omitChapterNumberOnSingleChapterBook'])
        except :
            self.chapNumOffSingChap     = None
        # Make any dependent folders if needed
        if not os.path.isdir(self.local.projGidFolder) :
            os.makedirs(self.local.projGidFolder)

        # Record some error codes
        # FIXME: much more needs to be done with this
        self.xetexErrorCodes =  {
            0   : 'Rendering succeful.',
            256 : 'Something really awful happened.'
                                }

        # Log messages for this module
        self.errorCodes     = {

            '1005' : ['ERR', 'PDF viewer failed with error: [<<1>>]'],
            '1010' : ['ERR', 'Style file [<<1>>] could not be created.'],
            '1040' : ['LOG', 'Created: [<<1>>]'],

            '0420' : ['WRN', 'TeX settings file has been frozen for debugging purposes.'],
            '0440' : ['LOG', 'Created: [<<1>>]'],
            '0460' : ['LOG', 'Settings changed in [<<1>>], [<<2>>] needed to be recreated.'],
            '0465' : ['LOG', 'File: [<<1>>] missing, created a new one.'],
            '0470' : ['ERR', 'Macro package [<<1>>] is not recognized by the system.'],

            '0600' : ['MSG', '<<1>> cannot be viewed, PDF viewer turned off.'],
            '0610' : ['LOG', 'Recorded [<<1>>] rendered pages in the [<<2>>] group.'],
            '0615' : ['ERR', 'XeTeX failed to execute with error: <<1>>'],
            '0617' : ['ERR', 'XeTeX failed to execute with this error: [<<1>>]'],
            '0620' : ['DBG', 'xetex command in <<1>>: <<2>> <<3>>'],
            '0625' : ['MSG', 'Rendering of [<<1>>] successful.'],
            '0630' : ['ERR', 'Rendering [<<1>>] was unsuccessful. <<2>> (<<3>>)'],
            '0635' : ['ERR', 'XeTeX error code [<<1>>] not understood by Rapuma.'],
            '0650' : ['ERR', 'Component type [<<1>>] not supported!'],
            '0690' : ['MSG', 'Dependent files unchanged, rerendering of [<<1>>] un-necessary.'],
            '0695' : ['MSG', 'Routing <<1>> to PDF viewer.'],
            '0700' : ['ERR', 'Rendered file not found: <<1>>'],
            '0710' : ['WRN', 'PDF viewing is disabled.'],
            '0720' : ['MSG', 'Saved rendered file to: [<<1>>]'],
            '0730' : ['ERR', 'Failed to save rendered file to: [<<1>>]'],

            '1000' : ['WRN', 'XeTeX debugging is set to [<<1>>]. These are the paths XeTeX is seeing: [<<2>>]'],
            '1090' : ['ERR', 'Invalid value [<<1>>] used for XeTeX debugging. Must use an integer of 0, 1, 2, 4, 8, 16, or 32']

        }

        # FIXME: It would be good if we could do a check for dependent files here


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

    def checkStartPageNumber (self) :
        '''Adjust page number for the current group. The current logic is
        if there is no number in the startPageNumber setting, we can put
        one in there as a suggestion. If there is already one there, the
        user will be responsible for seeing that it is correct.'''

#        import pdb; pdb.set_trace()

        try :
            # Simply try to return anything that is in the field
            cStrPgNo = self.projectConfig['Groups'][self.gid]['startPageNumber']
            if cStrPgNo != '' :
                return cStrPgNo
        except :
            # If nothing is there, we'll make a suggestion
            pGrp = str(self.projectConfig['Groups'][self.gid]['precedingGroup'])
            if pGrp == 'None' :
                self.projectConfig['Groups'][self.gid]['startPageNumber'] = 1
                self.tools.writeConfFile(self.projectConfig)
                return '1'
            else :
                # Calculate the suggested number based on the preceeding group
                try :
                    cStrPgNo    = str(self.projectConfig['Groups'][self.gid]['startPageNumber'])
                except :
                    cStrPgNo    = 1
                    self.projectConfig['Groups'][self.gid]['startPageNumber'] = 1
                try :
                    pGrpPgs     = int(self.projectConfig['Groups'][pGrp]['totalPages'])
                    pGrpStrPgNo = int(self.projectConfig['Groups'][pGrp]['startPageNumber'])
                except :
                    # FIXME: Maybe this could go out and find out exactly how many pages were in the preceeding group
                    pGrpPgs     = 1
                    pGrpStrPgNo = 1
                    self.projectConfig['Groups'][pGrp]['totalPages'] = 1
                    self.projectConfig['Groups'][pGrp]['startPageNumber'] = 1
                # Whether this is right or wrong set it the way it is
                self.projectConfig['Groups'][self.gid]['startPageNumber'] = (pGrpStrPgNo + pGrpPgs)
                self.tools.writeConfFile(self.projectConfig)
                return self.projectConfig['Groups'][pGrp]['startPageNumber']


    def makeExtFile (self, fileName, description) :
        '''Generic function to create an extension file if one does not already exist.'''

        if not os.path.exists(fileName) :
            with codecs.open(fileName, "w", encoding='utf_8') as writeObject :
                writeObject.write(self.tools.makeFileHeader(fileName, description, False))
            self.log.writeToLog(self.errorCodes['1040'], [self.tools.fName(fileName)])
            return True


    def makeCmpExtTexFileOn (self, fileName) :
        '''Create a component TeX extention macro "on" file for a specified component. A matching "off"
        file will be created as well.'''

        description = 'This is a component (on) TeX macro extension file which may override any macros \
        which were loaded for this rendering process. This file is read just before the component \
        working file. After the component is rendered, the accompanying off TeX file will be \
        loaded which will turn off any modified macro commands that this TeX file has set. The \
        user must edit this file in order for it to work right.'

        return self.makeExtFile(fileName, description)


    def makeCmpExtTexFileOff (self, fileName) :
        '''Create a component TeX extention macro "off" file for a specified component. This is to
        match the "on" file that was created.'''

        description = 'This is a component (off) style extension file which overrides the settings \
        that were loaded for this rendering process just prior to loading the component working \
        file. The commands in this style file will off-set the "on" settings causing the macro to \
        render as it did before the "on" styles were loaded. The user must edit this file for it \
        to work properly.'

        return self.makeExtFile(fileName, description)


    def makeCmpExtStyFileOn (self, fileName) :
        '''Create a component style extentions "on" file for a specified component. A matching "off"
        file will be created as well.'''

        description = 'This is a component (on) style extension file which overrides any settings \
        which were loaded for this rendering process. This file is read just before the component \
        working file. After the component is rendered, the accompanying off style file will be \
        loaded which will turn off any modified style commands that this style file has set. The \
        user must edit this file in order for it to work right.'

        return self.makeExtFile(fileName, description)


    def makeCmpExtStyFileOff (self, fileName) :
        '''Create a component style extentions "off" file for a specified component. This is to
        match the "on" file that was created.'''

        description = 'This is a component (off) style extension file which overrides the settings \
        that were loaded for this rendering process just prior to loading the component working \
        file. The commands in this style file will off-set the "on" settings causing the macro to \
        render as it did before the "on" styles were loaded. The user must edit this file for it \
        to work properly.'

        return self.makeExtFile(fileName, description)


    def makeGrpExtTexFile (self) :
        '''Create a group TeX extentions file for a specified group.'''

        description = 'This is the group TeX extention macro file which overrides settings in \
        the global TeX extension macro file.'

        return self.makeExtFile(self.local.grpExtTexFile, description)


    def makeGrpExtStyFile (self) :
        '''Create a group Style extentions file to a specified group.'''

        description = 'This is the group style extention file which overrides settings in \
        the main default component extentions settings style file.'

        return self.makeExtFile(self.local.grpExtStyFile, description)


###############################################################################
############################# DEPENDENCY FUNCTIONS ############################
###############################################################################
######################## Error Code Block Series = 0400 #######################
###############################################################################


    def makeSettingsTexFile (self) :
        '''Create the primary TeX settings file.'''

#        import pdb; pdb.set_trace()

        description = 'This is the primary TeX settings file for the ' + self.gid + ' group. \
        It is auto-generated so editing can be a rather futile exercise. This is unless you \
        set freezeTexSettings to True in the XeTeX manager configuration of the project.conf \
        file. Doing that will prevent the file from being remade. However, no configuration \
        changes will be reflected in the static settings file. Use this with care.'

        # Setting for internal testing
        outputTest = False

        # Check for freezeTexSettings in project.conf
        if self.projectConfig['Managers'][self.cType + '_Xetex'].has_key('freezeTexSettings') and \
                self.tools.str2bool(self.projectConfig['Managers'][self.cType + '_Xetex']['freezeTexSettings']) :
            self.log.writeToLog(self.errorCodes['0420'])
            return False

        def appendLine(line, realVal) :
            '''Use this to shorten the code and look for listy things.'''
            if type(line) == list :
                for s in line :
                    linesOut.append(self.proj_config.processNestedPlaceholders(s, realVal))
            else :
                linesOut.append(self.proj_config.processNestedPlaceholders(line, realVal))

        # Open a fresh settings file
        with codecs.open(self.local.macSettingsFile, "w", encoding='utf_8') as writeObject :
            writeObject.write(self.tools.makeFileHeader(self.local.macSettingsFileName, description))
            # Build a dictionary from the default XML settings file
            # Create a dict that contains only the data we need here
            macPackDict = self.tools.xmlFileToDict(self.local.macPackConfXmlFile)

            for sections in macPackDict['root']['section'] :
                for section in sections :
                    secItem = sections[section]
                    linesOut = []
                    if type(secItem) is list :
                        if outputTest :
                            print sections['sectionID']
                        linesOut.append('% ' + sections['sectionID'].upper())
                        for setting in secItem :
                            for k in setting.keys() :
                                if k == 'texCode' :
                                    if outputTest :
                                        print '\t', setting['key']
                                    realVal = self.macroConfig['Macros'][self.macPackId][sections['sectionID']][setting['key']]
                                    # Test any boolDepends that this setting might have
                                    if setting.has_key('boolDepend') :
                                        result = []
                                        if type(setting['boolDepend']) == list :
                                            for i in setting['boolDepend'] :
                                                result.append(self.affirm(i))
                                        else :
                                            result.append(self.affirm(setting['boolDepend']))
                                        # If 'None' didn't end up in the list, that means
                                        # every bool tested good so we can output the line
                                        if None not in result :
                                            if outputTest :
                                                print '\t', setting.get(k)
                                            appendLine(setting['texCode'], realVal)
                                    # Normal setting output
                                    elif setting.get(k) :
                                        if setting.get(k) != None :
                                            # We filter out zero values here (But what if we need one of them?)
                                            if not self.proj_config.processNestedPlaceholders(realVal) == '0' :
                                                if outputTest :
                                                    print '\t', setting.get(k)
                                                appendLine(setting['texCode'], realVal)

                    # Only write out sections that have something in them
                    if len(linesOut) > 1 :
                        writeObject.write('\n')
                        for line in linesOut :
                            writeObject.write(line + '\n')


            # Continue here with injecting the font settings which are guided by
            # the config file because the XML source(s) could vary
            writeObject.write('\n% INSTALLED FONTS\n')
            installedFonts = self.fontConfig['Fonts'].keys()
            cTypeFont = self.projectConfig['CompTypes'][self.cType.capitalize()]['fontName']
            for font in installedFonts :
                if font == cTypeFont :
                    # Output the primary font
                    for key in self.fontConfig['Fonts'][font]['TexMapping']['PrimaryFont'].keys() :
                        writeObject.write(self.proj_config.processNestedPlaceholders(self.fontConfig['Fonts'][font]['TexMapping']['PrimaryFont'][key]) + '\n')
                    # Output the seconday settings as well for this font
                    for key in self.fontConfig['Fonts'][font]['TexMapping']['SecondaryFont'].keys() :
                        writeObject.write(self.proj_config.processNestedPlaceholders(self.fontConfig['Fonts'][font]['TexMapping']['SecondaryFont'][key]) + '\n')
                else :
                    # There can only be one primary font, this is not it
                    for key in self.fontConfig['Fonts'][font]['TexMapping']['SecondaryFont'].keys() :
                        writeObject.write(self.proj_config.processNestedPlaceholders(self.fontConfig['Fonts'][font]['TexMapping']['SecondaryFont'][key]) + '\n')

                writeObject.write('\n')

            # Die here if testing
            if outputTest :
                self.tools.dieNow()
            # Report finished if not
            return True


    def affirm (self, boolDependInfo) :
        '''Affirm by returning True if the actual bool matches its
        state setting. Returning 'None' will cause a setting to be skipped.'''


        realBool = self.returnConfRefValue(boolDependInfo['#text']).lower()
        if boolDependInfo['@state'].lower() == realBool.lower() :
            return True


    def returnConfRefValue (self, ref) :
        '''Return the value of a given config reference. The ref syntax is
        as follows: [config:configObj|section|key]. This should be able to
        recuse as deep as necessary.'''

#        import pdb; pdb.set_trace()

        ref = ref.lstrip('[').rstrip(']')
        (holderType, holderKey) = ref.split(':', 1)
        if holderType.lower() == 'config' :
            val = holderKey.split('|')
            dct = ['self.' + val[0]]
            val.remove(val[0])
            for i in val :
                i = self.proj_config.processNestedPlaceholders(i, '')
                dct.append('["' + i + '"]')

            return eval(''.join(dct))


    def makeGidTexFile (self, cidList) :
        '''Create the main gid TeX control file.'''

        description = 'This is the group TeX control file. XeTeX will \
            read this file to get all of links to other instructions (macros) \
            needed to render the group, or a component of a group.'

        # Since a render run could contain any number of components
        # in any order, we will remake this file on every run. No need
        # for dependency checking
        if os.path.exists(self.local.gidTexFile) :
            os.remove(self.local.gidTexFile)

        # Create the main TeX settings file (made on every run)
        self.makeSettingsTexFile()

        # Start writing out the gid.tex file. Check/make dependencies as we go.
        # If we fail to make a dependency it will die and report during that process.
        # We bring in each element in the order necessary
        with codecs.open(self.local.gidTexFile, "w", encoding='utf_8') as gidTexObject :
            # Write out the file header
            gidTexObject.write(self.tools.makeFileHeader(self.local.gidTexFileName, description))
            # First bring in the main macro file
            gidTexObject.write('\\input \"' + self.local.primaryMacroFile + '\"\n')
            # Check for a preStyle extension file and load if it is there
            if os.path.exists(self.local.preStyTexExtFile) :
                gidTexObject.write('\\input \"' + self.local.preStyTexExtFile + '\"\n')
            ########
            # FIXME? To avoid problems with the usfmTex marginalverses macro code, we bring
            # in the stylesheets now. Will this cause any problems with other macPacks?
            ########
            # Load style files (default and extention come with the package)
            gidTexObject.write('\\stylesheet{' + self.local.defaultStyFile + '}\n')
            # Load the global style extensions
            gidTexObject.write('\\stylesheet{' + self.local.glbExtStyFile + '}\n')
            # Load the group style extensions (if needed)
            if self.projectConfig['Groups'][self.gid].has_key('useGrpStyOverride') and self.tools.str2bool(self.projectConfig['Groups'][self.gid]['useGrpStyOverride']) :
                self.makeGrpExtStyFile()
                gidTexObject.write('\\stylesheet{' + self.local.grpExtStyFile + '}\n')
            # Load the settings (usfmTex: if marginalverses, load code in this)
            gidTexObject.write('\\input \"' + self.local.macSettingsFile + '\"\n')
            # Load the TeX macro extensions for this macro package
            gidTexObject.write('\\input \"' + self.local.extTexFile + '\"\n')
            # Load the group TeX macro extensions (if needed)
            if self.projectConfig['Groups'][self.gid].has_key('useGrpTexOverride') and self.tools.str2bool(self.projectConfig['Groups'][self.gid]['useGrpTexOverride']) :
                self.makeGrpExtTexFile()
                gidTexObject.write('\\input \"' + self.local.grpExtTexFile + '\"\n')
            # Load hyphenation data if needed
            if self.useHyphenation :
                # This is the main hyphenation settings file, this must be loaded first
                gidTexObject.write('\\input \"' + self.proj_hyphenation.projHyphSetTexFile + '\"\n')
                # This is the character definition file for hyphenation, this should be loaded second
                gidTexObject.write('\\input \"' + self.proj_hyphenation.projHyphCharTexFile + '\"\n')
                # This is the exception words list (all the hyphenated words), this is loaded last
                gidTexObject.write('\\input \"' + self.proj_hyphenation.projHyphExcTexFile + '\"\n')
            # If this is less than a full group render, just go with default pg num (1)
            if cidList == self.projectConfig['Groups'][self.gid]['cidList'] :
                # Check if this setting is there
                startPageNumber = self.checkStartPageNumber()
                if startPageNumber > 1 :
                    gidTexObject.write('\\pageno = ' + str(startPageNumber) + '\n')
            # Insert Document properties and x1a compliant info if needed
            gidTexObject.write(self.makeXoneACompliant())
            # Now add in each of the components
            for cid in cidList :
                # Output files and commands for usfm cType
                if self.cType == 'usfm' :
                    cidSource       = os.path.join(self.local.projComponentFolder, cid, self.project.groups[self.gid].makeFileNameWithExt(cid))
                    cidTexFileOn    = os.path.join(self.local.projTexFolder, self.gid + '-' + cid + '-On-ext.tex')
                    cidTexFileOff   = os.path.join(self.local.projTexFolder, self.gid + '-' + cid + '-Off-ext.tex')
                    cidStyFileOn    = os.path.join(self.local.projStyleFolder, self.gid + '-' + cid + '-On-ext.sty')
                    cidStyFileOff   = os.path.join(self.local.projStyleFolder, self.gid + '-' + cid + '-Off-ext.sty')
                    # Check to see if a TeX macro override is needed
                    if self.projectConfig['Groups'][self.gid].has_key('compTexOverrideList') and cid in self.projectConfig['Groups'][self.gid]['compTexOverrideList'] :
                        self.makeCmpExtTexFileOn(cidTexFileOn)
                        gidTexObject.write('\\input \"' + cidTexFileOn + '\"\n')
                    # Check to see if a style override is needed (if so create "on" file)
                    if self.projectConfig['Groups'][self.gid].has_key('compStyOverrideList') and cid in self.projectConfig['Groups'][self.gid]['compStyOverrideList'] :
                        self.makeCmpExtStyFileOn(cidStyFileOn)
                        gidTexObject.write('\\stylesheet{' + cidStyFileOn + '}\n')
                    # Check for short books add omit statement
                    if self.chapNumOffSingChap and self.cidChapNumDict[cid] == 1 :
                        gidTexObject.write('\\OmitChapterNumbertrue\n') 
                    # Add the working file here
                    gidTexObject.write('\\ptxfile{' + cidSource + '}\n')
                    # Check again for short books turn off omit statement
                    if self.chapNumOffSingChap and self.cidChapNumDict[cid] == 1 :
                        gidTexObject.write('\\OmitChapterNumberfalse\n') 
                    # Check for for style override and add the "Off" style file here
                    if self.projectConfig['Groups'][self.gid].has_key('compStyOverrideList') and cid in self.projectConfig['Groups'][self.gid]['compStyOverrideList'] :
                        self.makeCmpExtStyFileOn(cidStyFileOff)
                        gidTexObject.write('\\stylesheet{' + cidStyFileOff + '}\n')
                    # Check for for TeX macro override and add the "Off" TeX file here
                    if self.projectConfig['Groups'][self.gid].has_key('compTexOverrideList') and cid in self.projectConfig['Groups'][self.gid]['compTexOverrideList'] :
                        self.makeCmpExtTexFileOff(cidTexFileOff)
                        gidTexObject.write('\\input \"' + cidTexFileOff + '\"\n')
                else :
                    self.log.writeToLog(self.errorCodes['0650'], [self.cType])

            # This can only hapen once in the whole process, this marks the end
            gidTexObject.write('\\bye\n')

        return True


    def makeXoneACompliant (self) :
        '''Insert the necessary TeX code into the header to give the
        appearance of being PDF x1-a compliant. If the feature is turned
        off then it will only inject relevant document properties that
        are good to have included no mater what. XeTeX output is x1-a
        for the most part, it just doesn't brag about it. The output
        here mostly works. :-) '''

#        import pdb; pdb.set_trace()
        
        allLines = ''
        # Set some of the vars for the output
        title = self.projectConfig['ProjectInfo']['projectTitle']
        subject = self.projectConfig['ProjectInfo']['projectDescription']
        author = ''.join(self.projectConfig['ProjectInfo']['translators'])
        creator = ''.join(self.projectConfig['ProjectInfo']['typesetters'])
        # I don't think this next bit is not right, what does +7 mean anyway?
        # It works for CDT time anyway, which I thought was -6
        offSet = "+07\'00\'"
        # To get the date stamp right, we strip out all the non-number
        # characters so we are left with: yyyymmddhhmmss
        comp = self.projectConfig['ProjectInfo']['projectCreateDate'].replace('-', '').replace(':', '').replace(' ', '')
        cDate = 'D:' + comp + offSet
        mDate = 'D:' + self.tools.fullFileTimeStamp() + offSet
        lines = [   
                    '\special{pdf:docinfo<<',
                    '/Title(' + title + ')%',
                    '/Subject(' + subject + ')%',
                    '/Author(' + author + ')%',
                    '/Creator(' + creator + ')%',
                    '/CreationDate(' + cDate + ')%',
                    '/ModDate(' + mDate + ')%',
                    '/Producer(XeTeX with Rapuma)%',
                    '/Trapped /False',
                    '/GTS_PDFXVersion(PDF/X-1:2003)%',
                    '/GTS_PDFXConformance(PDF/X-1a:2003)%',
                    '>> }'
                ]
        # Add PDF header declairations for PDF X1-A:2003 compliance (default is True)
        if self.tools.str2bool(self.layoutConfig['DocumentFeatures']['pdfX1a']) :
            icc = os.path.join(self.local.rapumaConfigFolder, 'ps_cmyk.icc')
            # Now create the insert line list
            xtralines =  [   '\special{pdf:fstream @OBJCVR (' + icc + ')}',
                                '\special{pdf:put @OBJCVR <</N 4>>}',
                                '%\special{pdf:close @OBJCVR}',
                                '\special{pdf:docview <<',
                                '/OutputIntents [ <<',
                                '/Type/OutputIndent',
                                '/S/GTS_PDFX',
                                '/OutputCondition (An Unknown print device)',
                                '/OutputConditionIdentifier (Custom)',
                                '/DestOutputProfile @OBJCVR',
                                '/RegistryName (http://www.color.og)',
                                '>> ] >>}'
                        ]
            lines = lines + xtralines

        # Whatever our output, process the lines
        for l in lines : 
            allLines = allLines + l + ' \n'

        return allLines


###############################################################################
################################# Main Function ###############################
###############################################################################
######################## Error Code Block Series = 0600 #######################
###############################################################################

    def run (self, gid, cidList, pgRange, override, save) :
        '''This will check all the dependencies for a group and then
        use XeTeX to render the whole group or a subset of components
        and even a page range in a single component.'''

#        import pdb; pdb.set_trace()

        # There must be a cidList. If one was not passed, default to
        # the group list
        cidListSubFileName      = ''
        saveFile                = ''
        saveFileName            = ''
        if not cidList :
            cidList = self.projectConfig['Groups'][gid]['cidList']
        else :
            # If there is a cidList, create an alternate ouput name.
            # This is so if the file is saved it will have a unique
            # name. the name needs to be ordered by ###-cid-gid.
            # We need to do this sooner than later.
            if len(cidList) > 1 :
                cidListSubFileName = '-'.join(cidList)
            else :
                cid = cidList[0]
                # Add a filler character to the ID
                cnid = "{:0>3}".format(self.cidPtIdDict[cid])
                cidListSubFileName = cnid + '-' + cid

        # Create, if necessary, the gid.tex file
        # First, go through and make/update any dependency files
        self.makeSettingsTexFile()
        # Now make the gid main setting file
        self.makeGidTexFile(cidList)
        # Dynamically create a dependency list for the render process
        # Note: gidTexFile is remade on every run, do not test against that file
        dep = [self.local.extTexFile, self.local.projectConfFile, self.local.layoutConfFile, 
                self.local.macroConfFile, self.local.illustrationConfFile, ]
        # Add component dependency files
        for cid in cidList :
            cidUsfm = self.project.groups[gid].getCidPath(cid)
            cidIlls = self.proj_illustration.getCidPiclistFile(cid)
            for f in [cidUsfm, cidIlls] :
                if os.path.exists(f) :
                    dep.append(f)
            # Treat adjustment file separate
            if self.cType == 'usfm' :
                cidAdj = self.project.groups[gid].getCidAdjPath(cid)
                if os.path.exists(cidAdj) :
                    dep.append(cidAdj)

        # Call the renderer
        # Create the environment that XeTeX will use. This will be temporarily set
        # by subprocess.call() just before XeTeX is run.
        texInputsLine = self.project.local.projHome + ':' \
                        + self.local.projStyleFolder + ':' \
                        + self.local.projTexFolder + ':' \
                        + self.local.projMacPackFolder + ':' \
                        + self.local.projMacroFolder + ':' \
                        + self.local.projGidFolder + ':.'

        # Create the environment dictionary that will be fed into subprocess.call()
        #envDict = dict(os.environ)
        envDict={}
        # These are project environment vars
        envDict['TEXINPUTS'] = texInputsLine
        # These are XeTeX environment vars that are run if the internal (fast) version
        # of XeTeX is being run, which is the default. If runExternalXetex is set to
        # False, the following special environment vars will be run. If set to true,
        # an external version of XeTeX, provided it is installed, will run with its own
        # environment vars set elsewhere
        runExternal = self.tools.str2bool(self.projectConfig['Managers'][self.cType + '_Xetex'].get('runExternalXetex', ''))
        if not runExternal :
            envDict['PATH'] = os.path.join(self.local.rapumaXetexFolder, 'bin', 'x86_64-linux')
            envDict['TEXMFCNF'] = os.path.join(self.local.rapumaXetexFolder, 'texmf-local', 'web2c')
            envDict['TEXFORMATS'] = os.path.join(self.local.rapumaXetexFolder, 'texmf-local', 'web2c', 'xetex')
            # To help with debugging the following hook has been added. This is not
            # something the user would ever use. It is only for developer diagnostics.
            # for infomation on what integers can be used refer to this URL:
            # http://www.dcs.ed.ac.uk/home/latex/Informatics/Obsolete/html/kpathsea/kpathsea.html
            debugXetex = self.projectConfig['Managers'][self.cType + '_Xetex'].get('debugKpse', None)
            if debugXetex :
                try :
                    if int(debugXetex) > 0 :
                        envDict['KPATHSEA_DEBUG'] = debugXetex
                        self.log.writeToLog(self.errorCodes['1000'], [str(debugXetex), str(envDict)])
                except :
                    self.log.writeToLog(self.errorCodes['1090'], [debugXetex])
        else :
            envDict.update(os.environ)

        # Create the XeTeX command argument list that subprocess.call() will run with
        # the environment vars we set above
        cmds = ['xetex', '-output-directory=' + self.local.projGidFolder, self.local.gidTexFile]

        # For debugging purposes, output the following DBG message
        if self.projectConfig['Managers'][self.cType + '_Xetex'].has_key('freezeTexSettings') and \
                self.tools.str2bool(self.projectConfig['Managers'][self.cType + '_Xetex']['freezeTexSettings']) :
            self.log.writeToLog(self.errorCodes['0620'], [os.getcwd(), str(envDict), " ".join(cmds)])

        # Run the XeTeX and collect the return code for analysis
        try :
            rCode = subprocess.call(cmds, env = envDict)
            # Analyse the return code
            if rCode == int(0) :
                self.log.writeToLog(self.errorCodes['0625'], [self.local.gidTexFileName])
            elif rCode in self.xetexErrorCodes :
                self.log.writeToLog(self.errorCodes['0630'], [self.local.gidTexFileName, self.xetexErrorCodes[rCode], str(rCode)])
            else :
                self.log.writeToLog(self.errorCodes['0635'], [str(rCode)])
        except Exception as e :
            # If subprocess fails it might be because XeTeX did not execute
            # we will try to report back something useful
            self.log.writeToLog(self.errorCodes['0615'], [str(e)])

        # Collect the page count and record in group (Write out at the end of the opp.)
        self.projectConfig['Groups'][gid]['totalPages'] = str(PdfFileReader(open(self.local.gidPdfFile)).getNumPages())
        # Write out any changes made to the project.conf file that happened during this opp.
        self.tools.writeConfFile(self.projectConfig)

        # Pull out pages if requested (use the same file for output)
        if pgRange :
            self.tools.pdftkPullPages(self.local.gidPdfFile, self.local.gidPdfFile, pgRange)

        # The gidPdfFile is the residue of the last render and if approved, can be
        # used for the binding process. In regard to saving and file naming, the
        # gidPdfFile will be copied but never renamed. It must remain intact.

        # If the user wants to save this file or use a custom name, do that now
        if save and not override :
            saveFileName = self.pid + '_' + gid
            if cidListSubFileName :
                saveFileName = saveFileName + '_' + cidListSubFileName
            if pgRange :
                saveFileName = saveFileName + '_pg(' + pgRange + ')'
            # Add date stamp
            saveFileName = saveFileName + '_' + self.tools.ymd()
            # Add render file extention
            saveFileName = saveFileName + '.pdf'
            # Save this to the Deliverable folder (Make sure there is one)
            if not os.path.isdir(self.local.projDeliverableFolder) :
                os.makedirs(self.local.projDeliverableFolder)
            # Final file name and path
            saveFile = os.path.join(self.local.projDeliverableFolder, saveFileName)
            # Copy, no news is good news
            if shutil.copy(self.local.gidPdfFile, saveFile) :
                self.log.writeToLog(self.errorCodes['0730'], [saveFileName])
            else :
                self.log.writeToLog(self.errorCodes['0720'], [saveFileName])            

        # If given, the override file name becomes the file name 
        if override :
            saveFile = override
            # With shutil.copy(), no news is good news
            if shutil.copy(self.local.gidPdfFile, saveFile) :
                self.log.writeToLog(self.errorCodes['0730'], [saveFileName])
            else :
                self.log.writeToLog(self.errorCodes['0720'], [saveFileName])

        # Once we know the file is successfully generated, add a background if defined
        viewFile = ''
        if self.useBackground :
            if saveFile :
                viewFile = self.pg_back.addBackground(saveFile)
            else :
                viewFile = self.pg_back.addBackground(self.local.gidPdfFile)
                
        # Add a timestamp and doc info if requested in addition to background
        if self.useDocInfo :
            if saveFile :
                if os.path.isfile(viewFile) :
                    viewFile = self.pg_back.addDocInfo(viewFile)
                else :
                    viewFile = self.pg_back.addDocInfo(saveFile)
            else :
                if os.path.isfile(viewFile) :
                    viewFile = self.pg_back.addDocInfo(viewFile)
                else :
                    viewFile = self.pg_back.addDocInfo(self.local.gidPdfFile)

        # Add a diagnostic layer to the rendered output. Normally this is
        # not used with a normal background layer
        if self.useDiagnostic :
            if saveFile :
                viewFile = self.fmt_diagnose.addTransparency(saveFile)
            else :
                viewFile = self.fmt_diagnose.addTransparency(self.local.gidPdfFile)

        # To avoid confusion with file names, if this is a saved file,
        # and it has a background, we need to remove the original, non-
        # background file (remembering originals are kept in the group
        # Component folder), then rename the -view version to whatever
        # the saved name should be
        if save or override :
            if os.path.isfile(saveFile) and os.path.isfile(viewFile) :
                # First remove
                os.remove(saveFile)
                # Next rename
                os.rename(viewFile, saveFile)

        ##### Viewing #####
        # First get the right file name to view
        if saveFile :
            # If there was a saveFile, that will be the viewFile
            viewFile = saveFile
        else :
            # The view file in this case is just temporary
            if not os.path.isfile(viewFile) :
                viewFile = self.local.gidPdfFile.replace(gid + '.pdf', gid + '-view.pdf')
                shutil.copy(self.local.gidPdfFile, viewFile)

        # Now view it
        if os.path.isfile(viewFile) :
            if self.pdfViewerCmd :
                # Add the file to the viewer command
                self.pdfViewerCmd.append(viewFile)
                # Run the XeTeX and collect the return code for analysis
                try :
                    subprocess.Popen(self.pdfViewerCmd)
                    return True
                except Exception as e :
                    # If we don't succeed, we should probably quite here
                    self.log.writeToLog(self.errorCodes['1005'], [str(e)])
            else :
                self.log.writeToLog(self.errorCodes['0710'])
        else :
            self.log.writeToLog(self.errorCodes['0700'], [self.tools.fName(viewFile)])
            
        # If we made it this far, return True
        return True
Beispiel #15
0
    def __init__(self, project, cfg, cType) :
        '''Do the primary initialization for this manager.'''

        super(Pdftk, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Create all the values we can right now for this manager.
        # Others will be created at run time when we know the cid.
        self.tools                  = Tools()
        self.project                = project
        self.local                  = project.local
        self.log                    = project.log
        self.cfg                    = cfg
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = cType
        self.Ctype                  = cType.capitalize()
        self.mType                  = project.projectMediaIDCode
        self.renderer               = 'pdftk'
        self.manager                = self.cType + '_' + self.renderer.capitalize()
        self.managers               = project.managers
        self.pg_back                = ProjBackground(self.pid, self.gid)
        self.fmt_diagnose           = ProjDiagnose(self.pid, self.gid)
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        # Get config objs
        self.projectConfig          = self.proj_config.projectConfig
        self.layoutConfig           = self.proj_config.layoutConfig
        self.userConfig             = self.project.userConfig
        # Some config settings
        self.pdfViewerCmd           = self.tools.getPdfViewerCommand(self.userConfig, self.projectConfig)
        self.useBackground          = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground'])
        self.useDocInfo             = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo'])

        # Get settings for this component
        self.managerSettings = self.projectConfig['Managers'][self.manager]
        for k, v in self.managerSettings.iteritems() :
            if v == 'True' or v == 'False' :
                setattr(self, k, self.tools.str2bool(v))
            else :
                setattr(self, k, v)

        # Make any dependent folders if needed
        if not os.path.isdir(self.local.projGidFolder) :
            os.makedirs(self.local.projGidFolder)


        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Messages for the pdftk module.'],

            '5010' : ['ERR', 'Subprocess failed with this error: <<1>>'],
            '5020' : ['MSG', 'PDF merge was successful. Output = <<1>>'],
            '5200' : ['ERR', 'Rendered file not found: <<1>>'],
            '5210' : ['WRN', 'PDF viewing is disabled.'],
            '5720' : ['MSG', 'Saved rendered file to: [<<1>>]'],
            '5730' : ['ERR', 'Failed to save rendered file to: [<<1>>]']

        }
Beispiel #16
0
class Pdftk (Manager) :

    # Shared values
    xmlConfFile     = 'pdftk.xml'

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

        super(Pdftk, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Create all the values we can right now for this manager.
        # Others will be created at run time when we know the cid.
        self.tools                  = Tools()
        self.project                = project
        self.local                  = project.local
        self.log                    = project.log
        self.cfg                    = cfg
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = cType
        self.Ctype                  = cType.capitalize()
        self.mType                  = project.projectMediaIDCode
        self.renderer               = 'pdftk'
        self.manager                = self.cType + '_' + self.renderer.capitalize()
        self.managers               = project.managers
        self.pg_back                = ProjBackground(self.pid, self.gid)
        self.fmt_diagnose           = ProjDiagnose(self.pid, self.gid)
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        # Get config objs
        self.projectConfig          = self.proj_config.projectConfig
        self.layoutConfig           = self.proj_config.layoutConfig
        self.userConfig             = self.project.userConfig
        # Some config settings
        self.pdfViewerCmd           = self.tools.getPdfViewerCommand(self.userConfig, self.projectConfig)
        self.useBackground          = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground'])
        self.useDocInfo             = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo'])

        # Get settings for this component
        self.managerSettings = self.projectConfig['Managers'][self.manager]
        for k, v in self.managerSettings.iteritems() :
            if v == 'True' or v == 'False' :
                setattr(self, k, self.tools.str2bool(v))
            else :
                setattr(self, k, v)

        # Make any dependent folders if needed
        if not os.path.isdir(self.local.projGidFolder) :
            os.makedirs(self.local.projGidFolder)


        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Messages for the pdftk module.'],

            '5010' : ['ERR', 'Subprocess failed with this error: <<1>>'],
            '5020' : ['MSG', 'PDF merge was successful. Output = <<1>>'],
            '5200' : ['ERR', 'Rendered file not found: <<1>>'],
            '5210' : ['WRN', 'PDF viewing is disabled.'],
            '5720' : ['MSG', 'Saved rendered file to: [<<1>>]'],
            '5730' : ['ERR', 'Failed to save rendered file to: [<<1>>]']

        }


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

    def sourceListFromCidList (self, cidList) :
        '''Return a sourceList derived from a given cidList.'''

        sourceList = []
        for cid in cidList :
            sourceList.append(self.sourceFromCid(cid))

        return sourceList

    def sourceFromCid (self, cid) :
        '''Return the full path with file name derived from a valid cid.'''
        
        return os.path.join(self.local.projComponentFolder, cid, cid + '.pdf')


###############################################################################
################################# Main Function ###############################
###############################################################################
######################## Error Code Block Series = 5000 #######################
###############################################################################

    def run (self, gid, cidList, pgRange, override, save) :
        '''This will check all the dependencies for a group and then
        use pdftk to "render" the whole group or a subset of components
        and even a page range in a single component.'''

#        import pdb; pdb.set_trace()

        # There must be a cidList. If one was not passed, default to
        # the group list
        cidListSubFileName      = ''
        saveFile                = ''
        saveFileName            = ''
        viewFile                = ''
        if not cidList :
            cidList = self.projectConfig['Groups'][gid]['cidList']

        # Make a list of files that pdftk will merge together
        sourceList = self.sourceListFromCidList(cidList)
                
        # Merge the files
        cmd = ['pdftk'] + sourceList + ['cat', 'output', self.local.gidPdfFile]

#        import pdb; pdb.set_trace()

        # No return from pdftk is good, we can continue on
        if not subprocess.call(cmd) : 
            # Collect the page count and record in group (Write out at the end of the opp.)
            self.projectConfig['Groups'][gid]['totalPages'] = str(PdfFileReader(open(self.local.gidPdfFile)).getNumPages())
            # Write out any changes made to the project.conf file that happened during this opp.
            self.tools.writeConfFile(self.projectConfig)

            # Pull out pages if requested (use the same file for output)
            if pgRange :
                self.tools.pdftkPullPages(self.local.gidPdfFile, self.local.gidPdfFile, pgRange)

            # The gidPdfFile is the residue of the last render and if approved, can be
            # used for the binding process. In regard to saving and file naming, the
            # gidPdfFile will be copied but never renamed. It must remain intact.

            # If the user wants to save this file or use a custom name, do that now
            if save and not override :
                saveFileName = self.pid + '_' + gid
                if cidListSubFileName :
                    saveFileName = saveFileName + '_' + cidListSubFileName
                if pgRange :
                    saveFileName = saveFileName + '_pg(' + pgRange + ')'
                # Add date stamp
                saveFileName = saveFileName + '_' + self.tools.ymd()
                # Add render file extention
                saveFileName = saveFileName + '.pdf'
                # Save this to the Deliverable folder (Make sure there is one)
                if not os.path.isdir(self.local.projDeliverableFolder) :
                    os.makedirs(self.local.projDeliverableFolder)
                # Final file name and path
                saveFile = os.path.join(self.local.projDeliverableFolder, saveFileName)
                # Copy, no news is good news
                if shutil.copy(self.local.gidPdfFile, saveFile) :
                    self.log.writeToLog(self.errorCodes['5730'], [saveFileName])
                else :
                    self.log.writeToLog(self.errorCodes['5720'], [saveFileName])            

            # If given, the override file name becomes the file name 
            if override :
                saveFile = override
                # With shutil.copy(), no news is good news
                if shutil.copy(self.local.gidPdfFile, saveFile) :
                    self.log.writeToLog(self.errorCodes['5730'], [saveFileName])
                else :
                    self.log.writeToLog(self.errorCodes['5720'], [saveFileName])

            # Once we know the file is successfully generated, add a background if defined
            if self.useBackground :
                if saveFile :
                    viewFile = self.pg_back.addBackground(saveFile)
                else :
                    viewFile = self.pg_back.addBackground(self.local.gidPdfFile)
                    
            # Add a timestamp and doc info if requested in addition to background
            if self.useDocInfo :
                if saveFile :
                    if os.path.isfile(viewFile) :
                        viewFile = self.pg_back.addDocInfo(viewFile)
                    else :
                        viewFile = self.pg_back.addDocInfo(saveFile)
                else :
                    if os.path.isfile(viewFile) :
                        viewFile = self.pg_back.addDocInfo(viewFile)
                    else :
                        viewFile = self.pg_back.addDocInfo(self.local.gidPdfFile)

            # To avoid confusion with file names, if this is a saved file,
            # and it has a background, we need to remove the original, non-
            # background file (remembering originals are kept in the group
            # Component folder), then rename the -view version to whatever
            # the saved name should be
            if save or override :
                if os.path.isfile(saveFile) and os.path.isfile(viewFile) :
                    # First remove
                    os.remove(saveFile)
                    # Next rename
                    os.rename(viewFile, saveFile)

            ##### Viewing #####
            # First get the right file name to view
            if saveFile :
                # If there was a saveFile, that will be the viewFile
                viewFile = saveFile
                self.log.writeToLog(self.errorCodes['5020'], [saveFile])
            else :
                # The view file in this case is just temporary
                if not os.path.isfile(viewFile) :
                    viewFile = self.local.gidPdfFile.replace(gid + '.pdf', gid + '-view.pdf')
                    shutil.copy(self.local.gidPdfFile, viewFile)
                    self.log.writeToLog(self.errorCodes['5020'], [self.tools.fName(viewFile)])

            if os.path.isfile(viewFile) :
                if self.pdfViewerCmd :
                    # Add the file to the viewer command
                    self.pdfViewerCmd.append(viewFile)
                    # Run the XeTeX and collect the return code for analysis
                    try :
                        subprocess.Popen(self.pdfViewerCmd)
                        return True
                    except Exception as e :
                        # If we don't succeed, we should probably quite here
                        self.log.writeToLog(self.errorCodes['5010'], [str(e)])
                else :
                    self.log.writeToLog(self.errorCodes['5210'])
            else :
                self.log.writeToLog(self.errorCodes['5200'], [self.tools.fName(viewFile)])
                
            # If we made it this far, return True
            return True
Beispiel #17
0
class Macro (object) :

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

        self.pid                            = pid
        self.gid                            = gid
        self.cType                          = cType
        self.user                           = UserConfig()
        self.userConfig                     = self.user.userConfig
        self.projHome                       = os.path.join(os.path.expanduser(os.environ['RAPUMA_PROJECTS']), self.pid)
        self.local                          = ProjLocal(pid, gid, cType)


#        import pdb; pdb.set_trace()


        self.proj_config                    = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig                  = self.proj_config.projectConfig
        self.layoutConfig                   = self.proj_config.layoutConfig
        self.tools                          = Tools()
        self.log                            = ProjLog(pid)
        # Create config placeholders
        self.layoutConfig                   = None
        self.illustrationConfig             = None
        self.macroConfig                    = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile)

        # Log messages for this module
        self.errorCodes     = {
            '3010' : ['ERR', 'No macro package is registered for the [<<1>>] component type.'],
            '3020' : ['ERR', 'Cannot update! No macro package is registered for the [<<1>>] component type.'],
            '3050' : ['ERR', 'Macro package file not found: [<<1>>]'],
            '3100' : ['ERR', 'Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.'],
            '3200' : ['ERR', 'Failed to install macro package: [<<1>>]'],
            '3300' : ['MSG', 'Installed macro package: [<<1>>], Reinitialized [<<2>>]'],
            '3310' : ['ERR', 'Failed to copy [<<1>>] to folder [<<2>>].'],
            '3400' : ['MSG', 'Removed macro package configuration settings for: [<<1>>] from the macro.conf file.'],
            '3500' : ['MSG', 'Removed macro package [<<1>>] folder and all files contained.'],
            '3600' : ['MSG', 'Updated component type [<<1>>] with macro package [<<2>>]'],
            '3650' : ['ERR', 'Failed to updated macro package [<<1>>]']
        }

###############################################################################
###################### Macro Package Handling Functions #######################
###############################################################################
######################## Error Code Block Series = 3000 #######################
###############################################################################

    def createMacroFiles (self, macPackId) :
        '''Create all the necessary macro file names with their assigned paths.'''

        self.projMacPackFolder = os.path.join(self.local.projMacPackFolder, macPackId)

        texFileIds = {'preStyTexExtFile':'preSty-ext.tex', 'macSettingsFile':'settings.tex', 
                        'extTexFile':'extension.tex', 'grpExtTexFile': self.gid + '-extension.tex', 
                        '':'', '':'', '':'', '':'', '':'', }
        styFileIds = {'glbExtStyFile':'extension.sty', 'grpExtStyFile': self.gid + '-extension.sty', 
                        '':'', '':'', '':'', '':'', '':''}


    def getMacPackIdFromFileName (self, fileName) :
        '''Return the macPack ID based on the file name'''

        # File name less ext is the ID
        parts = len(fileName.split('.'))
        return '.'.join(fileName.split('.')[:parts-1])
    
    
    def getMacPackIdFromSource (self, source) :
        '''Return the macPack ID based on the complete path and file name.'''

        # Get the file name from the path
        fileName = self.tools.fName(source)
        # Return the ID
        return self.getMacPackIdFromFileName(fileName)


    def addMacPack (self, source) :
        '''Add a macro package to the project. It will not work if
        the same package is already present. Remove must be used
        to get rid of the existing one first.'''

#        import pdb; pdb.set_trace()

        macPackId = self.getMacPackIdFromSource(source)
        confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.xml')
        if not os.path.isfile(source) :
            self.log.writeToLog(self.errorCodes['3050'], [source])

        # Do not add/install if there seems to be a macro package there already
        if self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] and os.path.exists(self.local.macroConfFile) :
            self.log.writeToLog(self.errorCodes['3100'], [macPackId])
            return False

        # Set the projectConf to the new/same package
        self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = macPackId
        self.tools.writeConfFile(self.projectConfig)

        # If we got this far, install the a fresh copy of the macPack
        self.installMacPackOnly(source)
        # Move the style files and custom TeX files out of the macPack
        self.moveMacStyles(macPackId)
        self.moveMacTex(macPackId)

        # Create a fresh macro.conf file if it dosn't exist
        if not os.path.isfile(self.local.macroConfFile) :
            self.macroConfig = self.tools.initNewConfig(self.local.macroConfFile, self.local.macroConfXmlFile)
        # Inject information from this particular macro package
        mInfo = self.tools.getXMLSettings(confXml)
        self.macroConfig['Macros'][macPackId] = mInfo.dict()

        # Save the settings now
        self.tools.writeConfFile(self.macroConfig)

        self.log.writeToLog(self.errorCodes['3300'], [macPackId, self.local.macroConfFileName])
        
        return True


    def moveMacStyles (self, macPackId) :
        '''Move the default macro package styles out of the freshly installed
        project macro package folder to the project Style folder.'''


#        import pdb; pdb.set_trace()

        # Collect the style files to copy
        for f in self.getMacStyExtFiles(macPackId) :
            source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f))
            target = os.path.join(self.local.projStyleFolder, f)
            self.tools.makedirs(self.local.projStyleFolder)
            # Do not overwrite existing files unless force is used
            if not os.path.exists(target) :
                shutil.copy(source, target)
                # Look for default and set to read-only
                defaultStyFile = os.path.join(self.local.projStyleFolder, macPackId + '.sty')
                if target == defaultStyFile :
                    self.tools.makeReadOnly(defaultStyFile)
            # Remove the source to avoid confusion
            if os.path.exists(target) :
                os.remove(source)
            else :
                self.log.writeToLog(self.errorCodes['3310'], [source,self.local.projStyleFolder])


    def getMacStyExtFiles (self, macPackId) :
        '''Return a list of macro package style extention files.'''

        sFiles = []
        macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId))
        for f in macPackFiles :
            if f.split('.')[1].lower() == 'sty' :
                sFiles.append(f)
        return sFiles


    def moveMacTex (self, macPackId) :
        '''Move the custom macro package TeX out of the freshly installed
        project macro package folder to the project TeX folder.'''

        # Collect the TeX extention files to copy
        for f in self.getMacTexExtFiles(macPackId) :
            source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f))
            target = os.path.join(self.local.projTexFolder, f)
            self.tools.makedirs(self.local.projTexFolder)
            # Do not overwrite existing files
            if not os.path.exists(target) :
                shutil.copy(source, target)
            # Remove the source to avoid confusion
            if os.path.exists(target) :
                os.remove(source)
            else :
                self.log.writeToLog(self.errorCodes['3310'], [source,self.local.projTexFolder])


    def getMacTexExtFiles (self, macPackId) :
        '''Return a list of macro package TeX extention files.'''

        tFiles = []
        macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId))
        for f in macPackFiles :
            if f.find('ext.tex') > 0 :
                tFiles.append(f)
        return tFiles


    def removeMacPack (self) :
        '''Remove the macro package from a component type'''

#        import pdb; pdb.set_trace()

        if self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] != '' :
            macPackId = self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage']
        else :
            self.log.writeToLog(self.errorCodes['3010'], [self.cType])
        # Aquire target to delete
        target = os.path.join(self.local.projMacroFolder, macPackId)

        # Remove the macPack settings from the config file if it is there
        try :
            del self.macroConfig['Macros'][macPackId]
            # Save the settings now
            self.tools.writeConfFile(self.macroConfig)
            self.log.writeToLog(self.errorCodes['3400'], [macPackId])
        except :
            pass

        # Now remove the macro folder (with all its contents)
        if os.path.exists(target) :
            shutil.rmtree(target)
            self.log.writeToLog(self.errorCodes['3500'], [macPackId])

        # Remove any style files associated with the macro
        styTarget = os.path.join(self.local.projStyleFolder, macPackId + '.sty')
        self.tools.makeExecutable(styTarget)
        self.tools.removeFile(styTarget)

        # Remove the reference for this macro package from the component type
        # that uses it. Normally that would probably be just be one of them.
        self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = ''
        self.tools.writeConfFile(self.projectConfig)


    def updateMacPack (self, source) :
        '''Update a macro package with the latest version but do not 
        touch the config file.'''


        # Do not update if no macro package is registered in the projectConfig
        if not self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] :
            self.log.writeToLog(self.errorCodes['3020'], [self.cType.capitalize()])
            return False

        macPackId = self.getMacPackIdFromSource(source)
        confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.xml')
        oldMacPackId = self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage']
        oldMacDir = os.path.join(self.local.projMacroFolder, oldMacPackId)
        newMacDir = os.path.join(self.local.projMacroFolder, macPackId)

        # Install the new macPack (but use the old settings)
        if self.installMacPackOnly(source) :
            # The current macro system must have a "master" style file.
            # This is a version of the ParaText style file. Because
            # an update might include an update to the style file,
            # we need to copy that from the macro folder to the Style
            # folder. We will do that now. The name of the file should
            # be the macPackId + ".sty", in theory.
            srcStyleFile = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.sty')
            oldStyleFile = os.path.join(self.local.projStyleFolder, oldMacPackId + '.sty')
            newStyleFile = os.path.join(self.local.projStyleFolder, macPackId + '.sty')
            # Unlock the old one so it can be deleted.
            self.tools.makeExecutable(oldStyleFile)
            self.tools.removeFile(oldStyleFile)
            # Now copy in the new one.
            shutil.copy(srcStyleFile, newStyleFile)
            # The style file should never be edited by the user, relock it
            self.tools.makeReadOnly(newStyleFile)
            # Update exsisting extention file names. We never want to loose 
            # any settings that the user may have added to the extention
            # files so we will rename the existing files to have the
            # new ID otherwise the system will not find them at render time.
            oldStyExtFile           = os.path.join(self.local.projStyleFolder, oldMacPackId + '_ext.sty')
            oldTexExtFile           = os.path.join(self.local.projTexFolder, oldMacPackId + '_ext.tex')
            oldTexPreStyExtFile     = os.path.join(self.local.projTexFolder, oldMacPackId + '_preSty-ext.tex')
            newStyExtFile           = os.path.join(self.local.projStyleFolder, macPackId + '_ext.sty')
            newTexExtFile           = os.path.join(self.local.projTexFolder, macPackId + '_ext.tex')
            newTexPreStyExtFile     = os.path.join(self.local.projTexFolder, macPackId + '_preSty-ext.tex')
            # By default, we protect any existing versions
            if os.path.exists(newStyExtFile) :
                os.remove(oldStyExtFile)
            else :
                self.tools.renameFile(oldStyExtFile, newStyExtFile)
            if os.path.exists(newTexExtFile) :
                os.remove(oldTexExtFile)
            else :
                self.tools.renameFile(oldTexExtFile, newTexExtFile)
            if os.path.exists(newTexPreStyExtFile) :
                os.remove(oldTexPreStyExtFile)
            else :
                self.tools.renameFile(oldTexPreStyExtFile, newTexPreStyExtFile)
            # Remove un-needed sty and tex files from the newMacDir to
            # avoid confusion. The ext files never are updated because
            # they could contain custom project code that we don't want
            # to loose in an update.
            for f in self.getMacStyExtFiles(macPackId) :
                source = os.path.join(newMacDir, f)
                if os.path.exists(source) :
                    os.remove(source)
            for f in self.getMacTexExtFiles(macPackId) :
                source = os.path.join(newMacDir, f)
                if os.path.exists(source) :
                    os.remove(source)
            # Remove the old macPack folder
            shutil.rmtree(oldMacDir)
            # Merge new settings into old section (change name to new ID)
            # When updating, we are assuming the new macro is in the same
            # family as the old one. As such, settings should be almost
            # identical, but in case new settings are being added, we will
            # merge them in now.
            oldConfSettings = self.macroConfig['Macros'][oldMacPackId]
            newConfSettings = self.tools.getXMLSettings(confXml)
            # Now merge
            newConfSettings.merge(oldConfSettings)
            # Inject the new section
#            self.tools.buildConfSection(self.macroConfig, macPackId)
            self.macroConfig['Macros'][macPackId] = newConfSettings.dict()
            # Delete the old section
            del self.macroConfig['Macros'][oldMacPackId]
            # Save the changes
            self.tools.writeConfFile(self.macroConfig)

            # Assuming everything went well we will change the macPackID on the cType
            self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = macPackId
            self.tools.writeConfFile(self.projectConfig)
            # Report success
            self.log.writeToLog(self.errorCodes['3600'], [self.cType.capitalize(), macPackId])
            return True
        # But if the install fails everything stays the same and we report
        else :
            self.log.writeToLog(self.errorCodes['3650'], [macPackId])
            return False


    def installMacPackOnly (self, source) :
        '''Install the new macro package but only that.'''

#        import pdb; pdb.set_trace()

        if self.tools.pkgExtract(source, self.local.projMacroFolder, self.local.macroConfXmlFile) :
            return True
        else :
            self.log.writeToLog(self.errorCodes['3200'], [self.tools.fName(source)])
            return False
Beispiel #18
0
class ProjCommander (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.projHome               = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid)
        self.local                  = ProjLocal(self.pid)
        self.proj_config            = Config(pid)
        self.proj_config.getProjectConfig()
        self.projectConfig          = self.proj_config.projectConfig
        self.projectMediaIDCode     = self.projectConfig['ProjectInfo']['projectMediaIDCode']

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


###############################################################################
########################## Command Creation Functions #########################
###############################################################################

    def removeScripts (self) :
        '''Remove any unnecessary group control scripts from the project.'''

        self.tools.dieNow('removeScripts() not implemented yet.')


    def updateScripts (self) :
        '''Update all the helper command scripts in a project.'''

        self.makeStaticScripts()
        self.makeGrpScripts()


    def makeGrpScripts (self) :
        '''Create scripts that process specific group components.'''

        if not os.path.isdir(self.local.projHelpScriptFolder) :
            os.mkdir(self.local.projHelpScriptFolder)

        # Output the scripts (If this is a new project we need to pass)
        if self.projectConfig.has_key('Groups') :
            for gid in self.projectConfig['Groups'].keys() :
                allScripts = self.getGrpScripInfo(gid)
                for key in allScripts.keys() :
                    fullFile = os.path.join(self.local.projHelpScriptFolder, key) + gid
                    with codecs.open(fullFile, "w", encoding='utf_8') as writeObject :
                        writeObject.write(self.makeScriptHeader(allScripts[key][0], allScripts[key][1]))
                        # Strip out extra spaces from command
                        cmd = re.sub(ur'\s+', ur' ', allScripts[key][1])
                        writeObject.write(cmd + '\n\n')

                    # Make the script executable
                    self.tools.makeExecutable(fullFile)
            
            self.tools.terminal('\nCompleted creating/recreating group helper scripts.\n')
        else :
            pass


    def makeStaticScripts (self) :
        '''Create helper scripts for a project to help with repetitive tasks.
        If any scripts are present with the same name they will be overwritten.
        Note: This is only for temporary use due to the lack of an interface at
        this time (20130306140636). It assumes the cType is usfm which, at some point
        may not be the case.'''

        if not os.path.isdir(self.local.projHelpScriptFolder) :
            os.mkdir(self.local.projHelpScriptFolder)

        # Output the scripts
        allScripts = self.getStaticScripInfo()
        for key in allScripts.keys() :
            fullFile = os.path.join(self.local.projHelpScriptFolder, key)
            with codecs.open(fullFile, "w", encoding='utf_8') as writeObject :
                writeObject.write(self.makeScriptHeader(allScripts[key][0], allScripts[key][1]))
                writeObject.write(allScripts[key][1] + '\n\n')

            # Make the script executable
            self.tools.makeExecutable(fullFile)

        self.tools.terminal('\nCompleted creating/recreating static helper scripts.\n')


    def makeScriptHeader (self, desc, cmd) :
        '''Make a helper script header.'''

        return '#!/bin/sh\n\n# Description: ' + desc + '\n\necho \necho Rapuma helper script: ' + desc + '\n\necho \necho command: ' + self.echoClean(cmd) + '\n\n'


    def echoClean (self, cmdStr) :
        '''Clean up a string for an echo statement in a shell script.'''

        clean = re.sub(ur'\;', ur'\\;', cmdStr)
        clean = re.sub(ur'\s+', ur' ', clean)

        return clean


    def getStaticScripInfo (self) :
        '''Create a dictionary of all the static auxillary script information used in
        most projects.'''

        pid                 = self.pid
        mid                 = self.projectMediaIDCode

        return {
                'addBible'      : ['Add Scripture components for a Bible group.',   'rapuma group '         + pid + ' BIBLE group add --source_path $1 '], 
                'addNT'         : ['Add Scripture components for an NT group.',     'rapuma group '         + pid + ' NT    group add --source_path $1 '], 
                'addOT'         : ['Add Scripture components for an OT group.',     'rapuma group '         + pid + ' OT    group add --source_path $1 '], 
                'archive'       : ['Archive this project',                          'rapuma project '       + pid + ' archive   save '], 
                'backup'        : ['Backup this project',                           'rapuma project '       + pid + ' backup    save '], 
                'cloudPull'     : ['Pull data for this project from the cloud',     'rapuma project '       + pid + ' cloud     restore '], 
                'cloudPush'     : ['Push data from this project to the cloud',      'rapuma project '       + pid + ' cloud     save $1 '], 
                'restore'       : ['Restore a backup.',                             'rapuma project '       + pid + ' backup    restore '], 
                'template'      : ['Create a template of the project.',             'rapuma project '       + pid + ' template  save --id $1 '], 
                'updateScripts' : ['Update the project scripts.',                   'rapuma project '       + pid + ' project update --update_type helper '], 
                'bind'          : ['Create the binding PDF file',                   'if [ "$1" ]; then CMD=" $1"; fi; if [ "$2" ]; then CMD=" $1 $2"; fi; rapuma project ' + pid + ' project bind $CMD '], 
                'placeholdOff'  : ['Turn off illustration placeholders.',           'rapuma settings '      + pid + ' ' + mid + '_layout Illustrations useFigurePlaceHolders False '], 
                'placeholdOn'   : ['Turn on illustration placeholders.',            'rapuma settings '      + pid + ' ' + mid + '_layout Illustrations useFigurePlaceHolders True '] 
            }


    def getGrpScripInfo (self, gid) :
        '''Create a dictionary of the auxillary group script information used in
        most projects.'''

#        import pdb; pdb.set_trace()

        # Set the vars for this function
        pid                 = self.pid
        cType               = self.projectConfig['Groups'][gid]['cType']
        Ctype               = cType.capitalize()
        renderer            = self.projectConfig['CompTypes'][Ctype]['renderer']
        self.proj_macro     = Macro(self.pid, gid)
        macroConfig         = self.proj_macro.macroConfig
        font                = ''
        if macroConfig and macroConfig['FontSettings'].has_key('primaryFont') :
            font            = macroConfig['FontSettings']['primaryFont']
        macro               = self.projectConfig['CompTypes'][Ctype]['macroPackage']
        mid                 = self.projectMediaIDCode
        # Return a dictionary of all the commands we generate
        return {
                'compare'       : ['Compare component working text with backup.',   'if [ "$1" ]; then CMD="--cid_list $1"; fi; rapuma group ' + pid + ' ' + gid + ' group compare --compare_type backup $CMD '], 
                'render'        : ['Render ' + gid + ' group PDF file.',            'if [ "$1" ]; then CMD="--cid_list $1"; fi; if [ "$2" ]; then CMD="--cid_list $1 $2"; fi; if [ "$3" ]; then CMD="--cid_list $1 $2 $3"; fi; rapuma group ' + pid + ' ' + gid + ' group render $CMD '], 
                'update'        : ['Update the ' + gid + ' group from its source.', 'if [ "$2" ]; then CMD="--cid_list $2"; fi; rapuma group ' + pid + ' ' + gid + ' group update --source_path $1 $CMD '], 
                'background'    : ['Re/Create the project background.',             'rapuma project '   + pid + ' project update --update_type background '], 
                'transparency'  : ['Re/Create the project diagnostic layer.',       'rapuma project '   + pid + ' project update --update_type diagnostic '], 
                'addFont'       : ['Add a font to the ' + gid + ' group.',          'rapuma package '   + pid + ' ' + gid + ' $1 font add -f '],
                'removeFont'    : ['Remove a font from the ' + gid + ' group.',     'rapuma package '   + pid + ' ' + gid + ' $1 font remove -f '],
                'primaryFont'   : ['Make font primary for the ' + gid + ' group.',  'rapuma package '   + pid + ' ' + gid + ' $1 font primary -f '],
                'updateFont'    : ['Update the ' + gid + ' font.',                  'rapuma package '   + pid + ' ' + gid + ' \"' + font + '\" font update '], 
                'updateMacro'   : ['Update the ' + gid + ' macro package.',         'rapuma package '   + pid + ' ' + gid + ' \"' + macro + '\" macro update '] 
            }
Beispiel #19
0
class Usfm (Group) :
    '''This class contains information about a type of component 
    used in a type of project.'''

    # Shared values
    xmlConfFile     = 'usfm.xml'

    def __init__(self, project, cfg) :
        super(Usfm, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Set values for this manager
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = 'usfm'
        self.Ctype                  = self.cType.capitalize()
        self.project                = project
        self.local                  = project.local
        self.tools                  = Tools()
        self.proj_font              = ProjFont(self.pid)
        self.proj_illustration      = ProjIllustration(self.pid, self.gid)
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getAdjustmentConfig()
        self.projectConfig          = self.proj_config.projectConfig
        self.adjustmentConfig       = self.proj_config.adjustmentConfig
        self.log                    = project.log
        self.cfg                    = cfg
        self.mType                  = project.projectMediaIDCode
        self.renderer               = project.projectConfig['CompTypes'][self.Ctype]['renderer']
        self.sourceEditor           = project.projectConfig['CompTypes'][self.Ctype]['sourceEditor']
        self.macPackId              = project.projectConfig['CompTypes'][self.Ctype]['macroPackage']
        # Get the comp settings
        self.compSettings           = project.projectConfig['CompTypes'][self.Ctype]
        # Build a tuple of managers this component type needs to use
        self.usfmManagers = ('text', self.renderer)

        # Init the general managers
        for self.mType in self.usfmManagers :
            self.project.createManager(self.mType)

        # Create the internal ref names we use in this module
        self.text                   = self.project.managers[self.cType + '_Text']
        # File names

        # Folder paths
        self.projScriptFolder       = self.local.projScriptFolder
        self.projComponentFolder    = self.local.projComponentFolder
        self.gidFolder              = os.path.join(self.projComponentFolder, self.gid)
        # File names with folder paths
        self.rapumaXmlCompConfig    = os.path.join(self.project.local.rapumaConfigFolder, self.xmlConfFile)



        # Get persistant values from the config if there are any
        newSectionSettings = self.tools.getPersistantSettings(self.projectConfig['CompTypes'][self.Ctype], self.rapumaXmlCompConfig)
        if newSectionSettings != self.projectConfig['CompTypes'][self.Ctype] :
            self.projectConfig['CompTypes'][self.Ctype] = newSectionSettings
        # Set them here
        for k, v in self.compSettings.iteritems() :
            setattr(self, k, v)

        # Module Error Codes
        self.errorCodes     = {

            #'USFM-000' : ['MSG', 'Messages for the USFM module.'],
            #'USFM-005' : ['MSG', 'Unassigned error message ID.'],
            #'USFM-010' : ['ERR', 'Could not process character pair. This error was found: [<<1>>]. Process could not complete. - usfm.pt_tools.getNWFChars()'],
            #'USFM-020' : ['ERR', 'Improper character pair found: [<<1>>].  Process could not complete. - usfm.pt_tools.getNWFChars()'],
            #'USFM-025' : ['WRN', 'No non-word-forming characters were found in the PT settings file. - usfm.pt_tools.getNWFChars()'],
            #'USFM-040' : ['ERR', 'Hyphenation source file not found: [<<1>>]. Process halted!'],
            #'USFM-080' : ['LOG', 'Normalizing Unicode text to the [<<1>>] form.'],
            #'USFM-090' : ['ERR', 'USFM file: [<<1>>] did NOT pass the validation test. Because of an encoding conversion, the terminal output is from the file [<<2>>]. Please only edit [<<1>>].'],
            #'USFM-095' : ['WRN', 'Validation for USFM file: [<<1>>] was turned off.'],
            #'USFM-100' : ['MSG', 'Source file editor [<<1>>] is not recognized by this system. Please double check the name used for the source text editor setting.'],
            #'USFM-110' : ['ERR', 'Source file name could not be built because the Name Form ID for [<<1>>] is missing or incorrect. Double check to see which editor created the source text.'],
            #'USFM-120' : ['ERR', 'Source file: [<<1>>] not found! Cannot copy to project. Process halting now.'],
            #'USFM-130' : ['ERR', 'Failed to complete preprocessing on component [<<1>>]'],
            #'USFM-140' : ['MSG', 'Completed installation on [<<1>>] component working text.'],
            #'USFM-150' : ['ERR', 'Unable to copy [<<1>>] to [<<2>>] - error in text.'],

            '0010' : ['LOG', 'Created the [<<1>>] master adjustment file.'],
            '0220' : ['ERR', 'Cannot find: [<<1>>] working file, unable to complete preprocessing for rendering.'],
            '0230' : ['LOG', 'Created the [<<1>>] component adjustment file.'],
            '0240' : ['LOG', 'Could not find adjustments section for [<<1>>], created place holder setting.'],
            '0245' : ['LOG', 'Could not find adjustments for [<<1>>]. No ajustment file has been output.'],
            '0255' : ['LOG', 'Illustrations not being used. The piclist file has been removed from the [<<1>>] illustrations folder.'],
            '0260' : ['LOG', 'Piclist file for [<<1>>] has been created.'],
            '0265' : ['ERR', 'Failed to create piclist file for [<<1>>]!'],
            '0300' : ['ERR', 'One or more illustration files are missing from the project. Please import these files before continuing.']
        }


###############################################################################
############################ Functions Begin Here #############################
###############################################################################
######################## Error Code Block Series = 0200 #######################
###############################################################################

    def makeFileName(self, cid) :
        '''From what we know, return the full file name.'''

        # FIXME: We default this to "base" but for a diglot implementation
        # this is not going to work because we need to have a second
        # file name. Cross that bridge...

        return cid + '_base'


    def makeFileNameWithExt(self, cid) :
        '''From what we know, return the full file name.'''

        return self.makeFileName(cid) + '.' + self.cType


    def getCidPath (self, cid) :
        '''Return the full path of the cName working text file. This assumes
        the cid is valid.'''

        return os.path.join(self.local.projComponentFolder, cid, self.makeFileNameWithExt(cid))


    def getCidAdjPath (self, cid) :
        '''Return the full path of the cName working text adjustments file. 
        This assumes the cName is valid. Note that all macro packages that have
        a manual adjustment feature must use this naming scheme. The name syntax
        comes from the "mother" macro package which is ptx2pdf.'''

        return os.path.join(self.local.projComponentFolder, cid, self.makeFileNameWithExt(cid) + '.adj')


    def render(self, gid, cidList, pages, override, save) :
        '''Does USFM specific rendering of a USFM component'''

#        import pdb; pdb.set_trace()

        # If the whole group is being rendered, we need to preprocess it
        cids = []
        if not cidList :
            cids = self.projectConfig['Groups'][gid]['cidList']
        else :
            cids = cidList

        # Preprocess all subcomponents (one or more)
        # Stop if it breaks at any point
        for cid in cids :
            if not self.preProcessGroup(gid, [cid]) :
                return False

        # With everything in place we can render the component.
        # Note: We pass the cidList straight through
        self.project.managers['usfm_' + self.renderer.capitalize()].run(gid, cidList, pages, override, save)

        return True


    def preProcessGroup (self, gid, cidList) :
        '''This will prepare a component group for rendering by checking for
        and/or creating any dependents it needs to render properly.'''

#        import pdb; pdb.set_trace()

        # Get some relevant settings
        # FIXME: Note page border has not really been implemented yet.
        # It is different from backgound management
        useIllustrations        = self.tools.str2bool(self.projectConfig['Groups'][gid]['useIllustrations'])
        useManualAdjustments    = self.tools.str2bool(self.projectConfig['Groups'][gid]['useManualAdjustments'])

        # See if the working text is present for each subcomponent in the
        # component and try to install it if it is not
        for cid in cidList :
            cType = self.cfg['cType']
            cidUsfm = self.getCidPath(cid)
            # Test for source here and die if it isn't there
            if not os.path.isfile(cidUsfm) :
                self.log.writeToLog(self.errorCodes['0220'], [cidUsfm], 'usfm.preProcessGroup():0220')
            # Add/manage the dependent files for this cid

# FIXME: Some changes may be needed here to guide creation of adjustment files
            # Component adjustment file
            cidAdjFile = self.getCidAdjPath(cid)
            if useManualAdjustments :
                self.createCompAdjustmentFile(cid)
            else :
                # If no adjustments, remove any exsiting file
                if os.path.isfile(cidAdjFile) :
                    os.remove(cidAdjFile)
            # Component piclist file
            cidPiclistFile = self.proj_illustration.getCidPiclistFile(cid)
            if useIllustrations :
                if self.proj_illustration.hasIllustrations(cid) :
                    # Check for missing illustrations (die here if not found)
                    if self.proj_illustration.missingIllustrations(cid) :
                        self.log.writeToLog(self.errorCodes['0300'])
                    # Create piclist file if not there or if the config has changed
                    if not os.path.isfile(cidPiclistFile) or self.tools.isOlder(cidPiclistFile, self.local.illustrationConfFile) :
                        # Now make a fresh version of the piclist file
                        if self.proj_illustration.createPiclistFile(cid) :
                            self.log.writeToLog(self.errorCodes['0260'], [cid])
                        else :
                            self.log.writeToLog(self.errorCodes['0265'], [cid])
                    else :
                        for f in [self.local.layoutConfFile, self.local.illustrationConfFile] :
                            if self.tools.isOlder(cidPiclistFile, f) or not os.path.isfile(cidPiclistFile) :
                                # Remake the piclist file
                                if self.proj_illustration.createPiclistFile(cid) :
                                    self.log.writeToLog(self.errorCodes['0260'], [cid])
                                else :
                                    self.log.writeToLog(self.errorCodes['0265'], [cid])
                else :
                    # Does not seem to be any illustrations for this cid
                    # clean out any piclist file that might be there
                    if os.path.isfile(cidPiclistFile) :
                        os.remove(cidPiclistFile)
            else :
                # If we are not using illustrations then any existing piclist file will be removed
                if os.path.isfile(cidPiclistFile) :
                    os.remove(cidPiclistFile)
                    self.log.writeToLog(self.errorCodes['0255'], [cid])

        # Any more stuff to run?

        return True

# FIXME: Moved this to xetex.py as that was the only place it was called from
    #def checkStartPageNumber (self) :
        #'''Adjust page number for the current group. The current logic is
        #if there is no number in the startPageNumber setting, we can put
        #one in there as a suggestion. If there is already one there, the
        #user will be responsible for seeing that it is correct.'''

##        import pdb; pdb.set_trace()

        #try :
            ## Simply try to return anything that is in the field
            #cStrPgNo = self.projectConfig['Groups'][self.gid]['startPageNumber']
            #if cStrPgNo != '' :
                #return cStrPgNo
        #except :
            ## If nothing is there, we'll make a suggestion
            #pGrp = str(self.projectConfig['Groups'][self.gid]['precedingGroup'])
            #if pGrp == 'None' :
                #self.projectConfig['Groups'][self.gid]['startPageNumber'] = 1
                #self.tools.writeConfFile(self.projectConfig)
                #return '1'
            #else :
                ## Calculate the suggested number based on the preceeding group
                #try :
                    #cStrPgNo    = str(self.projectConfig['Groups'][self.gid]['startPageNumber'])
                #except :
                    #cStrPgNo    = 1
                    #self.projectConfig['Groups'][self.gid]['startPageNumber'] = 1
                #try :
                    #pGrpPgs     = int(self.projectConfig['Groups'][pGrp]['totalPages'])
                    #pGrpStrPgNo = int(self.projectConfig['Groups'][pGrp]['startPageNumber'])
                #except :
                    ## FIXME: Maybe this could go out and find out exactly how many pages were in the preceeding group
                    #pGrpPgs     = 1
                    #pGrpStrPgNo = 1
                    #self.projectConfig['Groups'][pGrp]['totalPages'] = 1
                    #self.projectConfig['Groups'][pGrp]['startPageNumber'] = 1
                ## Whether this is right or wrong set it the way it is
                #self.projectConfig['Groups'][self.gid]['startPageNumber'] = (pGrpStrPgNo + pGrpPgs)
                #self.tools.writeConfFile(self.projectConfig)
                #return self.projectConfig['Groups'][pGrp]['startPageNumber']


    def createCompAdjustmentFile (self, cid) :
        '''Create an adjustment file for this cid. If entries exsist in
        the adjustment.conf file.'''

        description = 'Auto-generated text adjustments file for: ' + cid + '\n'

#        import pdb; pdb.set_trace()

        # Check for a master adj conf file
        if os.path.exists(self.local.adjustmentConfFile) :
            adjFile = self.getCidAdjPath(cid)
            # Clean up old file if there is one so we can start fresh
            if os.path.exists(adjFile) :
                os.remove(adjFile)
            # Nothing to do if no gid section is found
            if not self.adjustmentConfig.has_key(self.gid) :
                self.tools.buildConfSection(self.adjustmentConfig, self.gid)
            if not self.adjustmentConfig[self.gid].has_key(cid) :
                self.tools.buildConfSection(self.adjustmentConfig[self.gid], cid)
                self.adjustmentConfig[self.gid][cid]['%1.1'] = '1'
                self.tools.writeConfFile(self.adjustmentConfig)
                self.log.writeToLog(self.errorCodes['0240'], [cid])
                return False
            # Sort through commented adjustment lines ()
            if self.adjustmentConfig[self.gid].has_key(cid) :
                c = False
                for k in self.adjustmentConfig[self.gid][cid].keys() :
                    if not re.search(r'%|#', k) :
                        c = True
                if not c :
                    self.log.writeToLog(self.errorCodes['0245'], [cid])
                    return False
            # If we make it this far, create the new adjustment file
            with codecs.open(adjFile, "w", encoding='utf_8') as writeObject :
                writeObject.write(self.tools.makeFileHeader(adjFile, description, True))
                # Output like this: JAS 1.13 +1
                for k, v in self.adjustmentConfig[self.gid][cid].iteritems() :
                    if re.search(r'%|#', k) :
                        continue
                    adj = v
                    if int(v) > 0 : 
                        adj = '+' + str(v)
                    writeObject.write(cid.upper() + ' ' + k + ' ' + adj + '\n')

                self.log.writeToLog(self.errorCodes['0230'], [self.tools.fName(adjFile)])

            return True


    def createProjAdjustmentConfFile (self) :
        '''Create a project master component adjustment file that group component
        ajustment files will be created automatically from. This will run every 
        time preprocess is run but after the first time it will only add a sections
        for new groups or components.'''

        if not os.path.exists(self.adjustmentConfFile) :
            self.adjustmentConfig = ConfigObj(self.adjustmentConfFile, encoding='utf-8')
            self.adjustmentConfig.filename = self.adjustmentConfFile
            self.updateCompAdjustmentConf()
        return True


    def updateCompAdjustmentConf (self) :
        '''Update an adjustmentConfig based on changes in the projectConfig.'''

        for gid in self.projectConfig['Groups'].keys() :
            if gid not in self.adjustmentConfig.keys() :
                self.tools.buildConfSection(self.adjustmentConfig, gid)
            for comp in self.projectConfig['Groups'][gid]['cidList'] :
                if not self.adjustmentConfig[gid].has_key(comp) :
                    self.tools.buildConfSection(self.adjustmentConfig[gid], comp)
                self.adjustmentConfig[gid][comp]['%1.1'] = '1'
        self.tools.writeConfFile(self.adjustmentConfig)
        return True


###############################################################################
######################## USFM Component Text Functions ########################
###############################################################################
######################## Error Code Block Series = 0400 #######################
###############################################################################


    def getComponentType (self, gid) :
        '''Return the cType for a component.'''

#        import pdb; pdb.set_trace()

        try :
            cType = self.projectConfig['Groups'][gid]['cType']
        except Exception as e :
            # If we don't succeed, we should probably quite here
            self.log.writeToLog('COMP-200', ['Key not found ' + str(e)])
            self.tools.dieNow()

        return cType


    def isCompleteComponent (self, gid, cid) :
        '''A two-part test to see if a component has a config entry and a file.'''

        if self.hasCidFile(gid, cid) :
            return True


    def hasUsfmCidInfo (self, cid) :
        '''Return True if this cid is in the PT USFM cid info dictionary.'''

        if cid in self.usfmCidInfo().keys() :
            return True


    def hasCidFile (self, gid, cid) :
        '''Return True or False depending on if a working file exists 
        for a given cName.'''

        cType = self.projectConfig['Groups'][gid]['cType']
        return os.path.isfile(os.path.join(self.local.projComponentFolder, cid, cid + '.' + cType))


    def usfmCidInfo (self) :
        '''Return a dictionary of all valid information about USFMs used in PT. Note
        that a couple special non-standard IDs have been added at the top of the list.'''

    #            ID     Comp Name                               Comp ID                         PT ID  Chps
        return {
                '_z_' : ['USFM InternalCaller',                 'usfm_internal_caller',         '00',   0], 
                'gen' : ['Genesis',                             'genesis',                      '01',  50], 
                'exo' : ['Exodus',                              'exodus',                       '02',  40], 
                'lev' : ['Leviticus',                           'leviticus',                    '03',  27], 
                'num' : ['Numbers',                             'numbers',                      '04',  36], 
                'deu' : ['Deuteronomy',                         'deuteronomy',                  '05',  34], 
                'jos' : ['Joshua',                              'joshua',                       '06',  24], 
                'jdg' : ['Judges',                              'judges',                       '07',  21], 
                'rut' : ['Ruth',                                'ruth',                         '08',   4], 
                '1sa' : ['1 Samuel',                            '1_samuel',                     '09',  31], 
                '2sa' : ['2 Samuel',                            '2_samuel',                     '10',  24], 
                '1ki' : ['1 Kings',                             '1_kings',                      '11',  22], 
                '2ki' : ['2 Kings',                             '2_kings',                      '12',  25], 
                '1ch' : ['1 Chronicles',                        '1_chronicles',                 '13',  29], 
                '2ch' : ['2 Chronicles',                        '2_chronicles',                 '14',  36], 
                'ezr' : ['Ezra',                                'ezra',                         '15',  10], 
                'neh' : ['Nehemiah',                            'nehemiah',                     '16',  13], 
                'est' : ['Esther',                              'esther',                       '17',  10], 
                'job' : ['Job',                                 'job',                          '18',  42], 
                'psa' : ['Psalms',                              'psalms',                       '19', 150], 
                'pro' : ['Proverbs',                            'proverbs',                     '20',  31], 
                'ecc' : ['Ecclesiastes',                        'ecclesiastes',                 '21',  12], 
                'sng' : ['Song of Songs',                       'song_of_songs',                '22',   8], 
                'isa' : ['Isaiah',                              'isaiah',                       '23',  66], 
                'jer' : ['Jeremiah',                            'jeremiah',                     '24',  52], 
                'lam' : ['Lamentations',                        'lamentations',                 '25',   5], 
                'ezk' : ['Ezekiel',                             'ezekiel',                      '26',  48], 
                'dan' : ['Daniel',                              'daniel',                       '27',  12], 
                'hos' : ['Hosea',                               'hosea',                        '28',  14], 
                'jol' : ['Joel',                                'joel',                         '29',   3], 
                'amo' : ['Amos',                                'amos',                         '30',   9], 
                'oba' : ['Obadiah',                             'obadiah',                      '31',   1], 
                'jon' : ['Jonah',                               'jonah',                        '32',   4], 
                'mic' : ['Micah',                               'micah',                        '33',   7], 
                'nam' : ['Nahum',                               'nahum',                        '34',   3], 
                'hab' : ['Habakkuk',                            'habakkuk',                     '35',   3], 
                'zep' : ['Zephaniah',                           'zephaniah',                    '36',   3], 
                'hag' : ['Haggai',                              'haggai',                       '37',   2], 
                'zec' : ['Zechariah',                           'zechariah',                    '38',  14], 
                'mal' : ['Malachi',                             'malachi',                      '39',   4],
                'mat' : ['Matthew',                             'matthew',                      '41',  28], 
                'mrk' : ['Mark',                                'mark',                         '42',  16], 
                'luk' : ['Luke',                                'luke',                         '43',  24], 
                'jhn' : ['John',                                'john',                         '44',  21], 
                'act' : ['Acts',                                'acts',                         '45',  28], 
                'rom' : ['Romans',                              'romans',                       '46',  16], 
                '1co' : ['1 Corinthians',                       '1_corinthians',                '47',  16], 
                '2co' : ['2 Corinthians',                       '2_corinthians',                '48',  13], 
                'gal' : ['Galatians',                           'galatians',                    '49',   6], 
                'eph' : ['Ephesians',                           'ephesians',                    '50',   6], 
                'php' : ['Philippians',                         'philippians',                  '51',   4], 
                'col' : ['Colossians',                          'colossians',                   '52',   4], 
                '1th' : ['1 Thessalonians',                     '1_thessalonians',              '53',   5], 
                '2th' : ['2 Thessalonians',                     '2_thessalonians',              '54',   3], 
                '1ti' : ['1 Timothy',                           '1_timothy',                    '55',   6], 
                '2ti' : ['2 Timothy',                           '2_timothy',                    '56',   4], 
                'tit' : ['Titus',                               'titus',                        '57',   3], 
                'phm' : ['Philemon',                            'philemon',                     '58',   1], 
                'heb' : ['Hebrews',                             'hebrews',                      '59',  13], 
                'jas' : ['James',                               'james',                        '60',   5], 
                '1pe' : ['1 Peter',                             '1_peter',                      '61',   5], 
                '2pe' : ['2 Peter',                             '2_peter',                      '62',   3], 
                '1jn' : ['1 John',                              '1_john',                       '63',   5], 
                '2jn' : ['2 John',                              '2_john',                       '64',   1], 
                '3jn' : ['3 John',                              '3_john',                       '65',   1], 
                'jud' : ['Jude',                                'jude',                         '66',   1], 
                'rev' : ['Revelation',                          'revelation',                   '67',  22], 
                'tob' : ['Tobit',                               'tobit',                        '68', '?'], 
                'jdt' : ['Judith',                              'judith',                       '69', '?'], 
                'esg' : ['Esther',                              'esther',                       '70', '?'], 
                'wis' : ['Wisdom of Solomon',                   'wisdom_of_solomon',            '71', '?'], 
                'sir' : ['Sirach',                              'sirach',                       '72', '?'], 
                'bar' : ['Baruch',                              'baruch',                       '73', '?'], 
                'lje' : ['Letter of Jeremiah',                  'letter_of_jeremiah',           '74', '?'], 
                's3y' : ['Song of the Three Children',          'song_3_children',              '75', '?'], 
                'sus' : ['Susanna',                             'susanna',                      '76', '?'], 
                'bel' : ['Bel and the Dragon',                  'bel_dragon',                   '77', '?'], 
                '1ma' : ['1 Maccabees',                         '1_maccabees',                  '78', '?'], 
                '2ma' : ['2 Maccabees',                         '2_maccabees',                  '79', '?'], 
                '3ma' : ['3 Maccabees',                         '3_maccabees',                  '80', '?'], 
                '4ma' : ['4 Maccabees',                         '4_maccabees',                  '81', '?'], 
                '1es' : ['1 Esdras',                            '1_esdras',                     '82', '?'], 
                '2es' : ['2 Esdras',                            '2_esdras',                     '83', '?'], 
                'man' : ['Prayer of Manasses',                  'prayer_of_manasses',           '84', '?'], 
                'ps2' : ['Psalms 151',                          'psalms_151',                   '85', '?'], 
                'oda' : ['Odae',                                'odae',                         '86', '?'], 
                'pss' : ['Psalms of Solomon',                   'psalms_of_solomon',            '87', '?'], 
                'jsa' : ['Joshua A',                            'joshua_a',                     '88', '?'], 
                'jdb' : ['Joshua B',                            'joshua_b',                     '89', '?'], 
                'tbs' : ['Tobit S',                             'tobit_s',                      '90', '?'], 
                'sst' : ['Susannah (Theodotion)',               'susannah_t',                   '91', '?'], 
                'dnt' : ['Daniel (Theodotion)',                 'daniel_t',                     '92', '?'], 
                'blt' : ['Bel and the Dragon (Theodotion)',     'bel_dragon_t',                 '93', '?'], 
                'frt' : ['Front Matter',                        'front_matter',                 'A0',   0], 
                'int' : ['Introductions',                       'introductions',                'A7',   0], 
                'bak' : ['Back Matter',                         'back_matter',                  'A1',   0], 
                'cnc' : ['Concordance',                         'concordance',                  'A8',   0], 
                'glo' : ['Glossary',                            'glossary',                     'A9',   0], 
                'tdx' : ['Topical Index',                       'topical_index',                'B0',   0], 
                'ndx' : ['Names Index',                         'names_index',                  'B1',   0], 
                'xxa' : ['Extra A',                             'extra_a',                      '94',   0], 
                'xxb' : ['Extra B',                             'extra_b',                      '95',   0], 
                'xxc' : ['Extra C',                             'extra_c',                      '96',   0], 
                'xxd' : ['Extra D',                             'extra_d',                      '97',   0],
                'xxe' : ['Extra E',                             'extra_e',                      '98',   0], 
                'xxf' : ['Extra F',                             'extra_f',                      '99',   0], 
                'xxg' : ['Extra G',                             'extra_g',                      '100',  0], 
                'oth' : ['Other',                               'other',                        'A2',   0], 
                'eza' : ['Apocalypse of Ezra',                  'apocalypse_of_ezra',           'A4', '?'], 
                '5ez' : ['5 Ezra',                              '5_ezra_lp',                    'A5', '?'], 
                '6ez' : ['6 Ezra (Latin Epilogue)',             '6_ezra_lp',                    'A6', '?'], 
                'dag' : ['Daniel Greek',                        'daniel_greek',                 'B2', '?'], 
                'ps3' : ['Psalms 152-155',                      'psalms_152-155',               'B3', '?'], 
                '2ba' : ['2 Baruch (Apocalypse)',               '2_baruch_apocalypse',          'B4', '?'], 
                'lba' : ['Letter of Baruch',                    'letter_of_baruch',             'B5', '?'], 
                'jub' : ['Jubilees',                            'jubilees',                     'B6', '?'], 
                'eno' : ['Enoch',                               'enoch',                        'B7', '?'], 
                '1mq' : ['1 Meqabyan',                          '1_meqabyan',                   'B8', '?'], 
                '2mq' : ['2 Meqabyan',                          '2_meqabyan',                   'B9', '?'], 
                '3mq' : ['3 Meqabyan',                          '3_meqabyan',                   'C0', '?'], 
                'rep' : ['Reproof (Proverbs 25-31)',            'reproof_proverbs_25-31',       'C1', '?'], 
                '4ba' : ['4 Baruch (Rest of Baruch)',           '4_baruch',                     'C2', '?'], 
                'lao' : ['Laodiceans',                          'laodiceans',                   'C3', '?'] 

               }
Beispiel #20
0
    def __init__(self, pid, gid) :
        '''Instantiate this class.'''

        self.pid                    = pid
        self.gid                    = gid
        self.user                   = UserConfig()
        self.userConfig             = self.user.userConfig
        self.projHome               = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid)
        self.config                 = Config(self.pid)
        self.config.getProjectConfig()
        self.projectConfig          = self.config.projectConfig
        self.cType                  = self.projectConfig['Groups'][self.gid]['cType']
        self.Ctype                  = self.cType.capitalize()
        self.local                  = ProjLocal(pid, gid, self.cType)
        self.log                    = ProjLog(self.pid)
        self.tools                  = Tools()
        self.groups                 = {}
        self.components             = {}
        self.managers               = {}
        self.projectMediaIDCode     = self.projectConfig['ProjectInfo']['projectMediaIDCode']
        self.projectIDCode          = self.projectConfig['ProjectInfo']['projectIDCode']
        self.projectName            = self.projectConfig['ProjectInfo']['projectTitle']
        self.plid                   = self.projectConfig['ProjectInfo']['languageCode']
        self.psid                   = self.projectConfig['ProjectInfo']['scriptCode']
        
        # The gid cannot generally be set yet but we will make a placeholder
        # for it here and the functions below will set it. (I'm just say'n)

#        import pdb; pdb.set_trace()

        m = import_module('rapuma.manager.' + self.projectMediaIDCode)
        self.__class__ = getattr(m, self.projectMediaIDCode[0].upper() + self.projectMediaIDCode[1:])

        # Update the existing config file with the project type XML file
        # if needed
        newXmlDefaults = os.path.join(self.local.rapumaConfigFolder, self.projectMediaIDCode + '.xml')
        xmlConfig = self.tools.getXMLSettings(newXmlDefaults)
        newConf = ConfigObj(xmlConfig.dict(), encoding='utf-8').override(self.projectConfig)
        for s,v in self.projectConfig.items() :
            if s not in newConf :
                newConf[s] = v

        # Replace with new conf if new is different from old
        # Rem new conf doesn't have a filename, give it one
        if self.projectConfig != newConf :
            self.projectConfig = newConf
            self.projectConfig.filename = self.local.projectConfFile

        # Log messages for this module
        self.errorCodes     = {
            'PROJ-000' : ['MSG', 'Project module messages'],
            'PROJ-030' : ['MSG', 'Changed  [<<1>>][<<2>>][<<3>>] setting from \"<<4>>\" to \"<<5>>\".'],
            'PROJ-040' : ['ERR', 'Problem making setting change. Section [<<1>>] missing from configuration file.'],
            'PROJ-050' : ['ERR', 'Component [<<1>>] working text file was not found in the project configuration.'],
            'PROJ-060' : ['ERR', 'Component [<<1>>] was not found in the project configuration.'],
            'PROJ-070' : ['ERR', 'Source file not found: [<<1>>].'],
            'PROJ-080' : ['MSG', 'Successful copy of [<<1>>] to [<<2>>].'],
            'PROJ-090' : ['ERR', 'Target file [<<1>>] already exists. Use force (-f) to overwrite.'],

            '0205' : ['LOG', 'Created the [<<1>>] manager object.'],
            '0210' : ['LOG', 'Wrote out [<<1>>] settings to the project configuration file.'],
            '0211' : ['ERR', 'Failed to write out project [<<1>>] settings to the project configuration file.'],

            '0660' : ['ERR', 'Invalid component ID: [<<1>>].'],

        }
Beispiel #21
0
    def __init__(self, project, cfg) :
        super(Usfm, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Set values for this manager
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = 'usfm'
        self.Ctype                  = self.cType.capitalize()
        self.project                = project
        self.local                  = project.local
        self.tools                  = Tools()
        self.proj_font              = ProjFont(self.pid)
        self.proj_illustration      = ProjIllustration(self.pid, self.gid)
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getAdjustmentConfig()
        self.projectConfig          = self.proj_config.projectConfig
        self.adjustmentConfig       = self.proj_config.adjustmentConfig
        self.log                    = project.log
        self.cfg                    = cfg
        self.mType                  = project.projectMediaIDCode
        self.renderer               = project.projectConfig['CompTypes'][self.Ctype]['renderer']
        self.sourceEditor           = project.projectConfig['CompTypes'][self.Ctype]['sourceEditor']
        self.macPackId              = project.projectConfig['CompTypes'][self.Ctype]['macroPackage']
        # Get the comp settings
        self.compSettings           = project.projectConfig['CompTypes'][self.Ctype]
        # Build a tuple of managers this component type needs to use
        self.usfmManagers = ('text', self.renderer)

        # Init the general managers
        for self.mType in self.usfmManagers :
            self.project.createManager(self.mType)

        # Create the internal ref names we use in this module
        self.text                   = self.project.managers[self.cType + '_Text']
        # File names

        # Folder paths
        self.projScriptFolder       = self.local.projScriptFolder
        self.projComponentFolder    = self.local.projComponentFolder
        self.gidFolder              = os.path.join(self.projComponentFolder, self.gid)
        # File names with folder paths
        self.rapumaXmlCompConfig    = os.path.join(self.project.local.rapumaConfigFolder, self.xmlConfFile)



        # Get persistant values from the config if there are any
        newSectionSettings = self.tools.getPersistantSettings(self.projectConfig['CompTypes'][self.Ctype], self.rapumaXmlCompConfig)
        if newSectionSettings != self.projectConfig['CompTypes'][self.Ctype] :
            self.projectConfig['CompTypes'][self.Ctype] = newSectionSettings
        # Set them here
        for k, v in self.compSettings.iteritems() :
            setattr(self, k, v)

        # Module Error Codes
        self.errorCodes     = {

            #'USFM-000' : ['MSG', 'Messages for the USFM module.'],
            #'USFM-005' : ['MSG', 'Unassigned error message ID.'],
            #'USFM-010' : ['ERR', 'Could not process character pair. This error was found: [<<1>>]. Process could not complete. - usfm.pt_tools.getNWFChars()'],
            #'USFM-020' : ['ERR', 'Improper character pair found: [<<1>>].  Process could not complete. - usfm.pt_tools.getNWFChars()'],
            #'USFM-025' : ['WRN', 'No non-word-forming characters were found in the PT settings file. - usfm.pt_tools.getNWFChars()'],
            #'USFM-040' : ['ERR', 'Hyphenation source file not found: [<<1>>]. Process halted!'],
            #'USFM-080' : ['LOG', 'Normalizing Unicode text to the [<<1>>] form.'],
            #'USFM-090' : ['ERR', 'USFM file: [<<1>>] did NOT pass the validation test. Because of an encoding conversion, the terminal output is from the file [<<2>>]. Please only edit [<<1>>].'],
            #'USFM-095' : ['WRN', 'Validation for USFM file: [<<1>>] was turned off.'],
            #'USFM-100' : ['MSG', 'Source file editor [<<1>>] is not recognized by this system. Please double check the name used for the source text editor setting.'],
            #'USFM-110' : ['ERR', 'Source file name could not be built because the Name Form ID for [<<1>>] is missing or incorrect. Double check to see which editor created the source text.'],
            #'USFM-120' : ['ERR', 'Source file: [<<1>>] not found! Cannot copy to project. Process halting now.'],
            #'USFM-130' : ['ERR', 'Failed to complete preprocessing on component [<<1>>]'],
            #'USFM-140' : ['MSG', 'Completed installation on [<<1>>] component working text.'],
            #'USFM-150' : ['ERR', 'Unable to copy [<<1>>] to [<<2>>] - error in text.'],

            '0010' : ['LOG', 'Created the [<<1>>] master adjustment file.'],
            '0220' : ['ERR', 'Cannot find: [<<1>>] working file, unable to complete preprocessing for rendering.'],
            '0230' : ['LOG', 'Created the [<<1>>] component adjustment file.'],
            '0240' : ['LOG', 'Could not find adjustments section for [<<1>>], created place holder setting.'],
            '0245' : ['LOG', 'Could not find adjustments for [<<1>>]. No ajustment file has been output.'],
            '0255' : ['LOG', 'Illustrations not being used. The piclist file has been removed from the [<<1>>] illustrations folder.'],
            '0260' : ['LOG', 'Piclist file for [<<1>>] has been created.'],
            '0265' : ['ERR', 'Failed to create piclist file for [<<1>>]!'],
            '0300' : ['ERR', 'One or more illustration files are missing from the project. Please import these files before continuing.']
        }
Beispiel #22
0
class Pdf (Group) :
    '''This class contains information about a type of component 
    used in a type of project.'''

    # Shared values
    xmlConfFile     = 'pdf.xml'

    def __init__(self, project, cfg) :
        super(Pdf, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Set values for this manager
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = 'pdf'
        self.Ctype                  = self.cType.capitalize()
        self.project                = project
        self.local                  = project.local
        self.tools                  = Tools()
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getAdjustmentConfig()
        self.projectConfig          = self.proj_config.projectConfig
        self.log                    = project.log
        self.cfg                    = cfg
        self.mType                  = project.projectMediaIDCode
        self.renderer               = project.projectConfig['CompTypes'][self.Ctype]['renderer']
        self.compSource             = project.projectConfig['CompTypes'][self.Ctype]['compSource']

        # Get the comp settings
        self.compSettings           = project.projectConfig['CompTypes'][self.Ctype]

        # Build a tuple of managers this component type needs to use
        self.pdfManagers = ('fixed', self.renderer)

        # Init the general managers
        for self.mType in self.pdfManagers :
            self.project.createManager(self.mType)

        # Create the internal ref names we use in this module
        self.text                   = self.project.managers[self.cType + '_Fixed']
        # File names

        # Folder paths
        self.projScriptFolder       = self.local.projScriptFolder
        self.projComponentFolder    = self.local.projComponentFolder
        self.gidFolder              = os.path.join(self.projComponentFolder, self.gid)
        # File names with folder paths
        self.rapumaXmlCompConfig    = os.path.join(self.project.local.rapumaConfigFolder, self.xmlConfFile)

        # Get persistant values from the config if there are any
        newSectionSettings = self.tools.getPersistantSettings(self.projectConfig['CompTypes'][self.Ctype], self.rapumaXmlCompConfig)
        if newSectionSettings != self.projectConfig['CompTypes'][self.Ctype] :
            self.projectConfig['CompTypes'][self.Ctype] = newSectionSettings
        # Set them here
        for k, v in self.compSettings.iteritems() :
            setattr(self, k, v)

        # Module Error Codes
        self.errorCodes     = {

            '0000' : ['MSG', 'Messages for the PDF module.'],

        }


###############################################################################
############################ Functions Begin Here #############################
###############################################################################
######################## Error Code Block Series = 1000 #######################
###############################################################################



    def makeFileNameWithExt(self, cid) :
        '''From what we know, return the full file name.'''

        return self.makeFileName(cid) + '.' + self.cType


    def getCidPath (self, cid) :
        '''Return the full path of the cName working text file. This assumes
        the cid is valid.'''

        return os.path.join(self.local.projComponentFolder, cid, self.makeFileNameWithExt(cid))


    def render(self, gid, cidList, pages, override, save) :
        '''This will join together PDF files in a group. Though we call
        it render, it is more of a merging operation.'''

#        import pdb; pdb.set_trace()

        # If the whole group is being rendered, we need to preprocess it
        cids = []
        if not cidList :
            cids = self.projectConfig['Groups'][gid]['cidList']
        else :
            cids = cidList

        # Preprocess all subcomponents (one or more)
        # Stop if it breaks at any point
        for cid in cids :
            if not self.preProcessGroup(gid, [cid]) :
                return False

        # With everything in place we can render the component.
        # Note: We pass the cidList straight through
        self.project.managers['pdf_' + self.renderer.capitalize()].run(gid, cidList, pages, override, save)

        return True


    def preProcessGroup (self, gid, cidList) :
        '''This will prepare a PDF component group for a merging
        operation. Right now, I'm not sure what that means so this
        is just a place holder function incase something needs
        to be done at this point.'''

#        import pdb; pdb.set_trace()

        return True
Beispiel #23
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
Beispiel #24
0
class ProjDiagnose (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.layoutConfig               = self.proj_config.layoutConfig
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.log                        = ProjLog(pid)

        # to [px] is 72/25.4 
        self.mmToPx                     = 72 / 25.4
        # page width [px]
        self.paperPxWidth               = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageWidth']),1)
        # page height [px]
        self.paperPxHeight              = round(self.mmToPx * float(self.layoutConfig['PageLayout']['pageHeight']),1)

        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],
            '1310' : ['WRN', 'Failed to add diagnostic component: [<<1>>] with error: [<<2>>]']

        }


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


    def turnOnDiagnostic (self) :
        '''Change the layout config settings to turn on the diagnostic layer.'''

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


    def turnOffDiagnostic (self) :
        '''Change the layout config settings to turn off the diagnostic layer.'''

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


    def addTransparency (self, target, force = False) :
        '''Add a transparent layer to a rendered PDF file. This will
        add in diagnosing format issues. Using force will cause any
        existing layer file to be remade.'''

        # Do a quick check if the transparency needs to be remade
        # The transparency normally is not remade if one already exists.
        # If one is there, it can be remade in two ways, with a force
        # or a regenerate command.
        if force :
            self.createDiagnostic()
        elif self.tools.str2bool(self.layoutConfig['DocumentFeatures']['regenerateTransparency']) :
            self.createDiagnostic()
        else :
            # If there isn't one, make it
            if not os.path.exists(self.local.diagnosticFile) :
                self.createDiagnostic()

        # Create a special temp named file for the target
        tmpTarget = tempfile.NamedTemporaryFile().name
        # Copy the target to the tmpTarget
        shutil.copy(target, tmpTarget)
        # Overlay the transparency diagnostic file over the tmpTarget
        self.tools.mergePdfFilesPdftk(tmpTarget, self.local.diagnosticFile)

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

        # Copy the results back to the target (should be done now)
        shutil.copy(tmpTarget, viewFile)

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


    def createDiagnostic (self) :
        '''Create a diagnostic transparency (file) that will be
        superimposed over the page contents to help diagnose format
        issues. This will overwrite any existing transparency file and
        will add each recognoized diagnostic type found in the 
        diagnosticComponents config setting.'''

#        import pdb; pdb.set_trace()

        self.createBlankTransparency()

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

        return True


    def createBlankTransparency (self) :
        '''Create a blank background page according to the trim size
        specified.'''

        # Set the temp svg file name
        svgFile   = tempfile.NamedTemporaryFile().name

        # 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(self.paperPxWidth) + '''" height = "''' + str(self.paperPxHeight)+ '''">
                </svg>''')

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


###############################################################################
############################# Component Functions #############################
###############################################################################
######################## Error Code Block Series = 2000 #######################
###############################################################################

    def mergeLeading (self) :
        '''Create a diagnostic page component that has lines to indicate
        the text leading. This will be superimposed over the contents of
        the trim page, not in the background like a watermark, etc.'''

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

        # PAGE DIMENSIONS
        # The page dimensions extracted from layoutConfig are in [mm] and
        # must be converted to pixels [px], the conversion factor for [mm]
        # bodyFontSize [px]
        bodyFontSize = self.layoutConfig['TextElements']['bodyFontSize']
        bodyFontPxSize = round(float(bodyFontSize) * 72/72.27,3)
        # bodyTextLeading [px]
        bodyTextLeading = self.layoutConfig['TextElements']['bodyTextLeading']
        bodyTextPxLeading = round(float(bodyTextLeading) * 72/72.27,3)
        # top margin [px]
        topMargin = self.layoutConfig['PageLayout']['topMargin']
        topPxMargin = round(self.mmToPx * float(topMargin),1)
        # outside margin [px]
        outsideMargin = self.layoutConfig['PageLayout']['outsideMargin']
        outsidePxMargin = round(self.mmToPx * float(outsideMargin),1)
        #inside margin [px]
        insideMargin = self.layoutConfig['PageLayout']['insideMargin']
        insidePxMargin = round(self.mmToPx * float(outsideMargin),1)
        # bottom margin [px]
        bottomMargin = self.layoutConfig['PageLayout']['bottomMargin']
        bottomPxMargin = round(self.mmToPx * float(bottomMargin),1)
        # width of the body text
        textPxWidth = self.paperPxWidth - (outsidePxMargin + insidePxMargin)

        # Create the svg file
        with codecs.open(svgFile, 'wb') as fbackgr :            # open file for writing 
                # starting lines of SVG xml
            fbackgr.write( '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width = "''' + str(self.paperPxWidth)+ '''" height = "'''+str(self.paperPxHeight) + '''">
                \n    <!--RECTANGLE OF MARGINS-->\n''')
            fbackgr.write( '''<rect x = "''' + str(outsidePxMargin) + '''" y= "''' + str(topPxMargin) + '''" height = "''' + str(self.paperPxHeight - topPxMargin - bottomPxMargin) + '''" width = "''' + str(textPxWidth) + '''" style = "fill:none;fill-opacity:1;stroke:#ffc800;stroke-opacity:1;stroke-width:.2"/>
                \n    <!--START OF LINEGRID-->\n''')
            fbackgr.write( '''<path d= "m ''' + str(outsidePxMargin * 0.75-1) + "," + str(topPxMargin + bodyFontPxSize) + " " + str(textPxWidth + outsidePxMargin * 0.25)+ ''',0''')
                # filling the space between the top line and bottom margin, starting at distance
                # counter num = 0 up to the but not including the total of num x leading
                # equals the distance between top and bottom margin
            num = 0
            while (num < int(round(self.paperPxHeight - bottomPxMargin - topPxMargin)/bodyTextPxLeading)):
                    # lines are drawn in zigzag pattern: RTL when num is even and LTR when odd
                if num%2 == 0: 
                    fbackgr.write( ''' m 0, ''' + str(bodyTextPxLeading) + " -" + str(textPxWidth + outsidePxMargin * 0.25)+ ''',0''')
                else:
                    fbackgr.write( ''' m 0, ''' + str(bodyTextPxLeading) + " " + str(textPxWidth + outsidePxMargin * 0.25)+ ''',0''')
                num = num +1
                # draw all lines with following style 
            fbackgr.write( '''" style="stroke-width:0.2px;stroke:#ffc800;stroke-opacity:1"/>
                \n    <!--LINE NUMBERS-->\n''')
                # add line number '1' to top line just left of margin
            fbackgr.write( '''<text x="''' + str(outsidePxMargin * 0.75-2) + '''" y="''' + str(topPxMargin + bodyFontPxSize-3) + '''" style="font-family: Charis SIL;font-style:italic;font-size:7;fill:#760076"> 1''')
                # add line numbers to all lines down to bottom margin, starting with line number
                # counter linecount = 2, the distance counter runs from '0' till one short of 
                # the quotient (distance between top and bottom margin)/bodyTextPxLeading
            num = 0         # line counter
            linenumber = 2   # line number
            while (num < int(round(self.paperPxHeight - bottomPxMargin - topPxMargin)/bodyTextPxLeading)):
                fbackgr.write( '''<tspan x="''' + str(outsidePxMargin * 0.75-2) + '''" dy="''' + str(bodyTextPxLeading) + '''">''' + str(linenumber) + '''</tspan>''') 
                linenumber = linenumber +1  
                num = num +1
            fbackgr.write('''</text> 
                \n  <!--LINEGRID CAPTION-->
                <text  x="36" y="''' + str(self.paperPxHeight - bottomPxMargin+10) + '''" style="font-family: Charis SIL;font-style:italic;font-size:7;fill:#ffc800">page size: ''' + str(int(self.paperPxWidth/72*25.4+.5)) + ''' x ''' + str(int(self.paperPxHeight/72*25.4+.5)) + ''' mm ; font size: ''' + str(bodyFontSize) + ''' pt; leading: ''' + str(bodyTextLeading) + ''' pt</text>
                \n    <!--PURPLE LINES TOP AND BOTTOM MARGINS--> 
                <path d="M ''' + str(outsidePxMargin) + "," + str(topPxMargin) + " " + str(textPxWidth + outsidePxMargin) + "," + str(topPxMargin) + '''" style="fill:#ffffff;fill-opacity:1;stroke-width:0.4px;stroke:#760076;stroke-opacity:1"/>
                <path d="M ''' + str(outsidePxMargin) + "," + str(self.paperPxHeight - bottomPxMargin) + " " + str(textPxWidth + outsidePxMargin) + "," + str(self.paperPxHeight - bottomPxMargin) + '''" style="fill:#ffffff;fill-opacity:1;stroke-width:0.4px;stroke:#760076;stroke-opacity:1"/>
                </svg>''')
    
        # Convert the lines background component to PDF
        leadingPdf = self.tools.convertSvgToPdfRsvg(svgFile)

        # Merge leadingPdf with existing transparency
        results = self.tools.mergePdfFilesPdftk(self.local.diagnosticFile, leadingPdf)
        # Test and return if good
        if os.path.isfile(results) :
            return True
Beispiel #25
0
class ProjIllustration (object) :

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

        self.pid                        = pid
        self.gid                        = gid
        self.tools                      = Tools()
        self.local                      = ProjLocal(pid)
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.proj_config                = Config(pid, gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        self.proj_config.getIllustrationConfig()
        self.projectConfig              = self.proj_config.projectConfig
        self.layoutConfig               = self.proj_config.layoutConfig
        self.illustrationConfig         = self.proj_config.illustrationConfig
        self.cType                      = self.projectConfig['Groups'][gid]['cType']
        self.Ctype                      = self.cType.capitalize()
        self.log                        = ProjLog(pid)
        self.backgroundTypes            = ['watermark', 'lines']

        # Folder paths
        self.projComponentFolder        = self.local.projComponentFolder
        self.projIllustrationFolder     = self.local.projIllustrationFolder
        self.projConfFolder             = self.local.projConfFolder

        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],
            '0010' : ['ERR', 'Component type [<<1>>] not recognized!'],
            '0210' : ['WRN', 'Cannot copy [<<1>>] into the project Illustration folder. File already exists. Please use remove or update commands if the file needs to be replaced.'],
            '0220' : ['MSG', 'Copied [<<1>>] into the project Illustration folder.'],
            '0230' : ['ERR', 'Failed to Copy [<<1>>] into the project Illustration folder.'],
            '0240' : ['MSG', 'Illustration add operation complete!'],
            '0265' : ['LOG', 'Piclist file for [<<1>>] has been created.'],
            '0270' : ['WRN', 'Illustration file [<<1>>] not found in Illustration folder.'],
            '0280' : ['ERR', 'There was a problem trying to import the illustration file(s). A possible cause could be that there was no \\fig markers in the source imported into the project. I\'m just say\'n.'],

            '1010' : ['MSG', 'Removed illustration file [<<1>>] from project Illustration folder.'],
            '1020' : ['LOG', 'Request to removed illustration file [<<1>>] from project Illustration folder. File not found. Operation not complete'],
            '1030' : ['MSG', 'Illustration remove operation complete!'],

            '2010' : ['MSG', 'Updated illustration file [<<1>>] in project Illustration folder.'],
            '2020' : ['ERR', 'Update failed, file [<<1>>] not found in project Illustration folder. Use add illustration command to install the illustration.'],
            '2030' : ['ERR', 'Update failed on file [<<1>>]. Copy proceedure failed.'],
            '2040' : ['MSG', 'Illustration update operation complete!']

        }

###############################################################################
############################ Illustration Functions ###########################
###############################################################################
######################## Error Code Block Series = 0200 #######################
###############################################################################


    def addIllustrationFiles (self, path) :
        '''Import all the illustrations for a group that are found in
        the given path. This assumes illustrations are being used in
        one or more components in this group. Assumes valid path. Will
        fail if a copy doesn't succeed. If the file is already there,
        give a warning and do not copy.'''

#        import pdb; pdb.set_trace()

        try :
            for i in self.illustrationConfig[self.gid].keys() :
                cid = self.illustrationConfig[self.gid][i]['bid']
                fileName = self.illustrationConfig[self.gid][i]['fileName']
                target = os.path.join(self.projIllustrationFolder, fileName)
                # Check to see if the target exists
                if not os.path.isfile(target) :
                    source = os.path.join(path, fileName)
                    if os.path.isfile(source) :
                        # Make sure we have a target dir
                        if not os.path.isdir(self.projIllustrationFolder) :
                            os.makedirs(self.projIllustrationFolder)
                        # Copy in the source to the project
                        if not shutil.copy(source, target) :
                            self.log.writeToLog(self.errorCodes['0220'], [self.tools.fName(source)])
                        # Double check that it happened
                        if not os.path.isfile(target) :
                            self.log.writeToLog(self.errorCodes['0230'], [self.tools.fName(source)])
                else :
                    self.log.writeToLog(self.errorCodes['0210'], [self.tools.fName(target)])

            # If nothing above failed, we can return True now
            self.log.writeToLog(self.errorCodes['0240'])
            return True
        except :
            self.log.writeToLog(self.errorCodes['0280'])
            return False


    def missingIllustrations (self, bid) :
        '''Check for any missing illustration files for this component and
        report them. The assumption is that this component is supposed to
        have one or more illustrations in it so it should be used under
        hasIllustrations().'''
        
        missing = 0
        for i in self.illustrationConfig[self.gid].keys() :
            if self.illustrationConfig[self.gid][i]['bid'] == bid :
                fileName = self.illustrationConfig[self.gid][i]['fileName']
                target = os.path.join(self.local.projIllustrationFolder, fileName)
                if not os.path.exists(target) :
                    self.log.writeToLog(self.errorCodes['0270'], [fileName])
                    missing +=1

        # Returning True if something was missing
        if missing > 0 :
            return True


    def hasIllustrations (self, bid) :
        '''Return True if this component as any illustration associated with it.'''

        # Adjustment for Map cType
        if self.cType == 'map' :
            bid = 'map'

        # If we are missing the bid we fail (gracefully)
        try :
            for i in self.illustrationConfig[self.gid].keys() :
                if self.illustrationConfig[self.gid][i]['bid'] == bid :
                    return True
        except :
            return False


    def getCidPiclistFile (self, cid) :
        '''Return the full path of the cName working text illustration file. 
        This assumes the cName is valid.'''

        return os.path.join(self.local.projComponentFolder, cid, cid + '_base.' + self.cType + '.piclist')


    def createPiclistFile (self, cid) :
        '''Look in the cid for \fig data. Extract it from the cid and
        use it to create a piclist file for this specific cid. If
        there is no \fig data no piclist file will be made.'''

#        import pdb; pdb.set_trace()

        # Right now, this is the only place we need the macroConfig so
        # we will just make it local for now
        self.proj_config.getMacroConfig()
        macroConfig = self.proj_config.macroConfig

        cType = self.projectConfig['Groups'][self.gid]['cType']
        if cType == 'usfm' :
            piclistFile = self.getCidPiclistFile(cid)
        elif cType == 'map' :
            piclistFile = self.getCidPiclistFile(self.gid)
        else :
            self.log.writeToLog(self.errorCodes['0010'], [cType])

        macPackId = self.projectConfig['CompTypes'][cType.capitalize()]['macroPackage']
        cvSep = macroConfig['Macros'][macPackId]['Illustrations']['chapterVerseSeperator']
        thisRef = ''
        trueCid = cid
        obj = {}

        # Change cid for map cType
        # Note: The piclist IDs must be three characters long but need not be recognized USFM
        # file IDs. As such, we adjust the code to recognize 'map' as our ID for map rendering
        # operations. This seems to work for now.
#        if self.cType == 'map' :
#            cid = 'map'

        with codecs.open(piclistFile, "w", encoding='utf_8') as writeObject :
            writeObject.write('% This is an auto-generated usfmTex piclist file for this project.\n')
            writeObject.write('% Do not bother editing this file.\n\n')

            for i in self.illustrationConfig[self.gid].keys() :
                obj = self.illustrationConfig[self.gid][i]
                thisRef = ''
                # Filter out if needed with this
                if not self.tools.str2bool(obj['useThisIllustration']) :
                    continue
                # Is a caption going to be used on this illustration?
                caption = ''
                if self.illustrationConfig[self.gid][i]['bid'] == cid :
                    if self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useCaptions']) \
                        and self.tools.str2bool(self.illustrationConfig[self.gid][i]['useThisCaption']) :
                        if obj['caption'] :
                            caption = obj['caption']
                # Work out if we want a caption reference or not for this illustration
                if self.illustrationConfig[self.gid][i]['bid'] == cid :
                    if self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useCaptionReferences']) \
                        and self.tools.str2bool(self.illustrationConfig[self.gid][i]['useThisCaptionRef']) :
                        if obj['location'] :
                            thisRef = obj['location']
                        else :
                            thisRef = obj['chapter'] + cvSep + obj['verse']

                    # If we made it this far we can output the line
                    writeObject.write(obj['bid'] + ' ' + obj['chapter'] + '.' + obj['verse'] + \
                        ' |' + obj['fileName'] + '|' + obj['width'] + '|' + obj['position'] + \
                            '|' + obj['scale'] + '|' + obj['copyright'] + '|' + caption + '|' + thisRef + ' \n')
        # Report to log
        self.log.writeToLog(self.errorCodes['0265'], [trueCid])
        return True


###############################################################################
######################## Illustration Remove Functions ########################
###############################################################################
######################## Error Code Block Series = 1000 #######################
###############################################################################

    def removeIllustrationFiles (self) :
        '''Remove all the illustration files from the illustration folder for
        a group. This does not affect the illustration.conf file.'''

        for i in self.illustrationConfig[self.gid].keys() :
            cid = self.illustrationConfig[self.gid][i]['bid']
            fileName = self.illustrationConfig[self.gid][i]['fileName']
            target = os.path.join(self.projIllustrationFolder, fileName)
            # Check to see if the target exists, then delete, skip if not there
            if os.path.exists(target) :
                os.remove(target)
                self.log.writeToLog(self.errorCodes['1010'], [self.tools.fName(target)])
            else :
                self.log.writeToLog(self.errorCodes['1020'], [self.tools.fName(target)])

        # Report and return
        self.log.writeToLog(self.errorCodes['1030'])
        return True


###############################################################################
######################## Illustration Update Functions ########################
###############################################################################
######################## Error Code Block Series = 2000 #######################
###############################################################################

    def updateIllustrationFiles (self, path) :
        '''Update all the illustrations in a group from path as referenced in
        the illustration.conf file. Skip any referenced but not in path.'''

        for i in self.illustrationConfig[self.gid].keys() :
            cid = self.illustrationConfig[self.gid][i]['bid']
            fileName = self.illustrationConfig[self.gid][i]['fileName']
            target = os.path.join(self.projIllustrationFolder, fileName)
            source = os.path.join(path, fileName)
            # Check to see if the source exists, proceed if it does
            if os.path.isfile(source) :
                if os.path.isfile(target) :
                    # Copy the source to the project
                    if not shutil.copy(source, target) :
                        self.log.writeToLog(self.errorCodes['2010'], [self.tools.fName(source)])
                    else :
                        self.log.writeToLog(self.errorCodes['2030'], [self.tools.fName(source)])
                else :
                    self.log.writeToLog(self.errorCodes['2020'], [self.tools.fName(source)])

        # Report and return
        self.log.writeToLog(self.errorCodes['2040'])
        return True
Beispiel #26
0
class ProjProcess (object) :

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

        self.pid                    = pid
        self.tools                  = Tools()
        self.user                   = UserConfig()
        self.userConfig             = self.user.userConfig
        if projectConfig :
            self.projectConfig      = projectConfig
        else :
            self.proj_config        = Config(self.pid)
            self.proj_config.getProjectConfig()
            self.projectConfig      = self.proj_config.projectConfig
        self.local                  = ProjLocal(pid, gid, self.projectConfig)
        self.log                    = ProjLog(pid)

        # Log messages for this module
        self.errorCodes     = {
            '0000' : ['MSG', 'Placeholder message'],
            'XPRT-000' : ['MSG', 'Messages for export issues (probably only in project.py)'],
            'XPRT-005' : ['MSG', 'Unassigned error message ID.'],
            'XPRT-010' : ['ERR', 'Export file name could not be formed with available configuration information.'],
            'XPRT-020' : ['ERR', 'Unable to export: [<<1>>].'],
            'XPRT-030' : ['MSG', 'Files exported to [<<1>>].'],
            'XPRT-040' : ['MSG', 'Beginning export, please wait...'],
            'XPRT-050' : ['MSG', 'Unassigned error message ID.'],

            '1210' : ['MSG', 'Processes completed successfully on: [<<1>>] by [<<2>>]'],
            '1220' : ['ERR', 'Processes for [<<1>>] failed. Script [<<2>>] returned this error: [<<3>>]'],
            '1240' : ['MSG', 'Component group preprocessing [<<1>>] for group [<<2>>].'],
            '1260' : ['ERR', 'Installed the default component preprocessing script. Editing will be required for it to work with your project.'],
            '1265' : ['LOG', 'Component preprocessing script is already installed.'],

        }


###############################################################################
############################### Export Functions ##############################
###############################################################################
####################### Error Code Block Series = 0200 ########################
###############################################################################

# FIXME: This needs to be rewritten

    #def export (self, cType, cName, path = None, script = None, bundle = False, force = False) :
        #'''Facilitate the exporting of project text. It is assumed that the
        #text is clean and ready to go and if any extraneous publishing info
        #has been injected into the text, it will be removed by an appropreate
        #post-process that can be applied by this function. No validation
        #will be initiated by this function.'''
        
        ## FIXME - Todo: add post processing script feature

        ## Probably need to create the component object now
        #self.createComponent(cName)

        ## Figure out target path
        #if path :
            #path = self.tools.resolvePath(path)
        #else :
            #parentFolder = os.path.dirname(self.local.projHome)
            #path = os.path.join(parentFolder, 'Export')

        ## Make target folder if needed
        #if not os.path.isdir(path) :
            #os.makedirs(path)

        ## Start a list for one or more files we will process
        #fList = []

        ## Will need the stylesheet for copy
        #projSty = self.projectConfig['Managers'][cType + '_Style']['mainStyleFile']
        #projSty = os.path.join(self.local.projStyleFolder, projSty)
        ## Process as list of components

        #self.log.writeToLog('XPRT-040')
        #for cid in self.components[cName].getSubcomponentList(cName) :
            #cidCName = self.components[cName].getRapumaCName(cid)
            #ptName = PT_Tools(self).formPTName(cName, cid)
            ## Test, no name = no success
            #if not ptName :
                #self.log.writeToLog('XPRT-010')
                #self.tools.dieNow()

            #target = os.path.join(path, ptName)
            #source = os.path.join(self.local.projComponentFolder, cidCName, cid + '.' + cType)
            ## If shutil.copy() spits anything back its bad news
            #if shutil.copy(source, target) :
                #self.log.writeToLog('XPRT-020', [self.tools.fName(target)])
            #else :
                #fList.append(target)

        ## Start the main process here
        #if bundle :
            #archFile = os.path.join(path, cName + '_' + self.tools.ymd() + '.zip')
            ## Hopefully, this is a one time operation but if force is not True,
            ## we will expand the file name so nothing is lost.
            #if not force :
                #if os.path.isfile(archFile) :
                    #archFile = os.path.join(path, cName + '_' + self.tools.fullFileTimeStamp() + '.zip')

            #myzip = zipfile.ZipFile(archFile, 'w', zipfile.ZIP_DEFLATED)
            #for f in fList :
                ## Create a string object from the contents of the file
                #strObj = StringIO.StringIO()
                #for l in open(f, "rb") :
                    #strObj.write(l)
                ## Write out string object to zip
                #myzip.writestr(self.tools.fName(f), strObj.getvalue())
                #strObj.close()
            ## Close out the zip and report
            #myzip.close()
            ## Clean out the folder
            #for f in fList :
                #os.remove(f)
            #self.log.writeToLog('XPRT-030', [self.tools.fName(archFile)])
        #else :
            #self.log.writeToLog('XPRT-030', [path])

        #return True


###############################################################################
########################## Text Processing Functions ##########################
###############################################################################
######################## Error Code Block Series = 1200 #######################
###############################################################################

    def turnOnOffPreprocess (self, gid, onOff) :
        '''Turn on or off preprocessing on incoming component text.'''

        self.projectConfig['Groups'][gid]['usePreprocessScript'] = onOff
        self.tools.writeConfFile(self.projectConfig)
        self.log.writeToLog(self.errorCodes['1240'], [str(onOff), gid])


    def checkForPreprocessScript (self, gid) :
        '''Check to see if a preprocess script is installed. If not, install the
        default script and give a warning that the script is not complete.'''

        # First make sure the Scripts folder is there
        if not os.path.isdir(self.local.projScriptFolder) :
            os.makedirs(self.local.projScriptFolder)

        # Check and copy if needed
        if not os.path.isfile(self.local.groupPreprocessFile) :
            shutil.copy(self.local.rpmPreprocessFile, self.local.groupPreprocessFile)
            self.tools.makeExecutable(self.local.groupPreprocessFile)
            self.log.writeToLog(self.errorCodes['1260'])
        else :
            self.log.writeToLog(self.errorCodes['1265'])


    def runProcessScript (self, target, scriptFile) :
        '''Run a text processing script on a component. This assumes the 
        component and the script are valid and the component lock is turned 
        off. If not, you cannot expect any good to come of this.'''

        # subprocess will fail if permissions are not set on the
        # script we want to run. The correct permission should have
        # been set when we did the installation.
        err = subprocess.call([scriptFile, target])
        if err == 0 :
            self.log.writeToLog(self.errorCodes['1210'], [self.tools.fName(target), self.tools.fName(scriptFile)])
        else :
            self.log.writeToLog(self.errorCodes['1220'], [self.tools.fName(target), self.tools.fName(scriptFile), str(err)])
            return False

        return True


    def scriptInstall (self, source, target) :
        '''Install a script. A script can be a collection of items in
        a zip file or a single .py script file.'''

        scriptTargetFolder, fileName = os.path.split(target)
        if self.tools.isExecutable(source) :
            shutil.copy(source, target)
            self.tools.makeExecutable(target)
        elif self.tools.fName(source).split('.')[1].lower() == 'zip' :
            myZip = zipfile.ZipFile(source, 'r')
            for f in myZip.namelist() :
                data = myZip.read(f, source)
                # Pretty sure zip represents directory separator char as "/" regardless of OS
                myPath = os.path.join(scriptTargetFolder, f.split("/")[-1])
                try :
                    myFile = open(myPath, "wb")
                    myFile.write(data)
                    myFile.close()
                except :
                    pass
            myZip.close()
            return True
        else :
            self.tools.dieNow('Script is an unrecognized type: ' + self.tools.fName(source) + ' Cannot continue with installation.')


    def installPostProcess (self, cType, script, force = None) :
        '''Install a post process script into the main components processing
        folder for a specified component type. This script will be run on 
        every file of that type that is imported into the project. Some
        projects will have their own specially developed post process
        script. Use the "script" var to specify a process (which should be
        bundled in a system compatable way). If "script" is not specified
        we will copy in a default script that the user can modify. This is
        currently limited to Python scripts only which do in-place processes
        on the target files. The script needs to have the same name as the
        zip file it is bundled in, except the extention is .py instead of
        the bundle .zip extention.'''

        # Define some internal vars
        Ctype               = cType.capitalize()
        oldScript           = ''
        scriptName          = os.path.split(script)[1]
        scriptSourceFolder  = os.path.split(script)[0]
        scriptTarget        = os.path.join(self.local.projScriptFolder, self.tools.fName(script).split('.')[0] + '.py')
        if scriptName in self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] :
            oldScript = scriptName

        # First check for prexsisting script record
        if not force :
            if oldScript :
                self.log.writeToLog('POST-080', [oldScript])
                return False

        # In case this is a new project we may need to install a component
        # type and make a process (components) folder
        if not self.components[cType] :
            self.tools.addComponentType(self.projectConfig, self.local, cType)

        # Make the target folder if needed
        if not os.path.isdir(self.local.projScriptFolder) :
            os.makedirs(self.local.projScriptFolder)

        # First check to see if there already is a script file, return if there is
        if os.path.isfile(scriptTarget) and not force :
            self.log.writeToLog('POST-082', [self.tools.fName(scriptTarget)])
            return False

        # No script found, we can proceed
        if not os.path.isfile(scriptTarget) :
            self.scriptInstall(script, scriptTarget)
            if not os.path.isfile(scriptTarget) :
                self.tools.dieNow('Failed to install script!: ' + self.tools.fName(scriptTarget))
            self.log.writeToLog('POST-110', [self.tools.fName(scriptTarget)])
        elif force :
            self.scriptInstall(script, scriptTarget)
            if not os.path.isfile(scriptTarget) :
                self.tools.dieNow('Failed to install script!: ' + self.tools.fName(scriptTarget))
            self.log.writeToLog('POST-115', [self.tools.fName(scriptTarget)])

        # Record the script with the cType post process scripts list
        scriptList = self.projectConfig['CompTypes'][Ctype]['postprocessScripts']
        if self.tools.fName(scriptTarget) not in scriptList :
            self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] = self.tools.addToList(scriptList, self.tools.fName(scriptTarget))
            self.tools.writeConfFile(self.projectConfig)

        return True


    def removePostProcess (self, cType) :
        '''Remove (actually disconnect) a preprocess script from a

        component type. This will not actually remove the script. That
        would need to be done manually. Rather, this will remove the
        script name entry from the component type so the process cannot
        be accessed for this specific component type.'''

        Ctype = cType.capitalize()
        # Get old setting
        old = self.projectConfig['CompTypes'][Ctype]['postprocessScripts']
        # Reset the field to ''
        if old != '' :
            self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] = ''
            self.tools.writeConfFile(self.projectConfig)
            self.log.writeToLog('POST-130', [old,Ctype])

        else :
            self.log.writeToLog('POST-135', [cType.capitalize()])

        return True
Beispiel #27
0
    def __init__(self, project, cfg, cType) :
        '''Do the primary initialization for this manager.'''

        super(Xetex, self).__init__(project, cfg)

#        import pdb; pdb.set_trace()

        # Create all the values we can right now for this manager.
        # Others will be created at run time when we know the cid.
        self.tools                  = Tools()
        self.project                = project
        self.local                  = project.local
        self.log                    = project.log
        self.cfg                    = cfg
        self.pid                    = project.projectIDCode
        self.gid                    = project.gid
        self.cType                  = cType
        self.Ctype                  = cType.capitalize()
        self.mType                  = project.projectMediaIDCode
        self.renderer               = 'xetex'
        self.manager                = self.cType + '_' + self.renderer.capitalize()
        self.managers               = project.managers
        self.pg_back                = ProjBackground(self.pid, self.gid)
        self.fmt_diagnose           = ProjDiagnose(self.pid, self.gid)
        self.proj_config            = Config(self.pid, self.gid)
        self.proj_config.getProjectConfig()
        self.proj_config.getLayoutConfig()
        self.proj_config.getFontConfig()
        self.proj_config.getMacroConfig()
        # Bring in some manager objects we will need
        self.proj_font              = ProjFont(self.pid)
        self.proj_illustration      = ProjIllustration(self.pid, self.gid)
        self.proj_hyphenation       = ProjHyphenation(self.pid, self.gid)
        self.usfmData               = UsfmData()
        self.cidChapNumDict         = self.usfmData.cidChapNumDict()
        self.cidPtIdDict            = self.usfmData.cidPtIdDict()
        # Get config objs
        self.projectConfig          = self.proj_config.projectConfig
        self.layoutConfig           = self.proj_config.layoutConfig
        self.fontConfig             = self.proj_config.fontConfig
        self.macroConfig            = self.proj_config.macroConfig
        self.userConfig             = self.project.userConfig
        self.macPackId              = self.projectConfig['CompTypes'][self.Ctype]['macroPackage']
        # Some config settings

        self.pdfViewerCmd           = self.tools.getPdfViewerCommand(self.userConfig, self.projectConfig)
        self.sourceEditor           = self.projectConfig['CompTypes'][self.Ctype]['sourceEditor']
        self.useBackground          = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground'])
        self.useDiagnostic          = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDiagnostic'])
        self.useDocInfo             = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo'])

        # Get settings for this component
        self.managerSettings = self.projectConfig['Managers'][self.manager]
        for k, v in self.managerSettings.iteritems() :
            if v == 'True' or v == 'False' :
                setattr(self, k, self.tools.str2bool(v))
            else :
                setattr(self, k, v)

        # Set some Booleans (this comes after persistant values are set)
        # Setting hyphenation is a 2 step process, first check global, then group
        self.useHyphenation         = False
        if self.tools.str2bool(self.projectConfig['ProjectInfo']['hyphenationOn']) :
            if self.tools.str2bool(self.projectConfig['Groups'][self.gid]['useHyphenation']) :
                self.useHyphenation = True
                
        # In case the macro is not installed we need to skip over this
        try :
            self.chapNumOffSingChap     = self.tools.str2bool(self.macroConfig['Macros'][self.macPackId]['ChapterVerse']['omitChapterNumberOnSingleChapterBook'])
        except :
            self.chapNumOffSingChap     = None
        # Make any dependent folders if needed
        if not os.path.isdir(self.local.projGidFolder) :
            os.makedirs(self.local.projGidFolder)

        # Record some error codes
        # FIXME: much more needs to be done with this
        self.xetexErrorCodes =  {
            0   : 'Rendering succeful.',
            256 : 'Something really awful happened.'
                                }

        # Log messages for this module
        self.errorCodes     = {

            '1005' : ['ERR', 'PDF viewer failed with error: [<<1>>]'],
            '1010' : ['ERR', 'Style file [<<1>>] could not be created.'],
            '1040' : ['LOG', 'Created: [<<1>>]'],

            '0420' : ['WRN', 'TeX settings file has been frozen for debugging purposes.'],
            '0440' : ['LOG', 'Created: [<<1>>]'],
            '0460' : ['LOG', 'Settings changed in [<<1>>], [<<2>>] needed to be recreated.'],
            '0465' : ['LOG', 'File: [<<1>>] missing, created a new one.'],
            '0470' : ['ERR', 'Macro package [<<1>>] is not recognized by the system.'],

            '0600' : ['MSG', '<<1>> cannot be viewed, PDF viewer turned off.'],
            '0610' : ['LOG', 'Recorded [<<1>>] rendered pages in the [<<2>>] group.'],
            '0615' : ['ERR', 'XeTeX failed to execute with error: <<1>>'],
            '0617' : ['ERR', 'XeTeX failed to execute with this error: [<<1>>]'],
            '0620' : ['DBG', 'xetex command in <<1>>: <<2>> <<3>>'],
            '0625' : ['MSG', 'Rendering of [<<1>>] successful.'],
            '0630' : ['ERR', 'Rendering [<<1>>] was unsuccessful. <<2>> (<<3>>)'],
            '0635' : ['ERR', 'XeTeX error code [<<1>>] not understood by Rapuma.'],
            '0650' : ['ERR', 'Component type [<<1>>] not supported!'],
            '0690' : ['MSG', 'Dependent files unchanged, rerendering of [<<1>>] un-necessary.'],
            '0695' : ['MSG', 'Routing <<1>> to PDF viewer.'],
            '0700' : ['ERR', 'Rendered file not found: <<1>>'],
            '0710' : ['WRN', 'PDF viewing is disabled.'],
            '0720' : ['MSG', 'Saved rendered file to: [<<1>>]'],
            '0730' : ['ERR', 'Failed to save rendered file to: [<<1>>]'],

            '1000' : ['WRN', 'XeTeX debugging is set to [<<1>>]. These are the paths XeTeX is seeing: [<<2>>]'],
            '1090' : ['ERR', 'Invalid value [<<1>>] used for XeTeX debugging. Must use an integer of 0, 1, 2, 4, 8, 16, or 32']

        }
Beispiel #28
0
class ProjScript (object) :

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

        self.pid                        = pid
        self.gid                        = gid
        self.tools                      = Tools()
        self.local                      = ProjLocal(pid)
        self.user                       = UserConfig()
        self.userConfig                 = self.user.userConfig
        self.proj_config                = Config(pid, gid)
        self.proj_config.getProjectConfig()
        self.projectConfig              = self.proj_config.projectConfig
        self.cType                      = self.projectConfig['Groups'][gid]['cType']
        self.Ctype                      = self.cType.capitalize()
        self.log                        = ProjLog(pid)



        # Log messages for this module
        self.errorCodes     = {

            '0000' : ['MSG', 'Placeholder message'],

            '1010' : ['MSG', 'Script install process for [<<1>>] Succeeded.'],
            '1020' : ['ERR', 'Script install process for [<<1>>] failed.'],
            '1030' : ['ERR', 'Script type [<<1>>] not supported.'],
            '1040' : ['ERR', 'Script install cannot proceed for [<<1>>] because this script already exists in the project. You must remove it first before you can add another script.'],

            '2010' : ['MSG', 'Script remove process for [<<1>>] Succeeded.'],
            '2020' : ['ERR', 'Script remove process for [<<1>>] failed.'],

            '4210' : ['MSG', 'Processes completed successfully on: [<<1>>] by [<<2>>]'],
            '4220' : ['ERR', 'Processes for [<<1>>] failed. Script [<<2>>] returned this error: [<<3>>]'],
            '4260' : ['ERR', 'Installed the default component preprocessing script. Editing will be required for it to work with your project.'],
            '4265' : ['LOG', 'Component preprocessing script is already installed.'],
            '4310' : ['ERR', 'Script is an unrecognized type: [<<1>>] Cannot continue with installation.']


        }

###############################################################################
############################# Script Add Functions ############################
###############################################################################
######################## Error Code Block Series = 1000 #######################
###############################################################################


    def addScriptFiles (self, path, scriptType) :
        '''Import/add processing script files for a group that are found
        in the given path. Assumes valid path. Will fail if a copy
        doesn't succeed. If the file is already there, give a warning and
        will not copy.'''

#        import pdb; pdb.set_trace()

        source = path
        fileName = self.tools.fName(path)
        target = os.path.join(self.local.projScriptFolder, fileName)
        
        # Do an initial check to see if the script is already there
        # Never copy over the top of an existing script
        if os.path.isfile(target) :
            self.log.writeToLog(self.errorCodes['1040'], [fileName])
        # Make script folder if needed
        if not os.path.isdir(self.local.projScriptFolder) :
            os.makedirs(self.local.projScriptFolder)
        # Copy in the script
        if self.scriptInstall(source, target) :
            # Record the script file name
            if scriptType == 'preprocess' :
                self.projectConfig['Groups'][self.gid]['preprocessScript'] = fileName
            elif scriptType == 'postprocess' :
                self.projectConfig['Groups'][self.gid]['postprocessScript'] = fileName
            else :
                self.log.writeToLog(self.errorCodes['1030'], [scriptType])

            self.tools.writeConfFile(self.projectConfig)
            self.log.writeToLog(self.errorCodes['1010'], [fileName])
            return True
        else :
            self.log.writeToLog(self.errorCodes['1020'], [fileName])


###############################################################################
############################ Script Remove Functions ##########################
###############################################################################
######################## Error Code Block Series = 2000 #######################
###############################################################################


    def removeScriptFiles (self, scriptType) :
        '''Remove processing script files for a group.'''

        if scriptType == 'preprocess' :
            fileName = self.projectConfig['Groups'][self.gid]['preprocessScript']
        elif scriptType == 'postprocess' :
            fileName = self.projectConfig['Groups'][self.gid]['postprocessScript']
        else :
            self.log.writeToLog(self.errorCodes['1030'], [scriptType])

        target = os.path.join(self.local.projScriptFolder, fileName)
        # Remove the script (if it's not there, we don't care)
        try :
            os.remove(target)
        except :
            pass
        if not os.path.isfile(target) :
            if scriptType == 'preprocess' :
                self.projectConfig['Groups'][self.gid]['preprocessScript'] = ''
            elif scriptType == 'postprocess' :
                self.projectConfig['Groups'][self.gid]['postprocessScript'] = ''
            else :
                self.log.writeToLog(self.errorCodes['1030'], [scriptType])
            self.tools.writeConfFile(self.projectConfig)
            self.log.writeToLog(self.errorCodes['2010'], [fileName])
            return True
        else :
            self.log.writeToLog(self.errorCodes['2020'], [fileName])


###############################################################################
########################## General Script Functions ###########################
###############################################################################
######################## Error Code Block Series = 4000 #######################
###############################################################################


    def runProcessScript (self, target, scriptFile) :
        '''Run a text processing script on a component. This assumes the 
        component and the script are valid and the component lock is turned 
        off. If not, you cannot expect any good to come of this.'''

        # subprocess will fail if permissions are not set on the
        # script we want to run. The correct permission should have
        # been set when we did the installation.
        err = subprocess.call([scriptFile, target])
        if err == 0 :
            self.log.writeToLog(self.errorCodes['4210'], [self.tools.fName(target), self.tools.fName(scriptFile)])
        else :
            self.log.writeToLog(self.errorCodes['4220'], [self.tools.fName(target), self.tools.fName(scriptFile), str(err)])
            return False

        return True


    def scriptInstall (self, source, target) :
        '''Install a script. A script can be a collection of items in
        a zip file or a single .py script file.'''

        scriptTargetFolder, fileName = os.path.split(target)
        if self.tools.isExecutable(source) :
            if not shutil.copy(source, target) :
                self.tools.makeExecutable(target)
                return True
        elif self.tools.fName(source).split('.')[1].lower() == 'zip' :
            myZip = zipfile.ZipFile(source, 'r')
            for f in myZip.namelist() :
                data = myZip.read(f, source)
                # Pretty sure zip represents directory separator char as "/" regardless of OS
                myPath = os.path.join(scriptTargetFolder, f.split("/")[-1])
                try :
                    myFile = open(myPath, "wb")
                    myFile.write(data)
                    myFile.close()
                except :
                    pass
            myZip.close()
            return True
        else :
            self.log.writeToLog(self.errorCodes['4310'], [self.tools.fName(source)])
Beispiel #29
0
class ProjBinding (object) :

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

        self.pid                = pid
        self.tools              = Tools()
        self.user               = UserConfig()
        self.userConfig         = self.user.userConfig
        self.config             = Config(pid)
        self.pg_back            = ProjBackground(self.pid)
        self.config.getProjectConfig()
        self.config.getLayoutConfig()
        self.projectConfig      = self.config.projectConfig
        self.layoutConfig       = self.config.layoutConfig
        self.useBackground      = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useBackground'])
        self.useDocInfo         = self.tools.str2bool(self.layoutConfig['DocumentFeatures']['useDocInfo'])
        self.projHome           = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid)
        self.local              = ProjLocal(self.pid)
        self.log                = ProjLog(self.pid)
        self.pdfViewerCmd       = self.tools.getPdfViewerCommand(self.userConfig, self.projectConfig)

        # Log messages for this module
        self.errorCodes     = {
            '0205' : ['MSG', 'Unassigned message.'],
            '0210' : ['MSG', 'No contents are specified for binding.'],
            '0215' : ['ERR', 'Failed to bind contents into the [<<1>>] fille. Got error: [<<2>>]'],
            '0220' : ['ERR', 'Could not copy [<<1>>] temp file to [<<2>>] saved binding file.'],
            '0230' : ['MSG', 'Completed proccessing on the [<<1>>] binding file.'],
            '0235' : ['ERR', 'Failed to complete proccessing on the [<<1>>] binding file.'],
            '0240' : ['LOG', 'Recorded [<<1>>] rendered pages in the [<<2>>] binding file.'],
            '0260' : ['ERR', 'PDF viewer failed with this error: [<<1>>]'],
            '0265' : ['ERR', 'Rendered file not found: <<1>>'],
            '0270' : ['WRN', 'PDF viewing is disabled.'],
            '0280' : ['ERR', 'GS PDF file merge failed with this error: [<<1>>]'],
            '0300' : ['MSG', 'File binding operation in process, please wait...']

        }


###############################################################################
############################## Binding Functions ##############################
###############################################################################
######################## Error Code Block Series = 200 ########################
###############################################################################

#        import pdb; pdb.set_trace()


    def bind (self, save = False) :
        '''Bind all groups in the order they are indicated by group bindOrder
        settings. Note, because binding spans groups and the main body
        (project.py) mainly just works on one group at a time, this has to
        be called from outside project and project needs to be reinitialized
        each time a group is rendered from here.'''

#        import pdb; pdb.set_trace()

        # Get the order of the groups to be bound.
        bindOrder = {}
        # Put a safty in here in case there are no groups yet
        if not self.projectConfig.has_key('Groups') :
            return False

        # Build the bindOrder dict with ord num as key and file name as value
        for gid in self.projectConfig['Groups'].keys() :
            if not self.projectConfig['Groups'][gid].has_key('bindingOrder') :
                self.projectConfig['Groups'][gid]['bindingOrder'] = 0
                self.tools.writeConfFile(self.projectConfig)
            if int(self.projectConfig['Groups'][gid]['bindingOrder']) > 0 :
                gidPdfFile = os.path.join(self.local.projComponentFolder, gid, gid + '.pdf')
#                bindOrder[self.projectConfig['Groups'][gid]['bindingOrder']] = self.projectConfig['Groups'][gid]['bindingFile']
                bindOrder[self.projectConfig['Groups'][gid]['bindingOrder']] = gidPdfFile
        bindGrpNum = len(bindOrder)
        # Need not keep going if nothing was found
        if bindGrpNum == 0 :
            self.log.writeToLog(self.errorCodes['0210'])
            return False

        # Make an ordered key list
        keyList = bindOrder.keys()
        keyList.sort()

        # Output the bind files in order according to the list we made
        fileList = []
        for key in keyList :
            fileList.append(bindOrder[key])

        # First merge the master pages together
        tempFile = self.mergePdfFilesGs(fileList)

        # Now add background and doc info if requested
        bgFile = ''
        if self.useBackground :
            bgFile = self.pg_back.addBackground(tempFile)
        if self.useDocInfo :
            if bgFile :
                bgFile = self.pg_back.addDocInfo(bgFile)
            else :
                bgFile = self.pg_back.addDocInfo(tempFile)

        # If we are saving this make a name for it
        if save :
            bindFileName = self.pid + '_contents_' + self.tools.ymd()
            # Save this to the Deliverable folder (Make sure there is one)
            if not os.path.isdir(self.local.projDeliverableFolder) :
                os.makedirs(self.local.projDeliverableFolder)
            bindFile = os.path.join(self.local.projDeliverableFolder, bindFileName + '.pdf')
            if os.path.exists(bgFile) :
                if shutil.copy(bgFile, bindFile) :
                    self.log.writeToLog(self.errorCodes['0220'], [bgFile,bindFile])
            else :
                if shutil.copy(tempFile, bindFile) :
                    self.log.writeToLog(self.errorCodes['0220'], [tempFile,bindFile])

            # Direct to viewFile
            viewFile = bindFile
                
        else :
            if os.path.exists(bgFile) :
                viewFile = bgFile
            else :
                viewFile = tempFile

        # Binding should have been successful, report it now
        self.log.writeToLog(self.errorCodes['0230'], [viewFile])

        # View the file
        if os.path.isfile(viewFile) :
            if self.pdfViewerCmd :
                # Add the file to the viewer command
                self.pdfViewerCmd.append(viewFile)
                # Run the viewer
                try :
                    subprocess.Popen(self.pdfViewerCmd)
                    return True
                except Exception as e :
                    # If we don't succeed, we should probably quite here
                    self.log.writeToLog(self.errorCodes['0260'], [str(e)])
            else :
                self.log.writeToLog(self.errorCodes['0270'])
        else :
            self.log.writeToLog(self.errorCodes['0265'], [self.tools.fName(viewFile)])


    def mergePdfFilesGs (self, sourceList) :
        '''Using GS, merge multiple PDF files into one. The advantage of
        using Gs is that the index will be maintained. pdftk strips out
        the index, which is bad...'''

        # This is our working file for this operation
        tempFile = tempfile.NamedTemporaryFile().name + '.pdf'

        # FIXME: Note/Warning, the use "-dPDFSETTINGS=/prepress" can cause an issue when
        # rendering. A segmentation fault may occur. This will cause the bind feature
        # to fail because gs failed. The segmentation fault seems to occure from graphics
        # embedded in the PDF that are either too big or not a true grayscale, but rather
        # RGB. The /prepress setting will then fail which means the doc will fail to test
        # out right in PDF x1a tests.
        
        # There does not seem to be any good way around this at this time. With more
        # development time and perhaps better options, we might someday be able to get
        # the bind process to be completely automated and be able to pass all Adobe tests
        # for x1a and other issues. As there is not any real demand for Rapuma, it isn't
        # worth putting any more effort into this section of code. Therefore, the
        # /prepress setting will be dropped from here and the user will be required to
        # Run any output through an Adobe product to get output that will be acceptable
        # to Adobe. Adobe wins, I quite. :-(
        
        # This command structure was in production until the segmenation fault issue was found:
        # cmd = ['gs', '-dBATCH', '-dNOPAUSE', '-q', '-sDEVICE=pdfwrite', '-dPDFSETTINGS=/prepress', '-sOutputFile=' + tempFile]

        # For issues with segmentation faults, the following should help 
        # with debugging. Adjust as needed. for more info go to:
        #    http://ghostscript.com/doc/8.54/Use.htm#Debugging
        # cmd = ['gs', '-dBATCH', '-dNOPAUSE', '-q', '-sDEVICE=pdfwrite', '-sOutputFile=' + tempFile, '-dPDFWRDEBUG', '-E']; print cmd

        # This is the non-x1a compatable command that will normally be used
        cmd = ['gs', '-dBATCH', '-dNOPAUSE', '-q', '-sDEVICE=pdfwrite', '-sOutputFile=' + tempFile]

        # Continueing on, now add the files we want to bind together
        cmd = cmd + sourceList

        # Now bind the files
        try :
            self.log.writeToLog(self.errorCodes['0300'])
            subprocess.call(cmd)
            # Return our file name for further processing
            return tempFile
        except Exception as e :
            # If we don't succeed, we should probably quite here
            self.log.writeToLog(self.errorCodes['0280'], [str(e)])
Beispiel #30
0
class Project (object) :

    def __init__(self, pid, gid) :
        '''Instantiate this class.'''

        self.pid                    = pid
        self.gid                    = gid
        self.user                   = UserConfig()
        self.userConfig             = self.user.userConfig
        self.projHome               = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid)
        self.config                 = Config(self.pid)
        self.config.getProjectConfig()
        self.projectConfig          = self.config.projectConfig
        self.cType                  = self.projectConfig['Groups'][self.gid]['cType']
        self.Ctype                  = self.cType.capitalize()
        self.local                  = ProjLocal(pid, gid, self.cType)
        self.log                    = ProjLog(self.pid)
        self.tools                  = Tools()
        self.groups                 = {}
        self.components             = {}
        self.managers               = {}
        self.projectMediaIDCode     = self.projectConfig['ProjectInfo']['projectMediaIDCode']
        self.projectIDCode          = self.projectConfig['ProjectInfo']['projectIDCode']
        self.projectName            = self.projectConfig['ProjectInfo']['projectTitle']
        self.plid                   = self.projectConfig['ProjectInfo']['languageCode']
        self.psid                   = self.projectConfig['ProjectInfo']['scriptCode']
        
        # The gid cannot generally be set yet but we will make a placeholder
        # for it here and the functions below will set it. (I'm just say'n)

#        import pdb; pdb.set_trace()

        m = import_module('rapuma.manager.' + self.projectMediaIDCode)
        self.__class__ = getattr(m, self.projectMediaIDCode[0].upper() + self.projectMediaIDCode[1:])

        # Update the existing config file with the project type XML file
        # if needed
        newXmlDefaults = os.path.join(self.local.rapumaConfigFolder, self.projectMediaIDCode + '.xml')
        xmlConfig = self.tools.getXMLSettings(newXmlDefaults)
        newConf = ConfigObj(xmlConfig.dict(), encoding='utf-8').override(self.projectConfig)
        for s,v in self.projectConfig.items() :
            if s not in newConf :
                newConf[s] = v

        # Replace with new conf if new is different from old
        # Rem new conf doesn't have a filename, give it one
        if self.projectConfig != newConf :
            self.projectConfig = newConf
            self.projectConfig.filename = self.local.projectConfFile

        # Log messages for this module
        self.errorCodes     = {
            'PROJ-000' : ['MSG', 'Project module messages'],
            'PROJ-030' : ['MSG', 'Changed  [<<1>>][<<2>>][<<3>>] setting from \"<<4>>\" to \"<<5>>\".'],
            'PROJ-040' : ['ERR', 'Problem making setting change. Section [<<1>>] missing from configuration file.'],
            'PROJ-050' : ['ERR', 'Component [<<1>>] working text file was not found in the project configuration.'],
            'PROJ-060' : ['ERR', 'Component [<<1>>] was not found in the project configuration.'],
            'PROJ-070' : ['ERR', 'Source file not found: [<<1>>].'],
            'PROJ-080' : ['MSG', 'Successful copy of [<<1>>] to [<<2>>].'],
            'PROJ-090' : ['ERR', 'Target file [<<1>>] already exists. Use force (-f) to overwrite.'],

            '0205' : ['LOG', 'Created the [<<1>>] manager object.'],
            '0210' : ['LOG', 'Wrote out [<<1>>] settings to the project configuration file.'],
            '0211' : ['ERR', 'Failed to write out project [<<1>>] settings to the project configuration file.'],

            '0660' : ['ERR', 'Invalid component ID: [<<1>>].'],

        }

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

    def createManager (self, mType) :
        '''Check to see if a manager is listed in the config and load it if
        it is not already.'''

        fullName = self.cType + '_' + mType.capitalize()
        if fullName not in self.managers :
            self.addManager(mType)
            self.loadManager(mType)
            self.log.writeToLog(self.errorCodes['0205'], [fullName])


    def loadManager (self, mType) :
        '''Do basic load on a manager.'''

        fullName = self.cType + '_' + mType.capitalize()
        cfg = self.projectConfig['Managers'][fullName]
        module = import_module('rapuma.manager.' + mType)
        ManagerClass = getattr(module, mType.capitalize())
        manobj = ManagerClass(self, cfg, self.cType)
        self.managers[fullName] = manobj


    def addManager (self, mType) :
        '''Create a manager reference in the project config that components
        will point to.'''

#        import pdb; pdb.set_trace()

        fullName = self.cType + '_' + mType.capitalize()
        managerDefaults = None
        # Insert the Manager section if it is not already there
        self.tools.buildConfSection(self.projectConfig, 'Managers')
        if not self.projectConfig['Managers'].has_key(fullName) :
            self.tools.buildConfSection(self.projectConfig['Managers'], fullName)

        # Update settings if needed
        update = False
        managerDefaults = self.tools.getXMLSettings(os.path.join(self.local.rapumaConfigFolder, mType + '.xml'))
        for k, v, in managerDefaults.iteritems() :
            # Do not overwrite if a value is already there
            if not self.projectConfig['Managers'][fullName].has_key(k) :
                self.projectConfig['Managers'][fullName][k] = v
                # If we are dealing with an empty string, don't bother writing out
                # Trying to avoid needless conf updating here. Just in case we are
                # working with a list, we'll use len()
                if len(v) > 0 :
                    update = True
        # Update the conf if one or more settings were changed
        if update :
            if self.tools.writeConfFile(self.projectConfig) :
                self.log.writeToLog(self.errorCodes['0210'],[fullName])
            else :
                self.log.writeToLog(self.errorCodes['0211'],[fullName])


###############################################################################
############################ Group Level Functions ############################
###############################################################################
####################### Error Code Block Series = 0600 ########################
###############################################################################


    def renderGroup (self, cidList = '', pages = '', override = '', save = False) :
        '''Render a group of subcomponents or any number of components
        in the group specified in the cidList.'''

#        import pdb; pdb.set_trace()

        # If there are any cids listed we need to test them
        if cidList :
            self.isValidCidList(cidList)

        # Otherwise, do a basic test for exsistance and move on
        if self.projectConfig['Groups'].has_key(self.gid) :
            # Now create the group and pass the params on
            self.createGroup().render(self.gid, cidList, pages, override, save)
            return True


    def createGroup (self) :
        '''Create a group object that can be acted on. It is assumed
        this only happens for one group per session. This group
        will contain one or more compoenents. The information for
        each one will be contained in the group object.'''

#        import pdb; pdb.set_trace()

        # If the object already exists just return it
        if self.gid in self.groups: 
            return self.groups[self.gid]

        cType = self.projectConfig['Groups'][self.gid]['cType']
        # Create a special component object if called
        cfg = self.projectConfig['Groups'][self.gid]
        module = import_module('rapuma.group.' + cType)
        ManagerClass = getattr(module, cType.capitalize())
        groupObj = ManagerClass(self, cfg)
        self.groups[self.gid] = groupObj

        return groupObj


    def isValidCidList (self, thisCidlist) :
        '''Check to see if all the components in the list are in the group.'''

        cidList = self.projectConfig['Groups'][self.gid]['cidList']
        for cid in thisCidlist :
            # If this is not a usfm type we do not need to validate
            if self.cType == 'usfm' :
                if not cid in cidList :
                    self.log.writeToLog(self.errorCodes['0660'],[cid])


    def listAllComponents (self, cType) :
        '''Generate a list of valid component IDs and cNames for this cType.'''

        # Create the component object now with a special component caller ID
        self.createComponent('usfm_internal_caller')
        # Get the component info dictionary
        comps = self.components['usfm_internal_caller'].usfmCidInfo()
        # List and sort
        cList = list(comps.keys())
        cList.sort()
        # For now we'll output to terminal but may want to change this later.
        for c in cList :
            if c != '_z_' :
                print c, comps[c][1]


###############################################################################
############################ System Level Functions ###########################
###############################################################################

    def run (self, command, opts, userConfig) :
        '''Run a command'''

        if command in self.commands :
            self.commands[command].run(opts, self, userConfig)
        else :
            self.tools.terminalError('The command: [' + command + '] failed to run with these options: ' + str(opts))