int gs_band_donor_init(void **opaque, gs_memory_t *mem) { #ifdef DEBUG_PRINT emprintf(mem, "gs_band_donor_init\n"); #endif *opaque = NULL; return 0; }
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; }
void gx_san__release(gx_device_spot_analyzer **ppadev) { gx_device_spot_analyzer *padev = *ppadev; if (padev == NULL) { /* Can't use emprintf here! */ eprintf("Extra call to gx_san__release."); return; } if(--padev->lock < 0) { emprintf(padev->memory, "Wrong lock to gx_san__release."); return; } if (padev->lock == 0) { *ppadev = NULL; rc_decrement(padev, "gx_san__release"); } }
static int os2_printer_fopen(gx_io_device * iodev, const char *fname, const char *access, FILE ** pfile, char *rfname, uint rnamelen) { os2_printer_t *pr = (os2_printer_t *)iodev->state; char driver_name[256]; /* Make sure that printer exists. */ if (pm_find_queue(pr->memory, fname, driver_name)) { /* error, list valid queue names */ emprintf(pr->memory, "Invalid queue name. Use one of:\n"); pm_find_queue(pr->memory, NULL, NULL); return_error(gs_error_undefinedfilename); } strncpy(pr->queue, fname, sizeof(pr->queue)-1); /* Create a temporary file */ *pfile = gp_open_scratch_file(pr->memory, "gs", pr->filename, access); if (*pfile == NULL) return_error(gs_fopen_errno_to_code(errno)); return 0; }
/* 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; }
/* * 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; }