Archive for February, 2010

Checking the validity of your JMD

One of the things I need to do on a regular basis is check the JMD of an installation, when things don’t seem to be running as they should. More often than not, someone edited the JMD by hand, missed a quote mark – causing havoc with the table alignments.

At other times I just want to be able to read the JMD so that I know what steps are being taken, without having to load/reload various JMDs. And trust me, reading a raw JMD is not fun…

So I came up with the following python script. I’ll be putting it on my personal website at some point, so you can use it there too. In the mean time, have a go at this, if you have a Python CGI server… TableJMD 0.2a

The following highlighting is a backup taken from the corresponging post – in case it gets deleted from one day…

#! /Program Files/Python25/python

import re,sys,cgi

helpnote = """Table JMD 1.1
(C) 2009 Tai Kedzierski

This tool reads a JMD and writes HTML to STDOUT representing the JMD in a table, as it would be parsed by Central's JfServer.
It also highlights substitution variables not surrounded by double quote marks.

This can either be used from the command line, or as a CGI script on a web site.

From the command line, type:

python PATH

where PATH is the path to the JMD file

class JMDInvalidLine(Exception):

	def __init__(self,message):
		Exception.__init__(self,message)# = "The line was invalid"

# don't fail silently!
sys.stderr = sys.stdout

def readjmdfile(filename):

	readjmd( open(filename,'rU') )

def readjmd(fileh):

	jmdline = '.'
	job_t = dict()
	task_t = dict()

	print_t = dict()
	memory_t = dict()
	while jmdline:

		line = fileh.readline()
		if line == '': # EOF

		if re.match(r'(^#)|(^\s*$)',line): # comment or empty line

		jmdline = jmdsplit(line)
		if jmdline == None:

			print "<p>Error parsing line: %s</p>" % (line,)

		if jmdline[0] == '!f':
			if job_t.has_key(jmdline[1] ):

				job_t[jmdline[1] ].append(jmdline)

				job_t[jmdline[1] ] = [jmdline]
		elif jmdline[0] == '!x':

			if task_t.has_key(jmdline[1] ):
				task_t[jmdline[1] ].append(jmdline)

				task_t[jmdline[1] ] = [jmdline]

		elif jmdline[0] == '!p':
			if print_t.has_key(jmdline[1] ):

				print_t[jmdline[1] ].append(jmdline)

				print_t[jmdline[1] ] = [jmdline]
		elif jmdline[0] == '!m':

			if memory_t.has_key(jmdline[1] ):
				memory_t[jmdline[1] ].append(jmdline)

				memory_t[jmdline[1] ] = [jmdline]

	print "<h2>Job table ["+str(len(job_t))+" entries]</h2>"

	print "<table><tbody>"
	print r'<th>Job name</th> <th>Printer name</th> <th>Form file</th> <th>Preamble file</th> <th>Macro number</th> <th>Load flag</th> <th>Task id</th> <th>Input file</th> <th>Output file</th> <th>On error</th> <th>Comments</th>'

	print "</tbody></table>"
	print "<h2>Task table ["+str(len(task_t))+" entries]</h2>"

	print '<p><font color="blue">Blue</font>: not surrounded by doubled double-quotes<br /><font color="red">Red</font>: substitution name does not end with "."</p>'

	print "<table><tbody>"
	print r'<th>Task id</th> <th>[This column reserved]</th> <th>Program name</th> <th>Program options</th> <th>Comments</th>'

	print "</tbody></table>"
	print "<h2>Printer table ["+str(len(print_t))+" entries]</h2>"

	print "<table><tbody>"
	print r'<th>Printer name</th> <th>Printer id</th> <th>Physical device</th> <th>Print agent options</th> <th>Priority</th> <th>Comments</th>'

	print "</tbody></table>"
	print "<h2>Managed Memory table ["+str(len(memory_t))+" entries]</h2>"

	print "<table><tbody>"
	print r'<th>Printer id</th> <th>Managed Memory [bytes]</th> <th>Comments</th>'

	print "</tbody></table>"

def dumptablelines(table_t):

	keyset = table_t.keys()
	for entryset in keyset:

		for entry in range(len(table_t[entryset])):
			print "<tr>"

			for col in range(1,len(table_t[entryset][entry]) ):

				print "<td>",table_t[entryset][entry][col],"</td>",
			print "</tr>"

def jmdsplit(line):
	'''INPUT: a line of text from the JMD
OUTPUT: the line, tokenized into a list
	if line[0] != '!':# any meaningful line must start with '!'

		return None
	oline = line
	if not line[1] in [

		'f', # job
		'x', # task
		'p', # printer

		'm']: # shared memory
		raise JMDInvalidLine(line)
	jmdline = []

		while line:
			token, line = readfirsttoken(line)

			token = atprocess(token) # identify substitution variables and possible errors

	except JMDInvalidLine,msg:
		print msg,':',oline

	return jmdline

def atprocess(token):
	pat = re.compile(r'((?<!"")[a-zA-Z-]*@[a-zA-Z]+\.?|@[a-zA-Z]+\.(?!""))')

	# NO r'((?<!"")[a-zA-Z-]*@[a-zA-Z]+\.?|@[a-zA-Z]+\.?(?!""))'
	# OK r'((?<!"")[a-zA-Z-]*@[a-zA-Z]+\.?|@[a-zA-Z]+\.(?!""))'
	pat2 = re.compile(r'(@[a-zA-Z]+)(?=[^\.a-zA-Z#])')

	if not type(token) == str:
		print type(token)

	token = pat.sub(r'<font color="blue">\1</font>',token)

	token = pat2.sub(r'<font color="red">\1</font>',token)

	return token

def readfirsttoken(line):
	line = re.match(r"\s*(.*)",line).group(1) # remove leading whitespace

	if line == '':
		return ('','')
	pos = 0

	text = ''
	inquote = False
	inarg = True

	while inarg and pos < len(line):
		if line[pos] == ' ' and not inquote:

			inarg = False
		elif line[pos] == '"' and not inquote:

			inquote = True
			text += line[pos]

		elif line[pos] == '"' and inquote:
			if len(line) == pos+1:

				inarg = False
				inquote = False
			elif line[pos+1] == '"':

				text += '"'
				pos += 1

				inquote = False
				inarg = False
			text += line[pos]

			text += line[pos]
		pos += 1

	if inquote:
		raise JMDInvalidLine("Unclosed quote")
	return (text,line[len(text):])

# RUNTIME ================================================

def printJMD(thefile):
	print """<html><head><style type="text/css">

body {font-size:9pt}
td, th {
	border-left:2px solid #000000;
	padding: 3px;
	margin: 2px;


th {
	background-color: #ccffcc;
<title>Table JMD - Done</title>

<h1>Table JMD</h1>"""
	print "<!-- ",helpnote," -->"

	print "</body></html>"

def printform():
	print """<html>

<head><title>Table JMD</title></head>
<h1>Table JMD</h1>

<form action="" method="POST" name="logprint" enctype="multipart/form-data">

<p>Check your JMD Tables.</p>
File: <input type="file" name="jmdfile" /><br />
<input type="submit" />



#print "Content-Type: text/plain"

fs = cgi.FieldStorage()

if fs.has_key('jmdfile'):
	print "Content-Type: text/html"

	#print '<p><b>Displaying JMD table for '+fs['jmdfile'].filename+'</b></p>'

elif len(sys.argv) > 2 and sys.argv[1] == '-f':

elif len(sys.argv) > 1:

	if re.match(r'-h|/h|--help|/\?|-\?',sys.argv[1]):

		print helpnote
	print "Content-Type: text/html"

How to create an Acrobat 9.x.n Admin Install Point (AIP) – integrate MSP patches to a MSI installer

NOTA: this article is not an Adobe publication, and carries no guarantees of accuracy. This article may be subject to change at any time.

The technique described here can be applied to patch all the way to 9.4.x and beyond. It can also be applied to other MSI+MSP’s in general :-)

Normally to get your Acrobat to version 9.2, you must first install v9.0, then install 9.1.0, then proceed to 9.1.2, before applying 9.2 – applying each manually, instalelr after installer. If you are administering multiple computers, you may wish to create a standalone installer, that includes all the patches up to 9.2 for example.

Here’s how to do just that, refined from a procedure sent to me by a colleague, Steve. All together now: “Thank you Steve!!” :-)

To determine exactly which patch files you need (and which you should not install as part of the AIP chain) please refer to

The patch files can be downloaded from

  1. Copy the media Setup files to a location (i.e. C:\Acrobat9\) on your hard drive.
  2. Create a folder to a location on your hard drive (i.e. C:\A920aip\). This will be the AIP folder.
  3. Open the command prompt.
  4. Navigate to the media Setup files that you copied to your hard drive.
    $> cd C:\Acrobat9\
  5. Run the setup.exe to confirm the installer files are volume, by checking if there’s an option to install as trial.
    • If install as trial has been greyed out, then the files are volume and you can go ahead with the next steps.
    • If the trial option is available, first check that the full qualified path of the location to the installer files does not contain any Unicode characters (Cyrillic, Greek, etc).
    • If it doesn’t then it is necessary to obtain the correct installer files before continuing with the next steps
  6. Once the above preparation is done, you can either perform the full-interactive steps as follows, or run the silent method as described further down.

  7. Run administrative installation from the command prompt.
    $> msiexec.exe /a AcroPro.msi
  8. When prompted point it to the AIP folder (i.e. C:\A920aip\).
  9. Place the following Acrobat MSP update files into the AIP folder:
    { AcroProStdUpd910_T1_T2_incr.msp ,
    AcrobatUpd912_all_incr.msp ,
    AcrobatUpd920_all_incr.msp }
  10. At the command prompt, navigate to the newly created folder that has the AIP files.
    $> cd C:\A920aip\
  11. At the command prompt run the Acrobat 9.1 Update, specifying the fully qualified path to the MSP file
    $> msiexec.exe /a AcroPro.msi /p C:\A920aip\AcroProStdUpd910_T1_T2_incr.msp
  12. When prompted by the Acrobat installer, point the patch installation to the folder that you are running the installation from (i.e. C:\A920aip\ , the AIP folder). Let the installer run, and terminate.
  13. At the command prompt run the Acrobat 9.1.2 Update, specifying the fully qualified path to the MSP file
    $> msiexec.exe /a AcroPro.msi /p C:\A920aip\AcrobatUpd912_all_incr.msp
  14. When prompted by the Acrobat installer, point the patch installation to the AIP folder. Let the installer run, and terminate.
  15. At the command prompt run the Acrobat 9.2 Update, specifying the fully qualified path to the MSP file
    $> msiexec.exe /a AcroPro.msi /p C:\A920aip\AcrobatUpd920_all_incr.msp
  16. When prompted by the Acrobat installer, point the patch installation to the AIP folder. Let the installer run, and terminate.
  17. You should now have a complete Acrobat 9.2 MSI and administrative install point set of files

The following script is the silent equivalent of the steps above. Write it to a BAT file and run it.

cd C:\Acrobat9
mkdir C:\A920aip
msiexec.exe /a AcroPro.msi TARGETDIR=C:\A920aip /passive

cd \A920aip
msiexec.exe /a AcroPro.msi /p C:\A920aip\AcroProStdUpd910_T1_T2_incr.msp TARGETDIR=C:\A920aip /passive
msiexec.exe /a AcroPro.msi /p C:\A920aip\AcrobatUpd912_all_incr.msp TARGETDIR=C:\A920aip /passive
msiexec.exe /a AcroPro.msi /p C:\A920aip\AcrobatUpd920_all_incr.msp TARGETDIR=C:\A920aip /passive

Creating a Soft Font Cartridge in Output Designer 5.x (“certain characters do not print correctly/at all”)

NOTA: this article is not an Adobe publication, and carries no guarantees of accuracy. This article may be subject to change at any time.

The print agent needs to firstly know the page codes necessary for interpreting the bytes representing the characters you want to print. This can sometimes be dependant on the print agent, sometimes it depends on the system.

NOTE: The Print Agent manual 5.6 specifies: The use of Thai, Hebrew, and Arabic with Output Designer and Central is neither certified nor supported by Adobe.

Additionally, some characters from Baltic languages cannot be accessed because the glyphs are not supported by Central. Check that the correct symbol sets are in use, that the appropriate fonts are installed on the machine, and that the locale/encoding scheme is supported by the machine.

If you cannot provide UTF-8 data (the encoding jfmerge expects by default), you need to specify the encoding. For example WinLatin-1 is short-handed to “5”. To do this, use either:

  • -ass processing option, e.g. -ass5
  • ^symbolset dynamic command in the DAT data, e.g. ^symbolset 5

Remember that ^symbolset can be called anywhere in the data, even in the preamble. If you are having issues with data that exists in the preamble, try insterting ^symbolset 108 at the top of the custom preable. If you’re having problems with characters from your data, check whether it contains erroneous ^symbolset comands.

If you are mixing several files with different symbol sets (for example, using the ^file command), use the -autf8on option.

This is generally sufficient for printing in Germanic, Nordic and Latin-based languages. Greek and Cyrillic require special fonts to be loaded, as do certain extended characters, and so a soft font cartridge may be required.

Check your fields

First thing to check is that the field that is to display special characters has been set up correctly. If you tell the field that you will give it Latin-based symbols (W1 encoding, such as “a”, or “ê”), but then pass it something else (such as Greek or Cyrillic text), you won’t get the results expected at all…!

  1. Double-click the text field
  2. Click the Font button in the top right
  3. Ensure you have selected the correct font and symbol set
  4. Click OK, and OK again

So far so good. But you may not have the appropriate font, or your font was perhaps not available in the correct symbol set. That’s when you need to create a Soft Font Cartridge

Creating a soft font cartridge

  1. Open Output Designer 5.x
  2. Go to menu File > Presentment targets…
  3. Select the Presentment target to add fonts to
  4. Click the button Font Setup…
  5. The Font Cartridges tab shows what soft font cartridges are available.
    The highlighted ones are the ones that are effectively available for use.

  6. Select TrueType or Type 1 Fonts tab as appropriate
  7. Check that the font directory selected is correct so as to populate the list of fonts
  8. Check that the appropriate symbol set has been selected (list to the right) – W1 is for standard Latin and derivatives, CYR is for Cyrillic, etc
  9. Check that the appropriate Font Storage is selected (ref Designer Help topics accessible from menu Help > section Permanent soft fonts/Download options)
  10. Select all instances of the font you wish to use.
  11. For example, if adding Verdana, it is likely the required files will be verdana.ttf, verdanab.ttf, verdanai.ttf for regular, bold and italic Verdana, respectively.
    You can also include other fonts in the cartridge to identify sets of fonts.

  12. Give this soft font cartridge a name – it will be displayed in the Font Cartridges tab
  13. Click Create soft font
  14. Go to the Font Cartridge tab and select your new soft font cartridge
  15. Click OK

Important: you will need to add the soft font cartridge to every presentment target you intend to use with your design template.

Incorporate the cartridge in MDF for use with Central Output Server

  1. Go to menu File > Compile…
  2. Select the option “Embed soft fonts in template”
  3. Click OK

Force font data to be sent to the printer

  1. Add the -ajutfno option to your processing options
    • if using Test presentment… then this must be done under Menu Tools > Options… : Test presentment : Print options
    • if using Central this can be done via Control Center, or in the ^job line of the data files
  2. Launch process for printing files.

Enabling UTF-8 on Linux/UNIX/*NIX systems

Some systems don’t support certain encodings out of the box.

Check that the locale is installed on the unit by running:

$>	locale -a

This will list all locales. The locale name is case-sensitive. If the locale is not present, it must be installed by the system administrator.
Once the locale is installed: to make a system-wide change of the LANG variable so that daemons etc may see the change, use:

$>	chlang EN_US.UTF-8

The change takes effect at the next session login (so for system services this generally means that the machine needs reboot)

If that command is not available, manually set the $LANG environment variable to EN_US.UTF-8

Problems with Greek characters Delta and Omega

Greek characters delta and omega sometimes do not appear at all in the page. To work around this issue:

  1. Open jetkeys.ini from <Install path>\Adobe\Output Designer\Config
  2. Go to the section headed by the comment “; MS Code Page 1253 (Greek) to Unicode UCS-2 and vice versa
  3. Under the subsection labelled [Translate 63 to 102], add the following:
  4. 0xC4=0x2206
  5. Under the subsection labelled [Translate 102 to 63], add the following:
  6. 0x2206=0xC4
  7. Save jetkeys.ini and re-process your document.

Adding a new Presentment Target to Output Designer 5.x (“adding a new printer driver in Central”)

NOTA: this article is not an Adobe publication, and carries no guarantees of accuracy. This article may be subject to change at any time.

Sometimes people incorrectly talk about “printer drivers” when referring to Presentment Targets. Presentment targets are customization profiles that the Print Agent uses to tailor the printer language that is written out from the Print Agent (jfmerge.exe). Unless you have specified the use of Generic Windows Drivers, it is the Print Agent that produces the printing language, without involving the Windows driver (installed to Windows, not Central) installed on the local machine.

PostScript and PCL5 are the two currently most wide spread languages in use on printers at the time of writing. Central also supports ZPL (for Zebra printers), IPL (for Intermec printers) and PCL6 (which works best with Hewlett Packard printers). It can also produce flat PDF 1.4 output.

If your printer does not appear in the list of presentment targets, but does accept one of the supported languages, then you should be able to create a new profile for your printer. The fact that there is a certain list of printers provided out of the box is more to say that these printers are tested, and they are expected to render correctly. If you create a custom profile for a different printer, there is no actual guarantee that it will work.

You can create a new presentment target for your printer. Please note that:

  • fax printers are an exception – only the presentment targets provided out-of-the-box are expected to work with faxing capabilities. There is no sure way to add a different target in this case
  • there is no guarantee to support custom Presentment targets. Custom targets are the creator’s responsibility
  • when using multiple presentment targets for a form design, you must design for the most restrictive layout (the one with the largest margins)

How to add a Presentment target to your list in Output Designer / Editing a Presentment Target

To create a custom target, first find a target that works when used with your printer. This will depend on whether the printer is meant to understand PCL5, PCL6, PostScript, ZPL, IPL

We will assume for the purpose of this article that you have a (theoretical) printer from manufacturer “PrintBrand” and of model “100”. Its short name shall be designated as “PB100”

We further assume that using the HP LaserJet 5 Presentment target produces correct output on the PB100.

The short name of the HP LaserJet 5 presentment target is “hplj5”. The short name can be seen in Output Designer can be seen at the end of a presentment target’s name in the list of presentment targets, accessed from menu File > Presentment targets.

To create a new presentment target “PB100” based on a pre-existing “HPLJ5”

  1. Open <Program Files location>\Adobe\Output Designer [version]\Config
  2. Locate the hplj5.ics file
  3. Create a copy of hplj5.ics and rename it to pb100.ics – this file name will determine its ID in Designer, and for the -asp processing option
  4. Open pb100.ics in a text editor (e.g. NotePad)
  5. Edit PrinterName and and PrinterDesc as follows:
    PrinterName PB100 ; this is a comment to say it's a PrintBrand 100
    ; comments start with ";" and end with the line
    PrinterDesc My PrintBrand 100 printer
    • PrinterName identifies the printer short name – this should be kept the same as the file name
    • PrinterDesc provides the description seen in the presentment targets list.
  6. Customize any further items you need
    Most items are self-documented in the ICS files.
    You can customize and add page sizes, line heights, etc.
  7. Save the ICS file
  8. In Output Designer, open menu File > Presentment targets…
  9. Select the PrintBrand 100 printer that you have just defined, by ticking the checkbox on its left.
  10. Click the button Font Setup…
  11. Click “OK” to come directly back to the presentment targets. This will re-compile your ICS file for inclusion in future presentment targets. This will not affect MDF files created previously – thise will have to be recompiled to benefit from your changes
  12. Click “OK” to close the Presentment targets window
  13. Use “Test presentment”, or re-compile your form for the changed ICS to take effect.
  14. If afterwards you wish to modify the ICS, or edit an ICS that already exists, repeat steps 4-13

You may have an issue wherein page elements in the output are displaced/moved/in the wrong location when printing to one output device, but appear correctly on another output device, or PDF.

This is because the PDF target has margins of size 0 – this means that a page element is allowed to be printed to any part of a page, even the edge. Some printers impose hard margins on their pages, and page elements cannot appear there, and this is catered for at design time by checking that the form has been designed with the most restrictive margins in mind.

You can change the margin space that JfMerge will take into account in the Presentment Target file.

To see the margins in the design layout, use menu File > Presentment targets and setting the default presentment target. The result is that in the form design, a darker area around the edges of the page will appear, indicating that the current default target has margins set, and no design element should appear there.