Travailler avec des transformations dans PostScript | C++
Transformer un contenu dans un document PS
Dans cet article, nous examinons comment effectuer différentes transformations : translation, mise à l’échelle, rotation et cisaillement sur un chemin rectangulaire ajouté à PsDocument.
Nous divisons un extrait de code en plusieurs morceaux de code : le début, la fin et chaque transformation séparément. Une transformation en PostScript est toujours effectuée dans un état graphique lié par les opérateurs “gsave” et “grestore”. Par conséquent dans notre PsDocument, il existe des méthodes “WriteGraphicsSave()” et “WriteGraphicsRestore()”. Entre ces méthodes, nous pouvons ajouter n’importe quel contenu, y compris l’état graphique imbriqué, et effectuer des transformations ou des découpages. Ces transformations n’affecteront pas les états graphiques externes mais le feront sur ceux imbriqués.
Si nous effectuons une transformation sans les méthodes “WriteGraphicsSave()” et “WriteGraphicsRestore()” nous la faisons dans l’état graphique du niveau supérieur et tout le contenu qui se trouve dans PsDocument sera soumis à cette transformation .
Un algorithme permettant de définir toute transformation sur le contenu d’un document à partir de zéro comprend les étapes suivantes :
- Créez un flux de sortie pour le fichier PS résultant.
- Créez PsSaveOptions.
- Créez PsDocument avec le flux de sortie déjà créé et enregistrez les options.
- Enregistrez un état graphique. Nous avons donc créé un nouvel état graphique, l’état graphique précédent a été mis dans une pile d’états graphiques.
- Ajoutez la transformation nécessaire : translation, échelle, rotation, cisaillement ou toute combinaison de celles-ci. Dans notre code, nous montrons l’influence de chaque composant de transformation séparément et 3 à la fois au final.
- Ajoutez le contenu nécessaire à la transformation. Dans notre cas, nous avons créé un rectangle GraphicsPath à partir du rectangle puis l’avons rempli. Nous avons créé un rectangle avant toute transformation et l’avons simplement rempli après chaque transformation dans l’état graphique actuel.
- Restaurez l’état graphique pour revenir à l’état précédent où les transformations appliquées n’affectent pas. Dans notre cas, il s’agit d’un état graphique de niveau supérieur.
Dans ce morceau de code, nous créons PsDocument à partir d’un flux de sortie et de PsSaveOptions, traduisons l’état graphique de niveau supérieur en points 100 100 afin de décaler le premier rectangle, et enfin créons le premier rectangle GraphicsPath à partir du 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));
Ici, nous définissons la couleur Orange comme peinture actuelle pour l’état graphique de niveau supérieur et remplissons ce rectangle.
Le fichier PS résultant montrera la première forme située dans l’état graphique de niveau supérieur et qui n’a subi aucune 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 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Nous créons maintenant un nouvel état graphique qui sera traduit sur 250 points par l’axe X par rapport à l’état graphique de niveau supérieur et ajoutons à ce nouvel état graphique le même chemin rectangulaire peint avec la couleur Bleu. Au final, nous sortons de l’état graphique actuel sur l’état graphique de niveau supérieur.
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 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Traduisez l’état graphique de niveau supérieur afin de placer les rectangles suivants.
1 //Displace on 200 to the bottom.
2 document->Translate(0.0f, 200.0f);
Ici, nous créons un état graphique qui sera mis à l’échelle sur 0,5 par axe X et 0,75 par axe Y et ajoutons à ce nouvel état graphique le même chemin de rectangle peint avec la couleur Rouge. Au final, nous sortons de l’état graphique actuel sur l’état graphique de niveau supérieur.
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 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Traduisez l’état graphique de niveau supérieur afin de placer les rectangles suivants.
1 //Displace upper level graphics state on 250 to the right.
2 document->Translate(250.0f, 0.0f);
Ensuite, nous créons un nouvel état graphique qui pivotera de 45 degrés dans le sens des aiguilles d’une montre par rapport à l’état graphique de niveau supérieur et ajoutons à ce nouvel état graphique le même chemin rectangulaire peint avec la couleur Vert. Au final, nous sortons de l’état graphique actuel sur l’état graphique de niveau supérieur.
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 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Traduisez l’état graphique de niveau supérieur afin de placer les rectangles suivants dans l’espace vide de la 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);
Ensuite, nous créons un nouvel état graphique qui sera cisaillé et ajoutons à ce nouvel état graphique le même chemin rectangulaire peint avec la couleur Rose. Au final, nous sortons de l’état graphique actuel sur l’état graphique de niveau supérieur.
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 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Traduisez l’état graphique de niveau supérieur afin de placer les rectangles suivants dans l’espace vide de la page.
1 //Displace upper level graphics state on 250 to the right.
2 document->Translate(250.0f, 0.0f);
Nous créons maintenant le dernier état graphique qui sera soumis à une transformation complexe contenant des composants de translation, de mise à l’échelle et de rotation et ajoutons à ce nouvel état graphique le même chemin rectangulaire peint avec la couleur Aigue-marine.
Au final, nous sortons de l’état graphique actuel sur l’état graphique de niveau supérieur.
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 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Traduisez l’état graphique de niveau supérieur afin de placer le dernier rectangle dans l’espace vide de la 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);
Le dernier rectangle rempli que l’on a remis dans l’état graphique de niveau supérieur montre qu’il n’a pas subi de transformations d’états graphiques de niveau inférieur et de changement de couleurs dans celui-ci. La couleur orange est celle de la peinture actuelle.
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 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Enfin, nous fermons la page actuelle et enregistrons le 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 }
Le résultat de l’exécution de ce code est le suivant
Vous pouvez télécharger des exemples et des fichiers de données à partir de GitHub.