/* Special version, called with 8 bit grey input to be downsampled to 1bpp
 * output. */
int
tiff_downscale_and_print_page(gx_device_printer *dev, TIFF *tif, int factor,
                              int mfs, int aw, int bpc, int num_comps)
{
    int code = 0;
    byte *data = NULL;
    int size = gdev_mem_bytes_per_scan_line((gx_device *)dev);
    int max_size = max(size, TIFFScanlineSize(tif));
    int row;
    int width  = dev->width/factor;
    int height = dev->height/factor;
    gx_downscaler_t ds;

    code = TIFFCheckpointDirectory(tif);
    if (code < 0)
        return code;

    code = gx_downscaler_init(&ds, (gx_device *)dev, 8, bpc, num_comps,
                              factor, mfs, &fax_adjusted_width, aw);
    if (code < 0)
        return code;

    data = gs_alloc_bytes(dev->memory, max_size, "tiff_print_page(data)");
    if (data == NULL) {
        gx_downscaler_fin(&ds);
        return_error(gs_error_VMerror);
    }

    for (row = 0; row < height && code >= 0; row++) {
        code = gx_downscaler_copy_scan_lines(&ds, row, data, size);
        if (code < 0)
            break;

        code = TIFFWriteScanline(tif, data, row, 0);
        if (code < 0)
            break;
    }

    if (code >= 0)
        code = TIFFWriteDirectory(tif);

    gx_downscaler_fin(&ds);
    gs_free_object(dev->memory, data, "tiff_print_page(data)");

    return code;
}
static int
ds32_print_page(gx_device_printer * pdev, FILE * file)
{
    gx_device_ds32 *const dsdev = (gx_device_ds32 *)pdev;
    int code = 0;
    byte *data = NULL;
    int size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
    int row;
    int factor = dsdev->downscale.downscale_factor;
    int height = pdev->height / factor;
    int bpc = 8;
    int num_comps = 4;
    int trap_w = dsdev->downscale.trap_w;
    int trap_h = dsdev->downscale.trap_h;
    int *trap_order = dsdev->downscale.trap_order;
    gx_downscaler_t ds;

    if (num_comps == 4) {
        if (dsdev->icclink == NULL) {
            code = gx_downscaler_init_trapped(&ds, (gx_device *)pdev, 8, bpc, num_comps,
                factor, 0 /*mfs*/, &fax_adjusted_width, 0 /*aw*/, trap_w, trap_h, trap_order);
        } else {
            code = gx_downscaler_init_trapped_cm(&ds, (gx_device *)pdev, 8, bpc, num_comps,
                factor, 0 /*mfs*/, &fax_adjusted_width, 0 /*aw*/, trap_w, trap_h, trap_order,
                ds32_chunky_post_cm, dsdev->icclink, dsdev->icclink->num_output);
        }
    }
    if (code < 0)
        return code;

    data = gs_alloc_bytes(pdev->memory, size, "ds32_print_page(data)");
    if (data == NULL) {
        gx_downscaler_fin(&ds);
        return_error(gs_error_VMerror);
    }

    for (row = 0; row < height && code >= 0; row++) {
        code = gx_downscaler_getbits(&ds, data, row);
        if (code < 0)
            break;
#ifdef WRITE_PAM32	/* debugging code to write a PAM 32 file. */
        if ((code = debug_write_pam_32(dsdev, data, row, file)) < 0)
            break;
#endif
        /*************************************************************/
        /* User code to handle the row of data (dsdev->width bytes)  */
        /* Set code to a negative value (e.g. gs_error_ioerror) if a */
        /* problem occurs.                                           */
        /* Note that nothing needs to be written to 'file' and this  */
        /* can be set to '-' (stdout) to avoid leaving an empty file */
        /* if (row == 0) can be used to perform any initialization   */
        /*     needed to process the user data                       */
        /*************************************************************/

        if (code < 0)
            break;
    }

    /**************************************************************/
    /* User code to finish writing and perform any cleanup needed */
    /**************************************************************/

    gx_downscaler_fin(&ds);
    gs_free_object(pdev->memory, data, "tiff_print_page(data)");

    return code;
}