using org.pdfclown.documents; using org.pdfclown.documents.contents; using colorSpaces = org.pdfclown.documents.contents.colorSpaces; using org.pdfclown.documents.contents.composition; using org.pdfclown.documents.contents.fonts; using org.pdfclown.documents.contents.objects; using org.pdfclown.files; using org.pdfclown.objects; using System; using System.Drawing; using System.Drawing.Drawing2D; namespace org.pdfclown.samples.cli { /** This sample shows the effects of the manipulation of the CTM (Current Transformation Matrix), that is the logical device which affects the PDF page coordinate system used to place graphics contents onto the canvas. */ public class PageCoordinatesSample : Sample { private static readonly PdfName ResourceName_DefaultFont = new PdfName("default"); private static float Max( params float[] values ) { float maxValue = values[0]; foreach(float value in values) {maxValue = Math.Max(maxValue, value);} return maxValue; } public override bool Run( ) { // 1. Instantiate a new PDF file! File file = new File(); Document document = file.Document; // 2. Set the document properties and resources! Initialize(document); // 3. Insert the contents into the document! BuildContent(document); // (boilerplate metadata insertion -- ignore it) BuildAccessories(document,"Page coordinates","manipulating the CTM"); // 4. Serialize the PDF file! Serialize(file,false); return true; } private void BuildContent( Document document ) { // Add the page to the document! Page page = new Page(document); // Instantiates the page inside the document context. document.Pages.Add(page); // Puts the page in the pages collection. // Create a content composer for the page content stream! PrimitiveComposer composer = new PrimitiveComposer(page); string[] steps = new string[5]; colorSpaces::Color[] colors = new colorSpaces::Color[5]; SizeF pageSize = page.Size; BuildSteps(composer, steps, colors, pageSize); BuildLegend(composer, steps, colors, pageSize); composer.Flush(); } private string GetStepNote( PrimitiveComposer composer, string comment ) { // Get the CTM! Matrix ctm = composer.Scanner.State.Ctm; return "CTM (" + comment + "): " + ctm.Elements[0] + ", " + ctm.Elements[1] + ", " + ctm.Elements[2] + ", " + ctm.Elements[3] + ", " + ctm.Elements[4] + ", " + ctm.Elements[5]; } private void BuildLegend( PrimitiveComposer composer, string[] steps, colorSpaces::Color[] colors, SizeF pageSize ) { float maxCtmInversionApproximation; { float[] ctmInversionApproximations = new float[6]; { float[] initialCtmValues, finalCtmValues; { ContentScanner.GraphicsState state = composer.Scanner.State; initialCtmValues = state.GetInitialCtm().Elements; finalCtmValues = state.Ctm.Elements; } for( int index = 0, length = finalCtmValues.Length; index < length; index++ ) {ctmInversionApproximations[index] = Math.Abs(finalCtmValues[index]) - initialCtmValues[index];} } maxCtmInversionApproximation = Max(ctmInversionApproximations); } BlockComposer blockComposer = new BlockComposer(composer); blockComposer.LineSpace = new Length(.25f, Length.UnitModeEnum.Relative); composer.BeginLocalState(); composer.SetFillColor( new colorSpaces::DeviceRGBColor(115f/255,164f/255,232f/255) ); RectangleF frame = new RectangleF( 18, 18, pageSize.Width * .5f, pageSize.Height * .5f ); blockComposer.Begin(frame,AlignmentXEnum.Left,AlignmentYEnum.Top); composer.SetFont(ResourceName_DefaultFont,24); blockComposer.ShowText("Page coordinates sample"); SizeF breakSize = new SizeF(0,8); blockComposer.ShowBreak(breakSize); composer.SetFont(ResourceName_DefaultFont,8); blockComposer.ShowText( "This sample shows the effects of the manipulation of the CTM (Current Transformation Matrix), " + "that is the mathematical device which affects the page coordinate system used to place " + "graphic contents onto the canvas." ); blockComposer.ShowBreak(breakSize); blockComposer.ShowText( "The following steps represent the operations applied to this page's CTM in order to alter it. " + "Each step writes the word \"Step\" at the lower-left corner of the current page frame:" ); blockComposer.ShowBreak(breakSize); for(int i = 0; i < steps.Length; i++) { composer.SetFillColor(colors[i]); blockComposer.ShowText("Step " + i + ")"); composer.SetFillColor( new colorSpaces::DeviceRGBColor(115f/255,164f/255,232f/255) ); blockComposer.ShowText(" " + steps[i]); blockComposer.ShowBreak(breakSize); } blockComposer.ShowText("Note that the approximation (" + maxCtmInversionApproximation + ") of the CTM components at step 4 is due to floating point precision limits; their exact values should be 1.0, 0.0, 0.0, 1.0, 0.0, 0.0."); blockComposer.End(); composer.End(); } private void BuildSteps( PrimitiveComposer composer, string[] steps, colorSpaces::Color[] colors, SizeF pageSize ) { composer.SetFont(ResourceName_DefaultFont,32); RectangleF frame = new RectangleF( 0, 0, pageSize.Width, pageSize.Height ); // Step 0. { colors[0] = new colorSpaces::DeviceRGBColor(30f/255, 10f/255, 0); composer.SetFillColor(colors[0]); composer.SetStrokeColor(colors[0]); // Draw the page frame! composer.DrawRectangle(frame); composer.Stroke(); // Draw the lower-left corner mark! composer.ShowText( "Step 0", new PointF(0,pageSize.Height), AlignmentXEnum.Left, AlignmentYEnum.Bottom, 0 ); steps[0] = GetStepNote(composer,"default"); } // Step 1. { colors[1] = new colorSpaces::DeviceRGBColor(80f/255, 25f/255, 0); composer.SetFillColor(colors[1]); composer.SetStrokeColor(colors[1]); // Transform the coordinate space, applying translation! composer.Translate(72,72); // Draw the page frame! composer.DrawRectangle(frame); composer.Stroke(); // Draw the lower-left corner mark! composer.ShowText( "Step 1", new PointF(0,pageSize.Height), AlignmentXEnum.Left, AlignmentYEnum.Bottom, 0 ); steps[1] = GetStepNote(composer,"after translate(72,72)"); } // Step 2. { colors[2] = new colorSpaces::DeviceRGBColor(130f/255, 45f/255, 0); composer.SetFillColor(colors[2]); composer.SetStrokeColor(colors[2]); // Transform the coordinate space, applying clockwise rotation! composer.Rotate(-20); // Draw the page frame! composer.DrawRectangle(frame); composer.Stroke(); // Draw the coordinate space origin mark! composer.ShowText("Origin 2"); // Draw the lower-left corner mark! composer.ShowText( "Step 2", new PointF(0,pageSize.Height), AlignmentXEnum.Left, AlignmentYEnum.Bottom, 0 ); steps[2] = GetStepNote(composer,"after rotate(20)"); } // Step 3. { colors[3] = new colorSpaces::DeviceRGBColor(180f/255, 60f/255, 0); composer.SetFillColor(colors[3]); composer.SetStrokeColor(colors[3]); // Transform the coordinate space, applying translation and scaling! composer.Translate(0,72); composer.Scale(.5f,.5f); // Draw the page frame! composer.DrawRectangle(frame); composer.Stroke(); // Draw the lower-left corner mark! composer.ShowText( "Step 3", new PointF(0,pageSize.Height), AlignmentXEnum.Left, AlignmentYEnum.Bottom, 0 ); steps[3] = GetStepNote(composer,"after translate(0,72) and scale(.5,.5)"); } // Step 4. { colors[4] = new colorSpaces::DeviceRGBColor(230f/255, 75f/255, 0); composer.SetFillColor(colors[4]); composer.SetStrokeColor(colors[4]); // Transform the coordinate space, restoring its initial CTM! composer.Add( ModifyCTM.GetResetCTM( composer.Scanner.State ) ); // Draw the page frame! composer.DrawRectangle(frame); composer.Stroke(); // Draw the lower-left corner mark! composer.ShowText( "Step 4", new PointF(0,pageSize.Height), AlignmentXEnum.Left, AlignmentYEnum.Bottom, 0 ); steps[4] = GetStepNote(composer,"after resetting CTM"); } } private void Initialize( Document document ) { // 1. Set default page size (A4)! document.PageSize = PageFormat.GetSize(); // 2. Setting the document resources... // 2.1. Resources collection. Resources resources = new Resources(document); // Instantiates the resources collection inside the document context. document.Resources = resources; // Puts the resources collection in the common resources role. // 2.2. Fonts collection. FontResources fonts = new FontResources(document); // Instantiates the fonts collection inside the document context. resources.Fonts = fonts; // Puts the fonts collection in the common resources role. // Add a font to the fonts collection! fonts[ResourceName_DefaultFont] = new StandardType1Font( document, StandardType1Font.FamilyEnum.Courier, true, false ); } } }