Common Errors and Causes
Error: ** Error [ SUBROUTINE_CALL_FAIL ] **
Appliies to: jBASE
Cause: The bpi.bp.form api has not been exposed in the account / environment where you are running. See the article in the documentation: Exposing the BPI Forms API in other accounts
Unable to find block chrgTax in Oo document
Applies to: All MultiValue Platforms
Cause:
This issue could be caused by applying special characteristics to the {} directives. I’ve seen cases where if you do something like this in the template:
{repeat=tr;block=foo}
And somewhere within the content between the {} delimiter you’ve applied a font, or a special emphasis, that would actually get inserted into the middle of the tag. It’s important, therefore to ensure that everything between { and } including { and } themselves are just simple text with no special formatting.
To be very sure, might add the {repeat=tr;block=foo} into notepad or other other text editor that does not have the possibility of putting emphasis markup within the string and then paste it into the cell. This would ensure that you really just have the basic {directive} with no special emphasis accidently inserted into the {directive}.
Format error discovered in the file
See the article Format Error when opening document in Libre/Open Office
MultiValue Integration Guidelines
BP Forms interface programs are written in MultiValue basic and may include some unix scripts. These will be found in the BPI account in the bpi.bp.form directories. These routines should be cataloged into each account where it may be used (except on UniData systems where the policy is to catalog all programs using GLOBAL mode cataloging in which case, cataloging in each account is not required). Some low level libraries in bpi.bp may be called although most low level routines are now renamed and found in bpi.form.bp to isolate them from other modules.
Note: on Unidata, programs should be compiled with the -D switch and cataloged GLOBALLY
General Approach
Blue Prairie Forms can be utilized in a number of ways. Each approach trades ease of implementation and functionality. The easiest approach is to capture an entire print job and simply toss it onto a template. The drawback of this approach is that the content of the print job is one large object and, as such, no specialized styling can be done with specific parts of the print job. To have the capability of applying unique styling (e.g., fonts, colors, etc.), you must separate the elements of the print job. For example, if the customer’s name was its own object and given a name such as %customer_name%, the, you could insert the placeholder tag %customer_name% into the template and apply unique styling just to that placeholder tag.
Preferred Approach
In order to gain maximum control over the styling of your printed output, we prefer the technique of breaking the print job into many placeholders. This approach requires more time up front. With a form of moderate complexity, the easier approach might require less than an hour of programming/integration while the preferred approach might take a few hours, the benefit of having full control to style each element make the moderate amount of additional effort worth while.
Using Legacy Printing Logic
Forms development requires two fundamental steps:
- Painting of a form template using Open Office
- Creation of host-side logic to generate the print job
Step two, the creation of host side logic, can be approached in a number of ways:
Fresh Program
You can create a program or subroutine from scratch that gathers information and calls Blue Prairie Forms to print it. While this results in a clean, streamlined program, it requires that the developer more fully research and understand the rules that produce the output.
Modify Legacy Program
Another approach is to copy the legacy print program that currently produces the legacy printed output and modify it to use Blue Prairie Forms API calls. The downside of this approach is that your print program will retain some of the programming style of the legacy program. The degree of this downside will be determined by the quality of the legacy code.
The upside of this approach is that all of the specialized logic within the legacy print program will be retained and used. As a developer, you will not need to research and understand what the print program does or why it makes certain decisions about what to print. You simply follow the design pattern of the legacy code and replace legacy PRINT directives with Blue Prairie Forms api calls.
Changes to Legacy Code
In order to ensure that you do no harm to the legacy print programming system, we suggest that you make a copy of the legacy print program or subroutine then make the Blue Prairie Forms modifications to the copy.
We recommend that you follow these best practices when making modifications to the copy of the legacy program:
Commenting Convention
- Never remove or alter an original instruction. If it is to be disabled, you may comment it out in its unaltered state. We recommend adding a descriptive comment tag like *|||BPIFORMS||| to clearly indicate to a future reader that you intentionally commented out the instruction for the purpose of integrating Blue Prairie Forms
- For all added instructions, please suffix the instruction with a comment like ;*|||BPIFORMS||| to indicate that this instruction was added for Blue Prairie Forms integration.
Other Conventions
- Name the copy with a standard prefix. For example, of the legacy program was called INVOICE.PRINT, then your copy would be called <prefix>.INVOICE.PRINT. Prefix may be anything you wish so long as it is unique and would not collide with other existing programs. The author uses the prefix BPI (which stands for Blue Prairie, Inc.) for all Blue Prairie programs.
- Follow commenting convention rules defined above.
- Look for and comment-out all PRINTER ON, PRINTER CLOSE, PRINTER OFF directives in the program. If the print program does not contain any of these, then you may need to look into the calling program to determine if it performs the PRINTER directives. Using the preferred integration method, we do not want to send any printed output to the spooler.
- Look for and comment-out all PRINT statements. Below each commented PRINT directive there should be a Blue Prairie Forms API call added. The general idea is that rather than printing a value to a large print job, you will instead assign that same printed value to a placeholder tag name that will be injected into the form template.
- Add the Blue Prairie Forms subroutine block to the bottom of the program. These internal subroutines provide a stock template that can be modified to perform common tasks such as referencing a template, declaring output form filenames and converting repeating arrays (e.g., repeating lines in a form) into properly formatted repeating tag (see repeat tags).
Testing
Once the integration routine has been written and compiled, you will need to test it. Many MultiValue users do not have a development system so care must be taken to ensure that you do not disrupt legacy printing while you are debugging and testing your new Blue Prairie Forms integration.
To facilitate this testing, the author generally defines a new printer queue on the MultiValue system that is design only for testing. We then add logic into the legacy program which determines whether a print job should be routed through the legacy print program or to the new Blue Prairie Forms integration. A useful API is provided called bpi.checkEnabledQueue which allows you to create simple parameter files stored in MultiValue directories or hashed tables. These param files can be a great aid as you work to test the integration with minimal disruption to your production workflows.
bpi_simple_test_program
This section describes a simple test program that is provided with Blue Prairie Forms. This program can be used to test and prove that the MulitValue Server BASIC API programs are installed and functioning properly. This program produces a document called Q0___bpi_simple_test_program.odt. This document will, by default, be dropped into the .../queue/bpiform1 directory. If the phantom is configured and running, it will attempt to deliver the document to a CUPS printer called Q0 on the Open Office Print Server.
A Sample Program
Blue Prairie Forms in installed with a sample test program that:
- Uses a standard template provided with Blue Prairie Forms
- Requires no external data source
- Produces a finished odt document
- Is a good example that can be used as a starting point for your own integrations
This example program will be installed and tested to verify the stability of the Blue Prairie Forms by your installation technician. It should be left unaltered as it may be used in the future as a diagnostic test to validate the stability of the installation. You may make copies of the program for your own experimentation.
* bpi_simple_test_program
* Copyright (c) 2016 by Blue Prairie, Inc.
* Example program to test a simple form with common form elements
* 1/28/2016 by Bruce Decker
* ----------------------------------------------------------------
* Requires that the form template be placed in the directory indicated
* below. This does not actually print the form, it just produces (drops)
* a finished form into a specified location
*
* This example program will:
* 1) Set some non-repeating tags and values (see subroutine InitformVars)
* 2) Set some repeating tags and values using multiValued arrays
* 3) Call the templating system (see subroutine MakeAndPrint)
* 4) Write a finished document based on the named template
*
* Note: This example does not actually print. It just creates a document
*
* -----------------------------------------------------------------
pgmId = \bpi_simple_test_program\
tag = \\; val = \\
* Equate some vectors for our associated multiValue arrays
EQU d.lineNo TO 1
EQU d.qty TO 2
EQU d.partNo TO 3
EQU d.listPrice TO 4
EQU d.extPrice TO 5
*
GOSUB ParseSentence; *171212
GOSUB InitFormVars
*
* load the detail array with a simple loop
totExtPrice = 0
FOR lineNo = 1 TO 20
detail< d.lineNo, lineNo> = lineNo
detail< d.qty, lineNo> = lineNo
detail< d.partNo, lineNo> = \Part_\:lineNo
detail< d.listPrice, lineNo> = OCONV(lineNo, \MR2,$\)
detail< d.extPrice, lineNo> = OCONV(lineNo * lineNo, \MR2,$\)
totExtPrice = totExtPrice + ( lineNo * lineNo)
NEXT lineNo
* load footnotes array with static assignment
footnotes<1,-1> = \This is line 1 of the footnotes array\
footnotes<1,-1> = \This is line 2 of the footnotes array\
*
* Now make the document
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%totExtPrice%\, OCONV(totExtPrice, \MR2,$\), \\, vector, verbose)
*
GOSUB MakeAndPrint
*
GOTO Exit
* ------------------------------------------------------------------------------
* B L U E P R A I R I E F O R M S S U B R O U T I N E S
* ------------------------------------------------------------------------------
InitFormVars:
verbose = 5
blocks = \\; tags = \\; vals = \\; pos = 0;
* note that the 4th param is the 'block' specifier. Since blank these tags are not repeating
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%@PGM%\, pgmId, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%@WHO%\, @WHO, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%@LOGNAME%\, @LOGNAME, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%@USERNO%\, @USERNO, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%port%\, FIELD(OCONV(\\,\U50BB\), SPACE(1), 1), \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%TIME%\, OCONV(TIME(),\MTH\):SPACE(1):OCONV(DATE(),\D2/\), \\, vector, verbose); *|||BPI FORM|||
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%timeDate%\, TIMEDATE(), \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%dayOfWeek%\, OCONV(DATE(), 'DWAMADYL'), \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%powered_by%\, \Powered by Blue Prairie Forms (tm)\, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \\, \%reprint%\, \\, \\, vector, verbose)
detail = \\
footnotes = \\
RETURN
*
AddDetailLine:
* reserved for future use
RETURN
*
MoveDetailToArrays:
* now add the detail lines to the blocks/tags/vals arrays with a block name of 'det'. The template should feed
* these tags to a one-row table in openOffice. The first column in the table should be made to contain the plug-in
* directive "{repeat=tr;block=det} This will cause the table row to be repeated for the number of multivalues
* found in these tags. So, if you have 10 lines represented by 10 multivalues for each of the tags, then the
* table should be expanded automatically from 1 row as defined in the template to 10 rows (one for each multivalue)
* because each line in the detail array is represented (properly) as a multivalue.
CALL bpi.set.tagval2(blocks, tags, vals, \det\, \%d.lineNo%\, detail<d.lineNo>, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \det\, \%d.qty%\, detail<d.qty>, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \det\, \%d.partNo%\, detail<d.partNo>, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \det\, \%d.extPrice%\, detail<d.extPrice>, \\, vector, verbose)
CALL bpi.set.tagval2(blocks, tags, vals, \det\, \%d.listPrice%\, detail<d.listPrice>, \\, vector, verbose)
* load footnotes array to tag
CALL bpi.set.tagval2(blocks, tags, vals, \foot\, \%f.footnotes%\, footnotes, \\, vector, verbose)
RETURN
*
MakeAndPrint:
doc.format = \odt\
pathToBpiForms = "/dbms/BPIFORMS/bpi_forms"; * <--- change to suit your install
verbose = 5
GOSUB MoveDetailToArrays
CALL bpi.xmlencode( tags, tags, \all\, error)
CALL bpi.xmlencode( vals, vals, \all\, error)
*make a diag table from tags and vals
CALL bpi.makediag( blocks, tags, vals, \odt\, diag, status)
*add the diag table to the tags and vals array
CALL bpi.set.tagval2( blocks, tags, vals, \\, \%diag%\, diag, \\, vector, verbose)
GOSUB GetQueue
*construct the filename of the document
document_name_make = "%queueName%___bpi_simple_test_program%copy%.odt"
tag<2> = \%queueName%\; val<2> = bpiQueueName
* set variables for the form maker
params = \odt\
templatePath = "%pathToBpiForms%/templates/bpi_simple_test_program_tpl.odt"
templatePath = CHANGE( templatePath, \%pathToBpiForms%\, pathToBpiForms)
tempPath = "%pathToBpiForms%/working"
tempPath = CHANGE( tempPath, \%pathToBpiForms%\, pathToBpiForms)
* call the form maker
verbose = 5
FOR i = 1 TO copies
IF copies EQ 1 THEN
copyName = \\
END ELSE
copyName = \_\:i
END
output_docname = document_name_make
CALL bpi.tagval( tag, val, output_docname )
output_docname = CHANGE( output_docname, \%copy%\, copyName)
outputPath = "%pathToBpiForms%/queue/bpiform1/":output_docname
outputPath = CHANGE( outputPath, \%pathToBpiForms%\, pathToBpiForms)
outputPath = CHANGE( outputPath, \%queueName%\, bpiQueueName)
CRT \Making-> \:outputPath
CALL bpi.form.make2( templatePath, outputPath, tempPath, params, blocks, tags, vals, verbose, status, message)
*should now have a completed form at output.path
NEXT i
RETURN
*
GetQueue:
* We would normally grab the current assigned queue from a SP-ASSIGN or SETPTR command
* but for this test, we'll just use the queue Q0
bpiQueueName = \Q0\
RETURN
*
ParseSentence:
pgm = \bpi_simple_test_program\
sentence = CHANGE( @SENTENCE, SPACE(1), @AM)
maxSentence = DCOUNT(sentence, @AM)
start = @FALSE
copies = 1
FOR i = 1 TO maxSentence
word = sentence<i>
uc.word = OCONV( word, \MCU\)
BEGIN CASE
CASE start
BEGIN CASE
CASE uc.word[1,2] = \-C\
copies = OCONV( word, \MCN\)
IF NOT( copies ) THEN copies = 1
CASE @TRUE
END CASE
CASE word EQ pgm
start = @TRUE
CASE @TRUE
END CASE
NEXT i
RETURN
*
Exit:
STOP
Running the example program
To run the sample, program, simply type bpi_simple_test_program. Diagnostic messages will be displayed to the screen and a document will be written to the queue directory declared in the program. Depending on the specific location of directories in your Blue Prairie Forms installation, you may need to modify certain parts of the test program. Specifically, the variable pathToBpiForms should be adjusted to fit your installation.
If you wish to run a number of copies of a document to the queue directory, you may add the -c switch followed by the number of copies you wish to create. For example:
bpi_simple_test_program -c5
Will generate 5 documents:
Q0___bpi_simple_test_program_1.odt
Q0___bpi_simple_test_program_2.odt
Q0___bpi_simple_test_program_3.odt
Q0___bpi_simple_test_program_4.odt
Q0___bpi_simple_test_program_5.odt
Modifying the Example Template
A detailed explanation of the template process is provided in the User Manual. You can modify the example template but please make a backup of the original template (unchanged) before making modifications
Page Numbering Tutorial
Total Page Count
For templates that include just one copy (not a customer copy, office copy, etc) you can use the normal Open Office Insert > Fields > Page Count to insert the total number of pages in the document.
If you need to insert the total pages for each copy (e,g, total pages in the customer copy) then it gets a little trickier. There are a number of ways to accomplish this in open office but the easiest is through the use of the Cross Reference feature.
Open Office Cross References allow you to insert a cross reference marker somewhere in the document. You can then retrieve and display the page number where that cross reference marker exists elsewhere in the document.
The following technique can be used to create a “Total Page Count” cross reference. Assuming that each copy within the document is expected to be the same total number of pages, then this technique need only be applied to the first copy within the template. Since the subsequent copies will presumably be the same number of pages as the first, you can reference the same cross reference marker in the subsequent copies and you do not necessarily need to create new cross reference markers for each copy.
If on the other hand, it is possible to design each copy within the template to look quite different. For example, the first copy in the document could be for the customer and may contain less information than the office copy and therefore the office copy may span more pages. In this case, you will need to create multiple cross-reference markers, one for each copy so that you can track the different page counts of each copy.
Here is an example assuming a document with two copies; the first called ‘Merchant Copy” and the second called “Customer Copy.”
We insert a cross reference marker called “Total Page Count” right after the last content in the body of the page. By putting it here, we can then reference the page number where this cross reference marker exists and that is the total page count.
Here’s the procedure:
- Open the document.
- Place the cursor just after the last content in the body of the first copy of the template (in the sample above, we inserted just after the detail table in the Merchant Copy.
- Choose from the menu Insert > Cross-reference and the following page will appear.

- Choose “Set Reference” from the “Type” field then give the Cross Reference marker a name of “Total Page Count.”
- Press Insert then Close and the cross reference marker will be inserted into the document at the point where the cursor exists. Depending on the font size at the point where you inserted the cross-reference marker, it may to may not be visible.
- Scroll to the header section of your document where you want the total pages to be inserted. We’ll assume for this example that you wish to have the text “<Page Number> of <Total Page Count> inserted into a place in the header like this:

- Place the cursor where you want the current page number to be placed then choose “Insert > Fields > Page Number”. The box will now appear like this:

- Place the cursor to the right of the page number (1 in the above example) then press space bar the “of” then spacebar. Open office may change “of” to “Of”. You can fix it. Later.
- Choose from the menu “Insert > Cross Reference” then choose “Insert Reference” from the Type field and select the “Total Page Count” cross reference and press Insert then Close.
- Your total page count for this copy will now be displayed.

Open Office Styles Tutorial
This tutorial is designed to provide you with a basic orientation to Open Office Styles. The concept can be a little difficult to grasp at first but the goal of this tutorial is to give you a basic practical understanding of styles so that you can leverage this powerful capability within your forms.
Start Open Office and create a new blank document

Notice in the circled area above the word ‘Default’. This is your current page style for the first page. The first thing we will do is to create a new style based on ‘Default’ page style for the first copy within our document.

The styles docking toolbar will appear either in a separate window or (as is shown above) within a docked panel within the Open office editor.
Click on the circle button to display page styles and the display above will be shown

In the styles panel, click on the icon circled above (right) and the “Create Style” window will be displayed. We are going to create our page style for our “Office Copy” page. So, we entered the name “1 – Office Copy” into the name of this new style. Follow these steps and press OK.
Right click on the page style “Default” as shown below and a series of pop-up options will be shown that allows you to change the page style. Choose “1 – Office Copy” and that page style will be applied the current (and only) page as shown below (see circled area).

With the page style “1 Office Copy” setup and applied to this first page, we’ll now modify this page which will in turn modify the page style. Any page within the document that references the page style “1 Office Copy” will reflect the changes we make. This means that we do not need to manually change each page. Just change any page that uses this style and all other pages that use the same page style will be updated.
Modify the margins of the “1 – Office Copy” page style
Menu: Format > Page
Modify the page margins to .5 for left, right, top and bottom. This is standard for forms and allows us to place more information on each page.

Add a header to the page style
Menu: Format > Page > Header

Check the box “Header On” to enable the header for this page style and then press OK

Perform the same action for the Footer as shown above.
Now there should be rectangles in the document. The top small rectangle is the header area. The big rectangle in the middle of the document is the body area where our data will live and the small rectangle at the bottom is our footer that will repeat on each page of the document where this page style is applied.
For example, I’ve entered some text into the header just to illustrate.

The text above include an Open Office field to allow Open Office to inject the current page number into the header. To inject that field, I simply chose from the menu: Insert > Fields > Page Number. This feature allows Open Office to automatically inject the current page number. If we perform page numbering this way, then no matter how large your final document (pages), Open Office will manage your page numbering and you’ll not need any server-side code for this purpose.
Verify Page Options
There are a few quirks in OpenOffice having to do with how breaks (page breaks and section breaks) will automatically inject blank pages into the document. To prevent this misbehavior, I recommend setting the following options in Open Office and saving the document.
Menu: Tools > Options

- In the left panel, scroll down to ‘OpenOffice Writer” and click on it
- Then, under that section, click on “Print”
- Verify that “Print Automatically inserted blank pages” is unset
- For good measure, also unset “Left pages” and “Right Pages”
- Press OK
Place Data in Body of Document
Now, back in the document, place the cursor in the large rectangle representing the document text body.
Press return a few times to insert some paragraph marks just to give some play area in the document
Insert a Table
Menu: Table > Insert

Fill out the fields as shown above then press OK
A table will appear in the body of the document (see yellow circled area below)

Now, click the cursor in an area below the table and insert another table.

We will end up spitting the document so that the second table is on its own page. The top table will be the “Office Copy” and the bottom would be the “Customer Copy”.
First, we need to add another page style for our “Customer Copy."

Menu > Format > Styles and Shading
- In the Styles panel, click on the yellow highlighted button to select ‘page styles’
- Ensure that ‘1 – Office copy’ is selected
- Click the button circled in red to create a new style based on the selected style
- Enter the name “2 – Customer Copy” as shown
- Press OK
Create the break between the first table and second table
- Place your cursor in the white space after the end of the first table and before the start of the second table.
- From the menu, choose insert > more breaks > manual break
- This will pop-up a small editing window with these fields:
- Type = Page Break
- Style = <the page style for the second copy that you defined (e.g., 2 - Customer Copy)
- Change Page Number = Checked
- Set the page number to 1
- This will pop-up a small editing window with these fields:
This will inject a manual page break between the first and second tables. When the break to the second copy occurs, the style selected in the manual page break will be selected and this will change the header and footer for these pages.

Change the Customer Copy header to make it unique
- Click anywhere in the header of the second page and change it to read “This is the header for the customer copy”but leave the page number in place.

- Scroll back to the header on the first page and note that the header of the first page did not change. This is because the first page uses the style of “1 – Office Copy” and the header for that page style is kept separately from the header for the “2 – Customer Copy” page.
Test Automatic Pagination
Place the cursor into the document body after the table on the first page and press <return> until you overflow into a second page
- Note that the lines added to page 1 causes a normal overflow into page 2. But if we scroll to page 2 and observe the header of that page, we’ll see that the header is from the “1 – Office Copy” style and the auto page number changed to “2” from 1. This shows that auto page numbering works (at least for the Office Copy).

- Scroll down to the start of the second table and place the cursor into the body of the document after the second table. Observe the following

- At the bottom, we see that the page style “2 – Customer copy” is active for this page
- The page number display on the bottom task bar shows “Page 1 3/3”. This means that we are on page number 1 (because we reset the page number on the break” but in truth, within the document, we are actually on page 3 of 3 (3/3).
- Note that the header shows the proper header for the customer copy (see yellow highlight)
- Note that our auto-inserted page number is back to 1 because we reset the page number to 1 as part of the break directive we entered as part of the first row of the second table
Watermarks
There are several ways to create watermarks with Open Office. The easiest method is to simply place text into the header or footer of the document. A more involved technique involves the use of frames. A frame is an object that can be inserted into a page. You can place the frame into a header or footer of a page and then the frame will be repeated on all pages where that style applies. This means that if content applied to a page spans to multiple pages that this frame watermark will appear on each of the pages.
To create a frame-based watermark, follow this procedure:
- Place the cursor into any area of a footer on a page where you wish to have the watermark applied
- Menu Insert > Frame. The following page will display:

- Press okay and a rectangle will be inserted into the document with green anchor points marking its four corners.

- Drag the rectangle into the position on the page where you wish for the watermark to appear
- Use the green anchor points to resize the rectangle to a size that is suitable for the desired watermark.
- Enter text into the frame
- Choose Format > Character to set the properties of the text within the frame:

- Choose the “Font Effects” tab and set the font to ‘Outline’ and the color to ‘Gray 1’

- Press OK and then left-click on the frame to select it.
- Choose “Borders” and remove the borders from the frame as shown below

- Print Preview the document and check the position, color and other presentation of the frame.
- If you wish, temporarily inject lines into the page to test whether it is paginating correctly and preserving the watermark on each page.
Summary
These techniques are the basic building blocks for creating powerful forms and fully utilizing the features of Open Office to create powerful features without programming