Working with Transformations in PostScript | C++

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:

  1. Create an output stream for the resulting PS file.
  2. Create PsSaveOptions.
  3. Create PsDocument with the already created output stream and save options.
  4. Save a graphics state. So we created a new graphics state, the previous graphics state was put in a graphics states stack.
  5. 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.
  6. Add necessary content that is required to transform. In our case, we created a rectangle GraphicsPath from the rectangle and then fill it. We created one rectangle before any transformations and just fill it after every transformation in the current graphics state.
  7. 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 GraphicsPath from the rectangle.

 1    // The path to the documents directory.
 2    System::String dataDir = RunExamples::GetDataDir_WorkingWithCanvas();
 3    
 4    //Create output stream for PostScript document
 5    {
 6        System::SharedPtr<System::IO::Stream> outPsStream = System::MakeObject<System::IO::FileStream>(dataDir + u"Transformations_outPS.ps", System::IO::FileMode::Create);
 7        // Clearing resources under 'using' statement
 8        System::Details::DisposeGuard<1> __dispose_guard_0({ outPsStream});
 9        // ------------------------------------------
10        
11        try
12        {
13            //Create save options with default values
14            System::SharedPtr<PsSaveOptions> options = System::MakeObject<PsSaveOptions>();
15            
16            // Create new 1-paged PS Document
17            System::SharedPtr<PsDocument> document = System::MakeObject<PsDocument>(outPsStream, options, false);
18            
19            document->Translate(100.0f, 100.0f);
20            
21            //Create graphics path from the rectangle
22            System::SharedPtr<System::Drawing::Drawing2D::GraphicsPath> path = System::MakeObject<System::Drawing::Drawing2D::GraphicsPath>();
23            path->AddRectangle(System::Drawing::RectangleF(0.0f, 0.0f, 150.0f, 100.0f));

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
3            document->SetPaint(System::MakeObject<System::Drawing::SolidBrush>(System::Drawing::Color::get_Orange()));
4            
5            //Fill the first rectangle that is on on upper level graphics state and that is without any transformations.
6            document->Fill(path);
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 path painted with Blue color. In the end, we exit from the current graphics state on the upper-level graphics state.

 1            ////////////////////////////////////// Translation //////////////////////////////////////////////////////////////////////
 2            
 3            //Save graphics state in order to return back to this state after transformation
 4            document->WriteGraphicsSave();
 5            
 6            //Displace current graphics state on 250 to the right. So we add translation component to the current transformation.
 7            document->Translate(250.0f, 0.0f);
 8            
 9            //Set paint in the current graphics state
10            document->SetPaint(System::MakeObject<System::Drawing::SolidBrush>(System::Drawing::Color::get_Blue()));
11            
12            //Fill the second rectangle in the current graphics state (has translation transformation)
13            document->Fill(path);
14            
15            //Restore graphics state to the previus (upper) level
16            document->WriteGraphicsRestore();
17            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Translate upper level graphics state in order to put the next rectangles.

1            //Displace on 200 to the bottom.
2            document->Translate(0.0f, 200.0f);

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 path painted with Red color. In the end, we exit from the current graphics state on the upper-level graphics state.

 1            ////////////////////////////////////// Scaling //////////////////////////////////////////////////////////////////////////
 2            //Save graphics state in order to return back to this state after transformation
 3            document->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.
 6            document->Scale(0.5f, 0.75f);
 7            
 8            //Set paint in the current graphics state
 9            document->SetPaint(System::MakeObject<System::Drawing::SolidBrush>(System::Drawing::Color::get_Red()));
10            
11            //Fill the third rectangle in the current graphics state (has scale transformation)
12            document->Fill(path);
13            
14            //Restore graphics state to the previus (upper) level
15            document->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.
2            document->Translate(250.0f, 0.0f);

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 path 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
 3            document->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.
 6            document->Rotate(45);
 7            
 8            //Set paint in the current graphics state
 9            document->SetPaint(System::MakeObject<System::Drawing::SolidBrush>(System::Drawing::Color::get_Green()));
10            
11            //Fill the fourth rectangle in the current graphics state (has rotation transformation)
12            document->Fill(path);
13            
14            //Restore graphics state to the previus (upper) level
15            document->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.
2            document->Translate(-250.0f, 200.0f);

Then we create a new graphics state that will be sheared and add to this new graphics state the same rectangle path 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
 3            document->WriteGraphicsSave();
 4            
 5            //Shear current graphics state. So we add shear component to the current transformation.
 6            document->Shear(0.1f, 0.2f);
 7            
 8            //Set paint in the current graphics state
 9            document->SetPaint(System::MakeObject<System::Drawing::SolidBrush>(System::Drawing::Color::get_Pink()));
10            
11            //Fill the fifth rectangle in the current graphics state (has shear transformation)
12            document->Fill(path);
13            
14            //Restore graphics state to the previus (upper) level
15            document->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.
2            document->Translate(250.0f, 0.0f);

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 path 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
 3            document->WriteGraphicsSave();
 4            
 5            //Transform current graphics state with complex transformation. So we add translation, scale and rotation components to the current transformation.
 6            document->Transform(System::MakeObject<System::Drawing::Drawing2D::Matrix>(1.2f, -0.965925f, 0.258819f, 1.5f, 0.f, 50.0f));
 7            
 8            //Set paint in the current graphics state
 9            document->SetPaint(System::MakeObject<System::Drawing::SolidBrush>(System::Drawing::Color::get_Aquamarine()));
10            
11            //Fill the sixth rectangle in the current graphics state (has complex transformation)
12            document->Fill(path);
13            
14            //Restore graphics state to the previus (upper) level
15            document->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.
2            document->Translate(-250.0f, 200.0f);

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)
4            document->Fill(path);
5            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Finally we close the current page and save the document.

 1            //Close current page
 2            document->ClosePage();
 3            
 4            //Save the document
 5            document->Save();
 6        }
 7        catch(...)
 8        {
 9            __dispose_guard_0.SetCurrentException(std::current_exception());
10        }
11    }

See working with transformations in PS document in .NET or Java.


The result of running this code is the next

Transformations

You can download examples and data files from GitHub.

Have any questions about Aspose.Page?



Subscribe to Aspose Product Updates

Get monthly newsletters & offers directly delivered to your mailbox.