How to export pictures from General fields by using the ReportListener base class in Visual FoxPro 9 (894819)



The information in this article applies to:

  • Microsoft Visual FoxPro 9.0 Professional Edition

SUMMARY

This article describes how to export pictures from General fields in Microsoft Visual FoxPro to files on disk by using the ReportListener base class.

INTRODUCTION

Visual FoxPro General fields are frequently used to store picture files. For example, .jpg and .bmp files are picture files. The General field type contains not only information about the picture that is stored but also information about the registered server that was used to write the picture to the field. For example, if the registered server for .jpg files on your computer is Microsoft Photo Editor, information about Microsoft Photo Editor and about the .jpg file is concatenated. Then, this information is stored in the General field.

When a Visual FoxPro table that has a General field is moved to another computer, the images in the field may not display correctly because of the additional server information that is stored in the field. This behavior may occur on forms or in reports. This behavior may occur even when you just view the table and modify the General field. The field may not display the actual image. Instead, the user may only see an icon.

You cannot always know if a valid server is present on the computers where you have your tables. Therefore, you may not want to use the General field type when your application extensively uses images that are stored in tables. For example, you may want to store images in a Memo (Binary) field. Then, you can use the StrToFile function to write the images to disk when you have to. You may also use the Blob field type that was introduced in Visual FoxPro 9. Or you may use the PictureVal property of the Image control.

However, if you do decide to use a different method, you must use the pictures that you already have stored in General fields. It is not easy to retrieve these images from the Visual FoxPro table and to put them onto disk. However, the following process in Visual FoxPro 9 may help you address this issue.

MORE INFORMATION

This section shows two examples of how to extract images from the PHOTO field in the EMPLOYEE sample table which is included with Visual FoxPro 9. You do this by using the new ReportListener base class. The ReportListener base class was introduced in Visual FoxPro 9.

Note These examples will only work in Visual FoxPro 9.

The first example shows how to manually create a report and then how to run the report to extract the images. The second example shows a programmatic approach to the process. This second example demonstrates how to create and how to modify a report by using code. This second example also demonstrates how to override the Render and AfterBand events of the XMLListener FoxPro Foundation Class (FFC) to fine-tune the extraction process.

Note You must perform this process on a computer where the images that are in your General field are displayed correctly. The images must not be displayed as icons. If you run this process on a computer where you only see icons, only the icons will be extracted to disk.

Example 1: Manually extract pictures from a General field by using a ReportListener class

  1. Make sure that Visual FoxPro is configured to perform object-assisted reporting. To do this, run the following command in the Command window:

    SET REPORTBEHAVIOR 90

  2. Open the Report Designer by running the CREATE REPORT command in the Command window.
  3. Reduce the height of the Page Header and Page Footer bands so they are 0 inches high. These bands are not needed for this example.
  4. From the Report Controls toolbar, add a Picture/OLE Bound control to the Detail band. Then, resize both the Detail band and the Picture/OLE Bound control so they are large enough to display the pictures that are in your General field.
  5. If the Picture/OLE Bound Control Properties dialog box is not already open, double-click the Picture/OLE Bound control to open it, and then set the following options on the General tab:
    1. Under Control source type, click General Field Name.
    2. In the Control Source box, type PHOTO. This is the name of the General field in the EMPLOYEE table.
    3. Click to select the Center general field horizontally in frame check box.
  6. Save and then close the report. Note where you saved the report.
  7. Save the following code sample to a new program, and then run it. When you are prompted, choose the report that you saved in step 6. When the code finishes, all the pictures from the General field that is named PHOTO will be extracted to a folder. Additionally, the path of the extracted pictures is copied to the Clipboard.
    *-----------------------------------
    * Microsoft Knowledge Base Article 894819
    * EXAMPLE 1 
    * AUTHOR: Trevor Hancock
    * CREATED: February 22, 2005 09:59:32
    * ABSTRACT: This code extracts pictures to disk from the 
    *                      General field that is named PHOTO in the 
    *                      EMPLOYEE sample table.
    *-----------------------------------
    CLEAR
    LOCAL lcTestDir AS STRING, ;
    	loListener AS REPORTLISTENER
    
    *-- Create a subfolder of the current
    *-- Visual FoxPro TEMP folder to hold the extracted pictures.
    lcTestDir = ADDBS( SYS( 2023 ) ) + 'GenTst\'
    IF !DIRECTORY( lcTestDir, 1 )
    	MD ( lcTestDir )
    ELSE
    	*-- If DIR is already there, empty it.
    	LOCAL lcOldSetSafe AS STRING
    	lcOldSetSafe = SET( 'Safety' )
    	SET SAFETY OFF
    	ERASE ( lcTestDir + '*.*' )
    	SET SAFETY &lcOldSetSafe
    ENDIF
    
    *-- Open the EMPLOYEE table. The General field that is named
    *-- PHOTO has pictures in it.
    USE ADDBS( HOME() ) + 'Samples\Data\Employee.dbf'
    
    *-- Here we run the Report Output Application (ReportOutput.app) in HOME().
    *-- We pass it a 5. This means that we want the Report Output Application
    *-- to store an object reference to an HTMLListener-type
    *-- ReportListener base class to the second parameter that we pass: loListener.
    DO HOME() + 'ReportOutput.app' WITH 5, loListener
    
    *-- Tell the HTMLListener to save the output (the .HTM page)
    *-- to a specific folder. This folder is the one created earlier.
    loListener.TargetFileName = lcTestDir + 'tmp.htm'
    *-- Typically, a report that is run by using an HTMLListener
    *-- displays wait windows during processing and a
    *-- message box at the end. We want to turn this off. Therefore,
    *-- we set QuietMode = .T.
    *-- Note: This also turns off error reports from the ReportListener.
    loListener.QUIETMODE = .T.
    
    *-- Run the report by using the Listener.
    REPORT FORM GETFILE('FRX') OBJECT loListener
    *-- This command deletes the .html file that is produced.
    *-- We do not need it.
    ERASE ( loListener.TargetFileName )
    
    *-- Clean up, and then report that the process has finished.
    *-- To see the results, use Microsoft Windows Explorer to
    *-- view the folder path that is stored in the Windows Clipboard.
    CLOSE DATA ALL
    _CLIPTEXT = lcTestDir
    ? 'Pictures have been extracted to ', lcTestDir
    ? 'This path is in your Clipboard.'
    

Example 2: Programmatically extract pictures from a General field by using a ReportListener class

Save this code example to a new .prg file. Then, run the program to automatically create a report and to use the report to extract the photos from the EMPLOYEE table.

Note Either create a folder on the C drive that is named TmpTst or redefine the lcTargetDir variable to point to the folder of your choice.
*-----------------------------------
* Microsoft Knowledge Base Article 894819
* EXAMPLE 2
* AUTHOR: Trevor Hancock
* CREATED: February 22, 2005 09:59:32
* ABSTRACT: This code extracts pictures to disk from the 
*                 General field that is named PHOTO in the 
*                 EMPLOYEE sample table.
*-----------------------------------

*-- This opposite value of the constant will be used to set the QuietMode
*-- property of the ReportListener base class. When the value is .T., 
*-- wait windows that indicate processing appear. At the end, a message box
*-- appears. Any errors in the ReportListener base class also appear.
#DEFINE ShowReportFlow_Errs  .F.

LOCAL lcReportName AS STRING, ;
	lcGenFldName AS STRING, ;
	lnGenFldHeight AS INTEGER, ;
	lnGenFldWidth AS INTEGER, ;
	lcTargetDir AS STRING, ;
	lcFileNameExpr AS STRING, ;
	lcGenTable AS STRING

*-- Choose the name of a table to extract one General
*-- field from.
lcGenTable = ADDBS( HOME() ) + 'Samples\Data\employee.dbf'

*-- Set the name of the General field that you want to extract from.
lcGenFldName = 'PHOTO'

*-- Set the height that you want for the General field.
lnGenFldHeight = 8000.00

*-- Set the width that you want for the General field.
lnGenFldWidth = 7000.00

*-- Set the folder into which the General field
*-- pictures will be extracted.
lcTargetDir = 'C:\TmpTst\'

*-- By default, pictures that are rendered to disk by the ReportListener class
*-- use the following file name format: _n.jpg. In this format, n
*-- is a number. If you set this next variable to an expression, you 
*-- can override that behavior. In this example, we name
*-- the .jpg files based on the FIRST_NAME and LAST_NAME
*-- fields from the EMPLOYEE table that we are reporting from.

*-- This expression will be wrapped in the EVAL function when it is called. 
*-- Therefore, make sure that you specify an expression, a variable, or something else. 
*-- Possible choices include the RECNO function and other table field names.
lcFileNameExpr = ;
	"ALLT(Employee.First_Name) + '_' + ALLTRIM(Employee.Last_Name)"



*------ START REPORT CREATION
*
CLOSE DATA ALL
IF !DIRECTORY( lcTargetDir, 1 )
	MESSAGEBOX( 'Target output folder does not exist. Process stopped.', 48, '' )
	RETURN .F.
ENDIF
CD ( lcTargetDir )

*-- Clean up the folder first to prevent any file name conflicts.
LOCAL lcOldSetSafe as String
lcOldSetSafe = SET("Safety")
SET SAFETY OFF
DELETE FILE *.*
SET SAFETY &lcOldSetSafe

*-- Create a temporary cursor to base the report
*-- on, make the report, and then close the cursor.
*-- This temporary cursor is used to set up the report for the first time.
*-- It is not needed after the report is made.
lcReportName = lcTargetDir + 'GenReport'
CREATE CURSOR TEMP ( Fld1 G )
CREATE REPORT ( lcReportName ) FROM TEMP
USE IN TEMP

*-- Open the report file (FRX) as a table.
USE ( FORCEEXT( lcReportName, 'FRX' ) ) IN 0 ALIAS TheReport EXCLUSIVE
SELECT TheReport

*-- This command removes objects that are not required 
*-- from the report surface and from the report table itself.
*-- ObjType = 5 or 8: Removes the label and field objects from the footer.
*-- ObjType = 23 & FontStyle = 1: Removes a font resource
*-- that is not needed. The resource belonged to the label that was deleted.
DELETE ALL FOR (ObjType = 23 AND FontStyle = 1) ;
	OR INLIST(ObjType, 5, 8) IN TheReport

*-- This command adjusts the layout of the header and footer bands.
*-- ObjType = 9 & ObjCode = 1 means Page Header band.
*-- ObjType = 9 & ObjCode = 7 means Page Footer band.
*-  Reduce them because they are not required for this code.
UPDATE TheReport SET Vpos = 0, Hpos = 0, HEIGHT = 0 ;
	WHERE ObjType = 9 AND (ObjCode = 1 OR ObjCode = 7)

*-- Increase the height of the Detail band
*-- (ObjType = 9 & ObjCode = 4) to fit the
*-- Picture/OLE Bound control that is inserted by using the next command.
UPDATE TheReport SET Vpos = 0, Hpos = 0, HEIGHT = lnGenFldHeight ;
	WHERE ObjType = 9 AND ObjCode = 4

*-- Add a Picture/OLE Bound control to the report by inserting a
*-- record with appropriate values. Using an object that is based on the EMPTY
*-- class here and the GATHER NAME class later to insert the record makes it easier to
*-- see which values line up to which fields when compared to a large
*-- SQL-INSERT command).
LOCAL loNewRecObj AS EMPTY
loNewRecObj = NEWOBJECT('EMPTY')
ADDPROPERTY( loNewRecObj, 'PLATFORM', 'WINDOWS' )
ADDPROPERTY( loNewRecObj, 'Uniqueid', SYS(2015) )
ADDPROPERTY( loNewRecObj, 'ObjType', 17 ) && "Picture/OLE Bound control"
ADDPROPERTY( loNewRecObj, 'NAME', lcGenFldName ) && Name of the General field in the cursor.
ADDPROPERTY( loNewRecObj, 'Vpos', 2083.333) && Put it in the Detail band.
ADDPROPERTY( loNewRecObj, 'HEIGHT', lnGenFldHeight )
ADDPROPERTY( loNewRecObj, 'WIDTH', lnGenFldWidth )
ADDPROPERTY( loNewRecObj, 'DOUBLE', .T. ) && The picture is centered in the Picture/OLE Bound control
ADDPROPERTY( loNewRecObj, 'Supalways', .T. )
*-- For the Picture/OLE Bound control, the contents of the OFFSET field specify whether
*-- File name (0), General field name (1), or Expression (2) is the source.
ADDPROPERTY( loNewRecObj, 'Offset', 1 )

*-- Add the Picture/OLE Bound control record to the report.
APPEND BLANK IN TheReport
GATHER NAME loNewRecObj MEMO

*-- Clean up and then close the report table.
PACK MEMO
USE
*
*------ END REPORT CREATION


*-- Rerun the report by using the Listener subclass
*-- that is defined in later code.
USE ( lcGenTable )
LOCAL loXMLD_Listener AS ;
	XMLDISPLAYLISTENER OF ADDBS( HOME( ) ) + 'Ffc\_reportlistener.vcx'

loXMLD_Listener = NEWOBJECT( 'MyListener' )
loXMLD_Listener.TargetFileName = ADDBS( lcTargetDir ) + 'tmp.htm'
loXMLD_Listener.ImgFileNameExpr = lcFileNameExpr
loXMLD_Listener.QUIETMODE = !ShowReportFlow_Errs

WAIT WINDOW 'Rendering pictures.' NOWAIT NOCLEAR
REPORT FORM ( lcReportName ) OBJECT loXMLD_Listener
ERASE (loXMLD_Listener.TargetFileName)
ERASE ( FORCEEXT( lcReportName , '*' ) )
RELEASE loXMLD_Listener
WAIT WINDOW 'Complete!' TIMEOUT 2
RETURN



*----------------------------------
*----------------------------------
DEFINE CLASS MyListener AS XMLDISPLAYLISTENER OF ADDBS(HOME()) + 'Ffc\_reportlistener.vcx'
	ImgFileNameExpr = ''
	ImgFile = ''

	PROCEDURE RENDER(nFRXRecNo, nLeft, nTop, nWidth, nHeight, ;
			nObjectContinuationType, cContentsToBeRendered, GDIPlusImage)
		NODEFAULT

		*-- ImageFileBaseName is an optional prefix to be added
		*-- to generated image file names when image files are saved
		*-- to disk during the rendering of general fields in a report run.
		THIS.ImageFileBaseName = EVAL( THIS.ImgFileNameExpr )

		*-- This is the full path and file name of the picture file that will
		*-- be rendered to disk when the DODEFAULT function is called. This information is retrieved here
		*-- so we can clean it up in the AfterBand event.
		THIS.ImgFile	 = FORCEPATH(THIS.ImageFileBaseName + "_" + ;
			TRANSFORM(THIS.ImageFieldInstance + 1) + ".jpg", ;
			FULLPATH(THIS.ExternalFileLocation,ADDBS(JUSTPATH(THIS.TargetFileName))))

		DODEFAULT(nFRXRecNo,nLeft,nTop,nWidth,nHeight, ;
			nObjectContinuationType, cContentsToBeRendered, GDIPlusImage)
	ENDPROC


	PROCEDURE AFTERBAND(nBandObjCode, nFRXRecNo)
		NODEFAULT

		IF nBandObjCode = 4 && In Detail Band
			*-- Look for the image file on the disk.
			*-- If the image file is there, the code inside the IF...ENDIF
			*-- construct strips off the _n that is at the end of the file name,
			*-- just before the extension. This is added in the Render event by the
			*-- XMLDISPLAYLISTENER class. You do not have to clean up the file name.  However,
			*-- it looks cleaner. For example, "Scott_Rockfeld_1.jpg" would change to
			*-- "Scott_Rockfeld.jpg".
			IF FILE( THIS.ImgFile )
				LOCAL lnLastUnderscorePos AS INTEGER
				lnLastUnderscorePos = ATC('_', THIS.ImgFile, OCCURS( '_', THIS.ImgFile ) )
				RENAME ( THIS.ImgFile ) TO ;
					FORCEEXT( SUBSTR( THIS.ImgFile, 1, lnLastUnderscorePos - 1 ), 'JPG' )
			ENDIF
		ENDIF

		DODEFAULT( nBandObjCode, nFRXRecNo )
	ENDPROC
ENDDEFINE
For more information about exporting embedded bitmap images, click the following article number to view the article in the Microsoft Knowledge Base:

161832 How to export an embedded .bmp image into a file

For additional information about FRX table structure, see the "Table Structures of Table Files (.dbc, .frx, .lbx, .mnx, .pjx, .scx, .vcx)" topic in Visual FoxPro Help.

To review the Visual FoxPro 9.0 FRX table structure directly, run the following command in the Command window:

REPORT FORM HOME() + "\Tools\Filespec\90frx.frx" PREVIEW

REFERENCES

For more information about the ReportListener XML display-style foundation class, visit the following Microsoft Developer Network (MSDN) Web site:

Modification Type:MajorLast Reviewed:3/28/2005
Keywords:kbCodeSnippet kbhowto KB894819 kbAudDeveloper