Skip to main content
Skip table of contents

(v13) API events for eHVD

This page applies to Harlequin v13.1r0 and later; and to Harlequin Core but not Harlequin MultiRIP


The HVD API uses events to track and synchronize the sub-page raster elements cached by the OEM code, and to inform the OEM output code how pages are to be built up from sub-page raster elements.

For details of the event API and how to discover RIP APIs (including the event API) see (v13) RDR and Event APIs .

NOTE: From Harlequin v13, the API for using Harlequin VariData in external mode has been changed and is not backward compatible. The main changes are:

  •           A new SWEVT_RR_ELEMENT_QUERY_LOCK event replaces the SWEVT_RR_ELEMENT_QUERY and SWEVT_RR_ELEMENT_LOCK events.
  •           A new SWEVT_RR_PAGE_DEFINE event with a new Event ID replaces the existing SWEVT_RR_PAGE_DEFINE event.
  •           The format of the corresponding event message SWMSG_RR_PAGE_DEFINE has also changed.
  •           The contents of the information passed through the SWMSG_RR_PAGE_REF message have changed.

Initial connection between the RIP and the client code is established via the connect event (SWEVT_RR_CONNECT). The message SWMSG_RR_CONNECT has the following fields:

sw_tl_ref timeline

Identifies the job containing the pages.

uint8 cache_id[ RR_CACHE_ID_LENGTH ]

A null-terminated string identifying the cache to which we wish to connect. This discrimination allows a single RIP to have multiple HVD (retained raster-capable) implementations installed, and to direct its events at the appropriate one.

uint8 setup_id[ RR_SETUP_ID_LENGTH ]

A null-terminated string identifying the setup associated with this connection to the cache. The contract between provider and user of this interface is that this setup ID uniquely describes a configuration of the RIP in every respect; if this is not the case, the correct behavior cannot be guaranteed. That is, if any parameter of the page device or other salient configuration item differs between the creation and use of items in the cache, undefined behavior will result. As a special case, if this ID is an empty string, a cache instance is created upon connection and is immediately destroyed upon disconnection.

void *connection

If the event is successfully handled, this is set to an opaque handle uniquely identifying the connection.

HqBool position_independent_compatible

If the client code can handle position-independent scan results (that is, where the element x and y offsets can be non-zero), it should set this flag to TRUE. Client code written against a prior version of the API will not set this flag, naturally, and therefore the scanner will not operate in position independent mode.

HqBool allow_zero_elements

NOTE: Although this flag is available from Harlequin v13, Global Graphics does not currently recommend setting it.

If the client code can handle having zero elements in a page or tile then it should set this flag to TRUE. The default value is FALSE, which causes the HVD implementation to create an empty dummy element if there are no elements intersecting a page or tile. Client code written against a prior version of the API will not set this flag, and therefore will have at least one element per page or tile.

Every subsequent eHVD event has a message whose first two fields are the opaque void *connection reference and the job timeline reference sw_tl_ref timeline . They are omitted from the descriptions here for brevity. All handlers whose connection handle doesn't match the one provided in a subsequent eHVD event message should immediately return SW_EVENT_CONTINUE.

The connection between RIP and client code is destroyed on receipt of the SWEVT_RR_DISCONNECT event by the client code. The RIP does not issue such an event until the client code has indicated it is done with all pages in flight (see SWEVT_RR_PAGE_COMPLETE).

eHVD event calling sequence

The sequence of events and raster deliveries in HVD external mode is deliberately not formally defined and the possibility of the order changing is open. OEMs are, therefore, strongly recommended not to rely on any specific ordering in their code.

However, some generalities can be described that may help in understanding the intended use of the various events.

The SWEVT_RR_ELEMENT_DEFINE and SWEVT_RR_PAGE_DEFINE events occur during the scanning process, as commonality in raster elements is identified. In Harlequin MultiRIP 3.0 the whole requested page range is scanned before the first page is fully interpreted, although that situation is not guaranteed to continue in further versions and should not be assumed in OEM code.

The OEM code can make a decision as to whether it has everything it needs to print a page by reviewing a page in-flight definition (as provided by the SWEVT_RR_PAGE_DEFINE event) and checking whether it holds raster data for all of the sub-raster elements required by that page.

In general, event handlers should assume that any memory referenced by the messages, including the messages themselves, belongs to the module originating the event. No assumptions about the lifetime of such memory should be made beyond the scope of the event handler itself; if client code needs to reference such data, it must make a copy.

In order to make a real page from the pagebuffers it gets sent via the HVD external mode interface, a cache implementation (the client code), should play back each raster element in turn that is included for the page in the SWEVT_RR_PAGE_DEFINE event. The order is essential: the page should be initialized to zeros, or to the value of the pixel(s)

in the RR_PAGE_DEFINE_PAGE::background_id element, and then the element pointed to by the zeroth entry in the RR_PAGE_DEFINE_PAGE:: elements array of pointers should be played back first, followed by the element pointed to by the first array entry and so on.

The sequence is as follows:

1 The RIP scans the requested page range of the PDF file. The first event it sends is SWEVT_RR_PAGE_DEFINE with a pages array containing an array of pointers to RR_PAGE_DEFINE_PAGE structures.

When tiling output, each tile appears as a separate ::RR_PAGE_DEFINE_PAGE entry in the pages array. Note that throughout the HVD external mode interface, SWMSG_RR_PAGE_REF::page_index values represent an ordinal within the page range (and, where appropriate, the tile range) requested at pdfexecid time. They should not be assumed to bear any relation to PDF page labels or page numbers (one logical page usually is made up of several pagebuffers given that it is split into raster elements).

2 For each page in turn, the RIP issues a SWEVT_RR_ELEMENT_QUERY_LOCK event for each raster element on the page. For each raster element in turn, if it is not already in the cache, the RIP sets the OptimizedPDFId and OptimizedPDFUsageCount PGB parameters and outputs a PGB. The same mechanism is used for elements intended to be cached as for those which are variable data and appear only once the only difference is the usage count.

The single parameter set by the RIP in the message for the SWEVT_RR_ELEMENT_QUERY_LOCK event is the desired element ID, and the cache implementation should set the opaque handle in the event message to a non-null value to signify it has the element in its cache, and should ensure the element is not purged from its cache before it has received the corresponding SWEVT_RR_ELEMENT_UNLOCK event. Otherwise, it should set the handle to null which signifies the RIP should send the element. In that case, the RIP will issue a SWEVT_RR_ELEMENT_PENDING event which means that the client code should remember the fact that the RIP has promised the element (though it may not yet have arrived), so that subsequent queries are not answered with a null handle, leading to redundant sending of the same element. The RIP will then send the element followed by a further SWEVT_RR_ELEMENT_QUERY_LOCK event.

SWEVT_RR_ELEMENT_UNLOCK unlocks a raster element, and if no other locks apply, it is eligible for purging at the discretion of the cache implementation.

3 The RIP issues a SWEVT_RR_PAGE_READY event when all raster elements have been exported for the page or tile in question. This means that the cache implementation can go ahead and stitch the rasters to complete the page or tile.

4 When the raster stitching is finished the cache implementation must send a SWEVT_RR_PAGE_COMPLETE event to signal that the RIP is free to tidy up after the page or tile, including sending a disconnect event if SWEVT_RR_PAGE_COMPLETE events for all of the pages or in the case of tiling, for all of the tiles on all of the pages have been received.

NOTE:  SWEVT_RR_ELEMENT_UPDATE_RASTER is only used in HVD internal mode and is not relevant for HVD external mode.

The SWEVT_RR_ELEMENT_DEFINE events are issued for all elements required for the last issued SWMSG_RR_PAGE_DEFINE, one after the other, with bounding boxes already complete. This will not be the case in the future where the element definition may happen, for instance, before the bounding box is known. An update event will supply such data subsequently.

There may be elements which legitimately have degenerate bounding boxes (x1 > x2 or y1 > y2). These represent marking operators in the content stream which for some reason did not lead to a mark on the page. They may be clipped out, off the page, or otherwise empty. Caching an empty raster for these elements is still worthwhile because it means that the RIP can avoid interpreting the marking operators each time the page is played back.

eHVD raster element handling

There is a set of events for handling sub-page raster elements.

Raster elements are made known to the client code via the SWEVT_RR_ELEMENT_DEFINE event. On receipt of this event, the client code should add an entry into its cache table or other structure, keyed on the unique ID. The bounds described by x1, y1, x2, and y2 are relative to the pagebuffer containing the element when the RIP rasterizes the element. When position independence is off, this pagebuffer will be the same size as the page on which it appears, and typically all four coordinate values will be non-zero (that is, the salient part of the pagebuffer is somewhere in the middle of the pagebuffer, surrounded by whitespace). When position independence is on, x1 and y1 will be zero and x2 and y2 effectively become width and height. The pagebuffer is sized to just contain the element and there is little or no surrounding whitespace. SWMSG_RR_ELEMENT_DEFINE has these fields:

uint8 *id

Unique ID, length 16 bytes.

int32 x1

Minimum x extent of the raster element.

int32 y1

Minimum y extent of the raster element.

int32 x2

Maximum x extent of the raster element.

int32 y2

Maximum y extent of the raster element.

The SWEVT_RR_ELEMENT_UPDATE_HITS event is used by the RIP to inform the client code about changes to the usage count of a particular element. An increment happens each time the HVD scanner finds another use of the element, and a decrement occurs when a page containing the element is completed.

What the OEM code does with the hit count is private to the cache implementation, but the default might be to delete the raster straight away when the hit count reaches zero for a cache intended for single jobs. An OEM implementation may choose to ignore the hit count in the raster metadata maintained by the RIP.

SWMSG_RR_ELEMENT_UPDATE_HITS contains:

uint8 *id

Unique ID, length 16.

HqBool raise

Whether to raise or lower the hit count.

unsigned int hits_delta

Number of hits to add to or subtract from the overall hits remaining count.

A simple OEM implementation may choose to read OptimizedPDFUsageCount from the supplied rasters and treat those rasters with a value of 1 in that field as being variable and suitable for early purging.

eHVD pages in flight

The SWEVT_RR_PAGE_DEFINE event tells the client code the information it needs to construct the page from its constituent raster elements. The SWEVT_RR_PAGE_DEFINE event passes an array of pointers to structures representing pages or tiles, and is generated by the RIP during the scanning process; this event informs the client code of the pages and their contents, in terms of raster element handles.

NOTE:  These must be replayed in turn by the client code in order to stitch together the final page or tile. If the client code knows that all raster elements are already to hand, it can begin page output immediately.

NOTE:  The page definition provided by the RIP must be stored by the OEM code until it has been used, as it will not be re-sent by the RIP.

NOTE:  Any data contained in this event message should be regarded by the client code as transient; that is, if it needs to refer back to it, it must make its own deep copy rather than retain any pointer values.

From Harlequin v13, there may be more than one PAGE-DEFINE event for a page range. That means that the client code should append to its tables rather than overwrite them when a PAGE-DEFINE event arrives.

The SWMSG_RR_PAGE_DEFINE fields are:

uint32 page_offset ;

Page number offset of first page (counted as page 1) in this define.

This is used to get the page numbering correct when running a job using pdfexecid chunks and supplying PageRange to divide the job into chunks or when using Harlequin Scalable RIP.

uint32 page_count ;

The number of pages or tiles being defined.

uint32 page_index_offset ;

Page index offset of first page/tile in this define.

This will be the value of the SWMSG_RR_PAGE_REF::page_index that corresponds to the page or tile pointed to by pages[0] in this message.

RR_PAGE_DEFINE_PAGE **pages ;

Array of pointers to page structures, of length page_count . When tiling, each tile appears as a separate RR_PAGE_DEFINE_PAGE structure, sharing its RR_PAGE_DEFINE_PAGE::relative_page_offset with other tiles from the same page.

The RR_PAGE_DEFINE_PAGE structure describes each page or tile.

If tiling, the RIP guarantees that all tiles of a page that are being output will appear in the same ::SWEVT_RR_PAGE_DEFINE event, with adjacent entries in the SWMSG_RR_PAGE_DEFINE::pages array.

The RR_PAGE_DEFINE_PAGE fields are:

uint32 w

Width of the entire (untiled) page in pixels.

uint32 h

Height of the entire (untiled) page in pixels.

int32 x1, y1, x2, y2 ;

Bounding box of this page or tile's raster. This is a normalized half-open bounding box (i.e., x1 < x2 and y1 < y2, pixels at x2 and y2 are not included in the raster). If not tiling, then x1 and y1 will be zero, x2 and y2 will be the page width and height, and the bounding box will represent the entire page dimensions.

uint32 relative_page_number ;

Relative page number of this page or tile. This is a zero-based index of the page relative to the SWMSG_RR_PAGE_DEFINE::page_offset. It should be used to compute the output page number for this page or tile. When tiling, multiple adjacent tile entries in the SWMSG_RR_PAGE_DEFINE::pages array will share the same relative_page_number.

uint32 element_count

Number of raster elements on this page or tile.

RR_PAGE_DEFINE_ELEMENT **elements

Array of pointers to raster elements, of length element_count.

const uint8 *background_id ;

Unique ID of length ::RR_ELEMENT_ID_LENGTH , or NULL. This is used to identify an element used to initialize the background of the page or tile, when the erase color is not white. The page or tile background should be initialized to the color value of the pixel or pixels of the corresponding raster element. If the raster contains more than 1 pixel, all the pixels will have the same color value. A NULL entry indicates that the page or tile background should be initialized to white.

Normally the page or tile rasters should be initialized to white, before adding in each of the raster elements in turn. However, if any of the pages or tiles need a non-white background, a non-NULL background_id will be supplied. In this case the raster element corresponding to the ID will be if position independence is enabled a very small raster, either 1 x 1 or OptimizedPDFGridX x OptimizedPDFGridY (whichever is larger), and all of its pixels will be the required background color. If position independence is not enabled, the raster will be the full page size as usual, but only a very small bounding box will be indicated.

Each page or tile raster should be initialized to the required erase color by copying the value from one of the pixels in the background raster. After this, each of the raster elements corresponding to the RR_PAGE_DEFINE_ELEMENT IDs should be copied in as usual.

The RR_PAGE_DEFINE_ELEMENT structure appears once for each element on each page or tile.

uint8 *id

Pointer to the unique ID for this element (to be matched against the ID field in the SWEVT_RR_ELEMENT_DEFINE event).

int32 x

The x offset of the page element. This is relative to the page or tile raster bounding box defined in the ::RR_PAGE_DEFINE_PAGE structure referencing this element

int32 y

The y offset of the page element. This is relative to the page or tile raster bounding box defined in the ::RR_PAGE_DEFINE_PAGE structure referencing this element.

For more information on EraseColor see the Harlequin Extensions Manual , “EraseColor”.

eHVD element cache management

The SWEVT_RR_ELEMENT_QUERY_LOCK event is used by the RIP to ask the client code whether it already has a copy of the raster element with the given ID. The client code should return SW_EVENT_CONTINUE if it does not yet have, and is not expecting, a copy of the element in question.

To explain: The RIP may query the same ID multiple times before the raster arrives in the cache. In part this is because the rasterization and delivery of the element by the RIP is asynchronous but also the client code may impose further constraints. For this reason once the RIP receives a return value from this event of SW_EVENT_UNHANDLED, it will issue a SWEVT_RR_ELEMENT_PENDING event to signal that the element will be rendered and passed to the client. Once it has done so the client code should mark the element in its cache as “pending”. That way the second and subsequent queries can return SW_EVENT_HANDLED to avoid having the RIP render the element redundantly.

Note that it is valid for the returned element handle to be NULL: this either means that it exists in the cache but is degenerate, or it is pending as previously described. Also note that the value of this handle is not used by the RIP in external HVD, only in internal HVD. The SWEVT_RR_ELEMENT_QUERY_LOCK message contains:

const uint8 *id

Unique ID, length 16 bytes.

uintptr_t handle

Element handle an opaque reference to the cache instance, if any. This will not be used by the RIP directly if external retained raster is on.

The SWEVT_RR_ELEMENT_PENDING event uses the SWMSG_RR_ELEMENT_REF message to identify which element it concerns:

const uint8 *id

Unique ID, length 16 bytes.

The SWEVT_RR_ELEMENT_UPDATE_RASTER event is only used internally in the RIP when in internal HVD mode. The SWMSG_RR_ELEMENT_UPDATE_RASTER message contains the following:

const uint8 *id

Unique ID, length 16 bytes.

uintptr_t raster

Opaque handle to the raster data itself.

size_t size

Size of the raster data in bytes.

Once the cache implementation has responded to a SWEVT_RR_ELEMENT_QUERY_LOCK event with SW_EVENT_HANDLED, the element must not be purged before the corresponding SWEVT_RR_ELEMENT_UNLOCK event. This event uses the same SWMSG_RR_ELEMENT_REF message as SWEVT_RR_ELEMENT_PENDING above. Only one page's worth of elements (or in the case of tiling, one tile's worth of elements) will be locked at any given time by the RIP, and if the cache cannot contain all the necessary elements at once then this is an unrecoverable error.

The SWEVT_RR_PAGE_READY event is a signal from the RIP to the client code that every element for the given page or tile is either present in the cache or on its way there (see SWEVT_RR_ELEMENT_PENDING). The RIP will then expect a SWEVT_RR_PAGE_COMPLETE event from the client code when, and only when, output is fully completed for the page or tile assembled by the client code from the constituent raster elements. At that point, the RIP will issue SWEVT_RR_ELEMENT_UNLOCK and SWEVT_RR_ELEMENT_UPDATE_HITS as appropriate. Note also that the RIP will not issue a SWEVT_RR_DISCONNECT event until it has received every SWEVT_RR_PAGE_COMPLETE event it is expecting.

The message for both SWEVT_RR_PAGE_READY and SWEVT_RR_PAGE_COMPLETE is SWMSG_RR_PAGE_REF:

uint32 page_index

Index into the pages or tiles defined by ::SWMSG_RR_PAGE_DEFINE messages. Multiple ::SWEVT_RR_PAGE_DEFINE events may be received, for different page ranges or tiles of a job. This index is cumulative over all of the page definitions. The page index used in a page-ready or pagecomplete message is the zero-based offset into the SWMSG_RR_PAGE_DEFINE::pages array plus the SWMSG_RR_PAGE_DEFINE::page_index_offset offset. This calculation provides a unique index across all of the ::SWEVT_RR_PAGE_DEFINE events for the connection.

When using HVD external mode it is the responsibility of the OEM code to manage whatever caching of sub-page rasters is required. The cache implementation may choose to make use of the OptimizedPDFUsageCount value from each raster delivered and/or the hit count managed through the SWEVT_RR_ELEMENT_UPDATE_HITS event. Alternatively, it may choose to follow some other strategy.

NOTE:  To avoid stalling the RIP, the OEM code must copy raster data from the buffers to the plugin or raster output as rapidly as possible.

If the cache implementation deleted a cached raster before the next time the RIP queries for it, the RIP will regenerate the raster.

NOTE:  HVD API client code should check its tables and deliver output upon receipt of a Page Ready event. For example, it is not generally safe to rely on checking for pages ready to be output on receipt of a raster because if all raster elements for all pages in the current range are already cached, no such raster delivery will take place.

eHVD raster dimensions

This does not apply when position independence is enabled.

All rasters delivered will have nominally the full dimensions of the page, but a bounding box is supplied for each in the raster metadata supplied by the SWEVT_RR_ELEMENT_DEFINE event. The bbox in the element define event will match the bbox of the actual raster delivered to the back end when position independence is turned on; all rasters delivered when position independence is turned off are as large as the page and the bbox in the event tells the OEM how to extract the relevant part from

that raster. Empty bands above and below the area actually marked in each raster may be suppressed with TrimPage as usual. White space to the left and right of that marked area can be easily skipped in the cache implementation using the bounding box.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.