Ruby & Ghostscript - Um diálogo possível
Friday, April 27th, 2007Hoje trabalho em um empresa de Telefonia IP que atende outras telefônicas, com uma média de 800 mil minutos/mês. Desenvolvemos nossa própria plataforma, incluindo billing, rota de destinação, interface com cliente, etc., a primeira versão foi desenvolvida em 5 meses utilizando Java. A parte mais delicada do sistema é o tarifador, e o desempenho de atendimento para vários clientes já apresentava QoS baixo mas funcionava.
Tivemos um curso de Design Patterns onde precisaríamos de certificados para uma licitação, o instrutor disse “Vocês conhecem Ruby?”. Eu “What?”, ele disse “depois eu falo..”. No mesmo dia fui pesquisar e encontrei o livro “Programming Ruby The Pragmatic Programmers’ Guide”, apt-get install ruby, abri o Kate e comecei meus “Hello Worlds”, isso era umas 20:30. Aí comecei a exclamar “P.t. que pariu!!”, “Errensga!!” , “Afi!!”, minha esposa me chama para dormir, eu “daqui a pouco”… quando fui ver o as horas 05:48, nem vi as horas passarem. No mesmo dia de manhã eu não fui ao curso(tava quebrado!), fui apenas a tarde. O instrutor “Eai conhecem Ruby”, eu estava anestesiado e só consegui falar PQP! Aí ele me mostrou Rails “The Web Framework”. Nem precisava tanto… só Ruby já bastava.
Após o termino do curso fui “meter as caras” e refazer o tarifador, em uma semana e meia estava pronto e testado, foi para produção no 12º dia de desenvolvimento, mais leve, menos Kloc…
Refizemos a aplicação web de administração/cliente em Rails em 1 mês e 10 dias, ficando apenas os relatórios via web service utilizando a nossa API PsBuilder (PostscriptBuilder gera pdf e ps)
Tentei e teste as ferramentas para relatório do Ruby mas nenhum se adaptava as faturas de 60 mil registros de ligações.
Trabalhei na Xerox(uma escola!) quase 8 anos utilizávamos vários PDL(Page Description Language) em vários OS (SunOS, Solaris, Novell, Linux, AIX, etc). Utilizam muito “Programação Orientada a Estagiário” <-- EU, fui muito "usado"(no bom sentido) tive que aprender XES, PCL, PDL/FDL, HPGL/2 e Postscript, ah! sem saber linguagem de programação, estava no 2º grau ainda, eu era magro nessa época...
Lá na Xerox a última linguagem que aprendi foi VIPP que é a cópia do Postscript que é cópia do InterPress que é a cópia dos scripts JaM. Então qual era a vantagem do VIPP? Funções “helpers” do Postcript. Uma aplicação de gráfico de linhas que demorava uns 5 dias para ser feita em Postcript em VIPP eram duas ou três linhas.
E o interessante que Postscript sempre foi uma ótima linguagem, podemos programar na linguagem ao contrário da maioria dos PDLs que só marcavam as páginas(markup language). Tinha(tem) um grande problema: para usar Postscript nas laser da Xerox tem que pagar royalties altos, elevando o preço dos equipamentos, portanto usávamos PDL’s próprios ou mais baratos como o PCL(da HP).
E o que que isso tem a ver com “Ruby & Ghostscript - Um diálogo possível”?
Do inicio deste mês de abril para cá, venho desenvolvendo uma API para geração de documentos mais profissionais. A estratégia é simples mas trabalhosa:
- Desenvolver funções em Postscript para tarefas mas árduas, callbacks e cursores
- Gerar um código intermediário(em Postscript) originado por Ruby
- Compilar com Ghostscript e gerar os outputs
Posso afirmar agora que teremos uma liberdade e facilidade de criação de documentos profissionais tento para mídia digital ou para arte final em papel como (certificados, faturas, boletos, etc).
Um exemplo mais real:
Após o desenho do template/formulário em aplicações gráficas como CorelDraw, Inkscape, Photoshop, Gimp e outros a API importará o template e o desenvolvedor Ruby apenas posicionará os dados nos campos.
Outro exemplo de flexibilidade:
Adicionar linhas, texto, imagens, vetores, templates, etc no documento ou nos callbacks:
:before_page_create,
:after_page_create,
:odd_pages,
:even_pages,
:before_document_create,
:first_page,
:last_page,
:after_document_create
tendo a possibilidade de condição only ou except . Exemplo:
d=Document.new d.before_page_create :only => [3,4,9] do set Text.new "Relatório XXX", :align => :center end d.even_pages :except => [3] do set HorizontalLine.new :bottom, :start_in => '3 cm', :size => '10 cm' end d.render :pdf
Já terminei as funções em Postscript e criei um módulo via Ruby Extending comunicando com libgs.so, ainda não testei em Windows.
A API será Open Source. Daqui uns 2 meses teremos a primeira versão pública.
Abaixo a lista dos OUTPUTS
Displays
:lvga256 => “Linux vgalib, 256-color VGA modes => [Linux only]“,
:vgalib => “Linux vgalib, 16-color VGA modes => [Linux only]“,
:x11 => “X Windows version 11, release >=4 => [Unix and VMS only]“,
:x11alpha => “X Windows masquerading as a device with alpha capability”,
:x11cmyk => “X Windows masquerading as a 1-bit-per-plane CMYK device”,
:x11cmyk2 => “X Windows as a 2-bit-per-plane CMYK device”,
:x11cmyk4 => “X Windows as a 4-bit-per-plane CMYK device”,
:x11cmyk8 => “X Windows as an 8-bit-per-plane CMYK device”,
:x11gray2 => “X Windows as a 2-bit gray-scale device”,
:x11gray4 => “X Windows as a 4-bit gray-scale device”,
:x11mono => “X Windows masquerading as a black-and-white device”,
:x11rg16x => “X Windows with G5/B5/R6 pixel layout for testing.”,
:x11rg32x => “X Windows with G11/B10/R11 pixel layout for testing.”,
Impressoras
:atx23 => “Practical Automation ATX-23 label printer”,
:atx24 => “Practical Automation ATX-24 label printer”,
:atx38 => “Practical Automation ATX-38 label printer”,
:deskjet => “H-P DeskJet and DeskJet Plus”,
:djet500 => “H-P DeskJet 500; use -r600 for DJ 600 series”,
:fs600 => “Kyocera FS-600 (600 dpi)”,
:laserjet => “H-P LaserJet”,
:ljet2p => “H-P LaserJet IId/IIp/III* with TIFF compression”,
:ljet3 => “H-P LaserJet III* with Delta Row compression”,
:ljet3d => “H-P LaserJet IIID with duplex capability”,
:ljet4 => “H-P LaserJet 4 (defaults to 600 dpi)”,
:ljet4d => “H-P LaserJet 4 (defaults to 600 dpi) with duplex”,
:ljetplus => “H-P LaserJet Plus”,
:lj5mono => “H-P LaserJet 5 & 6 family (PCL XL), bitmap:”,
:lj5gray => “H-P LaserJet 5 & 6 family, gray-scale bitmap;”,
:lp2563 => “H-P 2563B line printer”,
:oce9050 => “OCE 9050 printe”,
:pxlmono => “H-P black-and-white PCL XL printers (LaserJet 5 and 6 family)”,
:pxlcolor => “H-P color PCL XL printers (e.g. Color LaserJet 4500)”,
Formato para Fax
:faxg3 => “Group 3 fax, with EOLs but no header or EOD”,
:faxg32d => “Group 3 2-D fax, with EOLs but no header or EOD”,
:faxg4 => “Group 4 fax, with EOLs but no header or EOD”,
:tiffcrle => “TIFF CCITT RLE 1-dim (= Group 3 fax with no EOLs)”,
:tiffg3 => “TIFF Group 3 fax (with EOLs)”,
:tiffg32d => “TIFF Group 3 2-D fax”,
:tiffg4 => “TIFF Group 4 fax”,
Vetor e Imagem
:epswrite => “EPS output (like PostScript Distillery)”,
:pdfwrite => “PDF output (like Adobe Acrobat Distiller)”,
:pswrite => “PostScript output (like PostScript Distillery)”,
:pxlmono => “Black-and-white PCL XL”,
:pxlcolor => “Color PCL XL”,
Raster file formats and devices
:bit => “Plain bits, monochrome”,
:bitrgb => “Plain bits, RGB”,
:bitcmyk => “Plain bits, CMYK”,
:bmpmono => “Monochrome MS Windows .BMP file format”,
:bmpgray => “8-bit gray .BMP file format”,
:bmpsep1 => “Separated 1-bit CMYK .BMP file format, primarily for testing”,
:bmpsep8 =>”Separated 8-bit CMYK .BMP file format, primarily for testing”,
:bmp16 => “4-bit (EGA/VGA) .BMP file format”,
:bmp256 => “8-bit (256-color) .BMP file format”,
:bmp16m => “24-bit .BMP file format”,
:bmp32b => “32-bit pseudo-.BMP file format”,
:cgmmono => “Monochrome (black-and-white) CGM — LOW LEVEL OUTPUT ONLY”,
:cgm8 => “8-bit (256-color) CGM — DITTO”,
:cgm24 => “24-bit color CGM — DITTO”,
:jpeg => “JPEG format, RGB output”,
:jpeggray => “JPEG format, gray output”,
:miff24 => “ImageMagick MIFF format, 24-bit direct color, RLE compressed”,
:pcxmono=> “PCX file format, monochrome (1-bit black and white)”,
:pcxgray=> “PCX file format, 8-bit gray scale”,
:pcx16 => “PCX file format, 4-bit planar (EGA/VGA) color”,
:pcx256 => “PCX file format, 8-bit chunky color”,
:pcx24b => “PCX file format, 24-bit color (3 8-bit planes)”,
:pcxcmyk => “PCX file format, 4-bit chunky CMYK color”,
:pbm => “Portable Bitmap (plain format)”,
:pbmraw => “Portable Bitmap (raw format)”,
:pgm => “Portable Graymap (plain format)”,
:pgmraw => “Portable Graymap (raw format)”,
:pgnm => “Portable Graymap (plain format), optimizing to PBM if possible”,
:pgnmraw => “Portable Graymap (raw format), optimizing to PBM if possible”,
:pnm => “Portable Pixmap (plain format) (RGB), optimizing to PGM or PBM”,
:pnmraw => “Portable Pixmap (raw format) (RGB), optimizing to PGM or PBM”,
:ppm => “Portable Pixmap (plain format) (RGB)”,
:ppmraw => “Portable Pixmap (raw format) (RGB)”,
:pkm => “Portable inKmap (plain format) (4-bit CMYK => RGB)”,
:pkmraw => “Portable inKmap (raw format) (4-bit CMYK => RGB)”,
:pksm => “Portable Separated map (plain format) (4-bit CMYK => 4 pages)”,
:pksmraw => “Portable Separated map (raw format) (4-bit CMYK => 4 pages)”,
:plan9bm => “Plan 9 bitmap format”,
:pngmono => “Monochrome Portable Network Graphics (PNG)”,
:pnggray => “8-bit gray Portable Network Graphics (PNG)”,
:png16 => “4-bit color Portable Network Graphics (PNG)”,
:png256 => “8-bit color Portable Network Graphics (PNG)”,
:png16m => “24-bit color Portable Network Graphics (PNG)”,
:psmono => “PostScript (Level 1) monochrome image”,
:psgray => “PostScript (Level 1) 8-bit gray image”,
:psrgb => “PostScript (Level 2) 24-bit color image”,
:tiff12nc => “TIFF 12-bit RGB, no compression”,
:tiff24nc => “TIFF 24-bit RGB, no compression (NeXT standard format)”,
:tifflzw => “TIFF LZW (tag = 5) (monochrome)”,
:tiffpack => “TIFF PackBits (tag = 32773) (monochrome)”


