associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

OfficeToPdf

Programa para conversão de arquivos do Microsoft Office (Word e Excel) para PDF através do PDFCreator.

Código

   1 # -*- coding: ISO-8859-1 -*-
   2 
   3 # Autor: Juracy Filho <juracy AT gmail (DOT) com>
   4 # Data: 01 de dezembro de 2005
   5 # OfficeToPDF.py
   6 #   Automação do processo de geração de PDF (e outros formatos também) a partir
   7 #   de arquivos Microsoft Word e Excel através do PDFCreator
   8 #
   9 # Versão: 1.1
  10 #
  11 # Requerimentos:
  12 #   Microsoft Windows (testado apenas no Windows 2000)
  13 #   Microsoft Word e/ou Microsoft Excel (testado apenas no Office 2000)
  14 #   Python 2.4 (provavelmente funcionará em versões anteriores, porém, não testado)
  15 #   pywin32 - http://starship.python.net/crew/mhammond/win32/
  16 #   PDFCreator 0.8.1 RC 9 ou superior - http://sector7g.wurzel6.de/pdfcreator/
  17 #          não funciona com a versão 0.8 e anteriores pela falta do suporte a COM
  18 
  19 import win32com.client as com
  20 from win32print import GetDefaultPrinter, SetDefaultPrinter
  21 from pythoncom import PumpWaitingMessages
  22 from pywintypes import com_error
  23 from time import sleep, clock
  24 import os
  25 import sys
  26 
  27 # Constantes
  28 PrinterName = 'PDFCreator'
  29 SleepTimeToWait = 0.5
  30 WaitTimeOut = 45 # segundos
  31 
  32 MicrosoftWordCOMClass = 'Word.Application'
  33 MicrosoftExcelCOMClass = 'Excel.Application'
  34 PDFCreatorCOMClass = 'PDFCreator.clsPDFCreator'
  35 
  36 class OLEServerNotFound(Exception):
  37     warning = 'Servidor COM %s não encontrado, ou não pôde ser instanciado'
  38 
  39 class OLEWrapper(object):
  40     """Manipulador padrão para conversão através de OLE Automation"""
  41     def __init__(self, OLEClass):
  42         try:
  43             self.OLEServer = com.Dispatch(OLEClass)
  44         except com_error:
  45             raise OLEServerNotFound, OLEServerNotFound.warning % OLEClass
  46 
  47     def __del__(self):
  48         if hasattr(self, 'OLEServer'):
  49             self.OLEServer = None
  50 
  51 class WordWrapper(OLEWrapper):
  52     """Manipulador para o Microsoft Word"""
  53     def __init__(self):
  54         OLEWrapper.__init__(self, MicrosoftWordCOMClass)
  55 
  56     def __del__(self):
  57         if hasattr(self, 'OLEServer'):
  58             self.OLEServer.Quit(False)
  59         OLEWrapper.__del__(self)
  60 
  61     def convert(self, filename):
  62         """Função para executar a conversão
  63 
  64         Chamada: obj.convert(nome_do_arquivo.doc)
  65         Passos:
  66           1 - Abrir o arquivo
  67           2 - Definir a impressora correta
  68           3 - Mudar opções do Word
  69           4 - Imprimir
  70           5 - Voltar a impressora default
  71           6 - Fechar o arquivo"""
  72 
  73         # Abrindo o arquivo
  74         self.OLEServer.Documents.Open(FileName = filename, ReadOnly = True,
  75                                       Revert = False, AddToRecentFiles = False)
  76 
  77         # Guardando informações
  78         current_printer = GetDefaultPrinter()
  79         current_printreverse = self.OLEServer.Options.PrintReverse
  80 
  81         SetDefaultPrinter(PrinterName)
  82         self.OLEServer.Selection.GoTo(What = 1, Which = -1)
  83         self.OLEServer.Options.PrintReverse = False
  84         self.OLEServer.ActiveDocument.PrintOut(Background = False)
  85 
  86         # Restaurando
  87         self.OLEServer.Options.PrintReverse = current_printreverse
  88         SetDefaultPrinter(current_printer)
  89 
  90         # Fechando
  91         self.OLEServer.ActiveDocument.Close(False)
  92 
  93 class ExcelWrapper(OLEWrapper):
  94     """Manipulador para o Microsoft Excel"""
  95     def __init__(self):
  96         OLEWrapper.__init__(self, MicrosoftExcelCOMClass)
  97 
  98     def __del__(self):
  99         if hasattr(self, 'OLEServer'):
 100             self.OLEServer.Quit()
 101         OLEWrapper.__del__(self)
 102 
 103     def convert(self, filename):
 104         # Abrindo o arquivo
 105         self.OLEServer.Workbooks.Open(Filename = filename, ReadOnly = True, AddToMru = False)
 106 
 107         # Guardando informações
 108         current_printer = GetDefaultPrinter()
 109 
 110         SetDefaultPrinter(PrinterName)
 111         self.OLEServer.ActiveWorkbook.PrintOut(Copies = 1)
 112 
 113         # Restaurando
 114         SetDefaultPrinter(current_printer)
 115 
 116         # Fechando
 117         self.OLEServer.ActiveWorkbook.Close(False)
 118 
 119 class PDFCreatorNotFound(Exception):
 120     warning = 'Não foi possível instanciar o PDFCreator, ' + \
 121               'provavelmente não está instalado, ' + \
 122               'ou é uma versão anterior a 0.8.1'
 123 
 124 class PDFCreatorNoStart(Exception):
 125     warning = 'Não foi possível iniciar o PDF Creator'
 126 
 127 class PDFCreator(object):
 128     formats = {'pdf': 0, 'png': 1, 'jpg': 2, 'bmp': 3, 'pcx': 4, 'tif': 5, 'ps': 6, 'eps': 7}
 129     def __init__(self):
 130         try:
 131             self.OLE = com.DispatchWithEvents(PDFCreatorCOMClass, PDFCreatorEvents)
 132         except com_error:
 133             raise PDFCreatorNotFound, PDFCreatorNotFound.warning
 134 
 135         self.OutputDirectory = os.getcwd()
 136         self.processos = 0
 137         self.started = False
 138 
 139     def start(self):
 140         if not self.started:
 141             self.started = True
 142             if not self.OLE.cStart('/NoProcessingAtStartup'):
 143                 if not self.OLE.cStart('/NoProcessingAtStartup', True):
 144                     raise PDFCreatorNoStart, PDFCreatorNoStart
 145             self.Options = self.OLE.cOptions
 146 
 147     def __del__(self):
 148         # Libera o servidor COM do PDFCreator, somente se tiver sido instanciado
 149         if hasattr(self, 'OLE'):
 150             if self.started:
 151                 self.OLE.cCloseRunningSession()
 152                 self.OLE.cClose()
 153             self.OLE = None
 154 
 155         # Libera o PDFCreatorOptions (caso o PDFCreator tenha sido iniciado - start)
 156         if hasattr(self, 'Options'):
 157             self.Options = None
 158 
 159     def SetOptions(self, filename, format):
 160         self.Options.AutosaveDirectory = self.OutputDirectory
 161         self.Options.UseAutosave = 1
 162         self.Options.UseAutosaveDirectory = 1
 163         self.Options.AutosaveFormat = self.formats[format]
 164         self.Options.AutosaveFilename = '%s.%s' % (os.path.splitext(filename)[0], format)
 165         # TODO: Colocar mais opções inerentes a formato, como DPI, Compressão, etc.
 166         self.OLE.cOptions = self.Options
 167 
 168     def wait(self):
 169         """Aguardar finalização da conversão"""
 170         start = clock()
 171         while pdf.processos > 0:
 172             if clock() - start > WaitTimeOut:
 173                 raise Exception, 'Tempo de espera máximo atingido: %d segundos' % WaitTimeOut
 174             sleep(SleepTimeToWait)
 175             PumpWaitingMessages()
 176 
 177 class PDFCreatorEvents(object):
 178     errors = {
 179         -2: 'Evento desconhecido',
 180         -1: 'Erro de teste para o evento',
 181          1: 'O ActiveX-Server ainda não foi iniciado! Por favor use a função "cStart()" para inicia-lo!',
 182          2: 'Uma instância do PDFCreator já está rodando. Não é permitido mais que uma instância de cada vez!',
 183          3: 'Nome de opção desconhecida', # Unknown Options Name '%1' in '%2'! 
 184          4: 'Erro no ghostscript', #Ghostscript Error: '%1'! 
 185          5: 'Arquivo não encontrado', #The file '%1' could not be found! 
 186          6: 'Um caminho ou arquivo válido não foi informado'} #No valid path or filename '%1' was given! 
 187 
 188     def OneReady(self):
 189         print "Arquivo %s gerado com sucesso !" % os.path.split(pdf.OLE.cOutputFilename)[1]
 190         pdf.processos -= 1
 191 
 192     def OneError(self):
 193         if pdf.OLE.cError.Number != 2: # Caso 2 utiliza o que está iniciado na memória
 194             print "PDFCreator Error: ", self.errors[pdf.OLE.cError.Number]
 195             # Mensagem de erro original
 196             #print pdf.OLE.cError.Number, pdf.OLE.cError.Description
 197 
 198 # Instância única para referenciar o PDFCreator
 199 try:
 200     pdf = PDFCreator()
 201 except PDFCreatorNotFound, mensagem:
 202     print mensagem
 203     sys.exit(1) # PDFCreator not found
 204 
 205 
 206 def DispatcherOLE(arquivo):
 207     """Função de dispatch para a classe correta conforme a extensão do arquivo"""
 208     OLEServers = {'.doc': WordWrapper, '.xls': ExcelWrapper}
 209     extensao = os.path.splitext(arquivo.lower())[1]
 210 
 211     if extensao in OLEServers:
 212         return OLEServers[extensao]()
 213     else:
 214         raise Exception, 'Tipo de documento (%s) não suportado' % extensao
 215 
 216 def ConvertFile(filename):
 217     print "Convertendo", os.path.split(filename)[1]
 218     server = DispatcherOLE(filename)
 219 
 220     if not pdf.started:
 221         pdf.start()
 222 
 223     pdf.OLE.cClearCache()
 224     # Pode-se usar qualquer formato listado em PDFCreator.formats além do PDF
 225     pdf.SetOptions(os.path.split(filename)[1], 'pdf') 
 226     pdf.OLE.cPrinterStop = True
 227     pdf.processos += 1
 228 
 229     try:
 230         server.convert(filename)
 231     finally:
 232         pdf.OLE.cPrinterStop = False
 233 
 234 def AskForFiles():
 235     import win32ui
 236     import win32con
 237 
 238     tipos = ['Documentos do Microsoft Word (*.doc)|*.doc',
 239              'Planilhas do Microsoft Excel (*.xls)|*.xls',
 240              '|']
 241 
 242     while True:
 243         dlg = win32ui.CreateFileDialog(1, None, None, 
 244                     (win32con.OFN_FILEMUSTEXIST|
 245                      win32con.OFN_EXPLORER|
 246                      win32con.OFN_ALLOWMULTISELECT),
 247                     '|'.join(tipos))
 248         dlg.SetOFNTitle('Arquivo para converter')
 249 
 250         if dlg.DoModal() != win32con.IDOK:
 251             break
 252 
 253         BatchProcess(dlg.GetPathNames())
 254 
 255     if pdf.processos > 0:
 256         print "Esperando finalização..."
 257         pdf.wait()
 258 
 259 def BatchProcess(filenames):
 260     for filename in filenames:
 261         try:
 262             if os.path.exists(filename):
 263                 ConvertFile(filename)
 264                 pdf.wait()
 265             else:
 266                 print 'O arquivo %s não pôde ser encontrado' % filename
 267         except Exception, mensagem:
 268             print 'O arquivo %s não pôde ser convertido\nErro: %s' % (filename, mensagem)
 269             pdf.processos = 0
 270 
 271 if __name__ == '__main__':
 272     #O OutputDirectory pode ser alterado para definir o diretório de gravação
 273     #pdf.OutputDirectory = r'c:\temp'
 274 
 275     if len(sys.argv) > 1:
 276         BatchProcess(sys.argv[1:])
 277     else:
 278         AskForFiles()

Exemplo de uso

O exemplo de uso já está na própria rotina (no final para ser mais exato).

Esta rotina faz uso extensivo de acesso a servidores COM. E já funciona muito bem com Microsoft Word e Excel, porém, é facilmente adaptável para outros servidores.

Recursos úteis na rotina

  • Acesso a servidores COM

  • Manipulação de callbacks (eventos) de servidores COM

  • Troca da impressora padrão
  • Diálogo de abertura de arquivos - MFC

  • Liberação da MFC para processar os eventos da fila: PumpWaitingMessages

Volta para CookBook.