From c73e4163c5ff16eae0c575900c3d117ae3403302 Mon Sep 17 00:00:00 2001 From: Darren Banfi <33876110+boingball@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:16:08 +0100 Subject: [PATCH] fix: add engine-aware 360/720 DPI support --- CHANGELOG.md | 2 + docs/MINTPRINT_PREFS.md | 2 + driver/config.c | 6 +- driver/config.h | 2 +- driver/driver_core.c | 13 ++-- src/MintPrintSettings.c | 139 ++++++++++++++++++++++++++++++++------- src/dpi_options.c | 2 +- src/dpi_options.h | 2 +- tests/test_dpi_options.c | 27 ++++++++ 9 files changed, 163 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 590a0fe..85895db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ to this page; replace that summary when preparing the next release. ## Unreleased +- **Engine-aware 360/720 DPI support (driver 41.19, issue #95).** Settings now accepts reported 360 and 720 DPI square modes and keeps generic printer resolutions separate from PWG Raster and Apple Raster capabilities. This prevents a generic high-resolution mode from being offered to a raster decoder that did not advertise it, while preserving the existing marked 300* compatibility override for confirmed printers. Asymmetric modes such as 1440x720 remain intentionally hidden until MintPRINT can represent independent X/Y DPI. + - **Large jobs tolerate slow printer back-pressure (driver 41.18).** The document-body upload now allows up to three minutes without TCP progress, while connection, control-message and final-response timeouts remain short. diff --git a/docs/MINTPRINT_PREFS.md b/docs/MINTPRINT_PREFS.md index 2de0450..c161e3b 100644 --- a/docs/MINTPRINT_PREFS.md +++ b/docs/MINTPRINT_PREFS.md @@ -79,6 +79,8 @@ remain safely one-sided. See `docs/DUPLEX_PRINTING.md`. ## DPI compatibility option +DPI choices are engine-aware. Settings accepts reported square 300/360/600/720 DPI modes, uses `pwg-raster-document-resolution-supported` for PWG Raster when present, and `RS...` values from `urf-supported` for Apple Raster. Other engines use the generic `printer-resolution-supported` list. If a printer omits a format-specific list, MintPRINT falls back to the generic list for compatibility with older printers and cached profiles. Asymmetric resolutions are not shown yet because the current driver stores one capture DPI for both axes. + Query normally fills the **DPI** cycle from the printer's reported `printer-resolution-supported` and PWG Raster resolution attributes. Some printers under-report this list: the Canon TS8300 series advertises only 600 diff --git a/driver/config.c b/driver/config.c index 4dc8ee6..b16e16f 100644 --- a/driver/config.c +++ b/driver/config.c @@ -311,7 +311,11 @@ LONG mp_config_load(struct MPConfig *cfg) if (mp_cfg_starts(g_config_line, "RESOLUTION=")) { value = g_config_line + 11; n = mp_cfg_parse_ulong(value, &ok); - cfg->resolution = (ok && n == 600UL) ? 600 : 300; + if (ok && (n == 300UL || n == 360UL || + n == 600UL || n == 720UL)) + cfg->resolution = (UWORD)n; + else + cfg->resolution = 300; continue; } diff --git a/driver/config.h b/driver/config.h index 9df5c90..5b760e8 100644 --- a/driver/config.h +++ b/driver/config.h @@ -27,7 +27,7 @@ struct MPConfig { UWORD port; char path[MP_CONFIG_PATH_MAX]; BOOL debug; - UWORD resolution; /* capture DPI: 300 or 600, see RESOLUTION= */ + UWORD resolution; /* capture DPI: 300/360/600/720, see RESOLUTION= */ char engine[MP_CONFIG_ENGINE_MAX]; char media[MP_CONFIG_OPTION_MAX]; char source[MP_CONFIG_OPTION_MAX]; diff --git a/driver/driver_core.c b/driver/driver_core.c index 0b9e228..55f53e6 100644 --- a/driver/driver_core.c +++ b/driver/driver_core.c @@ -55,7 +55,7 @@ * MP_DRIVER_REV (the version half) only bumps for something that * warrants a new version number outright, not on every rebuild. */ #define MP_DRIVER_REV 41 -#define MP_DRIVER_SUBREV 18 +#define MP_DRIVER_SUBREV 19 struct ExecBase *SysBase = NULL; struct DosLibrary *DOSBase = NULL; @@ -2374,11 +2374,12 @@ LONG PRT_STDARGS Render(LONG ct, LONG x, LONG y, LONG status, ...) * here. 4096x6144 @ 300dpi are the historical bounds; at * 600dpi they are doubled so common paper sizes (e.g. A4 * at 600dpi is ~4960x7014 dots) aren't clipped. */ - BOOL hires = (g_config.resolution == 600); - PED->ped_MaxXDots = hires ? 8192 : 4096; - PED->ped_MaxYDots = hires ? 12288 : 6144; - PED->ped_XDotsInch = hires ? 600 : 300; - PED->ped_YDotsInch = hires ? 600 : 300; + ULONG dpi = g_config.resolution ? + (ULONG)g_config.resolution : 300UL; + PED->ped_MaxXDots = (4096UL * dpi + 150UL) / 300UL; + PED->ped_MaxYDots = (6144UL * dpi + 150UL) / 300UL; + PED->ped_XDotsInch = (UWORD)dpi; + PED->ped_YDotsInch = (UWORD)dpi; PED->ped_NumRows = 1; } mp_log_3("Render pre-master special/maxX/maxY", x, diff --git a/src/MintPrintSettings.c b/src/MintPrintSettings.c index 64a5be5..7591079 100644 --- a/src/MintPrintSettings.c +++ b/src/MintPrintSettings.c @@ -289,6 +289,10 @@ int num_supported_print_modes = 0; #define MP_MAX_DPI_OPTIONS MP_DPI_MAX_OPTIONS int supported_dpi[MP_MAX_DPI_OPTIONS]; int num_supported_dpi = 0; +int supported_pwg_dpi[MP_MAX_DPI_OPTIONS]; +int num_supported_pwg_dpi = 0; +int supported_urf_dpi[MP_MAX_DPI_OPTIONS]; +int num_supported_urf_dpi = 0; static struct MPDpiOptions mp_dpi_options = { { 300, 0 }, { 0, 0 }, 1, 0, 300 }; @@ -402,34 +406,42 @@ void store_int_value(int dest[MAX_VALUES], int *count, int val) { dest[(*count)++] = val; } -static void mp_add_supported_dpi(int dpi) { +static BOOL mp_dpi_value_supported(int dpi) { + return dpi == 300 || dpi == 360 || dpi == 600 || dpi == 720; +} + +static void mp_add_supported_dpi_to(int *dest, int *count, int dpi) { int i; - if (dpi != 300 && dpi != 600) return; - for (i = 0; i < num_supported_dpi; ++i) { - if (supported_dpi[i] == dpi) return; + if (!dest || !count || !mp_dpi_value_supported(dpi)) return; + for (i = 0; i < *count; ++i) { + if (dest[i] == dpi) return; } - if (num_supported_dpi < MP_MAX_DPI_OPTIONS) - supported_dpi[num_supported_dpi++] = dpi; + if (*count < MP_MAX_DPI_OPTIONS) + dest[(*count)++] = dpi; } static int mp_normalise_ipp_dpi(ULONG resolution, UBYTE units) { ULONG dpi = resolution; if (units == 4) { - /* IPP dpcm -> dpi. Tolerances below absorb the integer rounding - * used by printers for 118dpcm/236dpcm (roughly 300/600dpi). */ + /* IPP dpcm -> dpi. Real printers commonly round the dpcm + * figure, so keep a small tolerance around known modes. */ dpi = (resolution * 254UL + 50UL) / 100UL; } else if (units != 3) { return 0; } if (dpi >= 295UL && dpi <= 305UL) return 300; + if (dpi >= 355UL && dpi <= 365UL) return 360; if (dpi >= 595UL && dpi <= 605UL) return 600; + if (dpi >= 715UL && dpi <= 725UL) return 720; return 0; } -static void mp_add_ipp_resolution(const UBYTE *raw, int value_len) { +static void mp_add_ipp_resolution_to(const UBYTE *raw, int value_len, + int *dest, int *count, + const char *scope) { ULONG xres; ULONG yres; int xdpi; @@ -444,11 +456,38 @@ static void mp_add_ipp_resolution(const UBYTE *raw, int value_len) { xdpi = mp_normalise_ipp_dpi(xres, raw[8]); ydpi = mp_normalise_ipp_dpi(yres, raw[8]); - /* The driver currently has one DPI setting for both axes, so do not - * advertise asymmetric printer resolutions it cannot represent. */ + /* MintPRINT currently stores one capture DPI for both axes. + * Ignore asymmetric modes (for example Epson 1440x720) until + * the config and raster writers can represent X/Y separately. */ if (xdpi && xdpi == ydpi) { - mp_add_supported_dpi(xdpi); - printf("Printer supports %d DPI\n", xdpi); + mp_add_supported_dpi_to(dest, count, xdpi); + printf("%s supports %d DPI\n", scope ? scope : "Printer", xdpi); + } +} + +static void mp_add_urf_resolutions(const char *value) { + const char *p; + + if (!value || value[0] != 'R' || value[1] != 'S') return; + p = value + 2; + while (*p) { + ULONG dpi = 0; + BOOL any = FALSE; + + while (*p >= '0' && *p <= '9') { + dpi = dpi * 10UL + (ULONG)(*p - '0'); + any = TRUE; + ++p; + } + if (any && dpi <= 32767UL && + mp_dpi_value_supported((int)dpi)) { + mp_add_supported_dpi_to(supported_urf_dpi, + &num_supported_urf_dpi, + (int)dpi); + printf("Apple Raster supports %lu DPI\n", dpi); + } + if (*p != '-') break; + ++p; } } @@ -1676,7 +1715,9 @@ static void apply_saved_option_state(struct Window *win) { TAG_DONE); } - if (num_supported_dpi == 0) { + if (num_supported_dpi == 0 && + num_supported_pwg_dpi == 0 && + num_supported_urf_dpi == 0) { g = find_gadget_by_id(GAD_RESOLUTION); if (g) GT_SetGadgetAttrs(g, win, NULL, @@ -3085,11 +3126,28 @@ void update_print_mode_dropdown(struct Window *win) { void update_dpi_dropdown(struct Window *win) { struct Gadget *g; + const int *reported = supported_dpi; + int reported_count = num_supported_dpi; int i; int count; BOOL has_compat = FALSE; - mp_dpi_build_options(supported_dpi, num_supported_dpi, + /* Generic printer-resolution-supported is not automatically a + * promise that a particular raster decoder accepts every mode. + * Prefer format-specific capabilities when the printer supplied + * them; fall back to the generic list for older/less-complete + * printers and legacy capability caches. */ + if (strcmp(driver_engine_buffer, "pwg-raster") == 0 && + num_supported_pwg_dpi > 0) { + reported = supported_pwg_dpi; + reported_count = num_supported_pwg_dpi; + } else if (strcmp(driver_engine_buffer, "urf") == 0 && + num_supported_urf_dpi > 0) { + reported = supported_urf_dpi; + reported_count = num_supported_urf_dpi; + } + + mp_dpi_build_options(reported, reported_count, mp_dpi_engine_allows_compat(driver_engine_buffer), driver_resolution, driver_resolution_explicit ? 1 : 0, @@ -3122,7 +3180,7 @@ void update_dpi_dropdown(struct Window *win) { GT_SetGadgetAttrs(g, win, NULL, GTCY_Labels, (ULONG)resolution_labels, GTCY_Active, (ULONG)mp_dpi_options.active, - GA_Disabled, num_supported_dpi > 0 ? FALSE : TRUE, + GA_Disabled, reported_count > 0 ? FALSE : TRUE, TAG_DONE); RefreshGList(g, win, NULL, 1); GT_RefreshWindow(win, NULL); @@ -3470,6 +3528,8 @@ static void mp_cache_clear_capabilities(void) { num_supported_print_modes = 0; num_supported_quality = 0; num_supported_dpi = 0; + num_supported_pwg_dpi = 0; + num_supported_urf_dpi = 0; num_media_tray_mappings = 0; has_media_ready = FALSE; jpeg_constraints_queried = FALSE; @@ -3594,6 +3654,16 @@ static BOOL mp_cache_write_file(CONST_STRPTR filename, FPuts(fh, line); } + for (i = 0; i < num_supported_pwg_dpi; ++i) { + snprintf(line, sizeof(line), "PWG_DPI=%d\n", supported_pwg_dpi[i]); + FPuts(fh, line); + } + + for (i = 0; i < num_supported_urf_dpi; ++i) { + snprintf(line, sizeof(line), "URF_DPI=%d\n", supported_urf_dpi[i]); + FPuts(fh, line); + } + Close(fh); return TRUE; } @@ -3766,8 +3836,18 @@ static BOOL mp_cache_load_file(CONST_STRPTR filename) { mp_cap_cache_line + 8); num_supported_quality++; } + } else if (strncmp(mp_cap_cache_line, "PWG_DPI=", 8) == 0) { + mp_add_supported_dpi_to(supported_pwg_dpi, + &num_supported_pwg_dpi, + atoi(mp_cap_cache_line + 8)); + } else if (strncmp(mp_cap_cache_line, "URF_DPI=", 8) == 0) { + mp_add_supported_dpi_to(supported_urf_dpi, + &num_supported_urf_dpi, + atoi(mp_cap_cache_line + 8)); } else if (strncmp(mp_cap_cache_line, "DPI=", 4) == 0) { - mp_add_supported_dpi(atoi(mp_cap_cache_line + 4)); + mp_add_supported_dpi_to(supported_dpi, + &num_supported_dpi, + atoi(mp_cap_cache_line + 4)); } } @@ -8019,6 +8099,8 @@ int query_printer_attributes(const char *ip, int port, char *response, int maxle num_supported_print_modes = 0; num_supported_quality = 0; num_supported_dpi = 0; + num_supported_pwg_dpi = 0; + num_supported_urf_dpi = 0; num_media_tray_mappings = 0; has_media_ready = FALSE; jpeg_constraints_queried = TRUE; @@ -8114,6 +8196,7 @@ int query_printer_attributes(const char *ip, int port, char *response, int maxle "print-scaling-supported", "print-quality-supported", "printer-resolution-default", "printer-resolution-supported", "pwg-raster-document-resolution-supported", + "urf-supported", "pwg-raster-document-sheet-back", "document-format-supported", "printer-make-and-model", "sides-supported", "operations-supported", @@ -8692,11 +8775,23 @@ int query_printer_attributes(const char *ip, int port, char *response, int maxle pwg_sheet_back_value); } } else if ((strcmp(name, "printer-resolution-default") == 0 || - strcmp(name, "printer-resolution-supported") == 0 || - strcmp(name, "pwg-raster-document-resolution-supported") == 0) && - value_tag == 0x32 && value_len == 9) { - mp_add_ipp_resolution((const UBYTE *)ipp_start + pos - value_len, - value_len); + strcmp(name, "printer-resolution-supported") == 0) && + value_tag == 0x32 && value_len == 9) { + mp_add_ipp_resolution_to( + (const UBYTE *)ipp_start + pos - value_len, + value_len, supported_dpi, &num_supported_dpi, + "Printer"); + } else if (strcmp(name, + "pwg-raster-document-resolution-supported") == 0 && + value_tag == 0x32 && value_len == 9) { + mp_add_ipp_resolution_to( + (const UBYTE *)ipp_start + pos - value_len, + value_len, supported_pwg_dpi, + &num_supported_pwg_dpi, "PWG Raster"); + } else if (strcmp(name, "urf-supported") == 0 && + (value_tag == 0x44 || value_tag == 0x41 || + value_tag == 0x42)) { + mp_add_urf_resolutions(value); } else if (strcmp(name, "print-quality-supported") == 0 && value_tag == 0x23 && value_len == 4) { /* print-quality-supported is an IPP enum (RFC 8011 diff --git a/src/dpi_options.c b/src/dpi_options.c index 9d79194..5f062a5 100644 --- a/src/dpi_options.c +++ b/src/dpi_options.c @@ -10,7 +10,7 @@ int mp_dpi_engine_allows_compat(const char *engine) static int mp_dpi_valid(int dpi) { - return dpi == 300 || dpi == 600; + return dpi == 300 || dpi == 360 || dpi == 600 || dpi == 720; } static int mp_dpi_find(const struct MPDpiOptions *options, int dpi) diff --git a/src/dpi_options.h b/src/dpi_options.h index 1579732..95f24fe 100644 --- a/src/dpi_options.h +++ b/src/dpi_options.h @@ -1,7 +1,7 @@ #ifndef MINTPRINT_DPI_OPTIONS_H #define MINTPRINT_DPI_OPTIONS_H -#define MP_DPI_MAX_OPTIONS 2 +#define MP_DPI_MAX_OPTIONS 8 struct MPDpiOptions { int values[MP_DPI_MAX_OPTIONS]; diff --git a/tests/test_dpi_options.c b/tests/test_dpi_options.c index b18bcb9..78b76a3 100644 --- a/tests/test_dpi_options.c +++ b/tests/test_dpi_options.c @@ -8,6 +8,9 @@ int main(void) struct MPDpiOptions options; int only_600[] = { 600 }; int both[] = { 300, 600 }; + int epson_generic[] = { 360, 720 }; + int epson_pwg[] = { 360 }; + int four_modes[] = { 300, 360, 600, 720 }; assert(mp_dpi_engine_allows_compat("pwg-raster")); assert(mp_dpi_engine_allows_compat("urf")); @@ -40,6 +43,30 @@ int main(void) assert(options.values[0] == 300 && options.compatibility[0] == 0); assert(options.selected == 300); + + mp_dpi_build_options(epson_generic, 2, 0, 300, 0, &options); + assert(options.count == 2); + assert(options.values[0] == 360 && options.compatibility[0] == 0); + assert(options.values[1] == 720 && options.compatibility[1] == 0); + assert(options.active == 0 && options.selected == 360); + + mp_dpi_build_options(epson_generic, 2, 0, 720, 1, &options); + assert(options.active == 1 && options.selected == 720); + + mp_dpi_build_options(epson_pwg, 1, 1, 300, 0, &options); + assert(options.count == 2); + assert(options.values[0] == 360 && options.compatibility[0] == 0); + assert(options.values[1] == 300 && options.compatibility[1] == 1); + assert(options.active == 0 && options.selected == 360); + + mp_dpi_build_options(four_modes, 4, 1, 360, 1, &options); + assert(options.count == 4); + assert(options.values[0] == 300); + assert(options.values[1] == 360); + assert(options.values[2] == 600); + assert(options.values[3] == 720); + assert(options.active == 1 && options.selected == 360); + puts("DPI option tests passed"); return 0; }