PDF::Builder::Content(3pm) | User Contributed Perl Documentation | PDF::Builder::Content(3pm) |
PDF::Builder::Content - Methods for adding graphics and text to a PDF
# Start with a PDF page (new or opened) my $pdf = PDF::Builder->new(); my $page = $pdf->page(); # Add new content object(s) my $content = $page->gfx(); # and/or (as separate object name) my $content = $page->text(); # Then call the methods below to add graphics and text to the page. # Note that negative coordinates can have unpredictable effects, so # keep your coordinates non-negative!
These methods add content to streams output for text or graphics objects. Unless otherwise restricted by a check that we are in or out of text mode, many methods listed here apply equally to text and graphics streams. It is possible that there are some which have no effect in one stream type or the other, but are currently lacking a check to prevent them from being inserted into an inapplicable stream.
All public methods listed, except as otherwise noted, return $self.
The methods in this section change the coordinate system for the current content object relative to the rest of the document. Note: the changes are relative to the original page coordinates (and thus, absolute), not to the previous position! Thus, "translate(10, 10); translate(10, 10);" ends up only moving the origin to "[10, 10]", rather than to "[20, 20]". There is one call, "transform_rel()", which makes your changes relative to the previous position.
If you call more than one of these methods, the PDF specification recommends calling them in the following order: translate, rotate, scale, skew. Each change builds on the last, and you can get unexpected results when calling them in a different order.
CAUTION: a text object ($content) behaves a bit differently. Individual translate, rotate, scale, and skew calls cancel out any previous settings. If you want to combine multiple transformations for text, use the "transform" call.
Note: Unless you have already moved (translated) the origin, it is, and will remain, at the lower left corner of the visible sheet. It will not automatically shift to another corner. For example, a rotation of +90 degrees (counter-clockwise) will leave the entire visible sheet in negative Y territory (0 at the left edge, -original_width at the right edge), while X remains in positive territory (0 at bottom, +original_height at the top edge).
This "rotate()" call permits any angle. Do not confuse it with the page rotation "rotate" call, which only permits increments of 90 degrees (with opposite sign!), but does shift the origin to another corner of the sheet.
$content->transform( -translate => [$dx,$dy], -rotate => $degrees, -scale => [$sx,$sy], -skew => [$skx,$sky], -matrix => [$a, $b, $c, $d, $e, $f], -point => [$x,$y] )
A six element list may be given ("-matrix") for a further transformation matrix:
$a = cos(rot) * scale factor for X $b = sin(rot) * tan(skew for X) $c = -sin(rot) * tan(skew for Y) $d = cos(rot) * scale factor for Y $e = translation for X $f = translation for Y
Performs multiple coordinate transformations at once, in the order recommended by the PDF specification (translate, rotate, scale, skew). This is equivalent to making each transformation separately, in the indicated order. A matrix of 6 values may also be given ("-matrix"). The transformation matrix is updated. A "-point" may be given (a point to be multiplied [transformed] by the completed matrix).
Unlike "transform", "-matrix" and "-point" are not supported.
$a = cos(rot) * scale factor for X $b = sin(rot) * tan(skew for X) $c = -sin(rot) * tan(skew for Y) $d = cos(rot) * scale factor for Y $e = translation for X $f = translation for Y
In text mode, the text matrix is returned. In graphics mode, $self is returned.
The following calls also affect the text state.
If no $style is given, the current setting is returned. If the style is being set, $self is returned so that calls may be chained.
If no $style is given, the current setting is returned. If the style is being set, $self is returned so that calls may be chained.
The ratio is the maximum length of the miter (inner to outer corner) divided by the line width. Any miter above this ratio will be converted to a bevel join. The practical effect is that lines meeting at shallow angles are chopped off instead of producing long pointed corners.
The default miter limit is 10.0 (approximately 11.5 degree cutoff angle). The smaller the limit, the larger the cutoff angle.
If no $ratio is given, the current setting is returned. If the ratio is being set, $self is returned so that calls may be chained.
If called without any arguments, a solid line will be drawn.
If called with one argument, the dashes and gaps (strokes and spaces) will have equal lengths.
If called with two or more arguments, the arguments represent alternating dash and gap lengths.
If called with a hash of arguments, the -pattern array may have one or more elements, specifying the dash and gap lengths. A dash phase may be set (-shift), which is a positive integer specifying the distance into the pattern at which to start the dashed line. Note that if you wish to give a shift amount, using "-shift", you need to use "-pattern" instead of one or two elements.
If an odd number of dash array elements are given, the list is repeated by the reader software to form an even number of elements (pairs).
If a single argument of -1 is given, the current setting is returned. This is an array consisting of two elements: an anonymous array containing the dash pattern (default: empty), and the shift (offset) amount (default: 0). If the dash pattern is being set, $self is returned so that calls may be chained.
The $tolerance value is silently clamped to be between 0 and 100.
If no $tolerance is given, the current setting is returned. If the tolerance is being set, $self is returned so that calls may be chained.
Straight line constructs
Note: None of these will actually be visible until you call "stroke" or "fill". They are merely setting up the path to draw.
Multiple additional "[$x,$y]" pairs are permitted, to draw joined multiple line segments. Note that this is not equivalent to a polyline (see "poly"), because the first "[$x,$y]" pair in a polyline is a move operation. Also, the "linecap" setting will be used rather than the "linejoin" setting for treating the ends of segments.
The difference between a polyline and a "line" with multiple "[$x,$y]" pairs is that the first pair in a polyline are a move, while in a line they are a draw. Also, "linejoin" instead of "linecap" is used to control the appearance of the ends of line segments.
Curved line constructs
Note: None of these will actually be visible until you call "stroke" or "fill". They are merely setting up the path to draw.
Set $move to a true value if this arc is the beginning of a new path instead of the continuation of an existing path. Either way, the current position will be updated to the end of the arc. Use "$rx == $ry" for a circular arc.
The optional $dir arc sweep direction defaults to 0 (false), for a counter-clockwise/anti-clockwise sweep. Set to 1 (true) for a clockwise sweep.
The optional $dir arc sweep direction defaults to 0 (false), for a counter-clockwise/anti-clockwise sweep. Set to 1 (true) for a clockwise sweep.
This is a shortcut to draw a section of elliptical (or circular) arc and connect it to the center of the ellipse or circle, to form a pie shape.
Within a text object, the text's baseline follows the Bezier curve.
Note that while multiple sets of three "[x,y]" pairs are permitted, these are treated as independent cubic Bezier curves. There is no attempt made to smoothly blend one curve into the next!
Internally, these splines are one or more cubic Bezier curves (see "curve") with the two control points synthesized from the two given points (a control point and the end point of a quadratic Bezier curve).
Note that while multiple sets of two "[x,y]" pairs are permitted, these are treated as independent quadratic Bezier curves. There is no attempt made to smoothly blend one curve into the next!
Further note that this "spline" does not match the common definition of a spline being a continuous curve passing through all the given points! It is a piecewise non-continuous cubic Bezier curve. Use with care, and do not make assumptions about splines for you or your readers. You may wish to use the "bspline" call to have a continuously smooth spline to pass through all given points.
Pairs of points (control point and end point) are consumed in a loop. If one point or coordinate is left over at the end, it is discarded (as usual practice for excess data to a routine). There is no check for duplicate points or other degeneracies.
Internally, these splines are one cubic Bezier curve (see "curve") per pair of input points, with the two control points synthesized from the tangent through each point as set by the polyline that would connect each point to its neighbors. The intent is that the resulting curve should follow reasonably closely a polyline that would connect the points, and should avoid any major excursions. See the discussions below for the handling of the control points at the endpoints (current point and last input point). The point at the end of the last line or curve drawn becomes the new current point.
%opts
"-colinear" applies only to interior runs of colinear points, between curves. It does not apply to runs at the beginning or end of the point list, which are drawn as line segments or linear constraints regardless of -firstseg and -lastseg settings.
Special cases
Adjacent points which are duplicates are consolidated. An extra coordinate at the end of the input point list (not a full "[x,y]" pair) will, as usual, be ignored.
This extends the path along an arc of a circle of the specified radius between "[$x1,$y1]" to "[$x2,$y2]". The current position is then set to the endpoint of the arc ("[$x2,$y2]").
Set $move to a true value if this arc is the beginning of a new path instead of the continuation of an existing path. Note that the default ($move = false) is not a straight line to P1 and then the arc, but a blending into the curve from the current point. It will often not pass through P1!
Set $larger to a true value to draw the larger ("outer") arc between the two points, instead of the smaller one. Both arcs are drawn clockwise from P1 to P2. The default value of false draws the smaller arc.
Set $reverse to a true value to draw the mirror image of the specified arc (flip it over, so that its center point is on the other side of the line connecting the two points). Both arcs are drawn counter-clockwise from P1 to P2. The default (false) draws clockwise arcs.
The $radius value cannot be smaller than half the distance from "[$x1,$y1]" to "[$x2,$y2]". If it is too small, the radius will be set to half the distance between the points (resulting in an arc that is a semicircle). This is a silent error.
If the path intersects with itself, the nonzero winding rule will be used to determine which part of the path is filled in. This basically fills in everything inside the path. If you would prefer to use the even-odd rule, pass a true argument. This basically will fill alternating closed sub-areas.
See the PDF Specification, section 8.5.3.3, for more details on filling.
If any $use_even_odd_fill parameter is given, use even-odd fill (W*) instead of winding-rule fill (W). It is common usage to make the "endpath()" call (n) after the "clip()" call, to clear the path (unless you want to reuse that path, such as to fill and/or stroke it to show the clip path). If you want to clip text glyphs, it gets rather complicated, as a clip port cannot be created within a text object (that will have an effect on text). See the object discussion in "Rendering Order" in PDF::Builder::Docs.
my $grfxC1 = $page->gfx(); my $textC = $page->text(); my $grfxC2 = $page->gfx(); ... $grfxC1->save(); $grfxC1->endpath(); $grfxC1->rect(...); $grfxC1->clip(); $grfxC1->endpath(); ... $textC-> output text to be clipped ... $grfxC2->restore();
# Use a named color # -> RGB color model # there are many hundreds of named colors defined in # PDF::Builder::Resource::Colors $content->fillcolor('blue'); # Use an RGB color (# followed by 3, 6, 9, or 12 hex digits) # -> RGB color model # This maps to 0-1.0 values for red, green, and blue $content->fillcolor('#FF0000'); # red # Use a CMYK color (% followed by 4, 8, 12, or 16 hex digits) # -> CMYK color model # This maps to 0-1.0 values for cyan, magenta, yellow, and black $content->fillcolor('%FF000000'); # cyan # Use an HSV color (! followed by 3, 6, 9, or 12 hex digits) # -> RGB color model # This maps to 0-360 degrees for the hue, and 0-1.0 values for # saturation and value $content->fillcolor('!FF0000'); # Use an HSL color (& followed by 3, 6, 9, or 12 hex digits) # -> L*a*b color model # This maps to 0-360 degrees for the hue, and 0-1.0 values for # saturation and lightness. Note that 360 degrees = 0 degrees (wraps) $content->fillcolor('&FF0000'); # Use an L*a*b color ($ followed by 3, 6, 9, or 12 hex digits) # -> L*a*b color model # This maps to 0-100 for L, -100 to 100 for a and b $content->fillcolor('$FF0000');
In all cases, if too few digits are given, the given digits are silently right-padded with 0's (zeros). If an incorrect number of digits are given, the next lowest number of expected digits are used, and the remaining digits are silently ignored.
# A single number between 0.0 (black) and 1.0 (white) is an alternate way # of specifying a gray scale. $content->fillcolor(0.5); # Three array elements between 0.0 and 1.0 is an alternate way of specifying # an RGB color. $content->fillcolor(0.3, 0.59, 0.11); # Four array elements between 0.0 and 1.0 is an alternate way of specifying # a CMYK color. $content->fillcolor(0.1, 0.9, 0.3, 1.0);
In all cases, if a number is less than 0, it is silently turned into a 0. If a number is greater than 1, it is silently turned into a 1. This "clamps" all values to the range 0.0-1.0.
# A single reference is treated as a pattern or shading space. # Two or more entries with the first element a Perl reference, is treated # as either an indexed colorspace reference plus color-index(es), or # as a custom colorspace reference plus parameter(s).
If no value was passed in, the current fill color (or stroke color) array is returned, otherwise $self is returned.
# Example my $image_object = $pdf->image_jpeg($my_image_file); $content->image($image_object, 100, 200);
Places an image on the page in the specified location (specifies the lower left corner of the image). The default location is "[0,0]".
If coordinate transformations have been made (see Coordinate Transformations above), the position and scale will be relative to the updated coordinates. Otherwise, "[0,0]" will represent the bottom left corner of the page, and $width and $height will be measured at 72dpi.
For example, if you have a 600x600 image that you would like to be shown at 600dpi (i.e., one inch square), set the width and height to 72. (72 Big Points is one inch)
Note that while this method is named form image, it is also used for the pseudoimages created by the barcode routines. Images are naturally dimensionless (1 point square) and need at some point to be scaled up to the desired point size. Barcodes are naturally sized in points, and should be scaled at approximately 1. Therefore, it would greatly overscale barcodes to multiply by image width and height within "formimage", and require scaling of 1/width and 1/height in the call. So, we leave scaling alone within "formimage" and have the user manually scale images by the image width and height (in pixels) in the call to "formimage".
Text State Parameters
All of the following parameters that take a size are applied before any scaling takes place, so you don't need to adjust values to counteract scaling.
CAUTION: be careful about using "charspace" if you are using a connected font. This might include Arabic, Devanagari, Latin cursive handwriting, and so on. You don't want to leave gaps between characters, or cause overlaps. For such fonts and typefaces, set the "charspace" spacing to 0.
Note that it is a limitation of the PDF specification (as of version 1.7, section 9.3.3) that only spacing with an ASCII space (x20) is adjusted. Neither required blanks (xA0) nor any multiple-byte spaces (including thin and wide spaces) are currently adjusted.
Note that scaling affects all of the character widths, interletter spacing, and interword spacing. It is inadvisable to stretch or compress text by a large amount, as it will quickly make the text unreadable. If your objective is to justify text, you will usually be better off using "charspace" and "wordspace" to expand (or slightly condense) a line to fill a desired width. Also see the "text_justify()" calls for this purpose.
If $mode is given, the current setting is replaced by that value and $self is returned (to permit chaining). If $mode is not given, the current setting is returned.
Use this for creating superscripts or subscripts (usually along with an adjustment to the font size). If $dist is given, the current setting is replaced by that value and $self is returned (to permit chaining). If $dist is not given, the current setting is returned.
Note: This does not work with the "save" and "restore" commands.
# Example (12 point Helvetica) my $pdf = PDF::Builder->new(); my $fontname = $pdf->corefont('Helvetica'); $content->font($fontname, 12);
Positioning Text
"distance" is analogous to graphic's "move", except that it is relative to the beginning of the previous text write, not to the coordinate origin. Note that subsequent text writes will be relative to this new starting (left) point and Y position! E.g., if you give a non-zero $dx, subsequent lines will be indented by that amount.
If passed with an argument, the "lead" distance is ignored and the next line starts that far up the page (positive value) or down the page (negative value) from the current line. "Y" increases upward, so a negative value would normally be used to get to the next line down.
An argument of 0 would simply return to the start of the present line, overprinting it with new text. That is, it acts as a simple carriage return, without a linefeed.
Note: This does not affect the PDF in any way. It only tells you where the the next write will occur.
Returns the width of the $string based on all currently set text-state attributes. These can optionally be overridden with %opts. Note that these values temporarily replace the existing values, not scaling them up or down. For example, if the existing charspace is 2, and you give in options a value of 3, the value used is 3, not 5.
Note: This does not affect the PDF in any way. It only tells you how much horizontal space a text string will take up.
Rendering Text
Single Lines
Options:
Example:
# 3 underlines: # distance 4, thickness 1, color red # distance 7, thickness 1.5, color yellow # distance 11, thickness 2, color (strokecolor default) -underline=>[4,[1,'red'],7,[1.5,'yellow'],11,2],
Example:
# 2 strikethroughs: # distance 4, thickness 1, color red # distance 7, thickness 1.5, color yellow -strikethru=>[4,[1,'red'],7,[1.5,'yellow']],
By the way, if the direction is RTL, HarfBuzz will reverse the text and return an array with the last character first (to be written LTR). Likewise, for BTT, HarfBuzz will reverse the text and return a string to be written from the top down. Languages which are normally written horizontally are usually set vertically with direction TTB. If setting text vertically, ligatures and kerning, as well as character connectivity for cursive scripts, are automatically turned off, so don't let the direction default to LTR or RTL in the Shaper call, and then try to fix it up in "textHS()".
Text is sent separately to HarfBuzz::Shaper in 'chunks' ('segments') of a single script (alphabet), a single direction (LTR, RTL, TTB, or BTT), a single font file, and a single font size. A chunk may consist of a large amount of text, but at present, "textHS()" can only output a single line. For long lines that need to be split into column-width lines, the best way may be to take the array of hashes returned by HarfBuzz::Shaper and split it into smaller chunks at spaces and other whitespace. You may have to query the font to see what the glyph CIDs are for space and anything else used.
It is expected that when "textHS()" is called, that the font and font size have already been set in PDF::Builder code, as this information is needed to interpret what HarfBuzz::Shaper is returning, and to write it to the PDF file. Needless to say, the font should be opened from the same file as was given to HarfBuzz::Shaper ("ttfont()" only, with .ttf or .otf files), and the font size must be the same. The appropriate location on the page must also already have been specified.
NOTE: as HarfBuzz::Shaper is still in its early days, it is possible that there will be major changes in its API. We hope that all changes will be upwardly compatible, but do not control this package and cannot guarantee that there will not be any incompatible changes that in turn require changes to PDF::Builder ("textHS()").
Returns total width in points.
Be careful when doing this, as you are dabbling in the black arts, directly setting PDF operations!
One interesting use is to split up an overly long object stream that is giving your editor problems when exploring a PDF file. Add a newline add("\n") every few hundred bytes of output or so, to do this. Note that you must use double quotes (quotation marks), rather than single quotes (apostrophes).
Use extreme care if inserting BT and ET markers into the PDF stream. You may want to use "textstart()" and "textend()" calls instead, and even then, there are many side effects either way. It is generally not useful to suspend text mode with ET/textend and BT/textstart, but it is possible, if you really need to do it.
Another, useful, case is when your input PDF is from the Chrome browser printing a page to PDF with headers and/or footers. In some versions, this leaves the PDF page with a strange scaling (such as the page height in points divided by 3300) and the Y-axis flipped so 0 is at the top. This causes problems when trying to add additional text or graphics in a new text or graphics record, where text is flipped (mirrored) upsidedown and at the wrong end of the page. If this happens, you might be able to cure it by adding
$scale = .23999999; # example, 792/3300, examine PDF or experiment! ... if ($scale != 1) { my @pageDim = $page->mediabox(); # e.g., 0 0 612 792 my $size_page = $pageDim[3]/$scale; # 3300 = 792/.23999999 my $invScale = 1.0/$scale; # 4.16666684 $text->add("$invScale 0 0 -$invScale 0 $size_page cm"); }
as the first output to the $text stream. Unfortunately, it is difficult to predict exactly what $scale should be, as it may be 3300 units per page, or a fixed amount. You may need to examine an uncompressed PDF file stream to see what is being used. It might be possible to get the input (original) PDF into a string and look for a certain pattern of "cm" output
.2399999 0 0 -.23999999 0 792 cm
or similar, which is not within a save/restore (q/Q). If the stream is already compressed, this might not be possible.
The "new()" call can set the -compress parameter to 'flate' (default) to compress all object streams, or 'none' to suppress compression and allow you to examine the output in an editor.
Note that calling this method, besides outputting a BT marker, will reset most text settings to their default values. In addition, BT itself will reset some transformation matrices.
Note that calling this method, besides outputting an ET marker, will output any accumulated poststream content.
2021-03-28 | perl v5.32.1 |