static void WarnBadInstruction(gs_font_type42 *pfont, int glyph_index) { char buf[gs_font_name_max + 1]; int l; gs_font_type42 *base_font = pfont; while ((gs_font_type42 *)base_font->base != base_font) base_font = (gs_font_type42 *)base_font->base; if (!base_font->data.warning_bad_instruction) { l = min(sizeof(buf) - 1, base_font->font_name.size); memcpy(buf, base_font->font_name.chars, l); buf[l] = 0; if (glyph_index >= 0) emprintf2(pfont->memory, "Failed to interpret TT instructions for glyph index %d of font %s. " "Continue ignoring instructions of the font.\n", glyph_index, buf); else emprintf1(pfont->memory, "Failed to interpret TT instructions in font %s. " "Continue ignoring instructions of the font.\n", buf); base_font->data.warning_bad_instruction = true; } }
/* Fill the page with the current color. */ int gs_fillpage(gs_state * pgs) { gx_device *dev = gs_currentdevice(pgs); int code; /* If we get here without a valid get_color_mapping_procs, fail */ if (dev_proc(dev, get_color_mapping_procs) == NULL || dev_proc(dev, get_color_mapping_procs) == gx_error_get_color_mapping_procs) { emprintf1(dev->memory, "\n *** Error: No get_color_mapping_procs for device: %s\n", dev->dname); return_error(gs_error_Fatal); } /* Processing a fill object operation */ gs_set_object_tag((gs_imager_state*) pgs, GS_PATH_TAG); gx_set_dev_color(pgs); code = (*dev_proc(dev, fillpage))(dev, (gs_imager_state *)pgs, gs_currentdevicecolor_inline(pgs)); if (code < 0) return code; return (*dev_proc(dev, sync_output)) (dev); }
FILE * gp_open_scratch_file (const gs_memory_t *mem, const char *prefix, char fname[gp_file_name_sizeof], const char *mode) { char thefname[256]; Str255 thepfname; OSErr myErr; short foundVRefNum; long foundDirID; FSSpec fSpec; FILE *f; int prefix_length = strlen(prefix); if (prefix_length > gp_file_name_sizeof) return NULL; strcpy (fname, (char *) prefix); { char newName[50]; tmpnam (newName); if ( prefix_length + strlen(newName) > gp_file_name_sizeof ) return NULL; strcat (fname, newName); } if ( strlen(fname) > 255 ) return NULL; if ( strrchr(fname,':') == NULL ) { memmove((char*)&thepfname[1],(char *)&fname[0],strlen(fname)); thepfname[0]=strlen(fname); myErr = FindFolder(kOnSystemDisk,kTemporaryFolderType,kCreateFolder, &foundVRefNum, &foundDirID); if ( myErr != noErr ) { emprintf(mem, "Can't find temp folder.\n"); return (NULL); } FSMakeFSSpec(foundVRefNum, foundDirID,thepfname, &fSpec); convertSpecToPath(&fSpec, thefname, sizeof(thefname) - 1); sprintf(fname,"%s",thefname); } else { sprintf((char*)&thefname[0],"%s\0",fname); memmove((char*)&thepfname[1],(char *)&thefname[0],strlen(thefname)); thepfname[0]=strlen(thefname); } f = gp_fopen (thefname, mode); if (f == NULL) emprintf1(mem, "**** Could not open temporary file %s\n", fname); return f; }
static int clist_fopen(char fname[gp_file_name_sizeof], const char *fmode, clist_file_ptr * pcf, gs_memory_t * mem, gs_memory_t *data_mem, bool ok_to_compress) { if (*fname == 0) { if (fmode[0] == 'r') return_error(gs_error_invalidfileaccess); *pcf = (clist_file_ptr)gp_open_scratch_file_64(mem, gp_scratch_file_name_prefix, fname, fmode); } else *pcf = gp_fopen(fname, fmode); if (*pcf == NULL) { emprintf1(mem, "Could not open the scratch file %s.\n", fname); return_error(gs_error_invalidfileaccess); } return 0; }
xps_item_t * xps_parse_xml(xps_context_t *ctx, byte *buf, int len) { xps_parser_t parser; XML_Parser xp; int code; parser.ctx = ctx; parser.root = NULL; parser.head = NULL; parser.error = NULL; xp = XML_ParserCreateNS(NULL, ' '); if (!xp) { gs_throw(-1, "xml error: could not create expat parser"); return NULL; } XML_SetUserData(xp, &parser); XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER); XML_SetStartElementHandler(xp, (XML_StartElementHandler)on_open_tag); XML_SetEndElementHandler(xp, (XML_EndElementHandler)on_close_tag); XML_SetCharacterDataHandler(xp, (XML_CharacterDataHandler)on_text); code = XML_Parse(xp, (char*)buf, len, 1); if (code == 0 || parser.error != NULL) { if (parser.root) xps_free_item(ctx, parser.root); if (XML_ErrorString(XML_GetErrorCode(xp)) != 0) emprintf1(parser.ctx->memory, "XML_Error: %s\n", XML_ErrorString(XML_GetErrorCode(xp))); XML_ParserFree(xp); gs_throw1(-1, "parser error: %s", parser.error); return NULL; } XML_ParserFree(xp); return parser.root; }
/* Fill the page with the current color. */ int gs_fillpage(gs_state * pgs) { gx_device *dev = gs_currentdevice(pgs); int code; gx_cm_color_map_procs * pprocs; pprocs = get_color_mapping_procs_subclass(dev); /* If we get here without a valid get_color_mapping_procs, fail */ if (pprocs == NULL || /* Deliberately use the terminal device here */ dev_proc(dev, get_color_mapping_procs) == gx_error_get_color_mapping_procs) { emprintf1(dev->memory, "\n *** Error: No get_color_mapping_procs for device: %s\n", dev->dname); return_error(gs_error_Fatal); } /* Processing a fill object operation */ dev_proc(pgs->device, set_graphics_type_tag)(pgs->device, GS_PATH_TAG); code = gx_set_dev_color(pgs); if (code != 0) return code; code = (*dev_proc(dev, fillpage))(dev, (gs_imager_state *)pgs, gs_currentdevicecolor_inline(pgs)); if (code < 0) return code; /* If GrayDetection is set, make sure monitoring is enabled. */ if (dev->icc_struct != NULL && dev->icc_struct->graydetection && !dev->icc_struct->pageneutralcolor) { dev->icc_struct->pageneutralcolor = true; /* start detecting again */ code = gsicc_mcm_begin_monitor(pgs->icc_link_cache, dev); } if (code < 0) return code; return (*dev_proc(dev, sync_output)) (dev); }
static int clist_fopen(char fname[gp_file_name_sizeof], const char *fmode, clist_file_ptr * pcf, gs_memory_t * mem, gs_memory_t *data_mem, bool ok_to_compress) { if (*fname == 0) { if (fmode[0] == 'r') return_error(gs_error_invalidfileaccess); if (gp_can_share_fdesc()) { *pcf = (clist_file_ptr)wrap_file(mem, gp_open_scratch_file_rm(mem, gp_scratch_file_name_prefix, fname, fmode), fmode); /* If the platform supports FILE duplication then we overwrite the * file name with an encoded form of the FILE pointer */ file_to_fake_path(*pcf, fname); } else { *pcf = (clist_file_ptr)wrap_file(mem, gp_open_scratch_file_64(mem, gp_scratch_file_name_prefix, fname, fmode), fmode); } } else { // Check if a special path is passed in. If so, clone the FILE handle clist_file_ptr ocf = fake_path_to_file(fname); if (ocf) { *pcf = wrap_file(mem, gp_fdup(((IFILE *)ocf)->f, fmode), fmode); } else { *pcf = wrap_file(mem, gp_fopen(fname, fmode), fmode); } } if (*pcf == NULL) { emprintf1(mem, "Could not open the scratch file %s.\n", fname); return_error(gs_error_invalidfileaccess); } return 0; }
static int test10(gs_state * pgs, gs_memory_t * mem) { gs_c_param_list list; gs_param_string nstr, OFstr; gs_param_float_array PSa; gs_param_float_array HWRa; gs_param_int_array HWSa; int HWSize[2]; float HWResolution[2], PageSize[2]; long MaxBitmap; int code; gx_device *dev = gs_currentdevice(pgs); float xlate_x, xlate_y; gs_rect cliprect; gs_c_param_list_write(&list, mem); code = gs_getdeviceparams(dev, (gs_param_list *) & list); if (code < 0) { lprintf1("getdeviceparams failed! code = %d\n", code); gs_abort(mem); } gs_c_param_list_read(&list); code = param_read_string((gs_param_list *) & list, "Name", &nstr); if (code < 0) { lprintf1("reading Name failed! code = %d\n", code); gs_abort(mem); } code = param_read_int_array((gs_param_list *) & list, "HWSize", &HWSa); if (code < 0) { lprintf1("reading HWSize failed! code = %d\n", code); gs_abort(mem); } emprintf3(mem, "HWSize[%d] = [ %d, %d ]\n", HWSa.size, HWSa.data[0], HWSa.data[1]); code = param_read_float_array((gs_param_list *) & list, "HWResolution", &HWRa); if (code < 0) { lprintf1("reading Resolution failed! code = %d\n", code); gs_abort(mem); } emprintf3(mem, "HWResolution[%d] = [ %f, %f ]\n", HWRa.size, HWRa.data[0], HWRa.data[1]); code = param_read_float_array((gs_param_list *) & list, "PageSize", &PSa); if (code < 0) { lprintf1("reading PageSize failed! code = %d\n", code); gs_abort(mem); } emprintf3(mem, "PageSize[%d] = [ %f, %f ]\n", PSa.size, PSa.data[0], PSa.data[1]); code = param_read_long((gs_param_list *) & list, "MaxBitmap", &MaxBitmap); if (code < 0) { lprintf1("reading MaxBitmap failed! code = %d\n", code); gs_abort(mem); } emprintf1(mem, "MaxBitmap = %ld\n", MaxBitmap); /* Switch to param list functions to "write" */ gs_c_param_list_write(&list, mem); /* Always set the PageSize. */ PageSize[0] = 72.0 * ypage_wid; PageSize[1] = 72.0 * xpage_len; PSa.data = PageSize; code = param_write_float_array((gs_param_list *) & list, "PageSize", &PSa); if (nstr.data[0] != 'v') { /* Set the OutputFile string file name */ OFstr.persistent = false; OFstr.data = outfile; OFstr.size = strlen(outfile); code = param_write_string((gs_param_list *) & list, "OutputFile", &OFstr); if (code < 0) { lprintf1("setting OutputFile name failed, code=%d\n", code); gs_abort(mem); } if (nstr.data[0] == 'x') { HWResolution[0] = HWResolution[1] = 72.0; } else { HWResolution[0] = HWResolution[1] = 360.0; } HWRa.data = HWResolution; HWSize[0] = (int)(HWResolution[0] * ypage_wid); HWSize[1] = (int)(HWResolution[1] * xpage_len); emprintf3(mem, "\tHWSize = [%d,%d], HWResolution = %f dpi\n", HWSize[0], HWSize[1], HWResolution[0]); HWSa.data = HWSize; code = param_write_float_array((gs_param_list *) & list, "HWResolution", &HWRa); code = param_write_int_array((gs_param_list *) & list, "HWSize", &HWSa); MaxBitmap = 1000000L; code = param_write_long((gs_param_list *) & list, "MaxBitmap", &MaxBitmap); } gs_c_param_list_read(&list); code = gs_putdeviceparams(dev, (gs_param_list *) & list); emprintf1(mem, "putdeviceparams: code=%d\n", code); gs_c_param_list_release(&list); /* note: initgraphics no longer resets the color or color space */ gs_erasepage(pgs); gs_initgraphics(pgs); { gs_color_space *cs = gs_cspace_new_DeviceGray(mem); gs_setcolorspace(pgs, cs); gs_setcolorspace(pgs, cs); gs_decrement(cs, "test10 DeviceGray"); } gs_clippath(pgs); gs_pathbbox(pgs, &cliprect); emprintf4(mem, "\tcliprect = [[%g,%g],[%g,%g]]\n", cliprect.p.x, cliprect.p.y, cliprect.q.x, cliprect.q.y); gs_newpath(pgs); switch (((rotate_value + 270) / 90) & 3) { default: case 0: /* 0 = 360 degrees in PS == 90 degrees in printer */ xlate_x = cliprect.p.x; xlate_y = cliprect.p.y; break; case 1: /* 90 degrees in PS = 180 degrees printer */ xlate_x = cliprect.q.x; xlate_y = cliprect.p.y; break; case 2: /* 180 degrees in PS == 270 degrees in printer */ xlate_x = cliprect.q.x; xlate_y = cliprect.q.y; break; case 3: /* 270 degrees in PS == 0 degrees in printer */ xlate_x = cliprect.p.x; xlate_y = cliprect.q.y; break; } emprintf2(mem, "translate origin to [ %f, %f ]\n", xlate_x, xlate_y); gs_translate(pgs, xlate_x, xlate_y); /* further move (before rotate) by user requested amount */ gs_translate(pgs, 72.0 * (float)xmove_origin, 72.0 * (float)ymove_origin); gs_rotate(pgs, (float)rotate_value + 270.0); gs_scale(pgs, scale_x * 72.0 / 2032.0, scale_y * 72.0 / 2032.0); gs_setlinecap(pgs, gs_cap_butt); gs_setlinejoin(pgs, gs_join_bevel); gs_setfilladjust(pgs, 0.0, 0.0); capture_exec(pgs); return 0; }
/* Exported for use by background printing. */ gx_device * setup_device_and_mem_for_thread(gs_memory_t *chunk_base_mem, gx_device *dev, bool bg_print, gsicc_link_cache_t **cachep) { int i, code; char fmode[4]; gs_memory_t *thread_mem; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device *ndev; gx_device_clist *ncldev; gx_device_clist_common *ncdev; gx_device_printer *npdev; gx_device *protodev; gs_c_param_list paramlist; gs_devn_params *pclist_devn_params; /* Every thread will have a 'chunk allocator' to reduce the interaction * with the 'base' allocator which has 'mutex' (locking) protection. * This improves performance of the threads. */ if ((code = gs_memory_chunk_wrap(&(thread_mem), chunk_base_mem )) < 0) { emprintf1(dev->memory, "chunk_wrap returned error code: %d\n", code); return NULL; } /* Find the prototype for this device (needed so we can copy from it) */ for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++) if (strcmp(protodev->dname, dev->dname) == 0) break; /* Clone the device from the prototype device */ if (protodev == NULL || (code = gs_copydevice((gx_device **) &ndev, protodev, thread_mem)) < 0) { gs_memory_chunk_release(thread_mem); return NULL; } ncldev = (gx_device_clist *)ndev; ncdev = (gx_device_clist_common *)ndev; npdev = (gx_device_printer *)ndev; gx_device_fill_in_procs(ndev); ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory = ncdev->bandlist_memory = thread_mem; ndev->PageCount = dev->PageCount; /* copy to prevent mismatch error */ npdev->file = pdev->file; /* For background printing when doing N copies with %d */ strcpy((npdev->fname), (pdev->fname)); ndev->color_info = dev->color_info; /* copy before putdeviceparams */ ndev->pad = dev->pad; ndev->log2_align_mod = dev->log2_align_mod; ndev->is_planar = dev->is_planar; #if CMM_THREAD_SAFE ndev->icc_struct = dev->icc_struct; /* Set before put params */ rc_increment(ndev->icc_struct); #endif /* get the current device parameters to put into the cloned device */ gs_c_param_list_write(¶mlist, dev->memory); if ((code = gs_getdeviceparams(dev, (gs_param_list *)¶mlist)) < 0) { emprintf1(dev->memory, "Error getting device params, code=%d. Rendering threads not started.\n", code); goto out_cleanup; } gs_c_param_list_read(¶mlist); if ((code = gs_putdeviceparams(ndev, (gs_param_list *)¶mlist)) < 0) goto out_cleanup; gs_c_param_list_release(¶mlist); /* In the case of a separation device, we need to make sure we get the devn params copied over */ pclist_devn_params = dev_proc(dev, ret_devn_params)(dev); if (pclist_devn_params != NULL) { code = devn_copy_params(dev, ndev); if (code < 0) { #ifdef DEBUG /* suppress a warning on a release build */ gs_note_error(gs_error_VMerror); #endif goto out_cleanup; } } /* Also make sure supports_devn is set correctly */ ndev->icc_struct->supports_devn = cdev->icc_struct->supports_devn; ncdev->page_uses_transparency = cdev->page_uses_transparency; if_debug3m(gs_debug_flag_icc, cdev->memory, "[icc] MT clist device = 0x%p profile = 0x%p handle = 0x%p\n", ncdev, ncdev->icc_struct->device_profile[0], ncdev->icc_struct->device_profile[0]->profile_handle); /* If the device is_planar, then set the flag in the new_device and the procs */ if ((ncdev->is_planar = cdev->is_planar)) gdev_prn_set_procs_planar(ndev); /* gdev_prn_allocate_memory sets the clist for writing, creating new files. * We need to unlink those files and open the main thread's files, then * reset the clist state for reading/rendering */ if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0) goto out_cleanup; /* close and unlink the temp files just created */ ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, true); ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, true); ncdev->page_info.cfile = ncdev->page_info.bfile = NULL; /* open the main thread's files for this thread */ strcpy(fmode, "r"); /* read access for threads */ strncat(fmode, gp_fmode_binary_suffix, 1); if ((code=cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &ncdev->page_info.cfile, thread_mem, thread_mem, true)) < 0 || (code=cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &ncdev->page_info.bfile, thread_mem, thread_mem, false)) < 0) goto out_cleanup; strcpy((ncdev->page_info.cfname), (cdev->page_info.cfname)); strcpy((ncdev->page_info.bfname), (cdev->page_info.bfname)); clist_render_init(ncldev); /* Initialize clist device for reading */ ncdev->page_info.bfile_end_pos = cdev->page_info.bfile_end_pos; /* The threads are maintained until clist_finish_page. At which point, the threads are torn down, the master clist reader device is changed to writer, and the icc_table and the icc_cache_cl freed */ #if CMM_THREAD_SAFE /* safe to share the link cache */ ncdev->icc_cache_cl = cdev->icc_cache_cl; rc_increment(cdev->icc_cache_cl, "setup_render_thread"); #else /* each thread needs its own link cache */ if (cachep != NULL) { if (*cachep == NULL) { /* We don't have one cached that we can reuse, so make one. */ if ((*cachep = gsicc_cache_new(thread_mem->thread_safe_memory)) == NULL) goto out_cleanup; } rc_increment(*cachep); ncdev->icc_cache_cl = *cachep; } else if ((ncdev->icc_cache_cl = gsicc_cache_new(thread_mem)) == NULL) goto out_cleanup; #endif if (bg_print) { gx_device_clist_reader *ncrdev = (gx_device_clist_reader *)ncdev; if (cdev->icc_table != NULL) { /* This is a background printing thread, so it cannot share the icc_table */ /* since this probably was created with a GC'ed allocator and the bg_print */ /* thread can't deal with the relocation. Free the cdev->icc_table and get */ /* a new one from the clist. */ clist_free_icc_table(cdev->icc_table, cdev->memory); cdev->icc_table = NULL; if ((code = clist_read_icctable((gx_device_clist_reader *)ncdev)) < 0) goto out_cleanup; } /* Similarly for the color_usage_array, when the foreground device switches to */ /* writer mode, the foreground's array will be freed. */ if ((code = clist_read_color_usage_array(ncrdev)) < 0) goto out_cleanup; } else { /* Use the same profile table and color usage array in each thread */ ncdev->icc_table = cdev->icc_table; /* OK for multiple rendering threads */ ((gx_device_clist_reader *)ncdev)->color_usage_array = ((gx_device_clist_reader *)cdev)->color_usage_array; } /* Needed for case when the target has cielab profile and pdf14 device has a RGB profile stored in the profile list of the clist */ ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash; /* success */ return ndev; out_cleanup: /* Close the file handles, but don't delete (unlink) the files */ if (ncdev->page_info.bfile != NULL) ncdev->page_info.io_procs->fclose(ncdev->page_info.bfile, ncdev->page_info.bfname, false); if (ncdev->page_info.cfile != NULL) ncdev->page_info.io_procs->fclose(ncdev->page_info.cfile, ncdev->page_info.cfname, false); ncdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ if (ndev != NULL) { gdev_prn_free_memory(ndev); gs_free_object(thread_mem, ndev, "setup_device_and_mem_for_thread"); } gs_memory_chunk_release(thread_mem); return NULL; }
/* Set up and start the render threads */ static int clist_setup_render_threads(gx_device *dev, int y, gx_process_page_options_t *options) { gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; gs_memory_t *chunk_base_mem = mem->thread_safe_memory; gs_memory_status_t mem_status; int i, band; int code = 0; int band_count = cdev->nbands; int band_height = crdev->page_info.band_params.BandHeight; crdev->num_render_threads = pdev->num_render_threads_requested; if(gs_debug[':'] != 0) dmprintf1(mem, "%% %d rendering threads requested.\n", pdev->num_render_threads_requested); if (crdev->num_render_threads > band_count) crdev->num_render_threads = band_count; /* don't bother starting more threads than bands */ /* Allocate and initialize an array of thread control structures */ crdev->render_threads = (clist_render_thread_control_t *) gs_alloc_byte_array(mem, crdev->num_render_threads, sizeof(clist_render_thread_control_t), "clist_setup_render_threads" ); /* fallback to non-threaded if allocation fails */ if (crdev->render_threads == NULL) { emprintf(mem, " VMerror prevented threads from starting.\n"); return_error(gs_error_VMerror); } memset(crdev->render_threads, 0, crdev->num_render_threads * sizeof(clist_render_thread_control_t)); crdev->main_thread_data = cdev->data; /* save data area */ /* Based on the line number requested, decide the order of band rendering */ /* Almost all devices go in increasing line order (except the bmp* devices ) */ crdev->thread_lookahead_direction = (y < (cdev->height - 1)) ? 1 : -1; band = y / band_height; /* If the 'mem' is not thread safe, we need to wrap it in a locking memory */ gs_memory_status(chunk_base_mem, &mem_status); if (mem_status.is_thread_safe == false) { return_error(gs_error_VMerror); } /* If we don't have one large enough already, create an icc cache list */ if (crdev->num_render_threads > crdev->icc_cache_list_len) { void *old = crdev->icc_cache_list; crdev->icc_cache_list = gs_alloc_byte_array(mem->thread_safe_memory, crdev->num_render_threads, sizeof(void*), "clist_render_setup_threads"); if (crdev->icc_cache_list == NULL) { crdev->icc_cache_list = NULL; return_error(gs_error_VMerror); } if (crdev->icc_cache_list_len > 0) memcpy(crdev->icc_cache_list, old, sizeof(void *)*crdev->icc_cache_list_len); memset(&crdev->icc_cache_list[crdev->icc_cache_list_len], 0, (crdev->num_render_threads - crdev->icc_cache_list_len) * sizeof(void *)); crdev->icc_cache_list_len = crdev->num_render_threads; gs_free_object(mem, old, "clist_render_setup_threads"); } /* Loop creating the devices and semaphores for each thread, then start them */ for (i=0; (i < crdev->num_render_threads) && (band >= 0) && (band < band_count); i++, band += crdev->thread_lookahead_direction) { gx_device *ndev; clist_render_thread_control_t *thread = &(crdev->render_threads[i]); ndev = setup_device_and_mem_for_thread(chunk_base_mem, dev, false, &crdev->icc_cache_list[i]); if (ndev == NULL) { code = gs_error_VMerror; /* set code to an error for cleanup after the loop */ break; } thread->cdev = ndev; thread->memory = ndev->memory; thread->band = -1; /* a value that won't match any valid band */ thread->options = options; thread->buffer = NULL; if (options && options->init_buffer_fn) { code = options->init_buffer_fn(options->arg, dev, thread->memory, dev->width, band_height, &thread->buffer); if (code < 0) break; } /* create the buf device for this thread, and allocate the semaphores */ if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &(thread->bdev), ndev, band*crdev->page_band_height, NULL, thread->memory, &(crdev->color_usage_array[0])) < 0)) break; if ((thread->sema_this = gx_semaphore_alloc(thread->memory)) == NULL || (thread->sema_group = gx_semaphore_alloc(thread->memory)) == NULL) { code = gs_error_VMerror; break; } /* Start thread 'i' to do band */ if ((code = clist_start_render_thread(dev, i, band)) < 0) break; } /* If the code < 0, the last thread creation failed -- clean it up */ if (code < 0) { band -= crdev->thread_lookahead_direction; /* update for 'next_band' usage */ /* the following relies on 'free' ignoring NULL pointers */ gx_semaphore_free(crdev->render_threads[i].sema_group); gx_semaphore_free(crdev->render_threads[i].sema_this); if (crdev->render_threads[i].bdev != NULL) cdev->buf_procs.destroy_buf_device(crdev->render_threads[i].bdev); if (crdev->render_threads[i].cdev != NULL) { gx_device_clist_common *thread_cdev = (gx_device_clist_common *)crdev->render_threads[i].cdev; /* Close the file handles, but don't delete (unlink) the files */ thread_cdev->page_info.io_procs->fclose(thread_cdev->page_info.bfile, thread_cdev->page_info.bfname, false); thread_cdev->page_info.io_procs->fclose(thread_cdev->page_info.cfile, thread_cdev->page_info.cfname, false); thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ gdev_prn_free_memory((gx_device *)thread_cdev); gs_free_object(crdev->render_threads[i].memory, thread_cdev, "clist_setup_render_threads"); } if (crdev->render_threads[i].buffer != NULL && options && options->free_buffer_fn != NULL) { options->free_buffer_fn(options->arg, dev, crdev->render_threads[i].memory, crdev->render_threads[i].buffer); crdev->render_threads[i].buffer = NULL; } if (crdev->render_threads[i].memory != NULL) { gs_memory_chunk_release(crdev->render_threads[i].memory); crdev->render_threads[i].memory = NULL; } } /* If we weren't able to create at least one thread, punt */ /* Although a single thread isn't any more efficient, the */ /* machinery still works, so that's OK. */ if (i == 0) { if (crdev->render_threads[0].memory != NULL) { gs_memory_chunk_release(crdev->render_threads[0].memory); /* free up the locking wrapper if we allocated one */ if (chunk_base_mem != mem) { gs_memory_locked_release((gs_memory_locked_t *)chunk_base_mem); gs_free_object(mem, chunk_base_mem, "clist_setup_render_threads(locked allocator)"); } } gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; /* restore the file pointers */ if (cdev->page_info.cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_info.cfname, fmode, &cdev->page_info.cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_info.cfile, 0, SEEK_SET, cdev->page_info.cfname); cdev->page_info.io_procs->fopen(cdev->page_info.bfname, fmode, &cdev->page_info.bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_info.bfile, 0, SEEK_SET, cdev->page_info.bfname); } emprintf1(mem, "Rendering threads not started, code=%d.\n", code); return_error(code); } crdev->num_render_threads = i; crdev->curr_render_thread = 0; crdev->next_band = band; if(gs_debug[':'] != 0) dmprintf1(mem, "%% Using %d rendering threads\n", i); return 0; }
/* Set up and start the render threads */ static int clist_setup_render_threads(gx_device *dev, int y) { gx_device_printer *pdev = (gx_device_printer *)dev; gx_device_clist *cldev = (gx_device_clist *)dev; gx_device_clist_common *cdev = (gx_device_clist_common *)cldev; gx_device_clist_reader *crdev = &cldev->reader; gs_memory_t *mem = cdev->bandlist_memory; gs_memory_t *chunk_base_mem = mem->thread_safe_memory; gs_memory_status_t mem_status; gx_device *protodev; gs_c_param_list paramlist; int i, code, band; int band_count = cdev->nbands; char fmode[4]; gs_devn_params *pclist_devn_params; crdev->num_render_threads = pdev->num_render_threads_requested; if(gs_debug[':'] != 0) dprintf1("%% %d rendering threads requested.\n", pdev->num_render_threads_requested); if (crdev->num_render_threads > band_count) crdev->num_render_threads = band_count; /* don't bother starting more threads than bands */ /* Allocate and initialize an array of thread control structures */ crdev->render_threads = (clist_render_thread_control_t *) gs_alloc_byte_array(mem, crdev->num_render_threads, sizeof(clist_render_thread_control_t), "clist_setup_render_threads" ); /* fallback to non-threaded if allocation fails */ if (crdev->render_threads == NULL) { emprintf(mem, " VMerror prevented threads from starting.\n"); return_error(gs_error_VMerror); } memset(crdev->render_threads, 0, crdev->num_render_threads * sizeof(clist_render_thread_control_t)); crdev->main_thread_data = cdev->data; /* save data area */ /* Based on the line number requested, decide the order of band rendering */ /* Almost all devices go in increasing line order (except the bmp* devices ) */ crdev->thread_lookahead_direction = (y < (cdev->height - 1)) ? 1 : -1; band = y / crdev->page_info.band_params.BandHeight; /* Close the files so we can open them in multiple threads */ if ((code = cdev->page_info.io_procs->fclose(cdev->page_cfile, cdev->page_cfname, false)) < 0 || (code = cdev->page_info.io_procs->fclose(cdev->page_bfile, cdev->page_bfname, false)) < 0) { gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; emprintf(mem, "Closing clist files prevented threads from starting.\n"); return_error(gs_error_unknownerror); /* shouldn't happen */ } cdev->page_cfile = cdev->page_bfile = NULL; strcpy(fmode, "r"); /* read access for threads */ strncat(fmode, gp_fmode_binary_suffix, 1); /* Find the prototype for this device (needed so we can copy from it) */ for (i=0; (protodev = (gx_device *)gs_getdevice(i)) != NULL; i++) if (strcmp(protodev->dname, dev->dname) == 0) break; if (protodev == NULL) { emprintf(mem, "Could not find prototype device. Rendering threads not started.\n"); return gs_error_rangecheck; } gs_c_param_list_write(¶mlist, mem); if ((code = gs_getdeviceparams(dev, (gs_param_list *)¶mlist)) < 0) { emprintf1(mem, "Error getting device params, code=%d. Rendering threads not started.\n", code); return code; } /* If the 'mem' is not thread safe, we need to wrap it in a locking memory */ gs_memory_status(chunk_base_mem, &mem_status); if (mem_status.is_thread_safe == false) { return_error(gs_error_VMerror); } /* Loop creating the devices and semaphores for each thread, then start them */ for (i=0; (i < crdev->num_render_threads) && (band >= 0) && (band < band_count); i++, band += crdev->thread_lookahead_direction) { gx_device *ndev; gx_device_clist *ncldev; gx_device_clist_common *ncdev; clist_render_thread_control_t *thread = &(crdev->render_threads[i]); /* Every thread will have a 'chunk allocator' to reduce the interaction * with the 'base' allocator which has 'mutex' (locking) protection. * This improves performance of the threads. */ if ((code = gs_memory_chunk_wrap(&(thread->memory), chunk_base_mem )) < 0) { emprintf1(mem, "chunk_wrap returned error code: %d\n", code); break; } thread->band = -1; /* a value that won't match any valid band */ if ((code = gs_copydevice((gx_device **) &ndev, protodev, thread->memory)) < 0) { code = 0; /* even though we failed, no cleanup needed */ break; } ncldev = (gx_device_clist *)ndev; ncdev = (gx_device_clist_common *)ndev; gx_device_fill_in_procs(ndev); ((gx_device_printer *)ncdev)->buffer_memory = ncdev->memory = ncdev->bandlist_memory = thread->memory; gs_c_param_list_read(¶mlist); ndev->PageCount = dev->PageCount; /* copy to prevent mismatch error */ #if CMM_THREAD_SAFE ndev->icc_struct = dev->icc_struct; /* Set before put params */ rc_increment(ndev->icc_struct); #endif if ((code = gs_putdeviceparams(ndev, (gs_param_list *)¶mlist)) < 0) break; /* In the case of a separation device, we need to make sure we get the devn params copied over */ pclist_devn_params = dev_proc(dev, ret_devn_params)(dev); if (pclist_devn_params != NULL) { code = devn_copy_params(dev, (gx_device*) ncdev); if (code < 0) return_error(gs_error_VMerror); } ncdev->page_uses_transparency = cdev->page_uses_transparency; if_debug3('{',"[{]MT clist device = 0x%x profile = 0x%x handle = 0x%x\n", ncdev, ncdev->icc_struct->device_profile[0], ncdev->icc_struct->device_profile[0]->profile_handle); /* gdev_prn_allocate_memory sets the clist for writing, creating new files. * We need to unlink those files and open the main thread's files, then * reset the clist state for reading/rendering */ if ((code = gdev_prn_allocate_memory(ndev, NULL, ndev->width, ndev->height)) < 0) break; /* Needed for case when the target has cielab profile and pdf14 device has a RGB profile stored in the profile list of the clist */ ncdev->trans_dev_icc_hash = cdev->trans_dev_icc_hash; thread->cdev = ndev; /* close and unlink the temp files just created */ cdev->page_info.io_procs->fclose(ncdev->page_cfile, ncdev->page_cfname, true); cdev->page_info.io_procs->fclose(ncdev->page_bfile, ncdev->page_bfname, true); /* open the main thread's files for this thread */ if ((code=cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &ncdev->page_cfile, thread->memory, thread->memory, true)) < 0 || (code=cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &ncdev->page_bfile, thread->memory, thread->memory, false)) < 0) break; clist_render_init(ncldev); /* Initialize clist device for reading */ ncdev->page_bfile_end_pos = cdev->page_bfile_end_pos; /* Use the same link cache in each thread and the same profile table. The threads are maintained until clist_finish_page. At which point, the threads are torn down and the master clist reader device is destroyed along with the icc_table and the icc_cache_cl */ #if CMM_THREAD_SAFE ncdev->icc_cache_cl = cdev->icc_cache_cl; #else ncdev->icc_cache_cl = gsicc_cache_new(crdev->memory); #endif ncdev->icc_table = cdev->icc_table; /* create the buf device for this thread, and allocate the semaphores */ if ((code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &(thread->bdev), cdev->target, band*crdev->page_band_height, NULL, thread->memory, clist_get_band_complexity(dev,y)) < 0)) break; if ((thread->sema_this = gx_semaphore_alloc(thread->memory)) == NULL || (thread->sema_group = gx_semaphore_alloc(thread->memory)) == NULL) { code = gs_error_VMerror; break; } /* Start thread 'i' to do band */ if ((code = clist_start_render_thread(dev, i, band)) < 0) break; } gs_c_param_list_release(¶mlist); /* If the code < 0, the last thread creation failed -- clean it up */ if (code < 0) { /* the following relies on 'free' ignoring NULL pointers */ gx_semaphore_free(crdev->render_threads[i].sema_group); gx_semaphore_free(crdev->render_threads[i].sema_this); if (crdev->render_threads[i].bdev != NULL) cdev->buf_procs.destroy_buf_device(crdev->render_threads[i].bdev); if (crdev->render_threads[i].cdev != NULL) { gx_device_clist_common *thread_cdev = (gx_device_clist_common *)crdev->render_threads[i].cdev; /* Close the file handles, but don't delete (unlink) the files */ thread_cdev->page_info.io_procs->fclose(thread_cdev->page_bfile, thread_cdev->page_bfname, false); thread_cdev->page_info.io_procs->fclose(thread_cdev->page_cfile, thread_cdev->page_cfname, false); thread_cdev->do_not_open_or_close_bandfiles = true; /* we already closed the files */ gdev_prn_free_memory((gx_device *)thread_cdev); gs_free_object(crdev->render_threads[i].memory, thread_cdev, "clist_setup_render_threads"); } if (crdev->render_threads[i].memory != NULL) gs_memory_chunk_release(crdev->render_threads[i].memory); } /* If we weren't able to create at least one thread, punt */ /* Although a single thread isn't any more efficient, the */ /* machinery still works, so that's OK. */ if (i == 0) { if (crdev->render_threads[0].memory != NULL) { gs_memory_chunk_release(crdev->render_threads[0].memory); /* free up the locking wrapper if we allocated one */ if (chunk_base_mem != mem) { gs_memory_locked_release((gs_memory_locked_t *)chunk_base_mem); gs_free_object(mem, chunk_base_mem, "clist_setup_render_threads(locked allocator)"); } } gs_free_object(mem, crdev->render_threads, "clist_setup_render_threads"); crdev->render_threads = NULL; /* restore the file pointers */ if (cdev->page_cfile == NULL) { char fmode[4]; strcpy(fmode, "a+"); /* file already exists and we want to re-use it */ strncat(fmode, gp_fmode_binary_suffix, 1); cdev->page_info.io_procs->fopen(cdev->page_cfname, fmode, &cdev->page_cfile, mem, cdev->bandlist_memory, true); cdev->page_info.io_procs->fseek(cdev->page_cfile, 0, SEEK_SET, cdev->page_cfname); cdev->page_info.io_procs->fopen(cdev->page_bfname, fmode, &cdev->page_bfile, mem, cdev->bandlist_memory, false); cdev->page_info.io_procs->fseek(cdev->page_bfile, 0, SEEK_SET, cdev->page_bfname); } emprintf1(mem, "Rendering threads not started, code=%d.\n", code); return_error(code); } crdev->num_render_threads = i; crdev->curr_render_thread = 0; if(gs_debug[':'] != 0) dprintf1("%% Using %d rendering threads\n", i); return 0; }
/* Write the actual file name at fname. */ static FILE * gp_open_scratch_file_generic(const gs_memory_t *mem, const char *prefix, char fname[gp_file_name_sizeof], const char *mode, bool b64) { /* The -8 is for XXXXXX plus a possible final / and -. */ int prefix_length = strlen(prefix); int len = gp_file_name_sizeof - prefix_length - 8; FILE *fp; if (gp_file_name_is_absolute(prefix, prefix_length)) *fname = 0; else if (gp_gettmpdir(fname, &len) != 0) strcpy(fname, "/tmp/"); else { if (strlen(fname) != 0 && fname[strlen(fname) - 1] != '/') strcat(fname, "/"); } if (strlen(fname) + prefix_length + 8 >= gp_file_name_sizeof) return 0; /* file name too long */ strcat(fname, prefix); /* Prevent trailing X's in path from being converted by mktemp. */ if (*fname != 0 && fname[strlen(fname) - 1] == 'X') strcat(fname, "-"); strcat(fname, "XXXXXX"); #ifdef HAVE_MKSTEMP { int file; char ofname[gp_file_name_sizeof]; /* save the old filename template in case mkstemp fails */ memcpy(ofname, fname, gp_file_name_sizeof); #ifdef HAVE_MKSTEMP64 if (b64) file = mkstemp64(fname); else file = mkstemp(fname); #else file = mkstemp(fname); #endif if (file < -1) { emprintf1(mem, "**** Could not open temporary file %s\n", ofname); return NULL; } #if defined(O_LARGEFILE) && defined(__hpux) if (b64) fcntl(file, F_SETFD, fcntl(file, F_GETFD) | O_LARGEFILE); #else /* Fixme : what to do with b64 and 32-bit mkstemp? Unimplemented. */ #endif fp = fdopen(file, mode); if (fp == NULL) close(file); } #else mktemp(fname); fp = (b64 ? gp_fopentemp : gp_fopentemp_64)(fname, mode); #endif if (fp == NULL) emprintf1(mem, "**** Could not open temporary file %s\n", fname); return fp; }
/* * Caller should make sure that this device supports saved_pages: * dev_proc(dev, dev_spec_op)(dev, gxdso_supports_saved_pages, NULL, 0) == 1 * * Returns < 0 if error, 1 if erasepage needed, 0 if no action needed. */ int gx_saved_pages_param_process(gx_device_printer *pdev, byte *param, int param_size) { byte *param_scan = param; int param_left = param_size; byte *token; int token_size, code, printed_count, collated_copies = 1; int tmp_num; /* during token scanning loop */ int erasepage_needed = 0; while (pdev->child) pdev = (gx_device_printer *)pdev->child; while ((token = param_parse_token(param_scan, param_left, &token_size)) != NULL) { switch (param_find_key(token, token_size)) { case PARAM_BEGIN: if (pdev->saved_pages_list == NULL) { if ((pdev->saved_pages_list = gx_saved_pages_list_new(pdev)) == NULL) return_error(gs_error_VMerror); /* We need to change to clist mode. Always uses clist when saving pages */ pdev->saved_pages_list->save_banding_type = pdev->space_params.banding_type; pdev->space_params.banding_type = BandingAlways; if ((code = gdev_prn_reallocate_memory((gx_device *)pdev, &pdev->space_params, pdev->width, pdev->height)) < 0) return code; erasepage_needed |= 1; } break; case PARAM_END: if (pdev->saved_pages_list != NULL) { /* restore to what was set before the "begin" */ pdev->space_params.banding_type = pdev->saved_pages_list->save_banding_type; gx_saved_pages_list_free(pdev->saved_pages_list); pdev->saved_pages_list = NULL; /* We may need to change from clist mode since we forced clist when saving pages */ code = gdev_prn_reallocate_memory((gx_device *)pdev, &pdev->space_params, pdev->width, pdev->height); if (code < 0) return code; erasepage_needed |= 1; /* make sure next page is erased */ } break; case PARAM_FLUSH: if (pdev->saved_pages_list != NULL) { /* Save the collated copy count so the list we return will have it */ collated_copies = pdev->saved_pages_list->collated_copies; gx_saved_pages_list_free(pdev->saved_pages_list); } /* Always return with an empty list, even if we weren't saving previously */ if ((pdev->saved_pages_list = gx_saved_pages_list_new(pdev)) == NULL) return_error(gs_error_VMerror); /* restore the original count */ pdev->saved_pages_list->collated_copies = collated_copies; break; case PARAM_COPIES: /* copies requires a number next */ /* make sure that we have a list */ if (pdev->saved_pages_list == NULL) { return_error(gs_error_rangecheck); /* copies not allowed before a 'begin' */ } /* Move to past 'copies' token */ param_left -= token - param_scan + token_size; param_scan = token + token_size; if ((token = param_parse_token(param_scan, param_left, &token_size)) == NULL || param_find_key(token, token_size) != PARAM_NUMBER) { emprintf(pdev->memory, "gx_saved_pages_param_process: copies not followed by number.\n"); return_error(gs_error_typecheck); } if (sscanf((const char *)token, "%d", &tmp_num) != 1) { emprintf1(pdev->memory, "gx_saved_pages_list_print: Number format error '%s'\n", token); code = gs_error_typecheck; return code; } pdev->saved_pages_list->collated_copies = tmp_num; /* save it for our loop */ break; case PARAM_PRINT: /* Move to past 'print' token */ param_left -= token - param_scan + token_size; param_scan = token + token_size; code = param_left; /* in case list is NULL, skip rest of string */ if (pdev->saved_pages_list != NULL) { if ((code = gx_saved_pages_list_print(pdev, pdev->saved_pages_list, param_scan, param_left, &printed_count)) < 0) return code; erasepage_needed |= 1; /* make sure next page is erased */ } /* adjust for all of the print parameters */ token_size += code; break; /* We are expecting an action keyword, so other keywords and tokens */ /* are not valid here (mostly the 'print' parameters). */ default: { byte *bad_token = gs_alloc_string(pdev->memory, token_size+1, "saved_pages_param_process"); byte *param_string = gs_alloc_string(pdev->memory, param_size+1, "saved_pages_param_process"); if (bad_token != NULL && param_string != NULL) { memcpy(bad_token, token, token_size); bad_token[token_size] = 0; /* terminate string */ memcpy(param_string, param, param_size); param_string[param_size] = 0; /* terminate string */ emprintf2(pdev->memory, "*** Invalid saved-pages token '%s'\n *** in param string '%s'\n", bad_token, param_string); gs_free_string(pdev->memory, bad_token, token_size+1, "saved_pages_param_process"); gs_free_string(pdev->memory, param_string, param_size+1, "saved_pages_param_process"); } } } /* Move to next token */ param_left -= token - param_scan + token_size; param_scan = token + token_size; } return erasepage_needed; }
/* * Print selected pages from the list to on the selected device. The * saved_pages_list is NOT modified, allowing for reprint / recovery * print. Each saved_page is printed on a separate page (unlike the * gdev_prn_render_pages above which prints several saved_pages on * a single "master" page, performing imposition). * * This is primarily intended to allow printing in non-standard order * (reverse, odd, even) or producing collated copies for a job. * * On success return the number of bytes consumed or error code < 0. * The printed_count will contain the number of pages printed. * * ------------------------------------------------------------------- * * The param string may begin with whitespace. The string is parsed * and the selected pages are printed. There may be any number of ranges * and or keywords separated by whitespace and/or comma ','. * * NB: The pdev printer device's PageCount is updated to reflect the * total number of pages produced (per the spec for this parameter) * since we may be producing multiple collated copies. * Also the list PageCount is updated after printing since * there may be a subsequent 'print' action. * keywords: * copies # Set the number of copies for subsequent printing actions * "copies 1" resets to a single copy * normal All pages are printed in normal order * reverse All pages are printed in reverse order * The following two options may be useful for duplexing. * odd All odd pages are printed, e.g. 1, 3, 5, ... * even All even pages are printed, e.g. 2, 4, 6, ... * NB: an extra blank page will be printed if the list * has an odd number of pages. * even0pad All even pages, but no extra blank page if there are * an odd number of pages on the list. * range syntax: * range range multiple ranges are separated by commas ',' * and/or whitespace. * # a specific page number is a valid range * * inclusive ranges below can have whitespace before * or after the dash '-'. * #-# a range consisting of all pages from the first * page # up to (and including) the second page #. * #-* all pages from # up through the last page * "1-*" is equivalent to "normal" * *-# all pages from the last up through # page. * "reverse" is equivalent to "*-1" */ int gx_saved_pages_list_print(gx_device_printer *pdev, gx_saved_pages_list *list, byte *param, int param_size, int *printed_count) { byte *param_scan = NULL; int param_left; int start_page = 0; /* 0 means start_page unknown */ int end_page = 0; /* < 0 means waiting for the end of a # - # range */ /* i.e, a '-' was scanned. 0 means unknown */ int tmp_num; /* during token scanning loop */ int code = 0, endcode = 0; byte *token; int copy, token_size; gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev; /* the following are used so we can go back to the original state */ bool save_bg_print = false; /* arbitrary, silence warning */ bool save_bandfile_open_close = false; /* arbitrary, silence warning */ gx_saved_page saved_page; clist_file_ptr saved_files[2]; /* save the current (empty) page while we print */ if ((code = do_page_save(pdev, &saved_page, saved_files)) < 0) { emprintf(pdev->memory, "gx_saved_pages_list_print: Error getting device params\n"); goto out; } /* save_page leaves the clist in writer mode, so prepare for reading clist */ /* files. When we are done with printing, we'll go back to write mode. */ if ((code = clist_close_writer_and_init_reader((gx_device_clist *)pdev)) < 0) goto out; /* While printing, disable the saved_pages mode and bg_print */ pdev->saved_pages_list = NULL; save_bg_print = pdev->bg_print_requested; /* Inhibits modifying the clist at end of output_page */ save_bandfile_open_close = crdev->do_not_open_or_close_bandfiles; crdev->do_not_open_or_close_bandfiles = true; pdev->PageCount = list->PageCount; /* adjust to value last printed */ /* loop producing the number of copies */ /* Note: list->collated_copies may change if 'copies' param follows the 'print' */ for (copy = 1; copy <= list->collated_copies; copy++) { int page_skip = 0; bool do_blank_page_pad; int page; /* Set to start of 'print' params to do collated copies */ param_scan = param; param_left = param_size; while ((token = param_parse_token(param_scan, param_left, &token_size)) != NULL) { saved_pages_key_enum key; page = 0; do_blank_page_pad = false; /* only set for EVEN */ key = param_find_key(token, token_size); switch (key) { case PARAM_DASH: if (start_page == 0) { emprintf(pdev->memory, "gx_saved_pages_list_print: '-' unexpected\n"); code = gs_error_typecheck; goto out; } end_page = -1; /* the next number will end a range */ break; case PARAM_STAR: page = list->count; /* last page */ case PARAM_NUMBER: if (page == 0) { if (sscanf((const char *)token, "%d", &page) != 1) { emprintf1(pdev->memory, "gx_saved_pages_list_print: Number format error '%s'\n", token); code = gs_error_typecheck; goto out; } } if (start_page == 0) { start_page = page; /* first number seen */ } else { /* a second number was seen after the start_page was set */ if (end_page < 0) { end_page = page; /* end of a '# - #' range */ page_skip = (end_page >= start_page) ? 1 : -1; } else { /* 'page' was NOT part of a range after printing 'page' will start a new range */ end_page = start_page; /* single page */ page_skip = 1; } } break; case PARAM_COPIES: /* copies requires a number next */ /* Move to past 'copies' token */ param_left -= token - param_scan + token_size; param_scan = token + token_size; if ((token = param_parse_token(param_scan, param_left, &token_size)) == NULL || param_find_key(token, token_size) != PARAM_NUMBER) { emprintf(pdev->memory, "gx_saved_pages_list_print: copies not followed by number.\n"); code = gs_error_typecheck; goto out; } if (sscanf((const char *)token, "%d", &tmp_num) != 1) { emprintf1(pdev->memory, "gx_saved_pages_list_print: Number format error '%s'\n", token); code = gs_error_typecheck; goto out; } list->collated_copies = tmp_num; /* save it for our loop */ break; case PARAM_NORMAL: /* sets both start and end */ start_page = 1; end_page = list->count; page_skip = 1; break ; case PARAM_REVERSE: /* sets both start and end */ start_page = list->count; end_page = 1; page_skip = -1; break; case PARAM_EVEN: /* sets both start and end */ do_blank_page_pad = (list->count & 1) != 0; /* pad if odd */ case PARAM_EVEN0PAD: /* sets both start and end */ start_page = 2; end_page = list->count; page_skip = 2; break ; case PARAM_ODD: /* sets both start and end */ start_page = 1; end_page = list->count; page_skip = 2; break ; case PARAM_UNKNOWN: case PARAM_BEGIN: case PARAM_END: case PARAM_FLUSH: case PARAM_PRINT: token_size = 0; /* non-print range token seen */ } if (end_page > 0) { /* Here we have a range to print since start and end are known */ int curr_page = start_page; gx_saved_pages_list_element *curr_elem = NULL; /* get the start_page saved_page */ if (start_page <= list->count) { for (curr_elem = list->head; curr_elem->sequence_number != start_page; curr_elem = curr_elem->next) { if (curr_elem->next == NULL) { emprintf1(pdev->memory, "gx_saved_pages_list_print: page %d not found.\n", start_page); code = gs_error_rangecheck;; goto out; } } } for ( ; curr_elem != NULL; ) { /* print the saved page from the current curr_elem */ if ((code = gx_output_saved_page(pdev, curr_elem->page)) < 0) goto out; curr_page += page_skip; if (page_skip >= 0) { if (curr_page > end_page) break; curr_elem = curr_elem->next; if (page_skip > 1) curr_elem = curr_elem->next; } else { /* reverse print order */ if (curr_page < end_page) break; curr_elem = curr_elem->prev; /* Below is not needed since we never print reverse even/odd */ if (page_skip < -1) curr_elem = curr_elem->prev; } if (curr_elem == NULL) { emprintf1(pdev->memory, "gx_saved_pages_list_print: page %d not found.\n", curr_page); code = gs_error_rangecheck;; goto out; } } /* If we were printing EVEN, we may need to spit out a blank 'pad' page */ if (do_blank_page_pad) { /* print the empty page we had upon entry */ /* FIXME: Note that the page size may not match the last odd page */ if ((code = gx_output_saved_page(pdev, &saved_page)) < 0) goto out; } /* After printing, reset to handle next page range */ start_page = (start_page == end_page) ? page : 0; /* if single page, set start_page */ /* from the number scanned above */ end_page = 0; } if (token_size == 0) break; /* finished with print ranges */ /* Move to next token */ param_left -= token - param_scan + token_size; param_scan = token + token_size; } } out: /* restore the device parameters saved upon entry */ *printed_count = pdev->PageCount - list->PageCount; list->PageCount = pdev->PageCount; /* retain for subsequent print action */ pdev->saved_pages_list = list; pdev->bg_print_requested = save_bg_print; crdev->do_not_open_or_close_bandfiles = save_bandfile_open_close; /* load must be after we've set saved_pages_list which forces clist mode. */ do_page_load(pdev, &saved_page, saved_files); /* Finally, do the finish page which will reset the clist to empty and write mode */ endcode = clist_finish_page((gx_device *)pdev, true); return code < 0 ? code : endcode < 0 ? endcode : param_scan - param; }