Edit Level File Imports
Chapter 7-5
Edit-Level File Imports
Importing files into a rich-text field in a note is a two step process.
- The file must be converted from its native format to a series of Notes Composite Data records (CD records) and stored on disk as a temporary file. One of the Notes Import DLLs performs this conversion.
- You must read the series of CD records into a buffer and use NSFItemAppend to append the buffer to a rich-text field in a note.
NOTE: The largest buffer you can append using NSFItemAppend is slightly smaller than 64 kilobytes. Handle larger files by breaking up the series of CD records (on CD record boundaries) into smaller sections and appending each section separately. We recommend that you hold buffer sizes to roughly 40 kilobytes (40,960 bytes).
This chapter examines two sample programs, IMPORTER and LGIMPORT, that import files into a note. IMPORTER assumes that the file being imported is less than 40 kilobytes in length after being converted to a series of CD records. LGIMPORT divides the series of CD records into sections and appends them separately, so it can import files larger than 64 kilobytes.
Notes Import DLLs
Notes lets you import a number of file formats, including (among others):
- Ami Pro documents
- Character text files
- ANSI Metafiles
- Tagged Image File Format (TIFF) files
- Windows bitmap files
- PCX image files
A different Notes Dynamic Link Library (DLL) file handles each supported file format. You can determine which Notes DLL supports a particular file format by examining the notes.ini file. For details, see the readme.txt files accompanying the IMPORTER and LGIMPORT example programs.
The IMPORTER Sample Program
The sample program IMPORTER creates a note that contains a representation of the imported file in its rich-text field. The program takes three or four command line arguments:
- The filename of the database to which the note is to be added
- The fully qualified pathname to the DLL appropriate for the format of the graphic being imported
- The filename of the file to be imported
- The name of a second DLL that the import DLL will use to import a word-processing document. If the file being imported is not a word-processing document, this parameter is omitted.
Converting the File To Domino Format
IMPORTER first calls the subroutine IMPORTCD, which loads into memory the DLL specified on the command line, and then gets the address of the import function defined in the DLL.
IMPORTER: Loading the Appropriate DLL, Getting Address of Import Function |
STATUS (LNCALLBACKPTR ProcAddress)(VOID IXContext, WORD Flags,
HMODULE hModule, CHAR AltLibraryName, CHAR PathName);
EDITIMPORTDATA EditImportData; / Import DLL data structure /
HMODULE hmod; / module handle /
STATUS Error; / Return status from Notes calls /
CHAR FileName; / File name to be imported /
CHAR TempName[] = "DEFAULT.CD"; / Temp Filename for import. /
CHAR ModuleName; / pointer to DLL module name /
CHAR DLLName / 2nd DLL for import of /
/ word-processing documents. /
ModuleName = szModulePath;
FileName = szFileName;
DLLName = szDLL;
/ Use OSLoadLibrary to load the import DLL and return a pointer to /
/ the main entry point. /
if (Error = OSLoadLibrary (ModuleName, (DWORD)0, &hmod, &ProcAddress))
{
printf ("OSLoadLibrary failed.\n");
goto Done;
}
The call to OSLoadLibrary loads into memory the DLL specified on the command line and returns the address of the import function.
Next, the program sets up an EditImportData structure, containing a filename and some default font information. The import function will create a temporary file with this name. The file will contain a sequence of Notes Composite Data records that corresponds to the original image.
Invoking the Conversion Function in Sample IMPORTER |
STATUS (LNCALLBACKPTR ProcAddress)(VOID IXContext, WORD Flags,
HMODULE hModule, CHAR AltLibraryName, CHAR PathName);
EDITIMPORTDATA EditImportData; / Import DLL data structure /
STATUS Error; / Return status from Notes calls /
CHAR FileName; / File name to be imported /
CHAR DLLName / 2nd DLL for import of /
/ word-processing documents. /
/ Call the ProcAddress() function whose address we located within /
/ the DLL loaded above. This is the SAME argument set that would /
/ be used by an import/export DLL written to handle a non-standard /
/ file format. /
/ /
/ The ProcAddress() function can import multiple files. Since /
/ only one file is being imported in this example, the flags will /
/ indicate that the file is the first (and also last) file to be /
/ imported. /
if (Error = (ProcAddress) (&EditImportData,
IXFLAG_FIRST | IXFLAG_LAST, / Both 1st and last import /
0, / Use default hmodule /
DLLName, / 2nd DLL, if needed. /
FileName)) / File to import. /
{
printf ("Call to DLL Entry point failed.\n");
goto Done;
}
The call to (*ProcAddress)() invokes the import function. The parameter "filename" contains the name of the file being imported, while the EditImportData structure contains the name of the temporary file to create.
When you import more than one file, the import DLLs may perform some special processing for the first or last file in the series. The code above sets the flags IX_FIRST and IX_LAST to indicate that only one file is to be imported; it is both the first and the last file. If the file being imported is a word-processing document, the DLLName parameter specifies the name of an additional DLL used to import that particular file format. For details, see the readme.txt file for either IMPORTER or LGIMPORT.
If the call is successful, the name of the temporary file must be returned to the calling routine.
Placing the Imported Data in a Rich Text Field
IMPORTER then calls the subroutine LOADCD, which opens the temporary file and calculates how large a buffer is needed to hold the file contents. If a buffer larger than MAX_ALLOC is needed, the program exits with an error message. Otherwise, LOADCD allocates a buffer of the proper size, reads the entire file into the buffer, and returns the buffers handle and the buffer length to the calling routine.
IMPORTER then calls the subroutine AddRichText to create a rich text field containing the information imported from the file. AddRichText allocates a buffer in which to construct the rich text item and then adds to the buffer a CDPARAGRAPH, a CDPABDEFINITION, and a CDPABREFERENCE. For information about these structures, see "Introduction to Rich Text."
Appending the Imported Data to the Note in Sample IMPORTER |
/ Lock down the Import buffer and get a pointer to it. /
pImpBuffer = (CHAR ) OSLockObject(hImpBuffer) + sizeof(WORD);
dwItemBufLen = dwBufLen - sizeof(WORD);
if ((dwCDBufLen + dwItemBufLen) > dwCDMaxBuf)
{
printf("\nCD Buffer not big enough for import file. ");
printf("Terminating...\n");
OSUnlockObject(hImpBuffer); / Unlock Import buffer. /
OSUnlockObject(hCDBuffer); / Unlock CD buffer. /
OSMemFree(hCDBuffer); / Free up the CD buffer. /
goto Done;
}
/ Move the imported data into CD buffer /
memmove(&pCDBuffer[dwCDBufLen], pImpBuffer, (WORD) dwItemBufLen);
dwCDBufLen += dwItemBufLen;
if (dwCDBufLen % 2) / if len is odd, /
dwCDBufLen++; / make it even /
/ Append the "Body" item to the note /
if (rError = NSFItemAppend(hNote,0,
"Body", strlen("Body"),
TYPE_COMPOSITE,
pCDBuffer, dwCDBufLen))
{
printf("\nError adding 'Body' item. Terminating...\n");
OSUnlockObject(hImpBuffer); / Unlock Import buffer. /
OSUnlockObject(hCDBuffer); / Unlock CD buffer. /
OSMemFree(hCDBuffer); / Free up the CD buffer. */
goto Done;
}
The code fragment above copies the buffer containing the imported data into the rich text buffer. It then uses the function NSFItemAppend to append the rich text buffer to the field in the note called "Body," as a record of type TYPE_COMPOSITE.
The code then unlocks and frees all buffers and returns to the calling routine. IMPORTER then saves the note to disk, cleans up, and ends.
The LGIMPORT Sample Program
LGIMPORT is similar to IMPORTER, except it is not restricted to importing files smaller than 64 kilobytes. The program takes three or four command line arguments:
- The filename of the database to which the note is to be added
- The fully qualified pathname to the DLL appropriate for the format of the graphic being imported
- The filename of the graphic file to be imported
- The name of a second DLL that the import DLL will use to import a word-processing document. If the file being imported is not a word-processing document, this parameter is omitted.
Converting the File to Domino or Notes Format
LGIMPORT first calls the subroutine ImportCD, which loads into memory the DLL specified on the command line, and then gets the address of the import function defined in the DLL. ImportCD is identical to the subroutine of the same name in the sample program IMPORTER.
Appending the Imported Data to the Note
After LGIMPORT creates a note in the database specified on the command line and writes a couple of simple data fields in the note, it calls the subroutine AppendImportItem to append the image (now in Notes CD record format).
AppendImportItem opens the temporary file containing the imported data and allocates a 64,000 byte buffer in which to construct the rich-text item. The function places a CDPARAGRAPH, a CDPABDEFINITION, and a CDPABREFERENCE into the buffer. The contents of the temporary file are then read into the buffer.
If the entire contents of the temporary file fit into the buffer, LGIMPORT appends the buffer to the note, unlocks and frees buffers, and returns to the main routine. If the file is too big to fit into the buffer, LGIMPORT parses the buffer one CD record at a time, adding up the lengths of the CD records parsed. When the length approaches CD_HIGH_WATER_MARK, LGIMPORT appends the buffer to the note. It then resets the file pointer to the first CD record not parsed, reads from the temporary file into the buffer again, and repeats the process of reading, parsing, and appending to the note until it has processed the entire file. After unlocking and freeing buffers, LGIMPORT returns to the main program, writes the note to disk, cleans up, and ends.
Appending the Imported Data to the Note in Sample LGIMPORT |
/ Keep on writing items until entire CD file has been appended /
while (bFlag == FALSE)
{
/ Seek file to end of previous CD record /
if (lseek (CDFileFD, longpos, SEEK_SET) != longpos)
{
/ Leave if error returned... /
free (pCDBuffer);
close (CDFileFD);
unlink (pszCDFile);
return (ERR_APPEND_RICHTEXT_ERROR);
}
/ Read the contents of the file into memory /
wReadLength = read(CDFileFD,
&pCDBuffer[wItemLength],
(WORD)(wCDBufferLength - wItemLength));
/ check for error /
if (wReadLength == 0xffff)
{
/ Leave if error returned... /
free (pCDBuffer);
close (CDFileFD);
unlink (pszCDFile);
return (ERR_APPEND_RICHTEXT_ERROR);
}
/ See whether the contents will fit in current item.... /
if (wReadLength < CD_HIGH_WATER_MARK)
{
/ fit what is left in a single buffer and leave loop /
bFlag = TRUE;
wItemLength += wReadLength;
}
else
{
/
* Parse the buffer one CD record at a time, adding up the lengths
* of the CD records. When the length approaches CD_HIGH_WATER_MARK,
* append the buffer to the note. Set the file pointer to the first
* record not parsed, read from the temp file into the buffer again,
* and repeat until end of temp file.
*
* All CD records begin with a signature word that indicates its
* type and record length. The low-order byte is the type and
* the high-order byte is the length. If the indicated length is 0,
* the next DWORD (32 bits) contains the record length. If the
* indicated length is 0xff, the next WORD (16 bits) contains the
* record length. Otherwise, the high order BYTE itself contains
* the record length.
/
wCDRecordLength = 0 ;
lLength = 0 ;
lCombinedLength = wItemLength + wCDRecordLength ;
while (lCombinedLength < CD_HIGH_WATER_MARK)
{
pbSig = &pCDBuffer[wItemLength];
wSig = (WORD)pbSig;
pbSig++;
/ find length of CD record. /
if (pbSig == 0) / record length is a DWORD /
{
pbSig++;
wCDRecordLength = (WORD)(DWORD)pbSig;
}
else if (pbSig == 0xFF) / record length is a WORD /
{
pbSig++;
wCDRecordLength = (WORD)pbSig;
}
else / record length is the BYTE /
wCDRecordLength = (WORD)pbSig;
if (wCDRecordLength % 2) / if len is odd, make it even /
wCDRecordLength++;
/ update CD buffer pointers /
lLength = lLength + (long)wCDRecordLength;
ltmpItemLength = (long)wItemLength + (long)wCDRecordLength;
if (ltmpItemLength < CD_BUFFER_LENGTH)
wItemLength += wCDRecordLength;
lCombinedLength = (long)ltmpItemLength + (long)wCDRecordLength;
}
}
/ Append the imported item to the note /
if (wItemLength > 0)
{
if (sError = NSFItemAppend(hNote,
(WORD) 0,
pszItemName,
(WORD) strlen(pszItemName),
TYPE_COMPOSITE,
pCDBuffer,
(DWORD)wItemLength))
{
/ Leave if error returned... /
free (pCDBuffer);
close (CDFileFD);
unlink (pszCDFile);
return (ERR(sError));
}
}
/ update import file position and reset next item length */
longpos += lLength ;
wItemLength = 0;
}