GFX Page Region Export (TMM)

Visual FoxPro 9.0 SP2
This page is an addition to the VFP SP2 CHM, covering a class that implements the interface of the SP2 Visual FoxPro Reporting System's FFC FX subsystem.
Back to TMM Index
GeneralAbout this class
GFXOutputClip provides copy-to-image file for designated page regions during a report run, so you can export the rendered regions for embedding in output targets. The techniques it uses are an alternative to xmlDisplayListener.copyImageFilesToExternalFileLocation, which are optimized for handling Image layout control content in a report.  GFXOutputClip's techniques are more complex but capable of  handling custom-rendered content for placeholder layout controls in the report.

GFXOutputClip defines page regions to be clipped-and-saved using the positional and dimensional for report layout controls, which may be of any OBJTYPE/OBJCODE. 

GFXOutputClip can take action in one of the following ways:
  • You add an instance of the class to the FXListener-derived reportlistener's GFXs collection, and mark certain report layout controls for its attention by adding appropriate MemberData rows for those controls. (The class has a PROTECTED property, sNamespace, to define its MemberData rows, which is initially set in the class constructor.)  This class looks only at the NAME column in FRX MemberData to discover layout controls it should handle.  GFXOutputClip automatically turns its behavior on for a report run when it discovers "its" MemberData in a report for at least one layout control.
  • You add an instance of some other class providing report decoration, knowledgeable about GFXOutputClip and requiring its services.  The other class can check the collection for an existing GFXOutputClip instance and, if one does not exist, unilaterally add an instance to the collection, where it might be shared by other classes participating in the run.  Your other class can now explicitly invoke GFXOutputClip's services as needed based on its own MemberData instructions or other requirements.  For example, a graphing class might mark certain controls in the report as graph placeholders with rows of MemberData in its own namespace, such as Spacefold.LSN.gfxCharting.  After it draws a graph to replace these placeholder controls, it invokes the GFXOutputClip instance to save a copy of the control's page region as a file, so Successor reportlisteners can "see" the graphs.  In this scenario there might be no GFXOutputClip-specific MemberData rows, so the  other class might want to use the GFXOutputClip.forceOn attribute to ensure availability.
  • You add an instance of some other class providing report decoration, which creates an instance of GFXOutputClip as a private member object.  This approach works similarly to the previous one, with explicit invocation,  but when the instance of GFXOutputClip is not shared you can set certain attributes for it differently than you might for a shared instance.  For example, the  shared instance might create JPGs if there were photographs in the report, but graphing class might want PNGs.  A private instance of GFXOutputClip could set the instance's mimetype property appropriately for the graphing class without complex save-restore behavior for this attribute, and without disturbing other report behavior.

Similar to UtilityReportListener's page-image-saving feature, GFXOutputClip has an internal method of creating and managing the filenames for the page region clip files it creates during the course of a run, with this difference: When UtilityReportListener creates page images, there is always exactly one image file per report page.  By contrast, GFXOutputClip creates as many page region clip files as there are instances of a single layout control marked for its attention.  For example, if you mark a textbox in the detail band of a report to be clipped-and-saved, and there are 500 rows in the dataset, there are 500 clip files.

Also similar to UtilityReportListener's page-image-saving feature, GFXOutputClip has different behavioral modes depending on the current ListenerType; it may create all images at the conclusion of a report run, or it may create all images for each report page at the time each page is rendered.  You are not guaranteed, therefore, any particular moment during the report when your page region clip files are available.
When you explicitly invoke the object's behavior, you can use its getCurrentClipFileName method to work with the image file it is currently preparing.  Whether the object is explicitly or automatically invoked, if the placeholder layout control is an Image control, GFXOutputClip replaces the ContentsToBeRendered argument during the Render event of the control  with the generated name of the file it will later save for this instance of the control.  This practice allows Successors to shared custom content.  For example, the graphing class or an RTF-creating component rendering rich content to a placeholder Image control can expect HTMLListener and other Successors to render their contributions properly.

Note Notes
The technique described in the last paragraph -- sharing rich content by mimicking the Report Engine's use of ContentsToBeRendered for images in the report -- is recommended as a best practice  for custom content providers. In fact, showing this technique is one of the prime reasons GFXOutputClip ships in SP2.

 The Init method for this class contains a few additional helpful notes on using it.

PEMs commentsProperties and Methods

This class adds the following public methods and properties beyond its implementation of the FX API's required ApplyFX method.

Properties and methods Description

forceOn Property

Require this object to turn itself on for a report run, even if it does not see any objects tagged for its attention with the Memberdata values it expects.

Default: .F.

gdiPlusLib Property

Class library to use for instantation of point, rect, and other helper objects.

Default: ""


GFXOutputClip uses several GDIPLUS.VCX objects in its work.  You can use objects implementing the same API from your own library if you specify one.  If you do not, by default GFXOutputClip looks for the objects in the class library from which its own-listener's FFCGraphics member object was instanced (which may be the standard FFC GDIPLUS.VCX or may be one you have substituted).

gdiPlusLibModule Property

Optional APP or EXE file (module) from which to instantiate helper objects in the gdiPlusLib class library.

Default: ""

getCurrentClipFileName Method

Provides generated output image copy filename for the current copy action.

margin Property

Integer value of frame margin (in pixels) to add to both width and height when determining the clip coordinates for the current portion of the page to be copied.

Default: 10


This value is somewhat imprecise as it gets translated to the region available in various placeholder layout controls.  It is exposed to give you a chance to figure out what value works well for your specific controls in a specific report, with your specific type of custom content.  Experiment in each scenario to get the best values, rather than settling on a single preferred default for use in all reports.

mimetype Property

Mimetype to use for image-copy files the object creates.

Default: "image/png"

This value is only minimally validated, so it is your responsibility to use appropriate mimetypes when assigning non-default values.

outputPageClip Method

Bindable procedure to save image copy files at the correct moment (the OutputPage event) during a paged report run.


Syntax: outputPageClip(m.nPageNo, ; m.eDevice, ;
        m.nDeviceType, ; m.nleft, m.nTop, m.nWidth, m.nHeight, ;
        m.nClipLeft,m.nClipTop, m.nClipWidth, m.nClipHeight )


Return Values: .T.



This method has the same signature as the baseclass ReportListener.OutputPage event, because in some output modes it is bound to that event.

setupImageClip Method

Saves a row to the aImageCopies array during rendering procedures when a page region is marked for an image copy, for later use in image copy procedures.


Syntax: setupImageClip(m.toListener, ;
        m.tnLeft, m.tnTop, m.tnWidth, m.tnHeight, ; m.tvContentsToBeRendered, m.tlImageControl)


Return Values: .T.



This method is invoked, either automatically or by your code, during the Render event to prepare an array row with the information required to save a specific region of a page.  It generates a unique filename for each row in the array as part of this process.


The following code is the Render CASE code in the ApplyFX method of a custom graphing object.  The object has a private cursor that indicates whether or not it needs to clip-and-save page region files for its output for each graph element in the report.  (It omits the clip-and-save step if it finds that a graph-producing layout control also has separate MemberData instructions in the GFXOutputClip-specific namespace.)  The setupImageClip method adds an array row for the layout control instance at this time:

            CASE m.tcMethodToken == "RENDER" AND ;
                  NOT ISNULL(m.toListener.FFCGraphics) AND ;
               m.lvReturn = OUTPUTFX_DEFAULT_RENDER_BEHAVIOR
               m.liFRXRecno =  m.toListener.getFRXRecno(m.tcMethodToken,m.tP1, m.tP2)
               IF SEEK(m.liFRXRecno,THIS.myCursor) AND NOT DELETED(THIS.MyCursor)
                  LOCAL lcGraphType, lcAlias, llUserMode               
                  SELECT (THIS.myCursor)
                  IF ImageSaveClip AND VARTYPE(THIS.oPrivateClipSaver) = "O"
                     * explicit call.
                     * we'll do this before any error,
                     * because we can show the modified results in our
                     * placeholder control
                     * for error reporting as well as normal output;
                     * we can clip-and-save the error feedback just as we 
                     * would clip-and-save the successful graph

                       m.tP2, m.tP3, m.tP4, m.tP5, m.tP7, ImageType)
                     IF ImageType  
                        m.tp7 = THIS.oPrivateClipSaver.GetCurrentClipFilename()
                        #IF DEBUGGING
                          m.tp7 = STRCONV(THIS.oPrivateClipSaver.GetCurrentClipFilename(),STRCONV_DBCS_UNICODE)
                  * now draw the graph...

In the AfterReport CASE of the ApplyFX method of the same graphing object, we explicitly invoke the GFXOutputClip instance to finalize the page region files that were set up earlier during their Render events.  Note that, in page-at-a-time output modes (ListenerTypes 0 and 2), nothing actually happens here because the work occurred during OutputPage event triggers for each page, earlier.  However, in all-pages-at-once cached mode (ListenerTypes 1 and 3), we must handle this step at the conclusion of the report. GFXOutputClip iterates through its array rows and produces all page region files at once:
            CASE m.tcMethodToken == "AFTERREPORT"
               IF VARTYPE(THIS.oPrivateClipSaver) = "O"
                  * send explicit call.
                       THIS.oPrivateClipSaver.ApplyFX(m.toListener, m.tcMethodToken, ;
                            m.tP1, m.tP2, m.tP3, m.tP4, m.tP5, m.tP6, ;
                            m.tP7, m.tP8, m.tP9, m.tP10, m.tP11, m.tP12)
                  THIS.oPrivateClipSaver.forceOn = .F.         

See AlsoSee Also