6 Advanced Topics
This chapter describes additional topics in the use of ActiveX.
This chapter includes the following sections:
6.1 Drawing Pages
The Drawing Page API provides a mechanism to render individual document pages onto a system device context. For example, this could be used to create thumbnail images, or could be used to tile pages to a printer device context, etc. There are two levels of complexity associated with this set of methods. The simplest use consists of invoking the DrawPage method or ExtDrawPage method (see Section A.3.17, "DrawPage" and Section A.3.19, "ExtDrawPage") with the page number, output rectangle and scaling factors. The resulting picture object will be placed in the PagePicture property (see Section A.2.61, "PagePicture").
If the application needs more control over the drawing process, the lower level methods can be used. To initialize the API, the InitDrawPage method (see Section A.3.34, "InitDrawPage") is invoked. It takes no parameters and must be called before any of the other methods are called. The initialization routines must also be called before either the DrawPage or GetDrawPageInfo methods are invoked (see Section A.3.17, "DrawPage," and Section A.3.25, "GetDrawPageInfo"). The GetDrawPageInfo method uses the page number, output resolution (units/inch), and two device context parameters to populate the read-only properties. The PageFormatWidth and PageFormatHeight properties (see Section A.2.59, "PageFormatWidth," and Section A.2.58, "PageFormatHeight") contain the width and height of the page in logical units (physical page size x output resolution). PageUnitsPerInch (see Section A.2.66, "PageUnitsPerInch") stores the output resolution passed into the GetDrawPageInfo method.
The DrawPageEx method (see Section A.3.18, "DrawPageEx") actually renders the desired page into the output device context. Upon successful completion the PageResultTop, PageResultLeft, PageResultRight, and PageResultBottom read-only properties (see Section A.2.65, "PageResultTop," Section A.2.63, "PageResultLeft," Section A.2.64, "PageResultRight," and Section A.2.62, "PageResultBottom") will be populated and the method will return TRUE. Otherwise, an Error event (see Section A.4.7, "Error") will be generated and the method will return FALSE. The PageResult*Ex properties are populated with the respective coordinates in DEVICE units. The PageResult*Ex properties are also populated when DrawPage (see Section A.3.17, "DrawPage") is invoked.
The DrawPageEx method (see Section A.3.18, "DrawPageEx") takes a number of parameters.
-
Page Number: This zero-based integer specifies which page, as defined by the original document, is to be rendered.
-
Flags: There are 3 flags which may be OR-ed together to control rendering:
-
1: Based on the colors used on the page, return a palette handle in the PagePaletteHandle property (see Section A.2.60, "PagePaletteHandle").
-
2: Assume OutputDC is not a metafile DC irrespective of what Windows reports.
-
4: Ignore the background when rendering.
-
-
Top, Left, Bottom, Right: Rectangle in device coordinates in which to render the page.
-
FormatWidth, FormatHeight, UnitsPerInch: Specifies the width and height in terms of the unitsperpage resolution factor of the area to which the text should be formatted (wrapped, justified, etc.)
-
OutputDC, FormatDC: The device context in which to render the output and the device context which allows queries relating to formatting. These may not be the same DC.
-
Palette: The palette to be used when rendering the page. If a palette is requested in the Flags parameter, it will be returned in the PagePaletteHandle property.
When the OutputDC is not a metafile or the Flags variable has bit 2 set (assume OutputDC is NOT a metafile DC irrespective of what Windows reports), the OutputDC will be set to the MM_ANISOTROPIC (abstract) mapping mode, the window and viewport extents will BOTH be set so that the page is drawn into the rectangle defined by the Top, Left, Bottom, Right parameters. If the OutputDC is a metafile, the device context will still be set to the MM_ANISOTROPIC mapping mode but only the window extents will be set. This will have the effect of creating a scalable metafile that can be played to any rectangle by setting the viewport and origin prior to metafile rendering.
Private Sub Picture1_Click() Rem ** When this picture control is clicked, the viewer will Rem create a thumbnail image of the first page of the Rem viewed document inside this control. It is then saved Rem out to a filename of choice ** Form1.MousePointer = vbHourglass oixctrl1.InitDrawPage oixctrl1.DrawPage 0, 0, 0, Picture1.Height, Picture1.Width, 10, 10, 120 Picture1.Cls Rem ** Calculate aspect ratio of original image so as to Rem "paint" it with a similar ratio. aratio =(oixctrl1.PageResultRight-oixctrl1.PageResultLeft) / (oixctrl1.PageResultBottom - oixctrl1.PageResultTop) If (aratio > 1) Then maxw = Picture1.Width - 1 maxh = Picture1.Width / aratio Else maxh = Picture1.Height maxw = Picture1.Height * aratio - 1 End If Form1.MousePointer = vbNormal Rem ** Collect output filename and create bitmap CommonDialog1.ShowSave If (Not CommonDialog1.CancelError) Then Picture1.PaintPicture oixctrl1.PagePicture, 0, 0, maxw, maxh, oixctrl1.PageResultLeft, oixctrl1.PageResultTop, oixctrl1.PageResultRight - oixctrl1.PageResultLeft, oixctrl1.PageResultBottom - oixctrl1.PageResultTop SavePicture Picture1.Image, CommonDialog1.filename End If Rem ** Clean up oixctrl1 oixctrl1.DeinitDrawPage End Sub
6.2 Memory IO
The Outside In Memory IO API allows the developer to get access to the same data that can be placed on the clipboard without having to use the clipboard operations. The copy method takes starting and ending OixPos objects and a flag to indicate the desired format for the data. The data can be requested as clipboard-formatted text or RTF and will be placed in the CopyBuffer property (see Section A.2.17, "CopyBuffer"). The size of the data is placed in the CopyBufferSize property (see Section A.2.18, "CopyBufferSize").
Private Sub ExportRTE_Menu_Click() Rem ** Export the selected text to an RTF file Oixctrll.Coopy 1, oixctrll.SelectionAnchor, oixctrll.SelectionEnd CommonDialog1.ShowSave If CommonDialog1.CancelError = False Then Kill CommonDialog1.FileName ' delete existing file first ' Write out data in CopyBuffer Open CommonDialog1.FileName for Binary Access Write As #1 Put #1, , oixctrll.CopyBuffer Close #1 End If End Sub
6.3 Redirected IO
In some cases, the programmer may need control over the file IO that is performed by the viewer. For example, suppose that the document wasn't disk-based but instead was stored in a database field. In this case, two options are available: 1) read the data out of the database field, store it in a temporary disk file and pass that temporary file name to the viewer; or 2) handle the viewer's request for file IO. The ActiveX control provides the programmer with this control through the use of events. There are eight event handlers that must be written to support Redirected IO. All event handlers will be passed at least three parameters: a file name, variant user data, and a file handle. Upon return, all event handlers indicate whether or not it was successful in completing the request.
When a file is opened, the Open event handler (see Section A.4.18, "Open") is responsible for returning the file handle. The file handle may be anything the programmer wants and will be passed back to the other IO events. Similarly, the Close event (see Section A.4.2, "Close") has the responsibility to close the document.
Data is passed to the viewer from the application during the Read event (see Section A.4.22, "Read"). The event handler is passed the number of bytes to be read as well as a buffer in which to place them. Reading is to take place at the current file position and it is the application's responsibility to keep track of this position. The user-data variant variable is a good place to store semi-static data such as this. When the read request is finished, the number of bytes read and a success indicator are returned to the viewer.
Quite often the viewer will need to read non-sequentially from the file. The current file position is manipulated through the Seek event handler (see Section A.4.24, "Seek"). The Seek event handler is passed an offset value and offset anchor flag. The anchor flag can be one of three values: current position, beginning of document, or end of document. The offset value indicates how many bytes to relocate the current file pointer relative to the anchor. The Seek event returns a success indicator to the viewer after file pointer repositioning.
There are two informational events that can be generated whenever the viewer needs more information regarding the file being processed. The Tell event handler (see Section A.4.27, "Tell") returns the current file position relative to the beginning of the document. The GetInfo event handler (see Section A.4.11, "GetInfo") provides specific information regarding the file. The following table shows some of the information that may be requested. Only the first three requests actually require action from the GetInfo event handler, however, all requests must be responded to using the following values to communicate success to the viewer.
GetInfoRequest | Return Type | Success Value |
---|---|---|
IOGETINFO_ISOLE2STORAGE | N/A | IOERR_FALSE |
IOGETINFO_FILENAME | String | IOERR_OK |
IOGETINFO_PATHNAME | String | IOERR_OK |
IOGETINFO_OSHANDLE | N/A | IOERR_BADINFOID |
IOGETINFO_HSPEC | N/A | IOERR_BADINFOID |
There are many documents that contain references to other files. When handling the viewer IO the application must be prepared to also handle these embedded references. When the viewer encounters an embedded file reference a GenSecond event (see Section A.4.9, "GenSecond") will be generated. The referenced filename will be passed to this event as a string. It then becomes the responsibility of the application to return the remaining parameters.
File | Description |
---|---|
FileSpec | The name of the file as a variant. If the application is going to handle the embedded file IO as well, this data can be anything. |
SpecType | The type of data placed in the FileSpec variable. The only supported value for this parameter is 3, indicating redirected IO. |
VarData | Variant data to be passed along with the FileSpec to the Open event |
Result | Success=1; Failed=0 |
Upon successful return of this event handler, an Open event will be generated for the embedded file.
Private Sub oixctrl1_Close(ByVal FileSpec As Variant, ByVal varFile As Variant, ByVal varData As Variant, pResult As Long) Rem ** IO Redirect Close event handler -- call 16-bit Rem file IO routines ** pResult = IOERR_OK lclose (varFile) End Sub Private Sub oixctrl1_GenSecond(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, ByVal FileName As String, pFileSpec As Variant, SpecType As Long, pvarData As Variant, pResult As Long) Rem ** IO Redirect GenSecond event handler -- build fully Rem qualified file from filename passed in. ** Dim NewFileName As String Dim FilePath As String pResult = IOERR_OK FilePath = FileSpec pos = InStr(FilePath, "\") While (pos <> 0) OldPos = pos pos = InStr(pos + 1, FilePath, "\") Wend NewFileName = Left(FilePath, OldPos) + FileName pFileSpec = NewFileName SpecType = 13 pvarData = "Anything can go here" End Sub Private Sub oixctrl1_GetInfo(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, ByVal InfoId As Long, pInfo As Variant, pResult As Long) Rem ** IO Redirect GetInfo event handler -- respond to viewer Rem requests for additional information. Currently, only Rem the cases shown are valid requests. ** Dim FilePath As String Dim FileName As String FilePath = FileSpec pResult = IOERR_BADINFOID Select Case InfoIf Case IOGETINFO_FILENAME: ' return just the filename portion of the filespec parameter' pos = InStr(FilePath, "\") While (pos <> 0) OldPos = pos pos = InStr(pos + 1, FilePath, "\") Wend FileName = Right(FilePath, Len(FilePath) - OldPos) pInfo = FileName pResult = IOERR_OK Case IOGETINFO_PATHNAME: ' return just the path to the ' filespec pInfo = FilePath pResult = IOERR_OK Case IOGETINFO_ISOLE2STORAGE: ' must respond to with ' IOERR_FALSE pResult = IOERR_FALSE End Select End Sub Private Sub oixctrl1_Open(ByVal FileSpec As Variant, ByVal varData As Variant, pvarFile As Variant, pResult As Long) Rem ** IO Redirect Open event handler -- call into the 16-bit Rem low level file IO routines. The handle returned from Rem the open is returned as pvarFile ** Dim hFile As Long pResult = IOERR_OK hFile = lopen(FileSpec, OF_READ) pvarFile = hFile If (hFile < 0) Then pResult = IOERR_NOFILE End If End Sub Private Sub oixctrl1_Read(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, pData As Variant, ByVal Size As Long, pCount As Long, pResult As Long) Rem ** IO Redirect Read event handler -- call into the 16-bit Rem routines to perform low level read on the file. ** Dim Buffer() As Byte Dim ReadResult As Long ReDim Buffer(Size) As Byte pResult = IOERR_OK pCount = lread(varFile, Buffer(0), Size) pData = Buffer If (pCount = -1) Then pResult = IOERR_UNKNOWN End If End Sub Private Sub oixctrl1_Seek(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, ByVal From As Integer, ByVal Offset As Long, pResult As Long) Rem ** IO Redirect Seek event handler -- call into 16-bit Rem routines to perform seek. ** Dim Tally As Long pResult = IOERR_OK If (llseek(varFile, Offset, From) = -1) Then pResult = IOERR_UNKNOWN End If End Sub Private Sub oixctrl1_Tell(ByVal FileSpec As Variant, ByVal varData As Variant, ByVal varFile As Variant, pOffset As Long, pResult As Long) Rem ** IO Redirect Tell event handler -- call into 16-bit Rem routines to get current file position. ** pResult = IOERR_OK pOffset = llseek(varFile, 0, FILE_CURRENT) End Sub