1. Perl
  2. Form creation
  3. here

Drawing basic figures and creating tables - PDF::Create forms with API2

I will explain how to create a table with PDF::API2.

PDF::API2 has the ability to create straight lines, fillable circles, triangles, rectangles and even more polygons.

If you can draw straight lines, you can combine them to create a table. If you want to set the background color for the table, you can do it by drawing a fillable rectangle.

HTML tables can be used to create tables. Here, let's make it possible to express the following structure as a PDF table.

<table>
  <tr> <td> Heading 1 </td> <td> Heading 2 </td> <td> Heading 3 </td> </tr>
  <tr> <td> Item </td> <td> Item </td> <td> Item </td> </tr>
  <tr> <td> Item </td> <td> Item </td> <td> Item </td> </tr>
</table>
Heading 1 Heading 2 Heading 3
Item Item Item
Item Item Item

Draw basic shapes

First of all, let's draw a basic shape with PDF::API2 as a basis for creating a table.

Generate content object for graphics

First of all, create a content object for the graphic. You can create a content object for graphics by calling the gfx method from the page object. This is a PDF::API2::Content object.

# Generate PDF object
my $pdf = PDF::API2->new;

# Create page
my $page = $pdf->page;

# Generate content objects for graphics
my $gfx = $page->gfx;

Draw a line

As the most basic drawing, let's draw a line with PDF::API2.

The move method creates a new path and moves you to the starting point. Use the line method to move the path to the end point.

No lines are actually drawn yet. To actually draw the line, call the stroke method.

# Setting the start point of the line
my $start_x = 100;
my $start_y = 100;
$gfx->move($start_x, $start_y);

# Set the end point of the line
my $end_x = 300;
my $end_y = 300;
$gfx->line($end_x, $end_y);

# Draw a line
$gfx->stroke;

When drawing figures in PDF::API2, remember the feeling of connecting paths and drawing.

Set line thickness

The line thickness can be set with the linewidth method. Call it before you stroke.

# Line thickness setting
$gfx->linewidth(3);

The line width is the same for the settings when drawing circles and polygons.

Set line color

The line color can be set with the strokecolor method. Call it before you stroke. The color can be specified by color name, RGB, or CMYK. Each color can have a 1-byte, 2-byte, 3-byte, or 4-byte value. For example, cyan can be set as%F000 or%FFFF000000000000.

# Set the line color (color name)
$gfx->strokecolor('red');

# Set line color (RGB)
$gfx->strokecolor('# 0123ab');

# Set line color (CMYK)
$gfx->strokecolor('%FF000000');

The line color is the same for the settings when drawing circles and polygons.

The color format is the same for the fill color settings.

Draw a triangle surrounded by a line

As an application of drawing a line, let's draw a triangle surrounded by a line. The line method connects the points and finally returns to the starting point.

use strict;
use warnings;
use utf8;

use PDF::API2;

# Generate PDF object
my $pdf = PDF::API2->new;

my $page = $pdf->page;

my $gfx = $page->gfx;

# Point P1
my $x1 = 100;
my $y1 = 100;
$gfx->move($x1, $y1);

# Point P2
my $x2 = 300;
my $y2 = 300;
$gfx->line($x2, $y2);

# Point P3
my $x3 = 500;
my $y3 = 100;
$gfx->line($x3, $y3);

# Return to point P1
$gfx->line($x1, $y1);

# Draw a line
$gfx->stroke;

my $pdf_file = 'render_tri.pdf';
$pdf->saveas($pdf_file);

Output triangle

You can use the poly method to set points to move and pass multiple points to draw a continuous line.

use strict;
use warnings;
use utf8;

use PDF::API2;

# Generate PDF object
my $pdf = PDF::API2->new;

my $page = $pdf->page;

my $gfx = $page->gfx;

# Point P1
my ($x1, $y1) = (100, 100);

# Point P2
my ($x2, $y2) = (300, 300);

# Point P3
my ($x3, $y3) = (500, 100);

# Draw triangles at once with poly method
$gfx->poly($x1, $y1, $x2, $y2, $x3, $y3, $x1, $y1);

# Draw a line
$gfx->stroke;

my $pdf_file = 'render_tri.pdf';
$pdf->saveas($pdf_file);

Fill

You can fill it by enclosing it in a path. Use the fill method to fill. The default fill color is black. Note that fill must be called before the stroke.

# Fill (before stroke)
$gfx->fill;

# Draw a line
$gfx->stroke;

There is also a fillstroke method that fills and draws lines at the same time.

# Fill and draw a line
$gfx->fillstroke;

Set the fill color

The fill color can be set with the fillcolor method. Call it before filling. The color can be specified by color name, RGB, or CMYK. Each color can have a 1-byte, 2-byte, 3-byte, or 4-byte value. For example, cyan can be set as%F000 or%FFFF000000000000.

# Set the fill color (color name)
$gfx->fillcolor('red');

# Set fill color (RGB)
$gfx->fillcolor('# 0123ab');

# Set fill color (CMYK)
$gfx->fillcolor('%FF000000');

Draw a rectangle

PDF::API2 has a convenient method for drawing rectangles. All shapes can be created by connecting lines using the above method, but since there are many steps to connect lines, a method for drawing basic shapes such as rectangles is prepared.

Let's create a rectangle and fill the background with a light gray color. There are rect method and rectxy method to draw a rectangle, but let's use rectxy which can draw a rectangle just by specifying the start point and the point opposite the diagonal line.

use strict;
use warnings;
use utf8;

use PDF::API2;

# Generate PDF object
my $pdf = PDF::API2->new;

# Create page
my $page = $pdf->page;

# Generate content objects for graphics
my $gfx = $page->gfx;

# Point P1
my $x1 = 100;
my $y1 = 100;

# Diagonal point opposite P3
my $x3 = 300;
my $y3 = 300;

# Draw a rectangle
$gfx->rectxy($x1, $y1, $x3, $y3);

# Make the line color a little lighter than black
$gfx->strokecolor('# 333');

# Make the background color very light gray
$gfx->fillcolor('# eee');

# Fill the rectangle and draw a line
$gfx->fillstroke;

my $pdf_file = 'basic_rect.pdf';
$pdf->saveas($pdf_file);

This is an example that can draw a rectangle that can be executed as it is.

Write other basic figures

I will introduce which method to use when you want to write other basic figures.

Write a circle

If you want to draw a circle, use the circle method. It can be used for bullet points in the list.

Draw an ellipse

If you want to draw an ellipse, use the ellipse method.

Write a fan shape

If you want to draw a sector, use the pie method.

Draw an arc

If you want to draw an arc, use the arc or bogen method. The bogen method can be used for rounded corners because you can specify the start and end points of the arc.

Draw an elliptical arc

If you want to draw an elliptical arc, use the bogen method.

Write a curve (Peget curve)

To draw a curve (Peget curve), use the curve method. You can draw a curve by the Peje curve algorithm.

Write a curve (spline curve)

To draw a curve (spline curve), use the spline method. You can draw a curve that connects points smoothly.

Draw a polygon

Use the poly method to draw a polygon. You can draw triangles, pentagons, hexagons, etc.

Rotate the shape

Use the rotate method to rotate the shape.

Enlarge/reduce the figure

Use the scale method to scale the shape.

To draw a horizontal line

There is a hline method that allows you to easily draw a horizontal line.

To draw a vertical line

There is a vline method that allows you to easily draw vertical lines.

To draw a dashed line

To draw a wavy line, use the linedash method.

Create a table

Now let's create a table.

The idea of the table is that there is a frame on the outermost side. There is also a frame for each item. If you can overlap the item frame and the outer frame, the table is complete. It seems that you can draw by combining the rectangles and overlapping the outside.

However, a problem with this method was discovered. If you draw twice at the same position, the lines will become darker.

So, let's create a table by simply drawing lines vertically and horizontally.

use PDF::API2;

# Generate PDF object
my $pdf = PDF::API2->new;

# Default global setting for paper size (to get paper size)
$pdf->mediabox(undef);

# Page height and width
my @page_size_infos = $pdf->mediabox;
my $page_width = $page_size_infos[2];
my $page_height = $page_size_infos[3];

# Item height
my $item_height = 25;

# Number of rows
my $rows_count = 20;

# Width of each item
my $name_width = 230;
my $unit_price_width = 80;
my $count_width = 80;
my $price_width = 80;
my $table_width = $name_width + $unit_price_width + $count_width + $price_width;

# Table height
my $table_height = $item_height * $rows_count;

# Starting point of the table

# Table outer frame
my $table_start_x = 50;
my $table_start_y = $page_height - 50;
my $table_other_side_x = $table_start_x + $table_width;
my $table_other_side_y = $table_start_y-$table_height;

# Create page
my $page = $pdf->page;

# Generate content objects for graphics
my $gfx = $page->gfx;

# Draw the upper line
$gfx->move($table_start_x, $table_start_y);
$gfx->line($table_start_x + $table_width, $table_start_y);

# Draw the line on the left
$gfx->move($table_start_x, $table_start_y);
$gfx->line($table_start_x, $table_start_y-$table_height);

my $cur_x = $table_start_x;
my $cur_y = $table_start_y;

# Drawing the frame of each item
for (my $column = 0; $column < $rows_count; $column ++) {
  
  # Name frame (right and bottom)
  $gfx->move($cur_x + $name_width, $cur_y);
  $gfx->line($cur_x + $name_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $name_width, $cur_y-$item_height);
  $cur_x += $name_width;

  # Unit price frame (right and bottom)
  $gfx->move($cur_x + $unit_price_width, $cur_y);
  $gfx->line($cur_x + $unit_price_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $unit_price_width, $cur_y-$item_height);
  $cur_x += $unit_price_width;

  # Quantity frame (right and bottom)
  $gfx->move($cur_x + $count_width, $cur_y);
  $gfx->line($cur_x + $count_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $count_width, $cur_y-$item_height);
  $cur_x += $count_width;

  # Price frame (right and bottom)
  $gfx->move($cur_x + $price_width, $cur_y);
  $gfx->line($cur_x + $price_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $price_width, $cur_y-$item_height);
  $cur_x += $price_width;
  
  # To the next line
  $cur_x = $table_start_x;
  $cur_y-= $item_height;
}

# Make the line color a little lighter than black
$gfx->strokecolor('# 333');

# Fill the rectangle and draw a line
$gfx->stroke;

my $pdf_file = 'table.pdf';
$pdf->saveas($pdf_file);

You have completed a table with no information.

Output result empty table,

Example table with headings and items

Finally, let's create an example table with headings and items.

The following table will be exported to PDF.

Name Unit Count Price
Book1 1000 3 3000
Book2 2000 6 12000
Book3 1500 5 7500
use strict;
use warnings;
use utf8;

use PDF::API2;

# Generate PDF object
my $pdf = PDF::API2->new;

# Default global setting for paper size (to get paper size)
$pdf->mediabox(undef);

# Page height and width
my @page_size_infos = $pdf->mediabox;
my $page_width = $page_size_infos[2];
my $page_height = $page_size_infos[3];

# Item height
my $item_height = 25;

# Number of rows
my $rows_count = 20;

# Width of each item
my $name_width = 230;
my $unit_price_width = 80;
my $count_width = 80;
my $price_width = 80;
my $table_width = $name_width + $unit_price_width + $count_width + $price_width;

# Table height
my $table_height = $item_height * $rows_count;

# Starting point of the table

# Table outer frame
my $table_start_x = 50;
my $table_start_y = $page_height - 50;
my $table_other_side_x = $table_start_x + $table_width;
my $table_other_side_y = $table_start_y-$table_height;

# Create page
my $page = $pdf->page;

# Generate content objects for graphics
my $gfx = $page->gfx;

# Generate content object for text
my $text = $page->text;
my $font = $pdf->corefont('Helvetica');
my $font_bold = $pdf->corefont('Helvetica-Bold');
my $font_size = 11;
$text->font($font, $font_size);
my $text_height = $font_size;

my $books = [
  {
    name =>'Book1',
    unit_price => 1000,
    count => 3,
  },
  {
    name =>'Book2',
    unit_price => 2000,
    count => 6,
  },
  {
    name =>'Book3',
    unit_price => 1500,
    count => 5,
  }
];;
my $books_length = @$books;

# Draw the upper line
$gfx->move($table_start_x, $table_start_y);
$gfx->line($table_start_x + $table_width, $table_start_y);

# Draw the line on the left
$gfx->move($table_start_x, $table_start_y);
$gfx->line($table_start_x, $table_start_y-$table_height);

my $cur_x = $table_start_x;
my $cur_y = $table_start_y;

my $item_padding_top = 17;
my $item_padding_left = 5;

# Drawing the frame of each item
for (my $column = 0; $column < $rows_count; $column ++) {
  
  my $book_index = $column - 1;
  
  # Name frame (right and bottom)
  # Heading
  $text->translate($cur_x + $item_padding_left, $cur_y-$item_padding_top);
  if ($column == 0) {
    $text->font($font_bold, $font_size);
    $text->text('Name');
  }
  # Item
  else {
    $text->font($font, $font_size);
    if ($book_index < $books_length) {
      $text->text($books->[$book_index]{name});
    }
  }
  $gfx->move($cur_x + $name_width, $cur_y);
  $gfx->line($cur_x + $name_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $name_width, $cur_y-$item_height);
  $cur_x += $name_width;

  # Unit price frame (right and bottom)
  $text->translate($cur_x + $item_padding_left, $cur_y-$item_padding_top);
  if ($column == 0) {
    $text->font($font_bold, $font_size);
    $text->text('Unit');
  }
  # Item
  else {
    $text->font($font, $font_size);
    if ($book_index < $books_length) {
      $text->text($books->[$book_index]{unit_price});
    }
  }
  $gfx->move($cur_x + $unit_price_width, $cur_y);
  $gfx->line($cur_x + $unit_price_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $unit_price_width, $cur_y-$item_height);
  $cur_x += $unit_price_width;

  # Quantity frame (right and bottom)
  # Heading
  $text->translate($cur_x + $item_padding_left, $cur_y-$item_padding_top);
  if ($column == 0) {
    $text->font($font_bold, $font_size);
    $text->text('Count');
  }
  # Item
  else {
    $text->font($font, $font_size);
    if ($book_index < $books_length) {
      $text->text($books->[$book_index]{count});
    }
  }
  $gfx->move($cur_x + $count_width, $cur_y);
  $gfx->line($cur_x + $count_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $count_width, $cur_y-$item_height);
  $cur_x += $count_width;

  # Price frame (right and bottom)
  # Heading
  $text->translate($cur_x + $item_padding_left, $cur_y-$item_padding_top);
  if ($column == 0) {
    $text->font($font_bold, $font_size);
    $text->text('Pirce');
  }
  # Item
  else {
    $text->font($font, $font_size);
    if ($book_index < $books_length) {
      $text->text($books->[$book_index]{unit_price} * $books->[$book_index]{count});
    }
  }
  $gfx->move($cur_x + $price_width, $cur_y);
  $gfx->line($cur_x + $price_width, $cur_y-$item_height);
  $gfx->move($cur_x, $cur_y-$item_height);
  $gfx->line($cur_x + $price_width, $cur_y-$item_height);
  $cur_x += $price_width;
  
  # To the next line
  $cur_x = $table_start_x;
  $cur_y-= $item_height;
}

# Make the line color a little lighter than black
$gfx->strokecolor('# 333');

# Fill the rectangle and draw a line
$gfx->stroke;

my $pdf_file = 'table_items.pdf';
$pdf->saveas($pdf_file);

Table with output result items

Related Informatrion