RubyのサーバーでPDFを生成する

1か月以上前に、Ruby開発者のチームで、スタートアップのレイアウトデザイナーを獲得しました。 とても幸運だったので、チームは非常に良かったので、勉強したいという私の希望は、優秀な専門家を獲得したいという希望と一致しました。



HTMLレイアウト自体には小さな価値があり、レイアウトデザイナをロードできるのはそれだけではありません。



当社のウェブサイトでは、ユーザーは自分で購入し、電子チケットによる確認がメールに送信されます。 これは注文の詳細を示します。また、良いプロジェクトではすべてが明るく明るくなければならないので、デザイナーはレシートのレイアウトを描きました。 さて、私は、レイアウトデザイナーとして、これらすべてをコードで実装するように指示されました。



Rubyのジェネレーターオプション



Ruby Toolbox Webサイトによると、PDFファイルを生成するための2つの基本的なアプローチがあります。





最初のオプションでは、HTMLページを生成してPDFに変換しますが、2番目のオプションでは、実際には、キャンバスで作業し、追加のレイヤーなしでドキュメントを生成できます。



私はおなじみのHTMLとCSSの世界から出てきたにもかかわらず、Prawnを使用してオプションを選択しました(ほとんどの場合、前のバージョンのPDFファイルはこの方法で生成されたためです)。



興味のある人は、私はあなたをhabrakatに招待します。



エビを扱う機能



このgemをプロジェクトに接続して構成する方法を説明するつもりはありません-ハブにはすでに同様の記事がありました このgemを使用したドキュメントレイアウトの機能について説明します。



ページ設定


私が最初に出会ったのは、用紙サイズです。 デフォルトで。 エビは、新しい文書にレター用紙サイズを使用します。



また、背景画像である余白フィールドを指定することもできます。



また、ピクセルではなく、活版印刷アイテムを使用していることを思い出してください。



img = "#{Prawn::DATADIR}/images/background.jpg" Prawn::Document.generate('hello.pdf', :page_size => "A4", :margin => 20, :background => img) do |pdf| pdf.text 'Hello World!' end
      
      





このコードは、20ポイントのマージン、背景画像background.jpg、およびテキスト「Hello World!」を持つA4サイズのシートにhello.pdfというドキュメントを生成します。



テキストブロック出力


ジェネレータは、 テキストtext_boxの 2種類のテキスト出力をサポートしています 。 最初のケースでは、カーソルが現在設定されている場所にテキスト行が表示されます。 2番目では、テキスト付きのコンテナが表示されます。幅高さのオプションで寸法を設定し、オプションのオーバーフロー (値:expandshrink_to_fitを受け入れます)、境界線、そして最も重要なパラメータの絶対位置:atで寸法を設定できます。



前に引用したコードで「Hello World!」を置き換える場合 「ハローハブル!」 その後、フォントに問題が発生します。



このプロジェクトでは、独自のフォントProxima Novaを使用します。 ジェネレーターが使用するフォントと使用するテキストスタイルを知るには、フォントを明示的に指定する必要があります。



 font_families: { proxima: { bold: "assets/fonts/proximanova-bold-webfont.ttf", normal: "assets/fonts/proximanova-reg-webfont.ttf", light: "assets/fonts/ProximaNova-Light.ttf" } } Prawn::Document.generate('hello.pdf') do |pdf| pdf.font_families.update("Proxima Nova" => @opts[:font_families][:proxima]) pdf.font "Proxima Nova", size: 12, style: bold pdf.text 'Hello World!' end
      
      





このコードは、フォントproximanova-bold-webfont.ttfを使用して、サイズが12ポイントの同じテキストを出力します。



フォントの色は、ドキュメント属性fill_colorを介して設定され、16進数の値を持つことができます。



 fill_color = "ffffff"
      
      





さらに、 default_leadingを使用して、またはテキストブロックでleadigパラメーターを直接指定することにより、行間隔を指定できます。 段落間のインデントは、 indent_paragraphsで指定されます。



テキストを印刷するとき、複雑なスペースがよく使用されます。 また、HTMLドキュメントを使用していないため、単に を指定できます。 その後、トリックに移動して、特別なメソッドPrawn :: Text :: NBSPに置き換える必要があります。



また、Prawnは、カーニングと文字間隔のそれぞれ:kerning:character_spacingなどのパラメーターも認識します。 カーニングはtrueまたはfalseを受け入れますが、 character_spacingはポイントで指定されます。



 default_leading 5 text string, :kerning => true, :character_spacing => 5 move_down 20 text string, :leading => 10, :indent_paragraphs => 60 move_down 20 text string + '#{Prawn::Text::NBSP * 10}' + string
      
      





このコードは、行間隔を5ポイントに設定し、カーニングと文字間隔が5ポイントの行を表示し、カーソルを20ポイント下げ、行間隔が10ポイント、段落間隔が60ポイントの行を表示します。 カーソルをさらに20ポイント下に移動し、10個の不可解なスペースで区切られた2行を表示します。



テキストを操作するときの追加機能のうち、パラメーターとしてテキストを回転させる機能に言及する価値があります:rotateは、値として度単位の角度を取ります。 オプションのパラメータ:rotate_aroundを使用して、回転の方向(デフォルト:upper_left )とHTMLの精神で行のフォーマットを設定することもできます。



 text " <font size='18'></font>  " + "<font name='Courier'>  </font>  font  " + "<font character_spacing='2'> </font>. ", :inline_format => true
      
      





しかし、私の仕事は本を印刷することではなく、注文と電子チケットに関する情報を表示することであったため、テキストの操作の詳細には触れませんでした。



ポジショニング


エビでは、エレメントは、 move_downを介しカーソルを下に移動することでドキュメントの上部から相対的に、または絶対的に配置されます。 想定されるように左上隅からではなく、グラフの構築の場合のように左下から来るため、主な困難が生じるのは絶対配置です。 この機能と、測定単位がポイントであり、通常のピクセルではないという事実により、レイアウトで最も困難が生じました。



 text_box 'test', :at[10,100]
      
      





このコードは、ページの下部に文字列「test」を出力します。ページの左マージンから10ポイント、下部から100ポイントです。



グラフィックプリミティブ


デザイナーが描いたレイアウトには、画像を実際に埋め込みたくないグラフィック要素がたくさんありました。 このような場合のために、ジェネレーターはグラフィックプリミティブ-線( horizo​​ntal_linevertical_line )、円( fill_circleおよびstroke_circle )および多角形( fill_polygonおよびstroke_polygon )で塗りつぶしの有無に関係なく機能します。



塗りつぶしの色はテキストの色と同じように使用され、 fill_colorでも設定されます。輪郭線の色はstroke_colorで指定されます。 さらに、 line_widthパラメーターを使用して線幅を指定できます。



これは、ストローク、中心線、三角形のポインターで円を描く関数の例です



 def draw_circle_part(colors, left, top, pdf) pdf do fill_color colors['circle_1'] fill_polygon [left['circles_left'], top['polygon_2_top']], [left['line_1'], top['polygon_1_top']], [left['line_2'], top['polygon_1_top']] fill_color colors['circle_2'] fill_circle [left['circles_left'], top['circles_top']], 28 stroke_color colors['circle_3'] line_width 1.5 stroke_circle [left['circles_left'], top['circles_top']], 28 stroke_color colors['circle_4'] stroke do horizontal_line left['line_1'], left['line_2'], :at => top['circles_top'] end line_width 1 end end
      
      





また、曲線や任意の線を描画できると便利な場合があります。 これを行うには、 stroke.lineメソッドstroke.curveメソッドを使用して、指定したポイントから別のポイントに直線と曲線を描画し、現在のカーソル位置からポイントまでの線にstroke.line_tostroke.curve_toを描画します。 さらに、カーブのパラメータを設定できます:bounds 、カーブが通過するポイントを示します。 さらに、ベジェ変換が構築に使用されます。



 stroke do line [300,200], [400,50] curve [500, 0], [400, 200], :bounds => [[600, 300], [300, 390]] end
      
      





画像


画像を扱うときは、サイズに非常に注意し、レイアウトよりも画像を200%大きく準備し、エビに明示的に寸法を設定することをお勧めします。ジェネレーターはレイアウトとまったく同じようにレンダリングするよりも画像を縮小できます。



個人的な経験から、注文の詳細のブロックにアイコンを挿入し、元のサイズのレイアウトから直接切り取った画像を使用すると、画像が拡大されていないかのようにぼやけた境界線が表示されました。 経験的に、私にとっては、挿入された画像と元の画像の理想的な比率を2対1に設定しました。幸いなことに、レイアウト内のすべてのオブジェクトはグラフィックプリミティブとして描画され、サイズ変更の問題はありませんでした。



デフォルトの画像は元のサイズで、カーソルが置かれているポイントに配置されます。 絶対配置の場合は、パラメーターatを使用します。 画像の同じ配置に関しては、パラメータを使用できます:position 、値を取る:left 、:center ,: rightまたは左境界からのポイント数とパラメータ:vposition 、値を取る:top 、:center ,: bottomまたはインデントで下の境界からポイント。



画像の高さと幅はそれぞれ:height:widthで設定できます。 さらに、1つのパラメーターのみが指定されている場合、2番目のパラメーターは比率が保持された状態で自動的に選択されます。 同様に、正確な寸法ではなく、 scaleを使用して画像の比例変化を指定できます。



 image "assets/images/details.png", :at => [25, 641], :height => 22 image "assets/images/prawn.png", :scale => 0.7, :position => :right, :vposition => 100
      
      





テーブル


テーブルを使用せずに領収書を作成することはほとんど不可能です。 Prawnは、テーブルを作成するための2つのメソッドを提供します: tablemake_table 両方のメソッドはテーブルを作成しますが、make_tableは作成されたテーブルのみを返すのに対して、テーブルはテーブルを作成した直後にレンダリングメソッドを呼び出すという唯一の違いがあります。

テーブルを作成する最も便利な方法は、データ配列の配列をメソッドに渡すことです。各内部配列は単一の行です。 make_tableで作成したオブジェクトを配列に渡すと、テーブル内にテーブルが作成されます。

また、キーを使用してハッシュをテーブルに転送することもできます。イメージの場合はイメージ、フォーマットされたテキストを挿入する場合はコンテンツです。



 cell_1 = make_cell(:content => "this row content comes directly ") cell_2 = make_cell(:content => "from cell objects") two_dimensional_array = [ ["..."], ["subtable from an array"], ["..."] ] my_table = make_table([ ["..."], ["subtable from another table"], ["..."] ]) image_path = "#{Prawn::DATADIR}/images/stef.jpg" table([ ["just a regular row", "", "", "blah blah blah"], [cell_1, cell_2, "", ""], ["", "", two_dimensional_array, ""], ["just another regular row", "", "", ""], [{:image => image_path}, "", my_table, ""]])
      
      





このコードはそのようなテーブルを出力します(ドキュメントの例):







テーブルには次のオプションを指定できます。



さらに、セルのパラメーターを設定できます。



これはテーブルプロパティの完全なリストではありませんが、エビのテーブルに慣れて、適用されるほとんどの問題を解決するには十分です。



おわりに



このジェネレーターでの作業の結果に基づいて、私は次のように言うことができます-私の特定のPrawnタスクでは、生成のために入力する必要があるコードは非常に面倒に見えますが、レシート自体にはプロジェクト内にルートがなく、生成されますバックエンドから受信したJSONデータセットから。

個人的には、エビのコードの繰り返しブロックを個別の関数に作成し、それらを適切な場所で単に呼び出し、Rubyコードを使用して入ってくるデータセットを解析し、オブジェクトを反復処理するのは便利でした。これはHTMLで行うのは非常に問題です。



困難なルートにいる2人の乗客のデータを入手したドキュメントの例は、ここからダウンロードできます。Eチケット



ただし、サイト上の既存のページのpdfバージョンを提供するだけの場合は、指定されたHTMLページから直接PDFファイルを作成できるPDFKitを使用する方が簡単で収益性が高くなります。



All Articles