PDL transform language
PDL Transforms are an advanced PaperCut NG feature. You need an understanding of printer description languages, and a basic knowledge of scripting.
A transform is a process that defines how to convert an input into a different output. In PaperCut NG, transforms are defined as simple scripts. There are a number of reasons for performing transforms on a print job. These include:
Extending compatibility
The output produced by one driver (such as PostScript) might not work on other printer brands without modification. For example, "TRAY = 1" on one printer might map to "TRAY = UPPER" on another. Use transforms to automatically make this adjustment to a print job. This is particularly important in a Find-Me printingFind-Me printing allows users to print to a single global print queue without selecting a printer, and then release the print job at any printer (via a Release Station). environment where a job rendered by one driver cannot print correctly on another printer type without modifying the print job.
Modify behaviour/output
Much is possible with the power of transforms. Maybe it’s always enabling the "eco print" mode when an email is printed, or automatically removing the "stapling" option if accidently selected on a printer the does not support stapling. Another use-case is "virtual stationery". Legacy reports could, for example, have a logo automatically added to each page.
Of note, PaperCut performs page analysis before transforms are applied. As such, a color logo added to a grayscale page, are recorded in PaperCut as grayscale.
Definitions
Before covering the details of the transform language, it’s important to understand the following terms:
Page Description Language (PDL)
A language used to describe the appearance of a printed page e.g. PostScript, Printer Control Language (PCL).
Printer Job Language (PJL) Header
Attributes in a PJL headerAttributes in a Printer Job Language (PJL) header define print job characteristics, such as, duplex, color, paper sizes, or media trays. Many printers rely on a PJL header, though many attributes are printer/manufacturer specific. define print job characteristics such as duplex, color, paper sizes, or media trays. Many printers rely on a PJL header, though many attributes are printer/manufacturer specific.
Transform script
A transform script defines a transform process to apply to a print job’s PDL. Transform scripts are written in a very simple scripting Domain Specific Language (DSL).
Source & target
The transform process refers to source and target print queues. A desired transform is applied when a job is transferred from a specific source queue (a global queue) to a specific target queue.
Writing a transform
Transforms are text files placed in a location (directory structure) that determines when the script applies. The file’s name and location must conform to the following path:
[install-path]/providers/print/[platform]/transforms/custom/[language]/[source]/[target].transform
- language - the PDL language of the print job (e.g. Postscript).
- source - the name of the queue that first received the print job (e.g. a global queue).
- target - the name of the queue the job is being transferred to print to.
For example, to apply a transform when a job is transferred from the queue named "Global Queue" to "Printer A", the transform file should be located at:
[install-path]/providers/print/[platform]/transforms/custom/postscript/global queue/printer a.transform
Matching rules for running a transform script
The following steps select the transform script to run for a redirection from printer type source to printer type destination, which both use printer language language.
-
Look first in the the custom directory then in the system directory.
-
Find the subdirectory whose name matches language e.g. if language is PostScript then look in custom/postscript.
-
Find the subdirectory whose name matches source. e.g. if source is PaperCut Global PostScript then look in custom/postscript/pc-win. (NOTE: On PaperCut NG systems, pc-win is an alias that matches printer type PaperCut Global PostScript).
-
Find the file whose name matches destination. e.g. if destination is HP Color LaserJet then look for custom/postscript/pc-win/hp.color.laserjet.transform. This does not exist so backtrack.
-
Backtracking takes you to custom/postscript/pc-win/default.transform, which does not exist, then custom/postscript/default.transform, which does not exist, then system/postscript/pc-win/hp.color.laserjet.type.transform, which does exist. (NOTE: printer type is matched against *.type.transform and printer name is matched agaist *.name.transform before printer type and name are matched against *.transform*)
-
Thus system/postscript/pc-win/hp.color.laserjet.transform is selected.
Examples
There are two example sources that script authors might find useful as starting points or reference:
[install-path]/providers/print/[platform]/transforms/examples
[install-path]/providers/print/[platform]/transforms/system
Transforms in the system directory are used by the PaperCut Global Print DriverThe PaperCut Global Print Driver is a print driver that works across multiple brands of MFPs. Use the Global Print Driver if your organization has a mix of printer brands. The Global Print Driver is a brand independent, maximum compatibility, signed print driver. It is designed to work on the widest possible range of printers out of the box and supports all main print features (duplex, color, paper size and tray selection).. They are system provided. Do not place your own scripts in the system directory. Place your own scripts in the custom directory. Any changes made in the system directory are overwritten in an upgrade.
Transform language functions & specification
Command | Description |
---|---|
var = expression | Evaluate the expression and assign the variable var with the result. |
IF condition THEN statement | Evaluate the condition expression, and if true execute the statement. |
FIND pattern [options] | Find the pattern in the spool fileA print spool file is generated when a user sends a document to a printer. The print spool stores the print job information, and sits in a print queue until it is retrieved and printed by a printer., and assign $0, $1, ... with the regular expression capture group. |
FIND pattern REPLACE text [options] | FIND as above, then replace the string matched by the pattern with text. |
STRIP_HEADER | Strip any PJL, XPIF, PostScript or other header in the spool file. |
DELETE_UNTIL text | Delete from the start of the spool file up to but not including the first occurrence of text. |
INSERT ofs len text | Insert the string text at the spool file offset ofs, after deleting len bytes. |
DELETE ofs, len | Delete len bytes from the spool file, starting at the offset ofs. |
REPEAT_ALL num_copies | Make num_copies of the input spool file in the output spool file after applying all other modifications. |
Assignment
var = expression
e.g. x = 1
Evaluate the expression and assign the variable var with the result. If the expression contains any $[N] and/or %variable% tokens, they are replaced with their current values. To prevent variable substitution, include __no_replace__ in the variable name e.g. __no_replace__template.
IF THEN
IF condition THEN statement
e.g.
IF duplex THEN pjl_duplex = "ON"
Evaluate the condition expression, and if true execute the statement.
Rules for evaluating expressions
-
A number x is true if x != 0.
-
A string pattern is true if it can be found in the spool file using the rules in the FIND pattern [options] below. Strings can be regular expressions or plain strings, and have two optional attributes FIRST | LAST and a search distance. FIRST | LAST does not affect logical evaluation. Search distance does. e.g.
/<<.*?\/ProcessColorModel\s*\/(DeviceCMYK|DeviceGray).*?>>\s*setpagedevice/ 10000
is true if this regular expression is matched in the first 10,000 bytes of the spool file.
-
Compare numbers with ==, !=, <, >, <= and >=.
-
Compare strings with == and !=. These comparisons are case insensitive and do not perform the searches in rule 2 above.
-
You can combine logical expressions, such as the results of 1-4 above, with NOT, AND, OR, XOR, and (...) parentheses. e.g. x == 1 AND NOT (y > 2 OR z <= 3)
FIND
FIND pattern [options]
where the pattern is a regular expression (regex) or string.
FIND /regex/ [REPLACE text] [options]
FIND "string" [REPLACE text] [options]
FIND searches for the pattern (a string or a regex) in the spool file and assigns $0, $1, ... from the resulting regular expression capture groups. If the pattern is a string, then only $0 is assigned.
e.g
FIND /<<.*?\/ProcessColorModel\s*\/(DeviceCMYK|DeviceGray).*?>>\s*setpagedevice/
could set $0 to "<< /ProcessColorModel /DeviceCMYK >> setpagedevice" and $1 to "DeviceCMYK" on a match.
Options
-
FIRST: Return the first match in the spool file. This is the default
-
LAST: Return the last match in the spool file.
-
n: Any positive integer. Search n bytes of the spool file. The default is to search one megabyte. A value of zero (0) searches through the entire spool file.
FIND REPLACE
FIND pattern REPLACE text [options]
where the pattern is a regular expression (regex) or string.
e.g
FIND "Letter" REPLACE "A4"
FIND /(\w+)\s=\sGrayscale/ REPLACE "$1 = BlackAndWhite"
FIND /MODE = \w+/ REPLACE "MODE = %my_var%"
FIND /MODE = \w+/ REPLACE "MODE = %my_var%" 2000
The FIND works as described above. Then REPLACE substitutes all the $[N] and %variable% values in the text, before replacing the string matched in the spool file with the substituted text.
ADD_HEADER
ADD_HEADER text
e.g.
ADD_HEADER my_header
Insert the string text as a header in the spool file.
STRIP_HEADER
STRIP_HEADER
Strip any PJL, XPIF, PostScript or other header (that our developers know about) from the spool file.
DELETE_UNTIL
DELETE_UNTIL text
e.g
DELETE_UNTIL "%!PS-Adobe-3.0"
Delete from the start of the spool file up to but not including the first occurrence of text.
REPEAT_ALL
REPEAT_ALL num_copies
e.g
REPEAT_ALL 3
Make num_copies of the input spool file in the output spool file after applying all other modifications.
Use this option for printers that don't support spool files copies commands, typically printers without hard disks. You usually detect the copies command in the source spool file then use REPEAT_ALL to repeat the source spool files the same number of copies.
Changes to the number of copies of a printed made here are not reflected in the Job Log.
INSERT
INSERT ofs len text
e.g
INSERT regex_ofs regex_len my_replacement
Insert the string text at the spool file offset ofs, after deleting len bytes.
Setting ofs to -1 appends text to the end of the spool file. In general, setting ofs to -n inserts text n - 1 bytes before the end of the spool file.
DELETE
DELETE ofs len
e.g
DELETE regex_ofs regex_len
Delete len bytes from the spool file, starting at offset ofs.
It is equivalent to:
INSERT ofs len ""
Pre-populated variables
Variable | Description | Example | Type |
---|---|---|---|
pc_pages | Number of pages in the spool file | 11 | number |
pc_copies | Number of copies in copies command in spool file, if one is detected. 1 otherwise. | 1 | number |
pc_page_width | Page width in mm | 210 | number |
pc_page_height | Page height in mm | 297 | number |
pc_paper_name | Paper size name | "A4" | string |
pc_grayscale | Is the document grayscale? | True | boolean |
pc_duplex | Is the document duplex? | False | boolean |
pc_tumble | Is the back side of a duplex job rotatated 180 degrees? | False | boolean |
pc_docname | Name of the document | "Homework.doc" | string |
pc_user | Name of the user | "Josh" | string |
pc_server | Print server name | "Server1" | string |
pc_printer | Printer name | "Hall printer" | string |
pc_driver | Name of the printer driverA printer driver or a print processor is a piece of software that converts the data to be printed to the form specific to a printer. The purpose of printer drivers is to allow applications to do printing without being aware of the technical details of each printer model. | "HP Color LaserJet CM4540 MFP PCL 6 (0.3.1545.9108)" | string |
pc_iso_date | Current date in ISO format | "2014-08-15" | string |
pc_uid | A randomly generated ID | "580459f0-2d19-11e4-b70a-f8b156c33bc3" | string |
pc_docname__clean__ | 'Cleaned' version of pc_docname. All character that will prevent display in PostScript, PJL or XML removed. | "Homework.doc" | string |
pc_user__clean__ | 'Cleaned' version of pc_user. | "Josh" | string |
Test mode
Test mode is designed to accelerate the development, testing and debugging of transform scripts. Test mode is enabled by adding the following line to a transform script:
ENABLE_TEST_MODE
or by settingEnableTransformTestMode=on
in the print-provider.conf file. The test mode option should only be enabled while developing scripts.Test mode causes the system to output additional log files to assist in developing and validating transforms. These files are located at:
[install-path]/providers/print/[platform]/transforms/logs/
The test mode files created are:
[job-id].log | This file contains logging information to help you understand the transform script’s state and variables. |
[job-id].before | The print job spool file BEFORE transformation. |
[job-id].after | The print job spool file AFTER transformation. |
Consider using a "visual diff" program to help compare before and after files.
Always disable test mode after developing your transform script. Test mode adds a considerable performance overhead and should not be used in a production environment.
Tips & tricks
-
Develop your scripts in TEST_MODE to help compare script behavior.
-
Use existing examples as starting points.
-
Print the same documents from source and destination printer drivers, and diff the resulting spool files. In most cases the things you need to modify are in the diff. e.g. One file has a header and the other does not, or one file sets color/grayscale with one string and the other file uses a different string.
-
Look at a PostScript Printer Description (PPD) file of the destination printer, and copy PostScript snippets into a transform.