/* Generic closing for the rendering device. */ int gdev_prn_async_render_close_device(gx_device_printer * prdev) { gx_device *const pdev = (gx_device *) prdev; return gdev_prn_close(pdev); }
static int npdl_close(gx_device *pdev) { gx_device_printer *const ppdev = (gx_device_printer *) pdev; gdev_prn_open_printer(pdev, 1); fputs("\033c1", ppdev->file); return gdev_prn_close(pdev); }
static int tfax_close(gx_device * pdev) { gx_device_tfax *const tfdev = (gx_device_tfax *)pdev; if (tfdev->tif) TIFFCleanup(tfdev->tif); return gdev_prn_close(pdev); }
int tiff_close(gx_device * pdev) { gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; if (tfdev->tif) TIFFCleanup(tfdev->tif); return gdev_prn_close(pdev); }
/* Close an capi fax (sff-encoded) document. */ static int cfax_prn_close(gx_device * pdev) { gx_device_printer * const ppdev = (gx_device_printer *)pdev; if (ppdev->file != NULL) { cfax_doc_end(ppdev->file); } return gdev_prn_close(pdev); }
/* Close the printer, writing the stream trailer. */ static int ljet5_close(gx_device * pdev) { gx_device_printer *const ppdev = (gx_device_printer *)pdev; int code = gdev_prn_open_printer(pdev, true); if (code < 0) return code; px_write_file_trailer(ppdev->file); return gdev_prn_close(pdev); }
static int escpage_close(gx_device * pdev) { gdev_prn_open_printer(pdev, 1); if (ppdev->Duplex && (pdev->PageCount & 1)) { fprintf(ppdev->file, "%c0dpsE", GS); } fputs(epson_remote_start, ppdev->file); fputs(epson_remote_start, ppdev->file); return gdev_prn_close(pdev); }
static int ljet4pjl_close(gx_device *pdev) { gx_device_printer *const ppdev = (gx_device_printer *)pdev; int code = gdev_prn_open_printer(pdev, 1); if (code < 0) return code; if ( ppdev->Duplex_set >= 0 && ppdev->Duplex ) { gdev_prn_open_printer(pdev, 1); fputs("\033&l0H", ppdev->file) ; } fputs("\033%-12345X", ppdev->file); return gdev_prn_close(pdev); }
static int ml600_close( gx_device *pdev) { int code = gdev_prn_open_printer(pdev, true); FILE *prn_stream; /* dprintf("gdevml6: ml600_close called\n"); */ if (code < 0) return code; prn_stream = ppdev->file; ml_finish(ppdev, prn_stream); return gdev_prn_close(pdev); }
/* and to reset the printer so the ink cartridge doesn't clog up. */ static int hpjet_close(gx_device * pdev) { gx_device_printer *const ppdev = (gx_device_printer *)pdev; int code = gdev_prn_open_printer(pdev, 1); if (code < 0) return code; if (ppdev->PageCount > 0) { if (ppdev->Duplex_set >= 0 && ppdev->Duplex) fputs("\033&l0H", ppdev->file); fputs("\033E", ppdev->file); } return gdev_prn_close(pdev); }
/* Close the win_pr2 driver */ static int win_pr2_close(gx_device * dev) { int code; int aborted = FALSE; win_pr2_copy_check(wdev); /* Free resources */ if (!wdev->nocancel) { if (!wdev->hDlgModeless) aborted = TRUE; else DestroyWindow(wdev->hDlgModeless); wdev->hDlgModeless = 0; } if (aborted) AbortDoc(wdev->hdcprn); else EndDoc(wdev->hdcprn); DeleteDC(wdev->hdcprn); if (wdev->win32_hdevmode != NULL) { GlobalFree(wdev->win32_hdevmode); wdev->win32_hdevmode = NULL; } if (wdev->win32_hdevnames != NULL) { GlobalFree(wdev->win32_hdevnames); wdev->win32_hdevnames = NULL; } code = gdev_prn_close(dev); /* delete unwanted temporary file */ if (wdev->fname[0]) unlink(wdev->fname); return code; }
int eprn_close_device(gx_device *device) { eprn_Eprn *eprn = &((eprn_Device *)device)->eprn; #ifdef EPRN_TRACE if_debug0(EPRN_TRACE_CHAR, "! eprn_close_device()...\n"); #endif if (eprn->scan_line.str != NULL) { gs_free(device->memory->non_gc_memory, eprn->scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), "eprn_close_device"); eprn->scan_line.str = NULL; } if (eprn->next_scan_line.str != NULL) { gs_free(device->memory->non_gc_memory, eprn->next_scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), "eprn_close_device"); eprn->next_scan_line.str = NULL; } return gdev_prn_close(device); }
/* Generic closing for the writer device. */ static int gdev_prn_async_write_close_device(gx_device * pdev) { gx_device_printer *const pwdev = (gx_device_printer *) pdev; gx_device_clist_writer *const pcwdev = &((gx_device_clist *) pdev)->writer; /* Signal render thread to close & terminate when done */ gx_page_queue_add_page(pcwdev, pwdev->page_queue, GX_PAGE_QUEUE_ACTION_TERMINATE, 0, 0); /* Wait for renderer to finish all pages & terminate req */ gx_page_queue_wait_until_empty(pwdev->page_queue); /* Cascade down to original close rtn */ gdev_prn_close(pdev); pwdev->free_up_bandlist_memory = 0; /* Deallocte dynamic stuff */ gdev_prn_dealloc(pwdev); return 0; }
/* Close the os2prn driver */ static int os2prn_close(gx_device * dev) { int code; LONG lOut; USHORT usJobID; /* tell printer that all is finished */ DevEscape(opdev->hdc, DEVESC_ENDDOC, 0L, NULL, &lOut, (PBYTE) & usJobID); /* Free resources */ GpiAssociate(opdev->hps, (HDC) NULL); GpiDestroyPS(opdev->hps); DevCloseDC(opdev->hdc); if (opdev->hpsMem != GPI_ERROR) GpiDestroyPS(opdev->hpsMem); if (opdev->hdcMem != DEV_ERROR) DevCloseDC(opdev->hdcMem); code = gdev_prn_close(dev); /* delete unwanted temporary file */ unlink(opdev->fname); return code; }
/* * Open this printer device in ASYNC (overlapped) mode. * This routine must always called by the concrete device's xx_open routine * in lieu of gdev_prn_open. */ int gdev_prn_async_write_open(gx_device_printer * pwdev, int max_raster, int min_band_height, int max_src_image_row) { gx_device *const pdev = (gx_device *) pwdev; int code; bool writer_is_open = false; gx_device_clist_writer *const pcwdev = &((gx_device_clist *) pwdev)->writer; gx_device_clist_reader *pcrdev = 0; gx_device_printer *prdev = 0; gs_memory_t *render_memory = 0; /* renderer's mem allocator */ pwdev->page_queue = 0; pwdev->bandlist_memory = 0; pwdev->async_renderer = 0; /* allocate & init render memory */ /* The big memory consumers are: */ /* - the buffer used to read images from the command list */ /* - buffer used by gx_real_default_strip_copy_rop() */ /* - line pointer tables for memory devices used in plane extraction */ /* - the halftone cache */ /* - the band rendering buffer */ /* The * 2's in the next statement are a ****** HACK ****** to deal with */ /* sandbars in the memory manager. */ if ((code = alloc_render_memory(&render_memory, pwdev->memory->non_gc_memory, RendererAllocationOverheadBytes + max_raster /* the first * 2 is not a hack */ + (max_raster + sizeof(void *) * 2) * min_band_height + max_src_image_row + gx_ht_cache_default_bits_size() * 2)) < 0) goto open_err; /* Alloc & init bandlist allocators */ /* Bandlist mem is threadsafe & common to rdr/wtr, so it's used */ /* for page queue & cmd list buffers. */ if ((code = alloc_bandlist_memory (&pwdev->bandlist_memory, pwdev->memory->non_gc_memory)) < 0) goto open_err; /* Dictate banding parameters for both renderer & writer */ /* Protect from user change, since user changing these won't be */ /* detected, ergo the necessary close/reallocate/open wouldn't happen. */ pwdev->space_params.banding_type = BandingAlways; pwdev->space_params.params_are_read_only = true; /* Make a copy of device for use as rendering device b4 opening writer */ code = gs_copydevice((gx_device **) & prdev, pdev, render_memory); pcrdev = &((gx_device_clist *) prdev)->reader; if (code < 0) goto open_err; /* -------------- Open cmd list WRITER instance of device ------- */ /* --------------------------------------------------------------- */ /* This is wrong, because it causes the same thing in the renderer */ pwdev->OpenOutputFile = 0; /* Don't open output file in writer */ /* Hack: set this vector to tell gdev_prn_open to allocate for async rendering */ pwdev->free_up_bandlist_memory = &gdev_prn_async_write_free_up_bandlist_memory; /* prevent clist writer from queuing path graphics & force it to split images */ pwdev->clist_disable_mask |= clist_disable_fill_path | clist_disable_stroke_path | clist_disable_complex_clip | clist_disable_nonrect_hl_image | clist_disable_pass_thru_params; if ((code = gdev_prn_open(pdev)) >= 0) { writer_is_open = true; /* set up constant async-specific fields in device */ reinit_printer_into_printera(pwdev); /* keep ptr to renderer device */ pwdev->async_renderer = prdev; /* Allocate the page queue, then initialize it */ /* Use bandlist memory since it's shared between rdr & wtr */ if ((pwdev->page_queue = gx_page_queue_alloc(pwdev->bandlist_memory)) == 0) code = gs_note_error(gs_error_VMerror); else /* Allocate from clist allocator since it is thread-safe */ code = gx_page_queue_init(pwdev->page_queue, pwdev->bandlist_memory); } /* ------------ Open cmd list RENDERER instance of device ------- */ /* --------------------------------------------------------------- */ if (code >= 0) { gx_semaphore_t *open_semaphore; /* Force writer's actual band params into reader's requested params */ prdev->space_params.band = pcwdev->page_info.band_params; /* copydevice has already set up prdev->memory = render_memory */ /* prdev->bandlist_memory = pwdev->bandlist_memory; */ prdev->buffer_memory = prdev->memory; /* enable renderer to accept changes to params computed by writer */ prdev->space_params.params_are_read_only = false; /* page queue is common to both devices */ prdev->page_queue = pwdev->page_queue; /* Start renderer thread & wait for its successful open of device */ if (!(open_semaphore = gx_semaphore_alloc(prdev->memory))) code = gs_note_error(gs_error_VMerror); else { gdev_prn_start_render_params thread_params; thread_params.writer_device = pwdev; thread_params.open_semaphore = open_semaphore; thread_params.open_code = 0; code = (*pwdev->printer_procs.start_render_thread) (&thread_params); if (code >= 0) gx_semaphore_wait(open_semaphore); code = thread_params.open_code; gx_semaphore_free(open_semaphore); } } /* ----- Set the recovery procedure for the mem allocator ----- */ if (code >= 0) { gs_memory_retrying_set_recover( (gs_memory_retrying_t *)pwdev->memory->non_gc_memory, prna_mem_recover, (void *)pcwdev ); } /* --------------------- Wrap up --------------------------------- */ /* --------------------------------------------------------------- */ if (code < 0) { open_err: /* error mop-up */ if (render_memory && !prdev) free_render_memory(render_memory); gdev_prn_dealloc(pwdev); if (writer_is_open) { gdev_prn_close(pdev); pwdev->free_up_bandlist_memory = 0; } } return code; }
static int npdl_set_page_layout(gx_device * dev) { int code; float margins[4]; /* Change the margins according to the paper size. */ switch (npdl_get_paper_size(dev)) { case PAPER_SIZE_A3: if (dev->MediaSize[0] > dev->MediaSize[1]) { /* Landscape */ margins[0] = L_MARGIN_A3; margins[1] = B_MARGIN_A3; margins[2] = R_MARGIN_A3; margins[3] = T_MARGIN_A3; } else { /* Portrait */ margins[1] = L_MARGIN_A3; margins[2] = B_MARGIN_A3; margins[3] = R_MARGIN_A3; margins[0] = T_MARGIN_A3; } break; case PAPER_SIZE_A5: if (dev->MediaSize[0] > dev->MediaSize[1]) { /* Landscape */ margins[0] = L_MARGIN_A5; margins[1] = B_MARGIN_A5; margins[2] = R_MARGIN_A5; margins[3] = T_MARGIN_A5; } else { /* Portrait */ margins[1] = L_MARGIN_A5; margins[2] = B_MARGIN_A5; margins[3] = R_MARGIN_A5; margins[0] = T_MARGIN_A5; } break; case PAPER_SIZE_B5: if (dev->MediaSize[0] > dev->MediaSize[1]) { /* Landscape */ margins[1] = L_MARGIN_B5; margins[2] = B_MARGIN_B5; margins[3] = R_MARGIN_B5; margins[0] = T_MARGIN_B5; } else { /* Portrait */ margins[0] = L_MARGIN_B5; margins[1] = B_MARGIN_B5; margins[2] = R_MARGIN_B5; margins[3] = T_MARGIN_B5; } break; case PAPER_SIZE_LETTER: if (dev->MediaSize[0] > dev->MediaSize[1]) { /* Landscape */ margins[1] = L_MARGIN_LETTER; margins[2] = B_MARGIN_LETTER; margins[3] = R_MARGIN_LETTER; margins[0] = T_MARGIN_LETTER; } else { /* Portrait */ margins[0] = L_MARGIN_LETTER; margins[1] = B_MARGIN_LETTER; margins[2] = R_MARGIN_LETTER; margins[3] = T_MARGIN_LETTER; } break; case PAPER_SIZE_POSTCARD: if (dev->MediaSize[0] > dev->MediaSize[1]) { /* Landscape */ margins[1] = L_MARGIN_POSTCARD; margins[2] = B_MARGIN_POSTCARD; margins[3] = R_MARGIN_POSTCARD; margins[0] = T_MARGIN_POSTCARD; } else { /* Portrait */ margins[0] = L_MARGIN_POSTCARD; margins[1] = B_MARGIN_POSTCARD; margins[2] = R_MARGIN_POSTCARD; margins[3] = T_MARGIN_POSTCARD; } break; case PAPER_SIZE_ENV4: case PAPER_SIZE_BPOSTCARD: margins[1] = L_MARGIN_ENV4; margins[2] = B_MARGIN_ENV4; margins[3] = R_MARGIN_ENV4; margins[0] = T_MARGIN_ENV4; break; default: /* A4 */ if (dev->MediaSize[0] > dev->MediaSize[1]) { /* Landscape */ margins[1] = L_MARGIN_A4; margins[2] = B_MARGIN_A4; margins[3] = R_MARGIN_A4; margins[0] = T_MARGIN_A4; } else { /* Portrait */ margins[0] = L_MARGIN_A4; margins[1] = B_MARGIN_A4; margins[2] = R_MARGIN_A4; margins[3] = T_MARGIN_A4; } break; } gx_device_set_margins(dev, margins, true); if (dev->is_open) { gdev_prn_close(dev); code = gdev_prn_open(dev); if (code < 0) return code; } return 0; }
int eprn_set_page_layout(eprn_Device *dev) { bool no_match = true, /* Are the requested flags supported for some size? */ landscape = dev->MediaSize[0] > dev->MediaSize[1]; /* It's not documented, but 'MediaSize' is the requested "PageSize" page device parameter value and hence is to be interpreted in default (not default default!) user space. */ const char *epref = dev->eprn.CUPS_messages? CUPS_ERRPREF: ""; const eprn_CustomPageDescription *best_cmatch = NULL; /* best custom page size match */ eprn_Eprn *eprn = &dev->eprn; const eprn_PageDescription *best_cdmatch = NULL, /* best custom page size match in discrete list*/ *best_dmatch = NULL, /* best discrete match */ *pd; /* loop variable */ float /* Page width and height in bp with w <= h (in a moment): */ w = dev->MediaSize[0], h = dev->MediaSize[1], /* pixmap device space margins in bp (canonical order): */ margins[4]; int quarters; ms_MediaCode desired = eprn->desired_flags; #ifdef EPRN_TRACE if_debug3(EPRN_TRACE_CHAR, "! eprn_set_page_layout(): PageSize = [%.0f %.0f], " "desired_flags = 0x%04X.\n", dev->MediaSize[0], dev->MediaSize[1], (unsigned int)desired); #endif /* Ensure w <= h */ if (w > h) { float temp; temp = w; w = h; h = temp; /* This has effectively split 'MediaSize[]' into 'w', 'h' and 'landscape'. */ } /* Initialization of primary return value */ eprn->code = ms_none; /* Put the LeadingEdge value into the desired flag pattern if it's set */ if (eprn->leading_edge_set) { if (eprn->default_orientation % 2 == 0) /* true on short edge first */ desired &= ~MS_TRANSVERSE_FLAG; else desired |= MS_TRANSVERSE_FLAG; } /* Find best match in discrete sizes */ if (eprn->media_overrides == NULL) pd = eprn->cap->sizes; else pd = eprn->media_overrides; while (pd->code != ms_none) { const ms_SizeDescription *ms = ms_find_size_from_code(pd->code); if (ms->dimen[0] > 0.0 /* ignore variable sizes */ && fabs(w - ms->dimen[0]) <= 5.0 && fabs(h - ms->dimen[1]) <= 5.0) { /* The size does match at 5 bp tolerance. This value has been chosen arbitrarily to be equal to PostScript's PageSize matching tolerance during media selection. The tolerance should really be that at which the printer in question distinguishes between sizes or smaller than that in order to at least prevent printing on unsupported sizes. */ if (best_dmatch == NULL || better_flag_match(desired, eprn->optional_flags, best_dmatch->code, pd->code)) best_dmatch = pd; if (flag_match(desired, eprn->optional_flags, pd->code)) no_match = false; } pd++; } /* Next find the best match among the custom size descriptions */ if (eprn->cap->custom != NULL) { const eprn_CustomPageDescription *cp = eprn->cap->custom; /* First check whether the size is in the supported range */ while (cp->width_max > 0.0) { if (cp->width_min <= w && w <= cp->width_max && cp->height_min <= h && h <= cp->height_max) { /* The size does match. */ if (best_cmatch == NULL || better_flag_match(desired, eprn->optional_flags, best_cmatch->code, cp->code)) best_cmatch = cp; if (eprn->media_overrides == NULL && flag_match(desired, eprn->optional_flags, cp->code)) no_match = false; } cp++; } /* If we have read a media configuration file, the flags to be matched must be sought in 'media_overrides'. */ if (best_cmatch != NULL && eprn->media_overrides != NULL) { for (pd = eprn->media_overrides; pd->code != ms_none; pd++) { if (ms_without_flags(pd->code) == ms_CustomPageSize) { if (best_cdmatch == NULL || better_flag_match(desired, eprn->optional_flags, best_cdmatch->code, pd->code)) best_cdmatch = pd; if (flag_match(desired, eprn->optional_flags, pd->code)) no_match = false; } } } } /* Now the 'best_*match' variables indicate for each of the categories of page descriptions to which extent the size is supported at all (non-NULL value) and what the best flag match in the category is. Here we now check for NULL values, i.e., size matches. */ if (best_dmatch == NULL) { /* No discrete match */ if (best_cmatch == NULL) { /* No match at all. */ eprintf3("%s" ERRPREF "This document requests a page size of %.0f x %.0f bp.\n", epref, dev->MediaSize[0], dev->MediaSize[1]); if (eprn->cap->custom == NULL) { /* The printer does not support custom page sizes */ if (eprn->media_overrides != NULL) eprintf1( "%s The media configuration file does not contain an entry for " " this size.\n", epref); else eprintf2("%s This size is not supported by the %s.\n", epref, eprn->cap->name); } else eprintf3( "%s This size is not supported as a discrete size and it exceeds " "the\n" "%s custom page size limits for the %s.\n", epref, epref, eprn->cap->name); return -1; } if (eprn->media_overrides != NULL && best_cdmatch == NULL) { eprintf6("%s" ERRPREF "This document requests a page size of %.0f x %.0f bp\n" "%s but there is no entry for this size in the " "media configuration file\n" "%s %s.\n", epref, dev->MediaSize[0], dev->MediaSize[1], epref, epref, eprn->media_file); return -1; } } /* Now we have: best_dmatch != NULL || best_cmatch != NULL && (eprn->media_overrides == NULL || best_cdmatch != NULL). */ /* Find a flag match among the size matches found so far */ { ms_MediaCode custom_code = ms_none; /* best custom page size match (either from cmatch or dcmatch) */ if (best_cmatch != NULL && (eprn->media_overrides == NULL || best_cdmatch != NULL)) custom_code = (eprn->media_overrides == NULL? best_cmatch->code: best_cdmatch->code); if (best_dmatch == NULL || best_cmatch != NULL && better_flag_match(desired, eprn->optional_flags, best_dmatch->code, custom_code)) { if (flag_match(desired, eprn->optional_flags, custom_code)) { if (eprn->media_overrides == NULL) { eprn->code = best_cmatch->code; margins[0] = best_cmatch->left; margins[1] = best_cmatch->bottom; margins[2] = best_cmatch->right; margins[3] = best_cmatch->top; } else { eprn->code = best_cdmatch->code; margins[0] = best_cdmatch->left; margins[1] = best_cdmatch->bottom; margins[2] = best_cdmatch->right; margins[3] = best_cdmatch->top; } } } else { if (flag_match(desired, eprn->optional_flags, best_dmatch->code)) { eprn->code = best_dmatch->code; margins[0] = best_dmatch->left; margins[1] = best_dmatch->bottom; margins[2] = best_dmatch->right; margins[3] = best_dmatch->top; } } } /* If we've found a match, 'code' is no longer 'ms_none'. */ if (eprn->code == ms_none) { eprn_flag_mismatch(eprn, no_match); return -1; } /* Adapt the orientation of default default user space if not prescribed */ if (!eprn->leading_edge_set) { if (eprn->code & MS_TRANSVERSE_FLAG) eprn->default_orientation = 3; /* This leads to 0 if landscape orientation is requested. */ else eprn->default_orientation = 0; } /* Now 'eprn->default_orientation % 2' describes the sheet's orientation in pixmap device space. If this does not agree with the width and height values in the device instance, we'll have to adapt them. This is only necessary if there is a significant difference between width and height. */ if (fabs(w - h) > 1 /* arbitrary */ && (eprn->default_orientation % 2 == 0) != (dev->width/dev->HWResolution[0] <= dev->height/dev->HWResolution[1])) { bool reallocate = false; #ifdef EPRN_TRACE if_debug0(EPRN_TRACE_CHAR, "! eprn_set_page_layout(): width-height change is necessary.\n"); #endif /* Free old storage if the device is open */ if (dev->is_open) { #ifdef EPRN_TRACE if_debug0(EPRN_TRACE_CHAR, "! eprn_set_page_layout(): Device is open.\n"); #endif reallocate = true; /* One could try and call the allocation/reallocation routines of the prn device directly, but they are not available in older ghostscript versions and this method is safer anyway because it relies on a documented API. */ gdev_prn_close((gx_device *)dev); /* ignore the result */ } /* Now set width and height via gx_device_set_media_size(). This function sets 'MediaSize[]', 'width', and 'height' based on the assumption that default user space has a y axis which is vertical in pixmap device space. This may be wrong and we have to fix it. Because fixing 'MediaSize[]' is simpler, gx_device_set_media_size() is called such that it gives the correct values for 'width' and 'height'. */ if (eprn->default_orientation % 2 == 0) { /* portrait orientation of the sheet in pixmap device space */ gx_device_set_media_size((gx_device *)dev, w, h); if (landscape) { dev->MediaSize[0] = h; dev->MediaSize[1] = w; } } else { /* landscape orientation in pixmap device space (transverse) */ gx_device_set_media_size((gx_device *)dev, h, w); if (!landscape) { dev->MediaSize[0] = w; dev->MediaSize[1] = h; } } /* If the device is/was open, reallocate storage */ if (reallocate) { int rc; rc = gdev_prn_open((gx_device *)dev); if (rc < 0) { eprintf2("%s" ERRPREF "Failure of gdev_prn_open(), code is %d.\n", epref, rc); return rc; } } } /* Increase the bottom margin for coloured modes except if it is exactly zero */ if (eprn->colour_model != eprn_DeviceGray && margins[1] != 0.0) margins[1] += eprn->cap->bottom_increment; /* Number of +90-degree rotations needed for default user space: */ quarters = eprn->default_orientation; if (landscape) quarters = (quarters + 1)%4; /* Store the top and left margins in the device structure for use by eprn_get_initial_matrix() and set the margins of the printable area if we may. gx_device_set_margins() (see gsdevice.c) copies the margins[] array to HWMargins[] which is presumably to be interpreted in default user space (see gs_initclip() in gspath.c), and if its second argument is true it also modifies the offset variable Margins[]. The first property means that gx_device_set_margins() can only be used if default user space and pixmap device space have the same "up" direction, and the second appropriates a parameter which is intended for the user. */ if (eprn->keep_margins) { eprn->down_shift = dev->HWMargins[3 - quarters]; eprn->right_shift = dev->HWMargins[(4 - quarters)%4]; } else { int j; eprn->down_shift = margins[3]; eprn->right_shift = margins[0]; if (quarters != 0) { /* The "canonical margin order" for ghostscript is left, bottom, right, top. Hence for, e.g., a +90-degree rotation ('quarters' is 1) of default user space with respect to pixmap device space the left margin (index 0) in default user space is actually the bottom margin (index 1) in pixmap device space, the bottom margin is the right one, etc. */ for (j = 0; j < 4; j++) dev->HWMargins[j] = margins[(j+quarters)%4]; /* 'HWMargins[]' is in bp (see gxdevcli.h) */ } else { /* Convert to inches */ for (j = 0; j < 4; j++) margins[j] /= BP_PER_IN; gx_device_set_margins((gx_device *)dev, margins, false); /* Of course, I could set HWMargins[] directly also in this case. This way is however less prone to break on possible future incompatible changes to ghostscript and it covers the most frequent case (portrait and short edge first). */ } } return 0; }