Implementing file-name mapping
This page applies to Harlequin v13.1r0 and later; and to Harlequin MultiRIP but not Harlequin Core.
The PostScript-language model of a file system is flat: there is no directory hierarchy. This is rarely true of operating systems. However, historically, a forward slash ( /
) character has been used in PostScript-language file names to delimit different components of the name, just as if there were directories and file names. The main distinction (apart from the implementation differences) is that in principle a
, a/
and a/b
can all exist as independent PostScript-language files. Most RIP implementations, including past and current versions of the Harlequin RIP, choose to ignore this subtle distinction and interpret /
as a directory separator, translating it to a backslash ( \
) on MS-DOS and Windows-based systems.
The PDF standard formalizes the representation of file names, including the use of forward slashes as directory separators. These names may still be mapped if necessary.
Each component of this string is then treated individually and is used as either a directory or file name. If it is a legal name under the rules of the operating system and does not clash with file names that have already been encountered, it is used directly. Otherwise, the name is looked up in a table stored in the file FILEMAP.DAT
in the SW
directory. If it is found, the corresponding operating system name in the table is used. If not, a new mapping is invented and added to the table in that file.
When generating file names FILERED.PS
in the SW
directory is used to provide standard reductions (contractions) of segments of each component. For example, FILERED.PS
includes the lines:
(Helvetica) (Helve)
(Oblique) (O)
which means that components including Helvetica
has that contracted to Helve
as part of the generation of a new name; similarly Oblique
is contracted to O
—so, for example, the font name HelveticaOblique
end ups as HelveO
.
Mapped names also have any extension replaced by .X00
, unless that clashes with an existing mapping; in that case .X01
, .X02
(and so on). is used (followed by .Y00
and so on).
All mapped names generated by HMR running under Windows are legal MS-DOS 8.3 file names and in upper case. On Windows RIPs, it is possible that some networked disks on which the RIP must access files may be MS-DOS FAT formatted, meaning that they are restricted to 8.3 filenames. The only effect on mapping is that some file names which would be valid on an NTFS or NT FAT volume are not valid on these disks. The Harlequin RIP automatically detects whether a disk is restricted to 8.3 names; it uses the appropriate rules for mapping file names as appropriate.
On Windows, where the operating system itself is case retentive (but not case sensitive), you must also map a component to a generated name if another component with the same name, but different capitalization has already been encountered; this fulfills the requirement that PostScript-language filenames are case sensitive. This mapping ensures that files which are only created and read from inside PostScript language are always handled correctly.
The listing of what case of PDL names should be used for the original operating system file name, and which are mapped to generated names is also held in FILEMAP.DAT
in the SW
directory. FILEMAP.DAT
comprises a series of entries, one for each file name encountered in jobs. Entries are in two forms:
(STRING) (string) C
is used for the first instance of a PDL file name that is also a valid OS file name on that platform.
The second string defines the case of a PDL file name that is taken as referring to a file on disk by that name:
(invalid::string) (INVALIDS.X00) M
is used for all PDL file names that are not invalid as OS file names, and for those names that match existing names except in case. The first string is the internal file name; the second is the name of a file on disk that is mapped to it.
File name mapping is generally transparent to the user. For example, when a font installation job invoked from the Fonts
menu is run, it makes font files in the SW/fonts
directory. The PDL file names must match the name of the font, which can be longer than the maximum allowed by the operating system, or may include illegal characters, such as a colon ( :
). All of that mapping is handled automatically, and you are not normally aware that it happened.
The most common case where special attention must be given to mapping is where a file already exists in the operating system’s filing structure, and you wish to access it from PostScript language. In this situation, you must disable the mapping process to ensure that you can access the file by the name you expect. In fact, only the case sensitivity part of the mapping is important here—the file on disk must already have a legal operating system name; otherwise, it could not be created.
Consider, for instance, a file named TestFile.ps
. You would expect to be able to read it with (TestFile.ps)(r) file
, and most of the time that would work. If, however, the RIP had encountered a file named TESTFILE.ps
, the RIP recorded that the file on disk called testfile.ps
(remembering that the operating system is case insensitive) equates to the PostScript file name TESTFILE.ps
. The code fragment above is interpreted as a request for a file with a mapped operating system name (probably TESTFILE.X00
); the file you created from some other application cannot be found because the RIP is looking for a file by the mapped name.
To allow the case mapping process to be turned off, you can use an empty (zero-length) component. This disables mapping for all further components (the empty component itself is discarded). A slash ( /
) at the beginning of a file name that does not include an explicit device name (or immediately after the device name if present) is interpreted as an initial empty component; elsewhere an empty component may be included as two slashes together.
Thus, the safe method of reading a file created by another application is: (/TestFile.ps)(r) file
. Other examples of file names where mapping is disabled are:
(%D%/mydirectory/testfile.ps) (%Hard Disk%OPIdirectory//XYZ.JPG)
In the last example, the OPIdirectory
component is mapped; the file name XYZ.JPG
is not mapped, because it is after the empty component ( //
).
When using the PostScript-language operator filenameforall
, you can request that file names returned are in either mapped or unmapped format by constructing the template string appropriately. If the template contains an empty component, the file names returned are in operating system format; if it does not, they are in the mapped form.
(%D%somedir/*) {==} 100 string filenameforall
returns names in mapped format if possible;
(%D%/somedir/*) {==} 100 string filenameforall
returns all names in unmapped format (because all returned names must start with %D%/somedir
, which contains an empty component).
filenameforall
falls back to unmapped names where necessary. In the first filenameforall
example above, if D:\somedir
contains a file that is the same as an existing name but contains one or more characters in a different case (say helvetica
, rather than the built-in Helvetica
), the file is returned with just the offending component unmapped, as (%D%somedir//helvetica.
The bulk of the file names returned are mapped.
The filename
operator in statusdict
(Device and file operator definitions) always returns the name used to create the file handle, which may have been unmapped.
PostScript-language filenames are always absolute (that is, relative to the root of the corresponding device; a file name always includes a full directory path after the device name, if present). If a file name does not include a device name and is being opened for writing (that is, the normal PostScript-language file search rules will not be followed), it is created on the %os%
device, rooted at the SW
directory. Any directories required by the file name are created automatically if a file is opened for writing.
The RIPs on Unix do not apply any mapping beyond the handling of /
because names are sufficiently representable.