Que tal abrir um quadro de texto no Scribus, rodar um script e obter rapidamente uma matéria jornalística montada, com foto, legenda e crédito da foto? E que tal levar todo um sistema editorial num pen drive? (Editor de texto, editor de fotos, paginador, saída em PDF/X-3). E que tal tudo isto com ferramentas gratuitas e de código aberto?
Essa foi minha idéia ao aprender Python e Scribus Scripting. Levar um sistema editorial de categoria profissional num pen drive. Instalei o X-Scribus, um Scribus especial para pen drives. Ele já tem o Ghostscript, Python e tudo o que é necessário para rodar em qualquer computador com Windows. Existe mais uma versão, o Portable Scribus 1.3.3.12, mas ele me apresentou problemas na visualização de impressão: não permitia a seleção de chapas (plates) individuais — ciano, magenta, amarelo ou preto, coisa importante para o ensino de seleção de cores.
Imagens
Para automatizar o Scribus, uma das primeiras providências é permitir ao Python do Scribus a manipulação de imagens. Para isso, usei PIL (Python Image Library).
No Linux, o Scribus usa o Python normal do sistema. Instale as bibliotecas PIL da forma usual de seu sabor de Linux. No Windows, o instalador do Python Image Library precisa de certas chaves no registro do Windows para saber em que lugar está instalado o sistema Python. Eu defini estas chaves para a pasta do Scribus, que é onde roda o interpretador Python no Windows. Ao baixar o PIL, cuide as versões: no Scribus versão 1.3.3 estável, o Python é versão 2.4. No Scribus 1.3.4 Beta, o Python é versão 2.5.
Para registrar o Python do Scribus, o meu arquivo reg tem o seguinte:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Python] [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore] [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore\2.4] [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore\2.4\InstallPath] @="c:\\SD\\PortableApps\\X-Scribus\\Bin\\Scribus\\" [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore\2.4\PythonPath] @="c:\\SD\\PortableApps\\X-Scribus\\Bin\\Scribus\\Lib\\;c:\\SD\\PortableApps\\X-Scribus\\Bin\\Scribus\\DLLs\\" [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore\2.5] [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore\2.5\InstallPath] @="c:\\SD\\Bin\\Scribus\\" [HKEY_LOCAL_MACHINE\SOFTWARE\Python\Pythoncore\2.5\PythonPath] @="c:\\SD\\Bin\\Scribus\\Lib\\;c:\\SD\\Bin\\Scribus\\DLLs\\"
Se você precisar registrar, copie este código, mude os caminhos para as pastas para casarem com seu sistema (em vermelho) e grave como texto simples, com a terminação .reg. Dê dois clics no arquivo para registrar as chaves no registro do Windows.
Depois de instalado o PIL, se as bibliotecas de imagem forem corretamente reconhecidas, não haverá erros ao rodar o script de montagem de matérias.
Repare que instalei tudo usando o HD. Depois de tudo instalado, copiei a pasta inteira para o pen drive. No caso, copiei a pasta X-Scribus para a pasta PortableApps (que é o padrão de aplicações portáteis que uso) do pen drive.
A seguir coloquei o script na pasta de scripts do Scribus, que no meu caso coloquei em c:\SD\PortableApps\X-Scribus\Bin\Scribus\Scripts.
No início do script existem algumas variáveis que podem ser modificadas pelo usuário, como a largura da valeta (gutter, espaço entre as colunas).
O script funciona assim: desenhe um retângulo ou selecione um. Vá ao menu Scripts, procure e rode este script. Aparecem diversas janelinhas de diálogo perguntando:
- Em quantas colunas será dividida a área de colunas da matéria? Escolha 2 ou mais.
- Qual a largura do espaço entrecolunas (gutter ou valeta)?
- Qual o corpo (tamanho) do sobretítulo?
- Qual o corpo do subtítulo?
- Quantas fotos tem a matéria? Por enquanto, o script só aceita uma.
- Quantas colunas ocupa a foto (de largura)? A foto é posicionada ao lado da primeira coluna de texto e agrupada com quadros para legenda e para crédito. As bibliotecas PIL são usadas para isto. As funções PIL medem a foto escolhida e o script calcula a altura do quadro de foto. Mais tarde, uso a tecla CTRL + Y para abrir o editor de texto interno do Scribus para editar os dois elementos (como se faz no Adobe inDesign). Na primeira linha vai a legenda e na linha de baixo vai o crédito.
Falta fazer as rotinas para manipular fotos no caso de uma matéria em apenas uma coluna. Se você escolher uma coluna, a foto será montada ao lado da matéria.
A seguinte seqüência de quadros mostra como funciona o script.
O quadro para título vai agrupado com o quadro para as colunas. Para editar o texto, uso a tecla CTRL + Y. Para importar texto, seleciono o grupo e uso CTRL + D (como no PageMaker e InDesign).
#!/usr/bin/env python2.4 # -*- coding: utf-8 -*- """ Build a newspaper story By prof. MS. José Antonio Meira da Rocha mailto:joseantoniorocha@gmail.com http://meiradarocha.jor.br Licença GPL """ import sys ########################### # Assegura que está rodando # dentro do Scribus ########################### try: import scribus except ImportError,err: print "This Python script is written for the Scribus scripting interface." print "It can only be run from within Scribus." sys.exit(1) # Carrega algumas constantes Scribus usadas neste script from scribus import UNIT_POINTS, LINE_SOLID, JOIN_MITTER, BUTTON_OK, ICON_WARNING ######################### # USER IMPORTS GO HERE # ######################### ##################################### # Usuário pode mudar estes parâmetros ##################################### numeroDeColunasPadrao = "3" entrecolunasPadrao = "14.1732" # 5mm corpoDeTituloPadrao = "48" corpoPadraoDoSobretitulo = "18" proporcaoDeEntrelinha = 1.0 ########################################## # Constantes que NÃO devem ser modificadas AROUND_FRAME = 1 BOUNDING_BOX = 2 CONTOUR_LINE = 3 ###################### # Locale strings # Textos para tradução ###################### pedirParaAbrirDoc = "<h2>Abra um documento</h2>" \ +"Ops! Abra um documento antes \nde rodar este comando." pedirParaAbrirQuadro = "<h2>Desenhe um quadro</h2>\n" \ +"Ops! Desenhe ou selecione um <b>quadro</b>\n" \ +"na área em que você quer montar a matéria." # labelDeColunas = 'Colunas' #Number of columns mensagemDeColunas = '<h2>Colunas</h2><p>Quantas colunas?</p>' #How many columns? # labelDeCorpoDeTitulo = "Corpo do título" #quadroDeTitulo mensagemDeCorpoDeTitulo = "<h2>Corpo do título</h2><p>Qual o corpo do título?</p>" #What the quadroDeTitulo size? # labelDeValetas = "Valetas" mensagemDeValetas = "<h2>Valetas</h2><p>Qual o tamanho da valeta?\n(Espaço entrecolunas)" # labelDeSobretitulo = "Corpo do sobretítulo" mensagemDeSobretitulo = "<h2>Corpo do sobretítulo</h2>\n<p><b>Qual o corpo do sobretítulo?</b>\n" \ + "(coloque zero se não houver sobretítulo)" # textFlowsAroundFrame = 'Abra a imagem' filtroDeImagens = 'Arquivos de imagens (*.jpg *.png *.tiff *.tif)' # labelDeImportarTexto = "Abra o texto" filtroDeArquivosTexto = "Arquivos de texto (*.txt *.html)" # labelDeLarguraDeImagem = "Largura da imagem" mensagemDeLarguraDeImagem = "<h2>Largura da Imagem</h2>\nQuantas colunas a imagem ocupará?" larguraDeColunasPadrao = "1" # labelDeImagem = "Quantas fotos" mensagemDeImagem = "<h2>Quantas fotos?</h2>\n<p>Quantas fotos a matéria tem?" numeroDeImagemPadrao = "1" labelDeQuadroDeTitulo = "Montar matéria" labelErroDeImportacao = "Não importei o arquivo" msgDeErroDeImportacao = "<h2>Não importei o arquivo</h2><p>Desculpe-me, não importei o arquivo por algum detalhe de formato incompatível ou arquivo não encontrado. <b>Sem problema</b>, tente de novo manualmente nais tarde.</p>" labelAbreDoc = "Abra um documento" dizAbreDoc = "<h2>Abra um documento</h2><p>Por favor, este comando exige que você abra um documento.</p>" #################### # End Locale strings #################### try: import Image except ImportError,err: print "This Python script is written for the PIL graphic interface." print "It should be instaled in Scribus Python tree." sys.exit(1) def pedeNumeroDeColunas(): """Pede número de colunas de texto que terá a matéria""" numeroDeColunas = scribus.valueDialog( labelDeColunas, mensagemDeColunas, numeroDeColunasPadrao ) if not numeroDeColunas: sys.exit() try: return int(numeroDeColunas) except: return numeroDeColunasPadrao def pedeCorpoDoTitulo(): """Pede tamanho das letras (corpo) do título, em pontos tipográficos.""" corpoDeTitulo = scribus.valueDialog(labelDeCorpoDeTitulo, mensagemDeCorpoDeTitulo, corpoDeTituloPadrao) if not corpoDeTitulo: sys.exit() try: return float(eval(corpoDeTitulo)) except: return float(corpoDeTituloPadrao) def pedeEntrecolunas(): """Pede o espaço entre as colunas de texto (gitter, valeta), em pontos tipográficos""" entrecolunas = scribus.valueDialog(labelDeValetas, mensagemDeValetas, entrecolunasPadrao) if not entrecolunas: sys.exit() try: return float(entrecolunas) except: return entrecolunasPadrao def pedeCorpoDoSobretitulo(): """Pede tamanho das letras (corpo) do sobretítulo, em pontos tipográfico""" corpoDoSobretitulo = scribus.valueDialog(labelDeSobretitulo, mensagemDeSobretitulo, corpoPadraoDoSobretitulo) if not corpoDoSobretitulo: sys.exit() try: return float(eval(corpoDoSobretitulo)) except: return float(corpoPadraoDoSobretitulo) def pedeImagem(): """Pergunta por fotos na matéria""" temImagem = scribus.valueDialog(labelDeImagem, mensagemDeImagem, numeroDeImagemPadrao) if not temImagem: sys.exit() try: return temImagem except: return temImagem def pedeTexto(quadroDeTitulo): """Load text file""" sourceCharcode = 'iso-8859-15' # change it to ur language # Diálogo de procurar arquivo textFile = scribus.fileDialog(labelDeImportarTexto,filtroDeArquivosTexto,"", haspreview=1, issave=True) # issave=False shows "Save" button if textFile: try: t = open(textFile).read() t = unicode(t, sourceCharcode) except: scribus.messageBox( labelErroDeImportacao, msgDeErroDeImportacao, ICON_WARNING, BUTTON_OK ) t="" return t else: t="" return t def flow(frame,mode): """Compatibiliza Scribus 1.3.3 e 1.3.4""" try: scribus.textFlowsAroundFrame(frame, mode) # Scribus 1.3.3 except: scribus.textFlowMode(frame, mode) # Scribus 1.3.4 def pedeColunasDeImagem(): """Solicita a quantidade de colunas que a foto vai ocupar.""" colunasDaFoto = scribus.valueDialog( labelDeLarguraDeImagem, mensagemDeLarguraDeImagem, larguraDeColunasPadrao ) if not colunasDaFoto: sys.exit() try: return float(eval(colunasDaFoto)) except: return float(larguraDeColunasPadrao) def pedeFoto(): """ Escolhe arquivo de foto scribus.fileDialog('caption', ['filter', 'defaultname',haspreview, issave])""" # Abre diálogo para escolha de arquivo arquivoDaFoto = scribus.fileDialog( textFlowsAroundFrame, filtroDeImagens, "", # Nome default do arquivo haspreview=1, issave=True ) # Tenta abrir arquivo fornecido try: foto = Image.open(arquivoDaFoto) # uso da biblioteca PIL except: scribus.messageBox( labelErroDeImportacao, msgDeErroDeImportacao, ICON_WARNING, BUTTON_OK ) foto = "" return foto,arquivoDaFoto def montaQuadroDaFoto(quadroDeColunas): """Build a image frame with credit and legenda based in text frame number of columns""" # foto,arquivoDaFoto = pedeFoto() try: larguraDaFoto,alturaDaFoto = foto.size # para pegar o tamanho da imagem em pixels except: larguraDaFoto,alturaDaFoto = 4,3 # Se não conseguir, define proporção 4 por 3 # Escolhe quantas colunas a foto vai "abrir" colunasDaFoto = pedeColunasDeImagem() # Calcula largura da foto entrecoluna = scribus.getColumnGap(quadroDeColunas) colunasDeTexto = scribus.getColumns(quadroDeColunas) # Define o tamanho da foto larguraDoQuadroDeColunas,alturaDoQuadroDeColunas = scribus.getSize(quadroDeColunas) larguraDaColuna = (larguraDoQuadroDeColunas - (entrecoluna*(colunasDeTexto-1))) / colunasDeTexto # calcula posição da imagem quadroDeColunasEsquerda, quadroDeColunasTopo = scribus.getPosition(quadroDeColunas) # Posiciona foto a partir da segunda coluna de texto quadroDaFotoEsquerda = quadroDeColunasEsquerda + larguraDaColuna + entrecoluna quadroDaFotoTopo = quadroDeColunasTopo # Calcula largura e altura da imagem larguraDoQuadroDaFoto = colunasDaFoto * larguraDaColuna + (entrecoluna * (colunasDaFoto - 1)) alturaDoQuadroDaFoto = larguraDoQuadroDaFoto * alturaDaFoto / larguraDaFoto # Cria quadro de imagem com a foto quadroDaFoto = scribus.createImage(quadroDaFotoEsquerda, quadroDaFotoTopo, larguraDoQuadroDaFoto, alturaDoQuadroDaFoto) # Define fluxo para "Afastar texto" flow(quadroDaFoto, AROUND_FRAME) # Define formatação gráfica do quadro de foto scribus.setLineWidth( 0.8, quadroDaFoto) scribus.setLineColor( "Black", quadroDaFoto) scribus.setLineShade( 100, quadroDaFoto) scribus.setLineStyle(LINE_SOLID, quadroDaFoto) scribus.setLineJoin(JOIN_MITTER, quadroDaFoto) try: scribus.loadImage(arquivoDaFoto, quadroDaFoto) except: print "Não pude carregar a imagem." # Calcula escala da imagem para caber no quadro escalaDaFoto = larguraDoQuadroDaFoto / larguraDaFoto # Resescalona quadro de imagem scribus.scaleImage(escalaDaFoto, escalaDaFoto, quadroDaFoto) ######################################## # LEGENDA ######################################## # Calcula dimensões do quadro de legenda quadroDaLegendaTopo = quadroDaFotoTopo + alturaDoQuadroDaFoto alturaDoQuadroDaLegenda = entrecoluna * 2 # Cria quadro de legenda quadroDaLegenda = scribus.createText(quadroDaFotoEsquerda, quadroDaLegendaTopo, larguraDoQuadroDaFoto, alturaDoQuadroDaLegenda) # # Melhoria: Colocar leitor de input para escrever legenda # Liga "afastador de texto" flow(quadroDaLegenda, AROUND_FRAME) ########################################## # CREDITO ########################################## # Define medidas do quadro para crédito de foto quadroDaFotoDireita = quadroDaFotoEsquerda + larguraDoQuadroDaFoto quadroDaFotoBaixo = quadroDaFotoTopo + alturaDoQuadroDaFoto # Cria quadro de crédito quadroDoCredito = scribus.createText(quadroDaFotoDireita, quadroDaFotoBaixo, alturaDoQuadroDaFoto, entrecoluna) # # Melhoria: Colocar input de crédito para escrever credito # if not finalizado # processaNaoFinalizado() # Liga "afastador de texto" flow(quadroDoCredito, AROUND_FRAME) # Agrupa os elementos foto, legenda e crédito scribus.groupObjects([quadroDaFoto, quadroDaLegenda, quadroDoCredito]) # Se tiver a legenda e crédito no mesmo arquivo que o texto da matéria #scribus.linkTextFrames(quadroDeTitulo, quadroDaLegenda) # Liga os textos da legenda e dos créditos scribus.linkTextFrames(quadroDaLegenda, quadroDoCredito) # Se tiver a legenda e crédito no mesmo arquivo que o texto da matéria #scribus.linkTextFrames(credit, quadroDeColunas) # Deixa o quadro de legenda "em pé" scribus.rotateObject(90, quadroDoCredito) def montaMateria(espacoDaMateria): """ Manipula um bloco de notícia """ # Pega as coordenadas X e Y do objeto selecionado esquerdaDoQuadro, topoDoQuadro = scribus.getPosition(espacoDaMateria) # Pega a largura e altura do quadro larguraDoQuadro, alturaDoQuadro = scribus.getSize(espacoDaMateria) # Apaga objeto original, que era usado só pra marcar o espaço. scribus.deleteObject(espacoDaMateria) # Cria um bloco de texto com as medidas extremas do objeto # E deleta o objeto (que pode ser até uma linha) quadroDeTitulo = scribus.createText(esquerdaDoQuadro, topoDoQuadro, larguraDoQuadro, alturaDoQuadro) # Pergunta pelo número de colunas numeroDeColunas = pedeNumeroDeColunas() # Pergunta pelo espaço entre-colunas entrecolunas = pedeEntrecolunas() # Pergunta pelo tamanho do título em pontos corpoDeTitulo = pedeCorpoDoTitulo() # Pergunta pelo tamanho do antetítulo corpoDoSobretitulo = pedeCorpoDoSobretitulo() ####################################### # TÍTULO ####################################### # Calcula o tamanho total dos títulos alturaDoQuadroDeTitulo = (corpoDeTitulo + corpoDoSobretitulo) * proporcaoDeEntrelinha direitaDoQuadro = esquerdaDoQuadro + larguraDoQuadro # Redimensiona quadro de título scribus.sizeObject(larguraDoQuadro, alturaDoQuadroDeTitulo, quadroDeTitulo) # Define o tipo de fluxo de texto: "afastar texto" flow(quadroDeTitulo, AROUND_FRAME) ####################################### # COLUNAS DE TEXTO ####################################### # Define medidas do quadro de textos topoDasColunas = topoDoQuadro + alturaDoQuadroDeTitulo alturaDasColunas = alturaDoQuadro - alturaDoQuadroDeTitulo # Cria quadro de texto quadroDeColunas = scribus.createText(esquerdaDoQuadro, topoDasColunas, larguraDoQuadro, alturaDasColunas) # Define o fluxo de texto como "afastar texto" flow(quadroDeColunas, AROUND_FRAME) # Determina colunagem scribus.setColumnGap(entrecolunas, quadroDeColunas) scribus.setColumns(numeroDeColunas, quadroDeColunas) ####################################### # LINCA TEXTOS # Linca quadros de título e de texto ####################################### scribus.linkTextFrames(quadroDeTitulo,quadroDeColunas) storyGroup = scribus.groupObjects([quadroDeTitulo, quadroDeColunas]) ####################################### # PEGA TEXTO ####################################### # Quadro de escolha de arquivo text = pedeTexto(quadroDeTitulo) try: #scribus.insertText(text,-1,quadroDeTitulo) # para ANEXAR ao final em vez de substituir scribus.setText(text,quadroDeTitulo) except: text="SobretítulonTítulonAutor da matérianComplemento ao autornPrimeiro ParágrafonOutros parágrafosOutros parágrafos..." scribus.setText(text,quadroDeTitulo) ####################################### # PEGA FOTO ####################################### # Ask image temImagem = pedeImagem() if temImagem: montaQuadroDaFoto(quadroDeColunas) def manipulaSelecao(): """ Gerencia objetos selecionados """ # Pega o objeto selecionado story = scribus.getSelectedObject(0) # Se há apenas um objeto selecionado if story and scribus.selectionCount() == 1 : # if story: montaMateria(story) scribus.docChanged(True) else: #Melhoria pra o futuro: #por default, abrir uma matéria do tamanho da página # De momento, só avisa scribus.messageBox( labelDeQuadroDeTitulo, pedirParaAbrirQuadro, ICON_WARNING, BUTTON_OK ) def manipulaDocumento(): """Manipula documentos """ # Se há documento aberto if scribus.haveDoc(): #Desliga redraw scribus.setRedraw(False) #Guarda unidades do usuário # Save unit unit = scribus.getUnit() #Define novas unidades como "pontos tipográfico" scribus.setUnit(UNIT_POINTS) ############################# # Manipula objeto selecionado ############################# manipulaSelecao() #Recupera medidas do usuário scribus.setUnit(unit) else: # Senão há documento aberto, avisa. scribus.messageBox( labelAbreDoc, dizAbreDoc, ICON_WARNING, BUTTON_OK ) def myCode(): """ User code """ ######################### # USER CODE GOES HERE # ######################### # Gerencia documento manipulaDocumento() ######################### # USER CODE ENDS HERE # ######################### def main(argv): """Default main entry point""" myCode() def main_wrapper(argv): try: scribus.statusMessage("Rodando o script...") scribus.progressReset() main(argv) finally: if scribus.haveDoc(): scribus.setRedraw(True) scribus.statusMessage("") scribus.progressReset() if __name__ == '__main__': main_wrapper(sys.argv)
Bibliografia
- ROSSUM, Guido Van. Python Tutorial. Capítulo 4, More control flow tools, FOR statements. Site web disponível em: <http://www.python.org/doc/2.4.4/tut/node6.html#SECTION006200000000000000000>. Acesso em 25. jul. 2008.
- SCRIBUS. Arquivo de ajuda. Versão 1.3.3.12. Capítulo For Developers, seção Scripter API, página Page Comands. Disponível no programa através da tecla F1.
Olá,
Estou montando um script para Scribus ideia é possibilitar que os usuários possam criar capas para livros on-line depois de selecionarem algumas opções.
Para isso Scribus deverá estar instalado no nosso servidor Linux, você sabe de alguma maneira para instala-lo?
Olá, Rogerio!
Normalmente, há pacotes para as principais distribuições GNU/Linux. Por exemplo, para Debian, o comando é o seguinte:
sudo apt-get install scribus
Muito obrigado pela resposta! Mas parece que o Scribus ainda não da suporte para servidores…
Tenho outra pergunta. Você conhece alguma maneira de ajusta o tamanho da fonte de um texto de acordo com tamanho do fixo de uma caixa de texto criada com “createText” automaticamente?
Abs
Ainda não há esta opção no Scribus.
Tenho crescido lendo seus artigos. Você pode me enviar um script para edição de matérias no Indesign?
Obrigado, Wanderson!
Infelizmente, não tenho script para montar matérias no INDD. Deixei de usar o InDesign e agora só faço scripts para o Scribus.
Olá professor
Sou estudante de jornalismo e me formo em 2010. Gosto muito de diagramação e trabalho bastante com isso na agência onde faço um estágio. Gostaria de seguir na área, mas preciso de orientação quanto a quais cursos fazer (e se é preciso), se é uma área que dá retorno ($$), onde posso procurar por oportunidades, etc.
OBS: Responda para meu e-mail.
Muito obrigado
Camila