Commit 3ff183f6 authored by Pranav Batra's avatar Pranav Batra Committed by Commit Bot

Reland "Add Linux support for printer resolutions"

This is a reland of 81e8107c.

An extra colon in the PPD string for one of the unit tests that resulted
in the CUPS PPD API using an uninitialized value and being terminated by
MSAN has been removed.

Original change's description:
> Add Linux support for printer resolutions
>
> Add Linux support for PPD resolution options.
>
> Test: ./printing_unittests
> Change-Id: I15d2245567e37938633c72daa2edc66cfa6f4b22
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2360472
> Commit-Queue: Pranav Batra <batrapranav@chromium.org>
> Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#800424}

Change-Id: I0ec422f96e2b8df1a5e6829110f87fde7d7dbe8e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2369371
Commit-Queue: Pranav Batra <batrapranav@chromium.org>
Auto-Submit: Pranav Batra <batrapranav@chromium.org>
Reviewed-by: default avatarDaniel Hosseinian <dhoss@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800705}
parent 1bd55414
......@@ -5,6 +5,7 @@
#include "printing/backend/cups_helper.h"
#include <stddef.h>
#include <stdio.h>
#include <vector>
......@@ -134,6 +135,51 @@ void GetDuplexSettings(ppd_file_t* ppd,
}
}
void GetResolutionSettings(ppd_file_t* ppd,
std::vector<gfx::Size>* dpis,
gfx::Size* default_dpi) {
static constexpr const char* kResolutions[] = {
"Resolution", "JCLResolution", "SetResolution",
"CNRes_PGP", "HPPrintQuality", "LXResolution"};
ppd_option_t* res;
for (const char* res_name : kResolutions) {
res = ppdFindOption(ppd, res_name);
if (res)
break;
}
if (!res)
return;
for (int i = 0; i < res->num_choices; i++) {
char* choice = res->choices[i].choice;
DCHECK(choice);
int len = strlen(choice);
if (len == 0) {
VLOG(1) << "Bad PPD resolution choice: null string";
continue;
}
int n = 0; // number of chars successfully parsed by sscanf()
int dpi_x;
int dpi_y;
sscanf(choice, "%ddpi%n", &dpi_x, &n);
if (n == len) {
dpi_y = dpi_x;
} else {
sscanf(choice, "%dx%ddpi%n", &dpi_x, &dpi_y, &n);
if (n != len) {
VLOG(1) << "Bad PPD resolution choice: " << choice;
continue;
}
}
if (dpi_x <= 0 || dpi_y <= 0) {
VLOG(1) << "Invalid PPD resolution dimensions: " << dpi_x << " " << dpi_y;
continue;
}
dpis->push_back({dpi_x, dpi_y});
if (!strcmp(choice, res->defchoice))
*default_dpi = dpis->back();
}
}
bool GetBasicColorModelSettings(ppd_file_t* ppd,
mojom::ColorModel* color_model_for_black,
mojom::ColorModel* color_model_for_color,
......@@ -550,6 +596,7 @@ bool ParsePpdCapabilities(cups_dest_t* dest,
caps.copies_max = GetCopiesMax(ppd);
GetDuplexSettings(ppd, &caps.duplex_modes, &caps.duplex_default);
GetResolutionSettings(ppd, &caps.dpis, &caps.default_dpi);
mojom::ColorModel cm_black = mojom::ColorModel::kUnknownColorModel;
mojom::ColorModel cm_color = mojom::ColorModel::kUnknownColorModel;
......
......@@ -4,6 +4,7 @@
#include "printing/backend/cups_helper.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "printing/backend/print_backend.h"
#include "printing/mojom/print.mojom.h"
......@@ -33,6 +34,15 @@ void VerifyCapabilityColorModels(const PrinterSemanticCapsAndDefaults& caps) {
EXPECT_FALSE(maybe_color.value());
}
std::string GeneratePpdResolutionTestData(const char* res_name) {
return base::StringPrintf(R"(*PPD-Adobe: 4.3
*OpenUI *%1$s/%1$s: PickOne
*%1$s 600dpi/600 dpi: " "
*Default%1$s: 600dpi
*CloseUI: *%1$s)",
res_name);
}
} // namespace
TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexShortEdge) {
......@@ -498,6 +508,98 @@ TEST(PrintBackendCupsHelperTest, TestPpdParsingCupsMaxCopies) {
}
}
TEST(PrintBackendCupsHelperTest, TestPpdParsingResolutionTagNames) {
constexpr const char* kTestResNames[] = {"Resolution", "JCLResolution",
"SetResolution", "CNRes_PGP",
"HPPrintQuality", "LXResolution"};
const std::vector<gfx::Size> kExpectedResolutions = {gfx::Size(600, 600)};
PrinterSemanticCapsAndDefaults caps;
for (const char* res_name : kTestResNames) {
EXPECT_TRUE(ParsePpdCapabilities(
/*dest=*/nullptr, /*locale=*/"",
GeneratePpdResolutionTestData(res_name).c_str(), &caps));
EXPECT_EQ(kExpectedResolutions, caps.dpis);
EXPECT_EQ(kExpectedResolutions[0], caps.default_dpi);
}
}
TEST(PrintBackendCupsHelperTest,
TestPpdParsingResolutionInvalidDefaultResolution) {
constexpr char kTestPpdData[] =
R"(*PPD-Adobe: "4.3"
*OpenUI *Resolution/Resolution: PickOne
*DefaultResolution: 500dpi
*Resolution 600dpi/600 dpi: ""
*CloseUI: *Resolution)";
PrinterSemanticCapsAndDefaults caps;
EXPECT_TRUE(ParsePpdCapabilities(/*dest=*/nullptr, /*locale=*/"",
kTestPpdData, &caps));
EXPECT_EQ(std::vector<gfx::Size>{gfx::Size(600, 600)}, caps.dpis);
EXPECT_TRUE(caps.default_dpi.IsEmpty());
}
TEST(PrintBackendCupsHelperTest, TestPpdParsingResolutionNoResolution) {
constexpr char kTestPpdData[] =
R"(*PPD-Adobe: "4.3"
*OpenUI *Resolution/Resolution: PickOne
*CloseUI: *Resolution)";
PrinterSemanticCapsAndDefaults caps;
EXPECT_TRUE(ParsePpdCapabilities(/*dest=*/nullptr, /*locale=*/"",
kTestPpdData, &caps));
EXPECT_TRUE(caps.dpis.empty());
EXPECT_TRUE(caps.default_dpi.IsEmpty());
EXPECT_TRUE(ParsePpdCapabilities(/*dest=*/nullptr, /*locale=*/"",
"*PPD-Adobe: \"4.3\"", &caps));
EXPECT_TRUE(caps.dpis.empty());
EXPECT_TRUE(caps.default_dpi.IsEmpty());
}
TEST(PrintBackendCupsHelperTest, TestPpdParsingResolutionNoDefaultResolution) {
constexpr char kTestPpdData[] =
R"(*PPD-Adobe: "4.3"
*OpenUI *Resolution/Resolution: PickOne
*Resolution 600dpi/600 dpi: ""
*CloseUI: *Resolution)";
PrinterSemanticCapsAndDefaults caps;
EXPECT_TRUE(ParsePpdCapabilities(/*dest=*/nullptr, /*locale=*/"",
kTestPpdData, &caps));
EXPECT_EQ(std::vector<gfx::Size>{gfx::Size(600, 600)}, caps.dpis);
EXPECT_TRUE(caps.default_dpi.IsEmpty());
}
TEST(PrintBackendCupsHelperTest, TestPpdParsingResolutionDpiFormat) {
constexpr char kTestPpdData[] =
R"(*PPD-Adobe: "4.3"
*JCLOpenUI *Resolution/Resolution: PickOne
*OrderDependency: 100 JCLSetup *Resolution
*DefaultResolution: 600dpi
*Resolution 500x500dpi/500 dpi: " "
*Resolution 0.5dpi/0.5 dpi: " "
*Resolution 5.0dpi/5 dpi: " "
*Resolution 600dpi/600 dpi: " "
*Resolution 0dpi/0 dpi: " "
*Resolution 1e1dpi/10 dpi: " "
*Resolution -3dpi/-3 dpi: " "
*Resolution -3x300dpi/dpi: " "
*Resolution 300x0dpi/dpi: " "
*Resolution 50/50: " "
*Resolution 50dpis/50 dpis: " "
*Resolution 30x30dpis/30 dpis: " "
*Resolution 2400x600dpi/HQ1200: " "
*JCLCloseUI: *Resolution)";
const std::vector<gfx::Size> kExpectedResolutions = {
gfx::Size(500, 500), gfx::Size(600, 600), gfx::Size(2400, 600)};
PrinterSemanticCapsAndDefaults caps;
EXPECT_TRUE(ParsePpdCapabilities(/*dest=*/nullptr, /*locale=*/"",
kTestPpdData, &caps));
EXPECT_EQ(kExpectedResolutions, caps.dpis);
EXPECT_EQ(kExpectedResolutions[1], caps.default_dpi);
}
TEST(PrintBackendCupsHelperTest, TestPpdSetsDestOptions) {
constexpr char kTestPpdData[] =
R"(*PPD-Adobe: "4.3"
......
......@@ -180,7 +180,7 @@ bool PrintSettingsFromJobSettings(const base::Value& job_settings,
!pages_per_sheet.has_value()) {
return false;
}
#if defined(OS_WIN)
#if defined(OS_WIN) || defined(OS_LINUX)
base::Optional<int> dpi_horizontal =
job_settings.FindIntKey(kSettingDpiHorizontal);
base::Optional<int> dpi_vertical =
......
......@@ -239,6 +239,42 @@ void PrintDialogGtk::UpdateSettings(
gtk_print_settings_set_n_copies(gtk_settings_, settings->copies());
gtk_print_settings_set_collate(gtk_settings_, settings->collate());
if (settings->dpi_horizontal() > 0 && settings->dpi_vertical() > 0) {
gtk_print_settings_set_resolution_xy(
gtk_settings_, settings->dpi_horizontal(), settings->dpi_vertical());
#if defined(USE_CUPS)
std::string dpi = base::NumberToString(settings->dpi_horizontal());
if (settings->dpi_horizontal() != settings->dpi_vertical())
dpi += "x" + base::NumberToString(settings->dpi_vertical());
dpi += "dpi";
// The resolution attribute (case-insensitive) has decent coverage
// in the CUPS PPD API (Resolution, SetResolution, JCLResolution,
// CNRes_PGP). See
// https://chromium.googlesource.com/chromiumos/third_party/cups/+/49a182a4c42d/cups/mark.c#266
// for more information.
//
// Many PPDs use pdftopdf directly to generate the print data and pdftopdf
// uses the CUPS PPD API internally to handle resolution selection.
//
// Many third-party filters such as the Brother print filter that
// do not use the CUPS PPD API are case sensitive and tend to support
// the Resolution PPD attribute. For this reason "cups-Resolution"
// makes the most sense here.
//
// TODO(crbug.com/1119956): Since PrintBackendCUPS parses the PPD file in
// Chromium, it should be possible to store the resolution attribute name
// as well as a map from the gfx::Size resolution to the std::string
// serialized value (in case a non-standard value such as 500x500dpi is
// present) in the PrinterCapsAndDefaults object. This object then needs to
// be passed over here (there are a couple ways this can be done) where it
// can be used to lookup the CUPS PPD resolution name and serialized DPI
// value to use. The main benefit of the approach would be full support
// for the HPPrintQuality and LXResolution PPD attributes which some PPD
// files use.
gtk_print_settings_set(gtk_settings_, "cups-Resolution", dpi.c_str());
#endif
}
#if defined(USE_CUPS)
// Set advanced settings first so they can be overridden by user applied
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment