- Perl ›
- Form creation ›
- here
Draw text - PDF::Create form with API2
To draw text in PDF with PDF::API2:
use PDF::API2; my $pdf = PDF::API2->new; my $page = $pdf->page; # Load core font my $font = $pdf->corefont('Helvetica'); # Get a content object that represents the text my $text = $page->text(); # Set font $text->font($font, 11); # Set drawing position (origin is lower left. By default, page width is 612 units and height is 792 units) $text->translate(30, 750); # Draw text $text->text('Hello World!'); my $pdf_file = 'text_core_font.pdf'; $pdf->saveas($pdf_file);
Get font object
In PDF::API2, get the font object that represents the font using the corefont method or ttfont method. Here, we are drawing using the corefont object.
# Load core font my $font = $pdf->corefont('Helvetica');
Use the corefont method to get the core font. Please note that corefont cannot draw Japanese. If you want to use Japanese, you need to specify a truetype font that supports Japanese with ttfont.
Content object that represents text
In PDF::API2, the content object that represents the text is obtained using the text method of the page object. Here, a content object that represents text is called a text object. This is a PDF::API2::Content object.
# Get a content object that represents the text my $text = $page->text();
Font settings
In PDF::API2, set the font with the font method of the text object.
# Set font $text->font($font, 11);
Specify drawing position
In PDF::API2, the drawing position is set by the translate method of the text object.
# Set drawing position (origin is lower left. By default, page width is 612 units and height is 792 units) $text->translate(30, 750);
The origin is at the bottom left. By default, the page width is 612 units and height is 792 units. For PDF coordinates, see the explanation of "Setting the paper size" in Creating a PDF file.
Please note that you cannot set page margins, number of lines of text, etc., and you need to specify the drawing position with the translate method. Note that you have to calculate and specify the drawing position yourself.
Drawing text
PDF::API2 draws text with the text method of the text object.
# Draw text $text->text('Hello World!');
When you draw text, the x-coordinate of the current drawing position moves to the end of the text. This position can be obtained with the textpos method.
Note that the text is written on the font baseline.
Set the font color
To set the font color, set the stroke color and fill color. Please note that there is no method that allows you to specify the font color directly.
# Set font color-color name $text->strokecolor('red'); $text->fillcolor('red'); # Set font color - RGB $text->strokecolor('red'); $text->fillcolor('red'); # Set font color - CMYK $text->strokecolor('%FF000000'); $text->fillcolor('%FF000000');
Text drawing is also created by using the content drawing function to create the outline path that exists in the font and then fill the inside of the path.
For a detailed explanation of drawing shapes and stroke color and fill color, refer to Drawing basic shapes and creating tables.
Set the background color
I will explain how to draw the background color and draw the text on it. In fact, there are pitfalls to this, so be careful.
The behavior of PDF::API2 is that the first defined content object is drawn first.
If you create a graphic content object after you create a text content object, the text will be erased graphically. So be sure to create the graphic content object first and the text content object later.
# Generate graphic content object first my $gfx = $page->gfx; # Generate text content object next my $text = $page->text; # Mistakes (Pit Pit, Graphic Text Disappears) my $text = $page->text; my $gfx = $page->gfx;
Another thing you need to do is set the font color. If you forget this, the background color will be the text color and it will be as if it was not drawn.
my $gfx = $page->gfx; # Don't forget to set the font color my $text = $page->text; $text->strokecolor('# 000'); $text->fillcolor('# 000'); # Draw background # ... # Draw text $text->text('Hello'); <pre> The background color is simply written using the function of the graphic content object. For example, if you draw a rectangle and fill it, it will be the background color. Please refer to <a href="/blog/20200101082734.html">Drawing basic figures and creating tables</a>. This will allow you to draw text on top of the background color. <h3>Draw underline</h3> To underline text in PDF::API2, specify the "-underline" option of the text method. "Auto" will automatically determine the position of the underline. <pre> # Draw underline $text->text('Hello World!', -Underline =>'auto');
Make it bold
To use bold in the core font, specify the font for bold.
# Load core font my $font = $pdf->corefont('Helvetica-Bold');
There are two types of expression in bold on a computer, either using a font for bold or using an application to draw overlapping characters.
Note that PDF::API2 does not have an API to make it bold. It has a method called hscale that expands the characters horizontally, but it also opens the character spacing, which makes it look unnatural for bold characters.
As an alternative, consider increasing the font size by 1.
# Increase font size by 1 as an alternative to bold $text->font($font, $font_size + 1);
Set character spacing
Use the charspace method to set the character spacing. The default is 0.
# Set character spacing $text->charspace(1);
Draw text around the specified position
To draw text centered on a specified position in PDF::API2, use the text_center method. The options are the same as the text method.
# Draw underline $text->text_center('Hello World!');
It can be used when you want to display the page heading etc. in the center of the screen.
Draw text with the specified position as the right edge
Use the text_right method to draw the text as a drawing with the specified position as the right edge in PDF::API2. The options are the same as the text method.
It can be used when you want to display the page number etc. at the bottom right.
# Draw underline $text->text_right('Hello World!');
Get the current text drawing position
To get the current text drawing position in PDF::API2, use the textpos method.
# Get the current text drawing position my ($x, $y) = $text->textpos();
You can use this value to check where a line break should occur and where page breaks should occur.
Get the font size
To get the font size of an existing text in PDF::API2, call the textstate method with no arguments and it will return a hash that represents the state of the text, so retrieve the fontsize value.
# Get the font size of existing text my %text_state = $text->textstate; my $font_size = $text_state{fontsize};
Get the height of the text
The height of the text is actually the same as the font size. Font size 16 is the same as 16 in PDF units. If you want to start a new line, you can use the font size to start a new line so that they do not overlap.
my $font_size = 16; my $text_height = $font_size;
Row height
There is a little space between the lines. The line height is the height of the text plus the width between the lines. If the font size (text height) is 11 and the line spacing is 2, the line height is 13.
# Font size (text height) my $font_size = 16; my $text_height = $font_size; # Line spacing my $line_margin = 2; # Line height my $line_height = $text_height + $line_margin;
For example, if you want to make a line break and move, you can subtract the line height.
Get the width of the text to draw
Use the advancewidth method to get the width of the text to draw in advance.
my $text_width = $text->advancewidth($string);
It can be used when performing a process called line break when it extends to the right side.
Margin settings
When using PDF::API2, if you want to set the margin on the page, you cannot set the margin value like the screen on the GUI. You have to calculate it yourself.
However, not only the disadvantages, but if you remember this method, you can change the margins on odd and even pages, which is very flexible.
Relationship between PDF unit and inch and mm
First of all, let's remember the relationship between the PDF unit specified in PDF and inches and mm.
One unit of PDF is 1/72 inch.
1 inch is 25.4 mm.
One unit of PDF is about 0.352777777777778mm when converted to mm. It's not divisible.
The 3 units of PDF are about 1.05833333333333mm when converted to mm. It's not divisible.
The 9 units of PDF are 3.175 mm when converted to mm.
The 3 units of PDF are close to 1mm. 9 units are good.
If you want the margins to be around 12mm to 13mm, use 36 in PDF units. The idea is that 9 units of PDF are 3.175 mm when converted to mm, so multiplying this by 4 gives 36 units, 12.7 mm.
In the explanation here, it is assumed that all the margins on the top, bottom, left, and right are 36 units.
# Margin on page my $page_top_margin = 36; # Page bottom margin my $page_bottom_margin = 36; # Page left margin my $page_left_margin = 36; # Page right margin my $page_right_margin = 36;
Note that the top and bottom margins are based on the baseline of the font, so the characters may be slightly out of the way they look.
Drawing start position and drawing end position
When considering the margins, it is necessary to calculate the XY coordinates of the drawing start position and the XY coordinates of the drawing end position.
First, let's get the width of the paper size. (Reference: Get the paper size of Create PDF file)
# Get the width and height of the paper size my @page_size_infos = $page->mediabox; my $page_width = $page_size_infos[2]; my $page_height = $page_size_infos[3];
Keep in mind that PDF coordinates start at the bottom left. If this is a typical PDF document, the width of the page is 612 units and the height is 792 units, so the coordinates at the top left of the page are (0, 791). Please note that it is not (0, 0).
The x-coordinate can be calculated as is, but the y-coordinate must be calculated by subtracting it from the height of the page.
The xy coordinates of the drawing start position and the xy coordinates of the drawing end position are calculated and calculated as follows.
# Start drawing x coordinate my $render_start_x = 0; # Start drawing y coordinate my $render_start_y = $page_height - 1; # End of drawing x coordinate my $render_end_x = $page_width - 1; # End of drawing y coordinate = 0; my $render_end_y = 0;
If you add a margin to this, it will be as follows.
# Start drawing x coordinate my $render_start_x = $page_left_margin; # Start drawing y coordinate my $render_start_y = $page_height - 1- $page_top_margin; # End of drawing x coordinate my $render_end_x = $page_width - 1- $page_right_margin; # Drawing end y coordinate my $render_end_y = $page_bottom_margin;
Text drawing corresponding to margins, a line break, and page breaks
Now, let's draw the text, paying attention to the margins, a line break, and page breaks. When it reaches the right end, it will start a new line. English only. English words are broken on a character-by-character basis. When you reach the bottom, the page will break.
use strict; use warnings; use utf8; use PDF::API2; my $pdf = PDF::API2->new; # Load core font my $font = $pdf->corefont('Helvetica-Bold'); my $font_size = 11; # Text height my $text_height = $font_size; # Line spacing my $line_margin = 2; # Line height my $line_height = $text_height + $line_margin; # 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]; # Page margin my $page_top_margin = 36; my $page_bottom_margin = 36; my $page_left_margin = 36; my $page_right_margin = 36; # Start drawing x coordinate my $render_start_x = $page_left_margin; # Start drawing y coordinate my $render_start_y = $page_height - 1- $page_top_margin; # End of drawing x coordinate my $render_end_x = $page_width - 1- $page_right_margin; # Drawing end y coordinate my $render_end_y = $page_bottom_margin; # Message (1000 repeats of the same message) my $message = 'Hello World!' X 1000; # Newline return x Save coordinates my $new_line_left_x = $render_start_x; # Current drawing position my $cur_render_x = $render_start_x; my $cur_render_y = $render_start_y; # Create page my $page = $pdf->page; # Get a content object that represents the text my $text = $page->text(); # Font settings $text->font($font, $font_size); # Move to drawing start position $text->translate($render_start_x, $render_start_y); # Extract one character at a time while ($message =~ /(.)/G) { my $char = $1; # Line break if the current drawing position exceeds the drawing end x coordinate if ($cur_render_x > $render_end_x) { $cur_render_x = $new_line_left_x; $cur_render_y-= $line_height; # Move to drawing start position $text->translate($cur_render_x, $cur_render_y); } # Page break if the current drawing position exceeds the drawing end y coordinate if ($cur_render_y < $render_end_y) { $cur_render_x = $render_start_x; $cur_render_y = $render_start_y; # Create page $page = $pdf->page; # Get a content object that represents the text $text = $page->text(); # Font settings $text->font($font, $font_size); # Move to drawing start position $text->translate($cur_render_x, $cur_render_y); } # Draw characters $text->text($char); # Update drawing position ($cur_render_x) = $text->textpos; } my $pdf_file = 'margin_full_text.pdf'; $pdf->saveas($pdf_file);
Three pages with margins that repeat "Hello World!" Are output.
In my environment, 12,000 characters take 1-2 seconds. PDF output is a surprisingly heavy process.
Output result margins, a line break, page breaks
How to represent HTML headlines and p tags?
I won't go into detail here, but I'll just give you a hint. At the very least, you need to parse the tags. Regular expressions and modules that parse HTML will be available. Is it general-purpose to use a module that parses HTML, considering that it will correspond to lists and tables later?
<h1>Title</h1> <p> message </p>
XML::Simple seems to be easy to use. With fixed format, you don't have to think about difficult things, so it would be nice if you could make Perl's multidimensional data structure with XML::Simple.
If you output in a fixed format like when making a book, CSS analysis seems to be unnecessary. It seems that it is necessary to set the margin programmatically for each heading and p tag.
How to break a line in English words?
It seems that you often don't want to cut English words in the middle of a line break. In this case, it's a little difficult, but think about it as follows.
First, remember the x-coordinate where the alphabet starts. Also, if an alphabetic character starts, save it in a string like $tmp_word. Let's wait for the text output yet and move on to the next loop. When a non-English character arrives, one English word is up to just before that. If the width of $tmp_word exceeds the right end, it will be output with a line break, and if it does not exceed, it will be output as it is.
Font for drawing Japanese
In order to draw Japanese with PDF::API2, a font that supports Japanese is required. The core font is English only.
Among the fonts, you need to use a font called TrueType Font.
Fonts are provided as a single file with a TrueType Font extension of ttf.
Get TrueType Font
There are TrueType fonts available for free, so get them. If you search, you will find many.
The caveat to choosing is to choose TrueType fonts instead of open fonts and fonts that can be embedded in PDF.
TrueType font files are portable, so they work the same on Windows, Mac, and Linux, as long as you don't use machine-dependent characters. There is a difference in how fonts are installed in the OS, but it does not matter if you read the file directly. (Reference:Is the font for Windows different from the font for Mac)
Here, I would like to introduce one TrueType font that can draw Japanese, called kochi-gothic-subst.ttf.
- kochi-gothic-subst.ttf (< a href = "https://github.com/indico/indico-fonts/tree/master/indico_fonts">Font list page)
Download it and place it wherever you like.
Loading TrueType Fonts
TrueType Fonts can be read with the ttfont method. Specify the path of the got TrueType Font.
my $true_type_font_file = '/path/kochi-gothic-subst.ttf'; my $font = $pdf->ttfont($true_type_font_file);
You can output Japanese by rewriting the location where the core font is read by corefont to the above.
As a promise when handling Perl Japanese, save the file in UTF-8 and use utf8.
use utf8;
In PDF::API2, the font is embedded in the PDF file, and the PDF file does not refer to the font file.
If you want to put the font file in the directory where you put the program and read it, it is easy to use the FindBin module and do the following.
use FindBin; my $true_type_font_file = "$FindBin::Bin/kochi-gothic-subst.ttf";
I want to make it bold with a TrueType font that supports Japanese
If you want to make it bold with TrueType Font that supports Japanese, use a font that has a bold font in addition to the normal font.
I have searched for Japanese, TrueType fonts, bold fonts, and commercially available ones, so I will introduce them.
Aozora Mincho Font
Download from below.
Unzip aozoramincho-readme-ttf.zip and you will find the following font files.
AozoraMincho-bold.ttf Bold AozoraMincho-thin.ttf AozoraMinchoBlack.ttf AozoraMinchoHeavy.ttf AozoraMinchoLight.ttf AozoraMinchoMedium.ttf AozoraMinchoRegular.ttf Normal
These files can be used for normal and bold.
# generally my $true_type_font_file = '/path/AozoraMinchoRegular.ttf'; my $font = $pdf->ttfont($true_type_font_file); # Bold my $true_type_font_bold_file = '/path/AozoraMincho-bold.ttf'; my $font_bold = $pdf->ttfont($true_type_font_file);
In Japanese fonts, it is assumed that the text baseline is almost always at the bottom of the character, but in Aozora Mincho font, the baseline is at the bottom of the character. In other words, the characters are drawn so that they extend to the upper right of the point moved by translate.
In this case, please note that the setting above the margin needs to be taken into consideration because the characters will protrude upward.
Examples for margins, a line break, page breaks, Japanese, and bold
This is an example that supports margins, a line break, page breaks, Japanese, and bold. "Hello world." Is displayed repeatedly, and only in the case of the world, it is in bold.
use strict; use warnings; use utf8; use FindBin; use PDF::API2; my $pdf = PDF::API2->new; # Reading TrueType fonts that support Japanese my $true_type_font_file = "$FindBin::Bin/AozoraMinchoRegular.ttf"; my $font = $pdf->ttfont($true_type_font_file); my $true_type_font_bold_file = "$FindBin::Bin/AozoraMincho-bold.ttf"; my $font_bold = $pdf->ttfont($true_type_font_bold_file); my $font_size = 11; # Text height my $text_height = $font_size; # Line spacing my $line_margin = 2; # Line height my $line_height = $text_height + $line_margin; # 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]; # Page margin my $page_top_margin = 36; my $page_bottom_margin = 36; my $page_left_margin = 36; my $page_right_margin = 36; # Start drawing x coordinate my $render_start_x = $page_left_margin; # Start drawing y coordinate my $render_start_y = $page_height - 1- $page_top_margin; # End of drawing x coordinate my $render_end_x = $page_width - 1- $page_right_margin; # Drawing end y coordinate my $render_end_y = $page_bottom_margin; # Message (1000 repeats of the same message) my $message = 'Hello world. 'x 1000; # Newline return x Save coordinates my $new_line_left_x = $render_start_x; # Current drawing position my $cur_render_x = $render_start_x; my $cur_render_y = $render_start_y; # Create page my $page = $pdf->page; # Get a content object that represents the text my $text = $page->text(); # Font settings $text->font($font, $font_size); # Move to drawing start position $text->translate($render_start_x, $render_start_y); # Extract one character at a time while ($message =~ /(.)/G) { my $char = $1; # Line break if the current drawing position exceeds the drawing end x coordinate if ($cur_render_x > $render_end_x) { $cur_render_x = $new_line_left_x; $cur_render_y-= $line_height; # Move to drawing start position $text->translate($cur_render_x, $cur_render_y); } # Page break if the current drawing position exceeds the drawing end y coordinate if ($cur_render_y < $render_end_y) { $cur_render_x = $render_start_x; $cur_render_y = $render_start_y; # Create page $page = $pdf->page; # Get a content object that represents the text $text = $page->text(); # Font settings $text->font($font, $font_size); # Move to drawing start position $text->translate($cur_render_x, $cur_render_y); } # Bold world if ($char eq 'world' || $char eq 'world') { $text->font($font_bold, $font_size); } else { $text->font($font, $font_size); } # Draw characters $text->text($char); # Update drawing position ($cur_render_x) = $text->textpos; } my $pdf_file = 'margin_full_text_jp.pdf'; $pdf->saveas($pdf_file);