Working with Transformations in PS file | Java
Transform a content in PS Document
In this article, we consider how to make different transformations: translation, scaling, rotation, and shearing onto a rectangle path added to PsDocument.
We split one code snippet into several pieces of code: the beginning, the end, and each transformation separately. A transformation in PostScript is made always in a graphics state that is bound by “gsave” and “grestore” operators. Therefore in our PsDocument, there are methods “writeGraphicsSave()” and “writeGraphicsRestore()”. Between these methods, we can add any content, including nested graphics state, and make any transformations or clipping. These transformations will not affect outer graphics states but will do on nested ones.
If we make a transformation without “writeGraphicsSave()” and “writeGraphicsRestore()” methods we make it in the graphics state of the upper level and all content that is in PsDocument will be undergone by this transformation.
An algorithm for setting any transformation on a document’s content from scratch includes the following steps:
- Create an output stream for the resulting PS file.
- Create PsSaveOptions.
- Create PsDocument with the already created output stream and save options.
- Save a graphics state. So we created a new graphics state, the previous graphics state was put in a graphics states stack.
- Add necessary transformation: translation, scale, rotation, shear, or any combination of it. In our code, we show the influence of every transformation component separately and 3 ones at a time in the end.
- Add necessary content that is required to transform. In our case, we created java.awt.geom.Rectangle2D and then fill it. We created one rectangle before any transformations and just fill it after every transformation in the current graphics state.
- Restore the graphics state to return back to the previous one where applied transformation(s) doesn’t affect. In our case, it is an upper-level graphics state.
In this piece of code, we create PsDocument from an output stream and PsSaveOptions, translate upper-level graphics state to points 100,100 in order to offset the first rectangle, and finally create the first rectangle:
1//Create an output stream for PostScript document
2FileOutputStream outPsStream = new FileOutputStream(dataDir + "Tranformations_outPS.ps");
3//Create a save options with A4 size
4PsSaveOptions options = new PsSaveOptions();
5
6// Create new PS Document with the page opened
7PsDocument document = new PsDocument(outPsStream, options, false);
8
9document.translate(100, 100);
10
11//Create a rectangle
12Shape shape = new Rectangle2D.Float(0, 0, 150, 100);
Here we set Orange color as the current paint for the upper-level graphics state and fill this rectangle.
The resulting PS file will demonstrate the first shape that is located in the upper-level graphics state and is not undergone any transformation:
1////////////////////////////////////// No transformations ///////////////////////////////////////////////////////////////
2//Set paint in graphics state on upper level
3document.setPaint(Color.ORANGE);
4
5//Fill the first rectangle that is on on upper level graphics state and that is without any transformations.
6document.fill(shape);
7/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Now we create a new graphics state that will be translated on 250 points by X-axis relatively to the upper-level graphics state and add to these new graphics state the same rectangle painted with Blue color. In the end, we exit from the current graphics state on the upper-level graphics state:
1////////////////////////////////////// Translation //////////////////////////////////////////////////////////////////////
2//Save graphics state in order to return back to this state after transformation
3document.writeGraphicsSave();
4
5//Displace current graphics state on 250 to the right. So we add translation component to the current transformation.
6document.translate(250, 0);
7
8//Set paint in the current graphics state
9document.setPaint(Color.BLUE);
10
11//Fill the second rectangle in the current graphics state (has translation transformation)
12document.fill(shape);
13
14//Restore graphics state to the previus (upper) level
15document.writeGraphicsRestore();
16/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Translate upper level graphics state in order to put the next rectangles:
1//Displace on 200 to the bottom.
2document.translate(0, 200);
Here we create a graphics state that will be scaled on 0.5 by X-axis and 0.75 by Y-axis and add to these new graphics state the same rectangle painted with Red color. In the end, we exit from the current graphics state on the upper-level graphics state:
1////////////////////////////////////// Scaling //////////////////////////////////////////////////////////////////////////
2//Save the graphics state in order to return back to this state after transformation
3document.writeGraphicsSave();
4
5//Scale current graphics state on 0.5 in X axis and on 0.75f in Y axis. So we add scale component to the current transformation.
6document.scale(0.5f, 0.75f);
7
8//Set paint in the current graphics state
9document.setPaint(Color.RED);
10
11//Fill the third rectangle in the current graphics state (has scale transformation)
12document.fill(shape);
13
14//Restore graphics state to the previus (upper) level
15document.writeGraphicsRestore();
16//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Translate the upper-level graphics state in order to put the next rectangles:
1//Displace upper level graphics state on 250 to the right.
2document.translate(250, 0);
Then we create a new graphics state that will be rotated 45 degrees clockwise relative to the upper-level graphics state and add to these new graphics state the same rectangle painted with Green color. In the end, we exit from the current graphics state on the upper-level graphics state:
1////////////////////////////////////// Rotation //////////////////////////////////////////////////////////////////////
2//Save graphics state in order to return back to this state after transformation
3document.writeGraphicsSave();
4
5//Rotate current graphics state on 45 degrees around origin of current graphics state (350, 300). So we add rotation component to the current transformation.
6document.rotate(45);
7
8//Set paint in the current graphics state
9document.setPaint(Color.GREEN);
10
11//Fill the fourth rectangle in the current graphics state (has rotation transformation)
12document.fill(shape);
13
14//Restore graphics state to the previus (upper) level
15document.writeGraphicsRestore();
16//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Translate the upper-level graphics state in order to put the next rectangles in the blank space on the page.
1//Returns upper level graphics state back to the left and displace on 200 to the bottom.
2document.translate(-250, 200);
Then we create a new graphics state that will be sheared and add to this new graphics state the same rectangle painted with Pink color. In the end, we exit from the current graphics state on the upper-level graphics state:
1////////////////////////////////////// Shearing //////////////////////////////////////////////////////////////////////
2//Save graphics state in order to return back to this state after transformation
3document.writeGraphicsSave();
4
5//Shear current graphics state. So we add shear component to the current transformation.
6document.shear(0.1f, 0.2f);
7
8//Set paint in the current graphics state
9document.setPaint(new Color(255,192,203));
10
11//Fill the fifth rectangle in the current graphics state (has shear transformation)
12document.fill(shape);
13
14//Restore graphics state to the previous (upper) level
15document.writeGraphicsRestore();
16//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Translate the upper-level graphics state in order to put the next rectangles in the blank space on the page.
1//Displace upper level graphics state on 250 to the right.
2document.translate(250, 0);
Now we create the last graphics state that will be undergone by complex transformation containing translation, scaling, and rotation components and add to this new graphics state the same rectangle painted with Aquamarine color. In the end, we exit from the current graphics state on the upper-level graphics state:
1////////////////////////////////////// Complex transformation ////////////////////////////////////////////////////////
2//Save graphics state in order to return back to this state after transformation
3document.writeGraphicsSave();
4
5//Transform current graphics state with complex transformation. So we add translation, scale and rotation components to the current transformation.
6document.transform(new AffineTransform(1.2f, -0.965925f, 0.258819f, 1.5f, 0f, 50));
7
8//Set paint in the current graphics state
9document.setPaint(new Color(127,255,212));
10
11//Fill the sixth rectangle in the current graphics state (has complex transformation)
12document.fill(shape);
13
14//Restore graphics state to the previus (upper) level
15document.writeGraphicsRestore();
16//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Translate the upper-level graphics state in order to put the last rectangle in the blank space on the page:
1//Returns upper level graphics state back to the left and displace on 200 to the bottom.
2document.translate(-250, 200);
The last filled rectangle that we put again in the upper-level graphics state shows that it was not undergone by transformations of graphics states of the lower level and changing of colors in it. Orange color is left current paint:
1////////////////////////////////////// Again no transformation ////////////////////////////////////////////////////////
2//Demonstrates that current graphics state's color is orange that was set up at the beginning of the code.
3//Fill the seventh rectangle in the current graphics state (has no transformation)
4document.fill(path);
5//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Finally we close the current page and save the document:
1//Close current page
2document.closePage();
3
4//Save the document
5document.save();
6}
See working with transformations in PS document in .NET.
The result of running this code is the next
You can download examples and data files from GitHub.