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.