itext7 Chinese development document

ready to translate : https://developers.itextpdf.com/content/itext-7-jump-start-tutorial/chapter-2-adding-low-level-content

Some basic operations

     The first chapter introduces some basic contents, while this chapter introduces some lower things. The following chapters will cover the operation of existing pdf contents. I hope you will wait patiently.

     When we talk about the low-level content in iText documents, we will refer to the PDF syntax written in the official PDF documents. A series of operations defined in PDF correspond in iText. For example, m operation corresponds to moveTo() method, l operation corresponds to lineTo() method, S operation corresponds to stroke() method, and so on. Through these methods, we can draw paths and shapes.

     Let's look at the following simple example:

-406 0 m
406 0 l
S

     The syntax of this paragraph in pdf means:

  1. Move to coordinates (- 406,0)
  2. Then draw a line from (- 406,0) to (406,0) to form a path
  3. Finally, draw this line, and stroke() corresponds to drawing the path

     This section corresponds to the operation in iText as follows:

canvas.moveTo(-406, 0)
            .lineTo(406, 0)
            .stroke();

     At first glance, it looks simple, but what is the object of canvas? Let's find the answer through a few examples.

Drawing coordinate system in pdf

Draw lines on the canvas

     Suppose we want to create a pdf as shown in the figure below:

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-nrmd6exf-1635433956338)( http://obkwqzjnq.bkt.clouddn.com/itext-2-1.png )]

     This example draws the X and Y coordinate systems on the pdf. Let's explain the process step by step:

PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PageSize ps = PageSize.A4.rotate();
PdfPage page = pdf.addNewPage(ps);
PdfCanvas canvas = new PdfCanvas(page);
// Draw the axes
pdf.close();
  • We no longer use the Document object
  • As mentioned in the first chapter, we created PdfWriter and PdfDocument objects.
  • Instead of creating a Document object with the default size of the page as before, we created a PdfPage with a specific PageSize
  • The page is A4 size and turns to landscape after rotation
  • After creating the PdfPage, we used it to create the PdfCanvas
  • A series of operations were carried out in PdfCanvas to complete the painting of our coordinate system
  • Finally, we want to add what we paint to the page, just close the PdfDocument object

In the previous chapter, we used document.close() to close the Document object. This operation actually closes the PdfDocument object secretly. Now there is no Document object here, so we have to close PdfDocument manually

     In PDF, all measurements are done in user units. By default, one user unit corresponds to one point. This means that there are 72 user units within one inch. In PDF, the X axis points to the right and the Y axis points up. If you use the PageSize object to create a page size, the origin of the coordinate system is in the lower left corner of the page. This coordinate system is used for all coordinates we use as operands of operators, such as m or l operations. We can change the coordinate system by changing the current transformation matrix.

Coordinate system and change matrix

     If you have taken geometry related courses, you should know that we can act on objects through a change matrix, which can translate, rotate, scale and so on. Suppose we want to move the coordinate system so that the origin of the coordinate system is in the middle of the page. In this case, the parameters we need to use the concatMatrix() method are:

canvas.concatMatrix(1, 0, 0, 1, ps.getWidth() / 2, ps.getHeight() / 2);

     The parameter of the concatMatrix() method is a 3 * 3 transformation matrix:

a   b   0
c   d   0
e   f   1

     Here, the third row of the matrix is a fixed value: (0,0,1). This is because we operate in two dimensions. The values of a,b,c and d can be used here to rotate, scale and translate the coordinate system. Of course, we don't need to specify that the x axis must be horizontal and the y axis must be vertical, but we specify that for simplicity, so we specify the values of a,b,c and d as 1,0,0 and 1. e and f define the translation distance. Here, the translation size is divided into 1 / 2 of the height and width of the written test ps

The graphics state

     The change matrix mentioned in the previous section is one of the image states of Page, as well as states such as line width, line drawing color, fill color, etc. In the following chapters, we will explore some other image states in more depth. Here we only need to know: the default line width is 1 user unit and the default line color is black. The following code is the process of drawing the figure above:

//1.Draw X axis
canvas.moveTo(-(ps.getWidth() / 2 - 15), 0)
        .lineTo(ps.getWidth() / 2 - 15, 0)
        .stroke();
//2.Draw X axis arrow
canvas.setLineJoinStyle(PdfCanvasConstants.LineJoinStyle.ROUND)
        .moveTo(ps.getWidth() / 2 - 25, -10)
        .lineTo(ps.getWidth() / 2 - 15, 0)
        .lineTo(ps.getWidth() / 2 - 25, 10).stroke()
        .setLineJoinStyle(PdfCanvasConstants.LineJoinStyle.MITER);
//3.Draw Y axis
canvas.moveTo(0, -(ps.getHeight() / 2 - 15))
        .lineTo(0, ps.getHeight() / 2 - 15)
        .stroke();
//4.Draw Y axis arrow
canvas.saveState()
        .setLineJoinStyle(PdfCanvasConstants.LineJoinStyle.ROUND)
        .moveTo(-10, ps.getHeight() / 2 - 25)
        .lineTo(0, ps.getHeight() / 2 - 15)
        .lineTo(10, ps.getHeight() / 2 - 25).stroke()
        .restoreState();
//5.Draw X serif
for (int i = -((int) ps.getWidth() / 2 - 61);
    i < ((int) ps.getWidth() / 2 - 60); i += 40) {
    canvas.moveTo(i, 5).lineTo(i, -5);
}
//6.Draw Y serif
for (int j = -((int) ps.getHeight() / 2 - 57);
    j < ((int) ps.getHeight() / 2 - 56); j += 40) {
    canvas.moveTo(5, j).lineTo(-5, j);
}
canvas.stroke();

     This code can be divided into the following parts:

  • 1 and 3 pieces of code should be familiar to you, that is, draw the x and y axes
  • The second code is to draw an arrow, that is, two lines are connected together. There are many styles of the intersection: 1) miter, two lines join at one point, 2) miter, the corner is miter, 3) circular, and the corner is circular. We call the moveTo and lineTo functions once and twice to build the path. Finally, we reset the style of the intersection to the default value, which will not affect some subsequent operations, but this is not the best way to restore the previous image state
  • The fourth code shows us a better operation method if we want to change the image state: 1) we use the saveState() method to save the current image state; 2) then change the image state, draw lines or other arbitrary shapes; 3) finally, we use the restoreState() method to restore the original image state, all in saveState() Subsequent operations to change the image state will be cancelled. This will be very effective when you perform many operations to change the image state
  • In paragraphs 5 and 6, we draw a separator for each 40 user units. It is worth noting that we do not call the stroke() function immediately, but after all paths are drawn, we call the function to draw.

     Of course, there is more than one way to draw lines and shapes on the canvas. Considering the speed of pdf generation, the size of generated files and the speed of rendering in the view, it is difficult to discuss whether the above method is good or bad, which will be discussed in later chapters.

Note here that saveState() and restoreState() must appear in pairs, and restoreState() cannot precede savaState()

Add gridlines

     Now, based on the previous picture, we change the width of the line, add a dotted line and add grid lines of different colors, as shown in the following figure:
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-aqp7fhhv-1635433956348)( http://obkwqzjnq.bkt.clouddn.com/itext-2-2.png )]
     In this example, we first define a series of Color objects:

Color grayColor = new DeviceCmyk(0.f, 0.f, 0.f, 0.875f);
Color greenColor = new DeviceCmyk(1.f, 0.f, 1.f, 0.176f);
Color blueColor = new DeviceCmyk(1.f, 0.156f, 0.f, 0.118f);

     In the official PDF document (ISO-32000), many color spaces are defined. Different color spaces correspond to different class es in iText. The most commonly used color spaces are DeviceGray (gray space, only one brightness parameter is required), DeviceRgb (RGB space, which is determined by red, green and blue) and DeviceCmyk (printing four-color space, which is composed of cyan, magenta, yellow and black), In this example, we use DeviceCmyk space.

Note that instead of using the Color defined by java.awt.Color, we use the Color class from iText, which can be found in the com.itextpdf.kernel.color package.

     If we want to draw a blue grid line, we can use the following code:

canvas.setLineWidth(0.5f).setStrokeColor(blueColor);
for (int i = -((int) ps.getHeight() / 2 - 57);
    i < ((int) ps.getHeight() / 2 - 56); i += 40) {
    canvas.moveTo(-(ps.getWidth() / 2 - 15), i)
            .lineTo(ps.getWidth() / 2 - 15, i);
}
for (int j = -((int) ps.getWidth() / 2 - 61);
    j < ((int) ps.getWidth() / 2 - 60); j += 40) {
    canvas.moveTo(j, -(ps.getHeight() / 2 - 15))
            .lineTo(j, ps.getHeight() / 2 - 15);
}
canvas.stroke();

     At the beginning, we set the width of the line to 0.5 user units. Next, we draw the route, and finally call the stroke() function.
     When drawing coordinates, we only use the previous method to draw, but before drawing, we change the line width and brush color.

canvas.setLineWidth(3).setStrokeColor(grayColor);

     After drawing the coordinate system, we draw the dotted line with 2 user widths:

canvas.setLineWidth(2).setStrokeColor(greenColor)
        .setLineDash(10, 10, 8)
        .moveTo(-(ps.getWidth() / 2 - 15), -(ps.getHeight() / 2 - 15))
        .lineTo(ps.getWidth() / 2 - 15, ps.getHeight() / 2 - 15).stroke();

     There can be many variables to define a line dash, but in this example, we only need three variables. The length of the solid dash is 10 user units, the gap between the solid lines is 10 user units, and the phase is 8 user units (the phase is 8 user units - the phase defines the distance in the dash pattern to start the dash.)

We can try other methods in PDF canvas. For example, curveTo() function is used to draw curves, rectangle() method is used to draw rectangles, and other methods. Besides using stroke method to draw lines, we can also use fill() Method to fill the path. The PdfCanvas class provides far more methods than the java version of the PDF operator, and also provides methods for constructing paths that are not available in PDF, such as ellipses and circles

     In the next section, we will discuss the part of the image state that can change the absolute position of the text

Text status

     The following figure shows the opening text of Star Wars 5 Empire counterattack:

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-k0evra0b-1635433956351)( http://obkwqzjnq.bkt.clouddn.com/itext2-3.png )]

     If you want to create such a pdf, the best way is to create a Paragraph object with different alignment, center the topic, align the content to the left, and then add the Paragraph object to the Document. In the advanced api(high-level approach) The continuous text will be divided into many segments. When the text length exceeds the page width, a line break will be introduced, and when the text content exceeds the page height, the page will be changed.
     Of course, we have a simpler way. Using the low-level approach, we just need to break the text into several pieces:

List<String> text = new ArrayList();
text.add("         Episode V         ");
text.add("  THE EMPIRE STRIKES BACK  ");
text.add("It is a dark time for the");
text.add("Rebellion. Although the Death");
text.add("Star has been destroyed,");
text.add("Imperial troops have driven the");
text.add("Rebel forces from their hidden");
text.add("base and pursued them across");
text.add("the galaxy.");
text.add("Evading the dreaded Imperial");
text.add("Starfleet, a group of freedom");
text.add("fighters led by Luke Skywalker");
text.add("has established a new secret");
text.add("base on the remote ice world");
text.add("of Hoth...");

     For simplicity, we put the coordinate system in the lower left corner to the upper left corner, and then we use the beginText() method to create a text object and change the state of the text:

canvas.concatMatrix(1, 0, 0, 1, 0, ps.getHeight());
canvas.beginText()
    .setFontAndSize(PdfFontFactory.createFont(FontConstants.COURIER_BOLD), 14)
    .setLeading(14 * 1.2f)
    .moveText(70, -40);
  • We change the text status to equal width bold and change the font size to 14, so that future fonts will be in this format
  • The spacing is 1.2 times the font size
  • Finally, we move 70 user units to the right and 40 user units down, that is (70, - 40) to start displaying text

     Next, we iterate through the strings in the text array one by one, and each string is on another line, maintaining the above spacing, and finally end with the endText() method.

for (String s : text) {
    //Add text and move to the next line
    canvas.newlineShowText(s);
}
canvas.endText();

Note: when we use newlineShowText(), it must be between beginText() and endText(), and the order between beginText() - endText() cannot be disordered

Cool text

     First look at the following effects:

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (IMG zanmurvs-1635433956353)( http://obkwqzjnq.bkt.clouddn.com/itext2-4.png )]

     Isn't it cool? In fact, it's very simple. Let's go step by step:

canvas.rectangle(0, 0, ps.getWidth(), ps.getHeight())
        .setColor(Color.BLACK, true)
        .fill();

     We first create a rectangle. The coordinates of the lower left corner of the rectangle are (0,0), and the width and height are the width and height of the page. Then we set the fill color to black. Of course, we can use setFillColor(Color.BLACK) to set the fill color, but here we use the more general setColor() method. setColor() The second parameter is whether to change the filling color. Finally, we fill the rectangle, and then the text part:

canvas.concatMatrix(1, 0, 0, 1, 0, ps.getHeight());
Color yellowColor = new DeviceCmyk(0.f, 0.0537f, 0.769f, 0.051f);
float lineHeight = 5;
float yOffset = -40;
canvas.beginText()
    .setFontAndSize(PdfFontFactory.createFont(FontConstants.COURIER_BOLD), 1)
    .setColor(yellowColor, true);
for (int j = 0; j < text.size(); j++) {
    String line = text.get(j);
    float xOffset = ps.getWidth() / 2 - 45 - 8 * j;
    float fontSizeCoeff = 6 + j;
    float lineSpacing = (lineHeight + j) * j / 1.5f;
    int stringWidth = line.length();
    for (int i = 0; i < stringWidth; i++) {
        float angle = (maxStringWidth / 2 - i) / 2f;
        float charXOffset = (4 + (float) j / 2) * i;
        canvas.setTextMatrix(fontSizeCoeff, 0,
                angle, fontSizeCoeff / 1.5f,
                xOffset + charXOffset, yOffset - lineSpacing)
            .showText(String.valueOf(line.charAt(i)));
    }
}
canvas.endText();

     As before, we put the coordinate system in the upper left corner again, defined yellow in Cmyk space, defined the line spacing and the starting position of y axis, and then began to add text. We used equal width bold and defined the font size as 1 user unit. Although there is only 1 user unit, we will enlarge the text later through the text matrix, where we We won't use setLeading() to set the line spacing because we don't use the newlineShowText() method. We set the text color and display it one by one instead of drawing the whole line before.

The image of each character is defined as the drawn path in the font. By default, the path of these characters is filled, which is why we set fill color to change the font color

     We start to cycle the text and read each line into a String. We need a series of mathematical variables to define the different elements of the text matrix that will be used to locate each font: we define an xOffset variable for each line to determine the starting position of the text in the current line. The font size is defined as 1 user unit, but we multiply it by a fontSizeCoeff, which takes It depends on the index of the rows in the text array. Colleagues, we will also define yOffset to determine the starting position of each row.
     Calculate the number of characters in each line, and then cycle all characters. We define an angle variable according to the position of the characters in the line. The charOffset variable depends on the index of the line and the position of the characters.
     Finally, set the transformation matrix of the text, a and d define the scaling scale, c parameter defines the tilt degree, and then calculate the coordinates of the characters to define the e and f parameters. After the position of each character is determined, use the showText() function to display the characters. This method will not start another line to display the characters. We start another line by looping, and finally use endext() To close the text object

This example is very complex, but we can see from this example that we can create any content. As long as we can do it in PDF, iText can also do it. But rest assured that future examples will be easier to understand

summary

     In this chapter, we have been trying various operations in PDF and corresponding operations in iText. We have learned a concept called graph state, which has the current transformation matrix, linewidth, color and other attributes. Text states are a subset of graphical states that cover all attributes related to text, such as text matrix, font and size of text, and many other attributes we have not discussed. We will cover it in detail in another tutorial. You may wonder why developers need to access low-level API s instead of using many of iText's high-level functions, which will be answered in the next chapter.

Tags: Java

Posted on Thu, 28 Oct 2021 13:06:38 -0400 by dakey