Hatena::ブログ(Diary)

エンジニアのソフトウェア的愛情 このページをアンテナに追加 RSSフィード Twitter

2016-05-28

Ruby/RSVG と Cairo を使って SVG から PDF を生成しためも

前回C++ で書いたのですが、調べたら Ruby でも同じように書くことができることがわかりました。

今回は libRSVG を Ruby から利用する Ruby/RSVG (rsvg2 gem) を利用。


require 'rsvg2'

source = <<~EOS
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg
   xmlns='http://www.w3.org/2000/svg'
   version='1.1'
   width='210mm'
   height='297mm'
>
  <rect x='100' y='100' width='100' height='100' fill='red' />
  <text
    font-family='sans-serif'
    font-size='18'
    x='200'
    y='200'>エンジニアのソフトウェア的愛情</text>
  <text
    font-size='12'
    x='200'
    y='220'>または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか</text>
</svg>
EOS

svg = RSVG::Handle.new_from_data(source)

dim = svg.dimensions

puts "document size = #{dim.width}x#{dim.height}"

surface = Cairo::PDFSurface.new('sample.pdf', dim.width, dim.height)

cairo = Cairo::Context.new(surface)

cairo.render_rsvg_handle(svg)

結果は前回と同じです。

Cairo::PDFSurface.new の第一引数文字列を渡すと出力先のファイルのファイル名と解釈します。第一引数write メソッドを持つオブジェクトを渡すと、そのメソッドを使って結果を書き出します。

詳しくはこちら: http://cairo.rubyforge.org/doc/ja/cairo-pdf-surface.html



SVG をベタに書くのがになったので、 Haml で描き直してみました。

ついでに Cairo::PDFSurface.new の第一引数Fileインスタンスwrite メソッドを持つオブジェクト)を渡しています。

HamlSVG を書く話は 以前の記事 なんかもご参照ください。

require 'haml'
require 'rsvg2'

source = Haml::Engine.new(<<EOS).to_html
!!! XML
%svg{xmlns: 'http://www.w3.org/2000/svg', version: '1.1', width: '210mm', height: '297mm'}
  %rect{x: 100, y: 100, width: 100, height: 100, fill: 'red'}
  %text{font: {family: 'sans-serif', size: 18}, x: 200, y: 200} エンジニアのソフトウェア的愛情
  %text{font: {size: 12}, x: 200, y: 220} または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか
EOS

svg = RSVG::Handle.new_from_data(source)

dim = svg.dimensions

puts "document size = #{dim.width}x#{dim.height}"

surface = Cairo::PDFSurface.new(File.new('sample.pdf', 'w'), dim.width, dim.height)

cairo = Cairo::Context.new(surface)

cairo.render_rsvg_handle(svg)


いつか読むはずっと読まない:バチガルピ短篇

イーガンをまとめて読んでいた時期があったせいか、第一篇を読んでてイーガンを読んでいるような錯覚をしてしまいました。設定が。

作風が似ているかどうか判断できるほど、わたしの SF 脳は肥えていない。

2016-05-27

Ruby で Haml を HTML にコンパイルする

使うべきは Haml::Engine#to_haml でした。

$ pry
[1] pry(main)> require 'haml'
=> true
[2] pry(main)> Haml::Engine.new('.hoge HOGE').to_html
=> "<div class='hoge'>HOGE</div>\n"

2016-05-24

libRSVG と Cairo を使って SVG から PDF を生成しためも

それぞれのサイト。


今回参照したドキュメント。


#if 0
# このファイルを Ruby で実行すると、このファイルを C++ のソースとしてコンパイルします。
# $ ruby minimum-svg2pdf.cpp
system('g++ --std=c++11 `pkg-config --cflags --libs cairo librsvg-2.0` -o minimum-svg2pdf minimum-svg2pdf.cpp')
__END__
#endif

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

#include <librsvg/rsvg.h>

#include <cairo/cairo.h>
#include <cairo/cairo-pdf.h>

static const std::string source =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
"<svg"
"   xmlns=\"http://www.w3.org/2000/svg\""
"   version=\"1.1\""
"   width=\"210mm\""
"   height=\"297mm\""
">"
"  <rect x=\"100\" y=\"100\" width=\"100\" height=\"100\" fill=\"red\" />"
"  <text"
"    font-family=\"sans-serif\""
"    font-size=\"18\""
"    x=\"200\""
"    y=\"200\">エンジニアのソフトウェア的愛情</text>"
"  <text"
"    font-size=\"12\""
"    x=\"200\""
"    y=\"220\">または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか</text>"
"</svg>";

int main(int argc, char* argv[])
{
    GError* error = 0; 
    RsvgHandle* svg = rsvg_handle_new_from_data(reinterpret_cast<const guint8*>(source.c_str()), source.length() , &error);

    if(error != 0)
    {
        std::cout << error->message << std::endl;
        return 0;
    }

    if(svg == 0)
    {
        std::cout << "no handle" << std::endl;
        return 0;
    }

    RsvgDimensionData dim;
    rsvg_handle_get_dimensions(svg, &dim);

    std::cout << "document size = " << dim.width << "x" << dim.height << std::endl;

    cairo_surface_t* surface = cairo_pdf_surface_create("sample.pdf", dim.width, dim.height);
    cairo_t* cairo = cairo_create(surface);
    cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0);
    rsvg_handle_render_cairo(svg, cairo);
    cairo_surface_flush(surface);

    cairo_destroy(cairo);
    cairo_surface_destroy(surface);

    rsvg_handle_close(svg, &error);

    if(error != 0)
    {
        std::cout << error->message << std::endl;
    }

    g_object_unref(svg);

    return 0;
}

コンパイルと実行*1

$ ruby minimum-svg2pdf.cpp
$ ./minimum-svg2pdf 
document size = 744x1052

こんなイメージの PDF ファイルが作成されます。

f:id:E_Mattsan:20160524232628p:image


いつか読むはずっと読まない:今ここにある世界のSF

フィクションであり、フィクションでない。


*1C/C++ のソースに Rubyスクリプトを埋め込んで自分に自分をコンパイルさせるやり方は横へなの鍋谷さんから教わりました。

2016-05-22

SVG から PDF

PDF を生成する prawn と、PrawnSVG を解釈する prawn-svg を使って、haml で書いた SVGPDF にした時のメモ。

prawn-svg の利用例として書かれているコードhaml で書き直したものです。


require 'prawn-svg'
require 'haml'

Prawn::Document.generate("test.pdf") do
  svg Haml::Engine.new(<<~EOS).to_html
    %svg
      %rect{width: 100, height: 100, fill: 'red'}
  EOS
end

2016-05-05

QRコードを生成したときのめも

QRencode で QR コードを生成し、Magick++(ImageMagickC++ API)で画像ファイルに保存してみた、ときのめも。

この二つのライブラリは、Mac の場合、 Homebrewインストールできます。


ソースコード

#include <iostream>
#include <sstream>
#include <algorithm>
#include <Magick++.h>
#include <qrencode.h>

int main(int argc, char* argv[])
{
    QRcode* qr = QRcode_encodeString(argv[1], 0, QR_ECLEVEL_H, QR_MODE_8, 1);
    int width = qr->width;

    std::ostringstream pbm_blob_sink;

    pbm_blob_sink << "P1\n" << width << " " << width << "\n";

    std::for_each(qr->data, qr->data + (width * width), [&](unsigned char data) { pbm_blob_sink << (data & 1) << " "; });

    std::string pbm_blob = pbm_blob_sink.str();
    Magick::Image image(Magick::Blob(pbm_blob.c_str(), pbm_blob.size()));
    image.write(argv[2]);

    return 0;
}

qrencode-sample.cpp という名前でファイルに保存してビルド

$ g++ --std=c++11 `Magick++-config --cppflags --cxxflags --ldflags --libs` -lqrencode -o qrencode-sample qrencode-sample.cpp

このブログのタイトルを QR コードに変換してみます。

$ ./qrencode-sample "エンジニアのソフトウェア的愛情 または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか http://d.hatena.ne.jp/E_Mattsan/" blog.png

結果。QR コードの 1 ドットを PNG の 1 ピクセルで表現しているので(それを拡大表示しているので)見た目がよくないですが、手持ちの iPhone の app で読み取ることができました。

f:id:E_Mattsan:20160505215004p:image:w360


エンコーディングShift-JIS の場合は、QR_MODE_KANJI を指定する必要があるようです。UTF-8 の場合、QR_MODE_8 で大丈夫でした。