1 /* Copyright (C) 2001 1999 Aladdin Enterprises. All rights reserved. 2 3 This software is provided AS-IS with no warranty, either express or 4 implied. 5 6 This software is distributed under license and may not be copied, 7 modified or distributed except as expressly authorized under the terms 8 of the license contained in the file LICENSE in this distribution. 9 10 For more information about licensing, please refer to 11 http://www.ghostscript.com/licensing/. For information on 12 commercial licensing, go to http://www.artifex.com/licensing/ or 13 contact Artifex Software, Inc., 101 Lucas Valley Road #110, 14 San Rafael, CA 94903, U.S.A., +1(415)492-9861. 15 */ 16 17 /* $Id: gsicc.c,v 1.13 2004/04/08 07:59:19 igor Exp $ */ 18 /* Implementation of the ICCBased color space family */ 19 20 #include "math_.h" 21 #include "memory_.h" 22 #include "gx.h" 23 #include "gserrors.h" 24 #include "gsstruct.h" 25 #include "stream.h" 26 #include "gxcspace.h" /* for gxcie.c */ 27 #include "gxarith.h" 28 #include "gxcie.h" 29 #include "gzstate.h" 30 #include "stream.h" 31 #include "icc.h" /* must precede icc.h */ 32 #include "gsicc.h" 33 34 35 typedef struct _icmFileGs icmFileGs; 36 37 struct _icmFileGs { 38 ICM_FILE_BASE 39 40 /* Private: */ 41 stream *strp; 42 }; 43 44 /* Garbage collection code */ 45 46 /* 47 * Discard a gs_cie_icc_s structure. This requires that we call the 48 * destructor for ICC profile, lookup, and file objects (which are 49 * stored in "foreign" memory). 50 * 51 * No special action is taken with respect to the stream pointer; that is 52 * the responsibility of the client. */ 53 private void 54 cie_icc_finalize(void * pvicc_info) 55 { 56 gs_cie_icc * picc_info = (gs_cie_icc *)pvicc_info; 57 58 if (picc_info->plu != NULL) { 59 picc_info->plu->del(picc_info->plu); 60 picc_info->plu = NULL; 61 } 62 if (picc_info->picc != NULL) { 63 picc_info->picc->del(picc_info->picc); 64 picc_info->picc = NULL; 65 } 66 if (picc_info->pfile != NULL) { 67 picc_info->pfile->del(picc_info->pfile); 68 picc_info->pfile = NULL; 69 } 70 } 71 72 private_st_cie_icc(); 73 74 /* 75 * Because the color space structure stores alternative color space in-line, 76 * we must enumerate and relocate pointers in these space explicity. 77 */ 78 gs_private_st_composite( st_color_space_CIEICC, 79 gs_paint_color_space, 80 "gs_color_space_CIEICC", 81 cs_CIEICC_enum_ptrs, 82 cs_CIEICC_reloc_ptrs ); 83 84 /* pointer enumeration routine */ 85 private 86 ENUM_PTRS_WITH(cs_CIEICC_enum_ptrs, gs_color_space * pcs) 87 return ENUM_USING( *pcs->params.icc.alt_space.type->stype, 88 &pcs->params.icc.alt_space, 89 sizeof(pcs->params.separation.alt_space), 90 index - 1 ); 91 92 ENUM_PTR(0, gs_color_space, params.icc.picc_info); 93 ENUM_PTRS_END 94 95 /* pointer relocation routine */ 96 private 97 RELOC_PTRS_WITH(cs_CIEICC_reloc_ptrs, gs_color_space * pcs) 98 RELOC_PTR(gs_color_space, params.icc.picc_info); 99 RELOC_USING( *pcs->params.icc.alt_space.type->stype, 100 &pcs->params.icc.alt_space, 101 sizeof(pcs->params.separation.alt_space) ); 102 RELOC_PTRS_END 103 104 105 /* 106 * Color space methods for ICCBased color spaces. 107 * 108 * As documented, ICCBased color spaces may be used as both base and 109 * alternative color spaces. Futhermore,, they can themselves contain paint 110 * color spaces as alternative color space. In this implementation we allow 111 * them to be used as base and alternative color spaces, but only to contain 112 * "small" base color spaces (CIEBased or smaller). This arrangement avoids 113 * breaking the color space heirarchy. Providing a more correct arrangement 114 * requires a major change in the color space mechanism. 115 * 116 * Several of the methods used by ICCBased color space apply as well to 117 * DeviceN color spaces, in that they are generic to color spaces having 118 * a variable number of components. We have elected not to attempt to 119 * extract and combine these operations, because this would save only a 120 * small amount of code, and much more could be saved by intorducing certain 121 * common elements (ranges, number of components, etc.) into the color space 122 * root class. 123 */ 124 private cs_proc_num_components(gx_num_components_CIEICC); 125 private cs_proc_base_space(gx_alt_space_CIEICC); 126 private cs_proc_init_color(gx_init_CIEICC); 127 private cs_proc_restrict_color(gx_restrict_CIEICC); 128 private cs_proc_concrete_space(gx_concrete_space_CIEICC); 129 private cs_proc_concretize_color(gx_concretize_CIEICC); 130 private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEICC); 131 private cs_proc_serialize(gx_serialize_CIEICC); 132 133 private const gs_color_space_type gs_color_space_type_CIEICC = { 134 gs_color_space_index_CIEICC, /* index */ 135 true, /* can_be_base_space */ 136 true, /* can_be_alt_space */ 137 &st_color_space_CIEICC, /* stype - structure descriptor */ 138 gx_num_components_CIEICC, /* num_components */ 139 gx_alt_space_CIEICC, /* base_space */ 140 gx_init_CIEICC, /* init_color */ 141 gx_restrict_CIEICC, /* restrict_color */ 142 gx_concrete_space_CIEICC, /* concrete_space */ 143 gx_concretize_CIEICC, /* concreteize_color */ 144 NULL, /* remap_concrete_color */ 145 gx_default_remap_color, /* remap_color */ 146 gx_install_CIE, /* install_cpsace */ 147 gx_spot_colors_set_overprint, /* set_overprint */ 148 gx_adjust_cspace_CIEICC, /* adjust_cspace_count */ 149 gx_no_adjust_color_count, /* adjust_color_count */ 150 gx_serialize_CIEICC, /* serialize */ 151 gx_cspace_is_linear_default 152 }; 153 154 155 /* 156 * Return the number of components used by a ICCBased color space - 1, 3, or 4 157 */ 158 private int 159 gx_num_components_CIEICC(const gs_color_space * pcs) 160 { 161 return pcs->params.icc.picc_info->num_components; 162 } 163 164 /* 165 * Return the alternative space for an ICCBasee color space, but only if 166 * that space is being used. 167 */ 168 private const gs_color_space * 169 gx_alt_space_CIEICC(const gs_color_space * pcs) 170 { 171 return (pcs->params.icc.picc_info->picc == NULL) 172 ? (const gs_color_space *)&pcs->params.icc.alt_space 173 : NULL; 174 } 175 176 /* 177 * Set the initial client color for an ICCBased color space. The convention 178 * suggested by the ICC specification is to set all components to 0. 179 */ 180 private void 181 gx_init_CIEICC(gs_client_color * pcc, const gs_color_space * pcs) 182 { 183 int i, ncomps = pcs->params.icc.picc_info->num_components; 184 185 for (i = 0; i < ncomps; ++i) 186 pcc->paint.values[i] = 0.0; 187 188 /* make sure that [ 0, ... 0] is in range */ 189 gx_restrict_CIEICC(pcc, pcs); 190 } 191 192 /* 193 * Restrict an color to the range specified for an ICCBased color space. 194 */ 195 private void 196 gx_restrict_CIEICC(gs_client_color * pcc, const gs_color_space * pcs) 197 { 198 int i, ncomps = pcs->params.icc.picc_info->num_components; 199 const gs_range * ranges = pcs->params.icc.picc_info->Range.ranges; 200 201 for (i = 0; i < ncomps; ++i) { 202 floatp v = pcc->paint.values[i]; 203 floatp rmin = ranges[i].rmin, rmax = ranges[i].rmax; 204 205 if (v < rmin) 206 pcc->paint.values[i] = rmin; 207 else if (v > rmax) 208 pcc->paint.values[i] = rmax; 209 } 210 } 211 212 /* 213 * Return the conrecte space to which this color space will map. If the 214 * ICCBased color space is being used in native mode, the concrete space 215 * will be dependent on the current color rendering dictionary, as it is 216 * for all CIE bases. If the alternate color space is being used, then 217 * this question is passed on the the appropriate method of that space. 218 */ 219 private const gs_color_space * 220 gx_concrete_space_CIEICC(const gs_color_space * pcs, const gs_imager_state * pis) 221 { 222 if (pcs->params.icc.picc_info->picc == NULL) { 223 const gs_color_space * pacs = (const gs_color_space *) 224 &pcs->params.icc.alt_space; 225 226 return cs_concrete_space(pacs, pis); 227 } else 228 return gx_concrete_space_CIE(NULL, pis); 229 } 230 231 /* 232 * Convert an ICCBased color space to a concrete color space. 233 */ 234 private int 235 gx_concretize_CIEICC( 236 const gs_client_color * pcc, 237 const gs_color_space * pcs, 238 frac * pconc, 239 const gs_imager_state * pis ) 240 { 241 const gs_icc_params * picc_params = &pcs->params.icc; 242 const gs_cie_icc * picc_info = picc_params->picc_info; 243 stream * instrp = picc_info->instrp; 244 icc * picc = picc_info->picc; 245 double inv[4], outv[3]; 246 cie_cached_vector3 vlmn; 247 gs_client_color lcc = *pcc; 248 int i, ncomps = picc_info->num_components; 249 250 /* use the altenate space concretize if appropriate */ 251 if (picc == NULL) 252 return picc_params->alt_space.type->concretize_color( 253 pcc, 254 (const gs_color_space *)&picc_params->alt_space, 255 pconc, 256 pis ); 257 258 /* set up joint cache as required */ 259 CIE_CHECK_RENDERING(pcs, pconc, pis, return 0); 260 261 /* verify and update the stream pointer */ 262 if (picc_info->file_id != (instrp->read_id | instrp->write_id)) 263 return_error(gs_error_ioerror); 264 ((icmFileGs *)picc->fp)->strp = instrp; 265 266 /* translate the input components */ 267 gx_restrict_CIEICC(&lcc, pcs); 268 for (i = 0; i < ncomps; i++) 269 inv[i] = lcc.paint.values[i]; 270 271 /* For input Lab color space massage the values into Lab range */ 272 273 if (picc_info->plu->e_inSpace == icSigLabData) { 274 inv[0] *= 100; 275 inv[1] = inv[1]*255 - 128; 276 inv[2] = inv[2]*255 - 128; 277 } 278 279 /* 280 * Perform the lookup operation. A return value of 1 indicates that 281 * clipping occurred somewhere in the operation, but the result is 282 * legitimate. Other non-zero return values indicate an error, which 283 * should not occur in practice. 284 */ 285 if (picc_info->plu->lookup(picc_info->plu, outv, inv) > 1) 286 return_error(gs_error_unregistered); 287 288 /* if the output is in the CIE L*a*b* space, convert to XYZ */ 289 if (picc_info->pcs_is_cielab) { 290 floatp f[3]; 291 const gs_vector3 * pwhtpt = &picc_info->common.points.WhitePoint; 292 293 294 f[1] = (outv[0] + 16.0) / 116.0; 295 f[0] = f[1] + outv[1] / 500.0; 296 f[2] = f[1] - outv[2] / 200; 297 298 for (i = 0; i < 3; i++) { 299 if (f[i] >= 6.0 / 29.0) 300 outv[i] = f[i] * f[i] * f[i]; 301 else 302 outv[i] = 108.0 * (f[i] - 4.0 / 29.0) / 841.0; 303 } 304 305 /* 306 * The connection space white-point is known to be D50, but we 307 * use the more general form in case of future revisions. 308 */ 309 outv[0] *= pwhtpt->u; 310 outv[1] *= pwhtpt->v; 311 outv[2] *= pwhtpt->w; 312 } 313 314 /* translate the output */ 315 vlmn.u = float2cie_cached(outv[0]); 316 vlmn.v = float2cie_cached(outv[1]); 317 vlmn.w = float2cie_cached(outv[2]); 318 319 gx_cie_remap_finish(vlmn, pconc, pis, pcs); 320 return 0; 321 } 322 323 /* 324 * Handle a reference or de-reference of the prameter structure of an 325 * ICCBased color space. For the purposes of this routine, the color space 326 * is considered a reference rather than an object, and is not itself 327 * reference counted (an unintuitive but otherwise legitimate state of 328 * affairs). 329 * 330 * Because color spaces store alternative/base color space inline, these 331 * need to have their reference count adjusted explicitly. 332 */ 333 private void 334 gx_adjust_cspace_CIEICC(const gs_color_space * pcs, int delta) 335 { 336 const gs_icc_params * picc_params = &pcs->params.icc; 337 338 rc_adjust_const(picc_params->picc_info, delta, "gx_adjust_cspace_CIEICC"); 339 picc_params->alt_space.type->adjust_cspace_count( 340 (const gs_color_space *)&picc_params->alt_space, delta ); 341 } 342 343 /* 344 * Increment color space reference counts. 345 */ 346 void 347 gx_increment_cspace_count(const gs_color_space * pcs) 348 { 349 pcs->type->adjust_cspace_count(pcs, 1); 350 } 351 352 private int 353 icmFileGs_seek(icmFile *pp, long int offset) 354 { 355 icmFileGs *p = (icmFileGs *)pp; 356 357 return spseek(p->strp, offset); 358 } 359 360 private size_t 361 icmFileGs_read(icmFile *pp, void *buffer, size_t size, size_t count) 362 { 363 icmFileGs *p = (icmFileGs *)pp; 364 uint tot; 365 int status = sgets(p->strp, buffer, size * count, &tot); 366 367 return (status < 0) ? status : tot; 368 } 369 370 private size_t 371 icmFileGs_write(icmFile *pp, void *buffer, size_t size, size_t count) 372 { 373 icmFileGs *p = (icmFileGs *)pp; 374 uint tot; 375 int status = sputs(p->strp, buffer, size * count, &tot); 376 377 return (status < 0) ? status : tot; 378 } 379 380 private int 381 icmFileGs_flush(icmFile *pp) 382 { 383 icmFileGs *p = (icmFileGs *)pp; 384 385 return s_std_write_flush(p->strp); 386 } 387 388 private int 389 icmFileGs_delete(icmFile *pp) 390 { 391 free(pp); 392 return 0; 393 } 394 395 /** 396 * gx_wrap_icc_stream: Wrap a Ghostscript stream as an icclib file. 397 * @strp: The Ghostscript stream. 398 * 399 * Creates an icmFile object that wraps @stream. 400 * 401 * Note: the memory for this object is allocated using malloc, and the 402 * relocation of the stream pointer is done lazily, before an icclu 403 * operation. It would probably be cleaner to allocate the icmFile in 404 * garbage collected memory, and have the relocation happen there, but 405 * I wanted to minimally modify Jan's working code. 406 * 407 * Return value: the stream wrapped as an icmFile object, or NULL on 408 * error. 409 **/ 410 private icmFile * 411 gx_wrap_icc_stream(stream *strp) 412 { 413 icmFileGs *p; 414 415 if ((p = (icmFileGs *) calloc(1,sizeof(icmFileGs))) == NULL) 416 return NULL; 417 p->seek = icmFileGs_seek; 418 p->read = icmFileGs_read; 419 p->write = icmFileGs_write; 420 p->flush = icmFileGs_flush; 421 p->del = icmFileGs_delete; 422 423 p->strp = strp; 424 425 return (icmFile *)p; 426 } 427 428 int 429 gx_load_icc_profile(gs_cie_icc *picc_info) 430 { 431 stream * instrp = picc_info->instrp; 432 icc * picc; 433 icmLuBase * plu = NULL; 434 icmFile *pfile = NULL; 435 436 /* verify that the file is legitimate */ 437 if (picc_info->file_id != (instrp->read_id | instrp->write_id)) 438 return_error(gs_error_ioerror); 439 /* 440 * Load the top-level ICC profile. 441 * 442 * If an ICC profile fails to load, generate an error. 443 * 444 * Testing demonstrates, however, Acrobat Reader silently 445 * ignores the error and uses the alternate color space. 446 * This behaviour is implemented by catching the error using 447 * a stopped context from within the interpreter (gs_icc.ps). 448 * 449 * Failure to allocate the top-level profile object is considered 450 * a limitcheck rather than a VMerror, as profile data structures 451 * are stored in "foreign" memory. 452 */ 453 if ((picc = new_icc()) == NULL) 454 return_error(gs_error_limitcheck); 455 { 456 icProfileClassSignature profile_class; 457 icColorSpaceSignature cspace_type; 458 gs_vector3 * ppt; 459 460 pfile = gx_wrap_icc_stream (instrp); 461 462 if ((picc->read(picc, pfile, 0)) != 0) 463 goto return_rangecheck; 464 465 /* verify the profile type */ 466 profile_class = picc->header->deviceClass; 467 if ( profile_class != icSigInputClass && 468 profile_class != icSigDisplayClass && 469 profile_class != icSigOutputClass && 470 profile_class != icSigColorSpaceClass ) 471 goto return_rangecheck; 472 473 /* verify the profile connection space */ 474 cspace_type = picc->header->pcs; 475 if (cspace_type == icSigLabData) 476 picc_info->pcs_is_cielab = true; 477 else if (cspace_type == icSigXYZData) 478 picc_info->pcs_is_cielab = false; 479 else 480 goto return_rangecheck; 481 482 /* verify the source color space */ 483 cspace_type = picc->header->colorSpace; 484 if (cspace_type == icSigCmykData) { 485 if (picc_info->num_components != 4) 486 goto return_rangecheck; 487 } else if ( cspace_type == icSigRgbData || 488 cspace_type == icSigLabData ) { 489 if (picc_info->num_components != 3) 490 goto return_rangecheck; 491 } else if (cspace_type == icSigGrayData) { 492 if (picc_info->num_components != 1) 493 goto return_rangecheck; 494 } 495 496 /* 497 * Fetch the lookup object. 498 * 499 * PostScript and PDF deal with rendering intent as strictly a 500 * rendering dictionary facility. ICC profiles allow a rendering 501 * intent to be specified for both the input (device ==> pcs) and 502 * output (pcs ==> device) operations. Hence, when using ICCBased 503 * color spaces with PDF, no clue is provided as to which source 504 * mapping to select. 505 * 506 * In the absence of other information, there are two possible 507 * selections. If our understanding is correct, when relative 508 * colorimetry is specified, the icclib code will map source 509 * color values to XYZ or L*a*b* values such that the relationship 510 * of the source color, relative to the source white and black 511 * points, will be the same as the output colors and the 512 * profile connection space illuminant (currently always D50) 513 * and pure black ([0, 0, 0]). In this case, the white and black 514 * points that should be listed in the color space are the 515 * profile connection space illuminant (D50) and pure black. 516 * 517 * If absolute colorimetry is employed, the XYZ or L*a*b* values 518 * generated will be absolute in the chromatic sense (they are 519 * not literally "absolute", as we still must have overall 520 * intensity information inorder to determine weighted spectral 521 * power levels). To achieve relative colorimetry for the output, 522 * these colors must be evaluated relative to the source white 523 * and black points. Hence, in this case, the appropriate white 524 * and black points to list in the color space are the source 525 * white and black points provided in the profile tag array. 526 * 527 * In this implementation, we will always request relative 528 * colorimetry from the icclib, and so will use the profile 529 * connection space illuminant and pure black as the white and 530 * black points of the color space. This approach is somewhat 531 * simpler, as it allows the color space white point to also 532 * be used for L*a*b* to XYZ conversion (otherwise we would 533 * need to store the profile connection space illuminant 534 * separately for that purpose). The approach does reduce to 535 * to some extent the range of mappings that can be achieved 536 * via the color rendering dictionary, but for now we believe 537 * this loss is not significant. 538 * 539 * For reasons that are not clear to us, the icclib code does 540 * not support relative colorimetry for all color profiles. For 541 * this reason, we specify icmDefaultIntent rather than 542 * icRelativeColormetric. 543 * 544 * NB: We are not color experts; our understanding of this area 545 * may well be incorrect. 546 */ 547 plu = picc->get_luobj( picc, 548 icmFwd, 549 icmDefaultIntent, 550 0, /* PCS override */ 551 icmLuOrdNorm ); 552 if (plu == NULL) 553 goto return_rangecheck; 554 555 /* 556 * Get the appropriate white and black points. See the note on 557 * rendering intent above for a discussion of why we are using 558 * the profile space illuminant and pure black. (Pure black need 559 * not be set explicitly, as it is the default.) 560 */ 561 ppt = &picc_info->common.points.WhitePoint; 562 ppt->u = picc->header->illuminant.X; 563 ppt->v = picc->header->illuminant.Y; 564 ppt->w = picc->header->illuminant.Z; 565 566 picc_info->picc = picc; 567 picc_info->plu = plu; 568 picc_info->pfile = pfile; 569 } 570 571 return 0; 572 573 return_rangecheck: 574 if (plu != NULL) 575 plu->del(plu); 576 if (picc != NULL) 577 picc->del(picc); 578 if (pfile != NULL) 579 pfile->del(pfile); 580 return_error(gs_error_rangecheck); 581 } 582 583 /* 584 * Install an ICCBased color space. 585 * 586 * Note that an ICCBased color space must be installed before it is known if 587 * the ICC profile or the alternate color space is to be used. 588 */ 589 private int 590 gx_install_CIEICC(const gs_color_space * pcs, gs_state * pgs) 591 { 592 const gs_icc_params * picc_params = (const gs_icc_params *)&pcs->params.icc; 593 gs_cie_icc * picc_info = picc_params->picc_info; 594 595 /* update the stub information used by the joint caches */ 596 gx_cie_load_common_cache(&picc_info->common, pgs); 597 gx_cie_common_complete(&picc_info->common); 598 return gs_cie_cs_complete(pgs, true); 599 } 600 601 602 /* 603 * Constructor for ICCBased color space. As with the other color space 604 * constructors, this provides only minimal initialization. 605 */ 606 int 607 gs_cspace_build_CIEICC( 608 gs_color_space ** ppcspace, 609 void * client_data, 610 gs_memory_t * pmem ) 611 { 612 gs_cie_icc * picc_info; 613 gs_color_space * pcs; 614 615 /* 616 * The gs_cie_icc_s structure is the only CIE-based color space structure 617 * which accesses additional memory for which it is responsible. We make 618 * use of the finalization procedure to handle this task, so we can use 619 * the generic CIE space build routine (otherwise we would need a 620 * separate build routine that provided its own reference count freeing 621 * procedure). 622 */ 623 picc_info = gx_build_cie_space( ppcspace, 624 &gs_color_space_type_CIEICC, 625 &st_cie_icc, 626 pmem ); 627 628 if (picc_info == NULL) 629 return_error(gs_error_VMerror); 630 631 gx_set_common_cie_defaults(&picc_info->common, client_data); 632 /* 633 * Now set the D50 WhitePoint. The above function does not set any 634 * valid WhitepPoint since PostScript always requires this, but ICC 635 * assumes a D50 WhitePoint as a default 636 */ 637 picc_info->common.points.WhitePoint.u = (float)0.9642; /* Profile illuminant - D50 */ 638 picc_info->common.points.WhitePoint.v = 1.0000; 639 picc_info->common.points.WhitePoint.w = (float)0.8249; 640 picc_info->common.install_cspace = gx_install_CIEICC; 641 picc_info->num_components = 0; 642 picc_info->Range = Range4_default; 643 picc_info->instrp = NULL; 644 picc_info->pcs_is_cielab = false; 645 picc_info->picc = NULL; 646 picc_info->plu = NULL; 647 picc_info->pfile = NULL; 648 649 pcs = *ppcspace; 650 pcs->params.icc.picc_info = picc_info; 651 return 0; 652 } 653 654 /* ---------------- Serialization. -------------------------------- */ 655 656 private int 657 gx_serialize_CIEICC(const gs_color_space * pcs, stream * s) 658 { 659 const gs_icc_params * p = &pcs->params.icc; 660 gs_cie_icc *picc = p->picc_info; 661 uint n; 662 int code = gx_serialize_cspace_type(pcs, s); 663 long avail, pos, count; 664 byte buf[100]; 665 666 if (code < 0) 667 return code; 668 code = gx_serialize_cie_common_elements(pcs, s); 669 if (code < 0) 670 return code; 671 code = sputs(s, (byte *)&picc->num_components, sizeof(picc->num_components), &n); 672 if (code < 0) 673 return code; 674 code = sputs(s, (byte *)&picc->Range, sizeof(picc->Range), &n); 675 if (code < 0) 676 return code; 677 if (sseek(picc->instrp, 0) < 0) 678 return_error(gs_error_unregistered); /* Unimplemented. */ 679 if (savailable(picc->instrp, &avail) != 0) 680 return_error(gs_error_unregistered); /* Unimplemented. */ 681 code = sputs(s, (byte *)&avail, sizeof(avail), &n); 682 if (code < 0) 683 return code; 684 for (pos = 0; pos < avail; pos += count) { 685 count = min(sizeof(buf), avail - pos); 686 code = sgets(picc->instrp, buf, count, &n); 687 if (code < 0) 688 return code; 689 code = sputs(s, buf, count, &n); 690 if (code < 0) 691 return code; 692 } 693 return sputs(s, (byte *)&picc->pcs_is_cielab, sizeof(picc->pcs_is_cielab), &n); 694 } 695